第4.2章:无光照着色器教程
无光照着色器(Unlit Shader)是最基础的着色器类型,不计算任何光照效果,主要用于显示基础颜色和纹理。本章将详细讲解builtin-unlit着色器的原理和使用方法,以及如何创建自定义的无光照着色器。
🎯 学习目标
通过本章学习,你将掌握:
- builtin-unlit着色器的结构和原理
- 基础颜色和纹理显示的实现方法
- 透明度和混合模式的使�?- 无光照着色器的性能优势
- 无光照着色器的适用场景和最佳实�?
📖 builtin-unlit着色器解析
着色器基础结构
Cocos Creator的builtin-unlit着色器是所有无光照渲染的基础模板�?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| CCEffect %{ techniques: - name: opaque passes: - vert: builtin/internal/sprite-vs:vert frag: builtin/internal/sprite-fs:frag properties: mainTexture: { value: grey } mainColor: { value: [1, 1, 1, 1], editor: { type: color } } colorScaleAndCutoff: { value: [1, 1, 1, 0.5] } embeddedMacros: CC_USE_EMBEDDED_ALPHA: false }%
|
顶点着色器分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| CCProgram sprite-vs %{ precision highp float; #include <builtin/uniforms/cc-global> #include <builtin/uniforms/cc-local> in vec3 a_position; in vec2 a_texCoord; in vec4 a_color; out vec2 v_uv; out vec4 v_color; vec4 vert () { vec4 pos = vec4(a_position, 1); v_color = a_color; return pos; } }%
|
片元着色器分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| CCProgram sprite-fs %{ precision mediump float; #include <builtin/uniforms/cc-global> in vec2 v_uv; in vec4 v_color; uniform sampler2D mainTexture; uniform Constant { vec4 mainColor; vec4 colorScaleAndCutoff; }; vec4 frag () { vec4 texColor = texture(mainTexture, v_uv); vec4 color = texColor * mainColor * v_color; if (color.a < colorScaleAndCutoff.w) { discard; } return color; } }%
|
🎨 基础颜色和纹理显�?
纯色显示
创建一个只显示颜色不使用纹理的无光照着色器�?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| CCEffect %{ techniques: - name: solid-color passes: - vert: vs:vert frag: fs:frag properties: mainColor: { value: [1, 0, 0, 1], editor: { type: color } } }%
CCProgram vs %{ precision highp float; in vec3 a_position; vec4 vert () { vec4 pos = vec4(a_position, 1); return cc_matViewProj * cc_matWorld * pos; } }%
CCProgram fs %{ precision mediump float; uniform Constant { vec4 mainColor; }; vec4 frag () { return mainColor; } }%
|
纹理采样和UV操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| CCProgram texture-fs %{ precision mediump float; in vec2 v_uv; uniform sampler2D mainTexture; uniform TextureParams { vec4 mainColor; vec2 tilingOffset; float rotation; }; vec2 transformUV(vec2 uv) { float cosA = cos(rotation); float sinA = sin(rotation); mat2 rotMat = mat2(cosA, -sinA, sinA, cosA); uv = (uv - 0.5) * rotMat + 0.5; return uv; } vec4 frag () { vec2 uv = transformUV(v_uv); vec4 texColor = texture(mainTexture, uv); return texColor * mainColor; } }%
|
多纹理混�?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| CCProgram dual-texture-fs %{ precision mediump float; in vec2 v_uv; uniform sampler2D texture1; uniform sampler2D texture2; uniform BlendParams { vec4 color1; vec4 color2; float blendFactor; int blendMode; }; vec4 frag () { vec4 tex1 = texture(texture1, v_uv) * color1; vec4 tex2 = texture(texture2, v_uv) * color2; vec4 result; if (blendMode == 0) { result = mix(tex1, tex1 * tex2, blendFactor); } else if (blendMode == 1) { result = mix(tex1, tex1 + tex2, blendFactor); } else { result = mix(tex1, tex2, blendFactor); } return result; } }%
|
🌟 透明度和混合模式
Alpha透明度控�?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| CCEffect %{ techniques: - name: transparent passes: - vert: vs:vert frag: fs:frag blendState: targets: - blend: true blendSrc: src_alpha blendDst: one_minus_src_alpha blendDstAlpha: one_minus_src_alpha rasterizerState: cullMode: none depthStencilState: depthTest: true depthWrite: false properties: mainTexture: { value: white } mainColor: { value: [1, 1, 1, 0.5], editor: { type: color } } alphaThreshold: { value: 0.1, editor: { range: [0, 1] } } }%
|
动态透明度效�?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| precision mediump float; #include <builtin/uniforms/cc-global> in vec2 v_uv; uniform sampler2D mainTexture; uniform AnimationParams { vec4 mainColor; float fadeSpeed; float fadeMin; float fadeMax; }; vec4 frag () { vec4 texColor = texture(mainTexture, v_uv); float alpha = mix(fadeMin, fadeMax, timeAlpha); vec4 color = texColor * mainColor; color.a *= alpha; return color; } }%
|
Alpha Test vs Alpha Blend
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| precision mediump float; in vec2 v_uv; uniform sampler2D mainTexture; uniform TestParams { vec4 mainColor; float alphaThreshold; }; vec4 frag () { vec4 color = texture(mainTexture, v_uv) * mainColor; if (color.a < alphaThreshold) { discard; } return color; } }%
precision mediump float; in vec2 v_uv; uniform sampler2D mainTexture; uniform BlendParams { vec4 mainColor; float opacity; }; vec4 frag () { vec4 color = texture(mainTexture, v_uv) * mainColor; color.a *= opacity; return color; } }%
|
�?无光照着色器的性能优势
渲染性能对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @ccclass('UnlitPerformanceTest') export class UnlitPerformanceTest extends Component { @property(MeshRenderer) meshRenderer: MeshRenderer = null!; @property([Material]) testMaterials: Material[] = []; private currentMaterial: number = 0; private frameCount: number = 0; private fpsSum: number = 0; start() { this.schedule(this.switchMaterial, 3.0); this.schedule(this.logPerformance, 1.0); } update() { this.frameCount++; this.fpsSum += 1.0 / director.getDeltaTime(); } private switchMaterial() { if (this.testMaterials.length > 0) { this.meshRenderer.setMaterial(this.testMaterials[this.currentMaterial], 0); console.log(`Switched to material: ${this.currentMaterial}`); this.currentMaterial = (this.currentMaterial + 1) % this.testMaterials.length; this.frameCount = 0; this.fpsSum = 0; } } private logPerformance() { if (this.frameCount > 0) { const avgFPS = this.fpsSum / this.frameCount; console.log(`Average FPS: ${avgFPS.toFixed(2)}`); } } }
|
移动端优�?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| CCProgram mobile-optimized-fs %{ precision mediump float; in vec2 v_uv; uniform sampler2D mainTexture; uniform Constants { vec4 mainColor; }; vec4 frag () { } }%
|
🎯 适用场景和最佳实�?
UI元素渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| CCEffect %{ techniques: - name: ui-unlit passes: - vert: ui-vs:vert frag: ui-fs:frag rasterizerState: cullMode: none blendState: targets: - blend: true blendSrc: src_alpha blendDst: one_minus_src_alpha depthStencilState: depthTest: false depthWrite: false properties: mainTexture: { value: white } mainColor: { value: [1, 1, 1, 1], editor: { type: color } } }%
|
特效和粒子系�?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| CCProgram effect-fs %{ precision mediump float; #include <builtin/uniforms/cc-global> in vec2 v_uv; in vec4 v_color; uniform sampler2D mainTexture; uniform EffectParams { vec4 tintColor; float intensity; float time; }; vec4 frag () { vec4 texColor = texture(mainTexture, v_uv); vec4 color = texColor * v_color * tintColor; color.rgb *= intensity; color.rgb *= flicker; return color; } }%
|
天空盒渲�?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| CCProgram skybox-fs %{ precision mediump float; in vec3 v_worldPos; uniform samplerCube skyboxTexture; uniform SkyboxParams { vec4 tintColor; float exposure; float rotation; }; vec4 frag () { vec3 dir = normalize(v_worldPos); float cosA = cos(rotation); float sinA = sin(rotation); mat3 rotMat = mat3( cosA, 0, sinA, 0, 1, 0, -sinA, 0, cosA ); dir = rotMat * dir; return skyColor; } }%
|
🛠�?实践项目
项目1:渐变背景着色器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| CCProgram gradient-fs %{ precision mediump float; in vec2 v_uv; uniform GradientParams { vec4 topColor; vec4 bottomColor; float gradientPower; float offset; }; vec4 frag () { float gradientFactor = pow(v_uv.y + offset, gradientPower); gradientFactor = clamp(gradientFactor, 0.0, 1.0); return color; } }%
|
项目2:滚动纹理着色器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| CCProgram scrolling-fs %{ precision mediump float; #include <builtin/uniforms/cc-global> in vec2 v_uv; uniform sampler2D mainTexture; uniform ScrollParams { vec4 mainColor; vec2 scrollSpeed; vec2 tiling; }; vec4 frag () { vec2 offset = cc_time.x * scrollSpeed; vec4 texColor = texture(mainTexture, scrollUV); return texColor * mainColor; } }%
|
项目3:多层视差背�?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| CCProgram parallax-fs %{ precision mediump float; #include <builtin/uniforms/cc-global> in vec2 v_uv; uniform sampler2D layer1; uniform sampler2D layer2; uniform sampler2D layer3; uniform ParallaxParams { vec4 layer1Color; vec4 layer2Color; vec4 layer3Color; vec3 scrollSpeeds; }; vec4 frag () { vec2 uv1 = v_uv + vec2(cc_time.x * scrollSpeeds.x, 0.0); vec2 uv2 = v_uv + vec2(cc_time.x * scrollSpeeds.y, 0.0); vec2 uv3 = v_uv + vec2(cc_time.x * scrollSpeeds.z, 0.0); vec4 color1 = texture(layer1, uv1) * layer1Color; vec4 color2 = texture(layer2, uv2) * layer2Color; vec4 color3 = texture(layer3, uv3) * layer3Color; vec4 result = color1; result = mix(result, color2, color2.a); result = mix(result, color3, color3.a); return result; } }%
|
💡 优化建议
性能优化
- 减少纹理采样次数: 合并多个纹理到一张图集中
- *使用适当的精�?: 在移动端使用mediump而不是highp
- 避免分支语句: 在片元着色器中尽量避免if语句
- 合理使用Alpha Test: 只在必要时使用,因为会产生分�?
内存优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @ccclass('TextureManager') export class TextureManager extends Component { private static _instance: TextureManager; private _textureCache: Map<string, Texture2D> = new Map(); static getInstance(): TextureManager { if (!this._instance) { this._instance = new TextureManager(); } return this._instance; } getTexture(path: string): Texture2D | null { if (this._textureCache.has(path)) { return this._textureCache.get(path)!; } if (!err && texture) { this._textureCache.set(path, texture); } }); return null; } releaseTexture(path: string) { if (this._textureCache.has(path)) { this._textureCache.delete(path); } } }
|
📚 总结
无光照着色器是着色器编程的基础,具有以下特点:
优势
- 性能优越: 不需要光照计算,渲染速度�?- *简单易�?: 代码结构简单,易于学习和修�?- 兼容性好: 在各种设备上都有良好的性能表现
- *用途广�?: 适用于UI、特效、背景等多种场景
适用场景
- UI界面渲染
- 特效和粒子系�?- 天空盒和背景
- 简单的2D游戏
- 性能要求较高的场�?
掌握无光照着色器是学习更复杂着色器的重要基础,建议在实际项目中多加练习和应用�?
*下一步学�?