第5.2章:Surface Shader结构详解 Surface Shader是Cocos Creator中最重要的着色器类型,它提供了高层次的抽象来简化复杂的光照计算。本章将深入解析Surface Shader的完整结构,帮助你理解其工作原理和最佳实践。
🎯 学习目标 通过本章学习,你将掌握:
Surface Shader的完整结构组�?- 顶点阶段和片元阶段的分工和联�?- 输入输出数据流的详细解析 内置函数和变量的使用方法 Surface Shader的编译机�? 📖 Surface Shader基本结构 完整的Surface Shader模板 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 CCEffect %{ techniques: - name: opaque passes: - vert: standard-vs:vert frag: standard-fs:frag properties: mainColor: { value: [1 , 1 , 1 , 1 ], editor: { type: color } } roughness: { value: 0.8 , editor: { range: [0 , 1 ] } } metallic: { value: 0.0 , editor: { range: [0 , 1 ] } } normalMap: { value: normal } normalScale: { value: 1.0 , editor: { range: [0 , 2 ] } } embeddedMacros: CC_USE_LIGHTMAP: false CC_RECEIVE_SHADOW: true migrations: macros: CC_USE_LIGHTMAP: { from: 2.0 .0 , default: false } CC_RECEIVE_SHADOW: { from: 2.0 .0 , default: true } }% CCProgram standard-vs %{ precision highp float; // 包含标准的uniform缓冲�? // 顶点输入属�? // 顶点输出到片元着色器的数�? // 顶点着色器主函�? vec4 vert () { // 标准的顶点变�? StandardVertInput In; CCVertInput(In); // 顶点位置变换 vec4 pos = In.position; pos = cc_matWorld * pos; CC_TRANSFER_WORLDPOS(pos); // 法线变换 CCTransferWorldNormalAndTangent(In.normal , In.tangent); // UV坐标传�? CC_TRANSFER_UV(In.texCoord); return cc_matViewProj * pos; } }% CCProgram standard-fs %{ precision mediump float; // 包含光照和材质相关的头文�? // 片元输入数据 // 材质属性uniform uniform Properties { sampler2D mainTexture; sampler2D normalMap; vec4 mainColor; float roughness; float metallic; float normalScale; }; // Surface函数 - 定义材质属�? void surf (out StandardSurface s) { // 基础颜色采样 vec4 baseColor = texture(mainTexture , CC_GET_UV()) * mainColor; // 法线贴图处理 vec3 normal = texture(normalMap , CC_GET_UV()).rgb; normal = normal * 2.0 - 1.0 ; normal = normalize(CC_GET_WORLDNORMAL() + normal * normalScale); // 设置Surface属�? s.albedo = baseColor.rgb; s.normal = normal; s.roughness = roughness; s.metallic = metallic; s.alpha = baseColor.a; } // 片元着色器主函�? vec4 frag () { StandardSurface s; surf(s); // 应用标准光照模型 vec4 color = CC_STANDARD_SURFACE_ENTRY(s); return color; } }%
🔧 结构组件详解 1. CCEffect配置�? 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 CCEffect %{ - name: opaque - vert: vs:vert frag: fs:frag cullMode: back depthStencilState: depthTest: true depthWrite: true blendState: targets: - blend: false mainTexture: { value: grey , editor: { displayName: "Main Texture" } } mainColor: { value: [1 ,1 ,1 ,1 ], editor: { type: color , displayName: "Main Color" } } embeddedMacros: CC_USE_LIGHTMAP: false CC_RECEIVE_SHADOW: true migrations: properties: mainColor: { from: 1.0 .0 , to: mainTint } }%
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 CCProgram vertex-shader %{ precision highp float ; #include <builtin/uniforms/cc-global> // 全局uniform #include <builtin/uniforms/cc-local> // 本地变换矩阵 #include <builtin/uniforms/cc-shadow> // 阴影相关 #include <builtin/inputs/cc-position> // 位置输入 #include <builtin/inputs/cc-normal> // 法线输入 #include <builtin/inputs/cc-texcoord> // UV坐标输入 #include <builtin/inputs/cc-tangent> // 切线输入 #include <builtin/inputs/cc-color> // 顶点颜色输入 #include <builtin/outputs/cc-worldpos> // 世界位置输出 #include <builtin/outputs/cc-worldnormal> // 世界法线输出 #include <builtin/outputs/cc-uv> // UV坐标输出 #include <builtin/outputs/cc-fog> // 雾效输出 out vec3 v_viewPos; out mat3 v_tbn; vec4 vert () { CCVertInput(In); vec4 worldPos = cc_matWorld * In.position; CC_TRANSFER_WORLDPOS(worldPos); CC_TRANSFER_UV(In.texCoord); vec3 worldNormal = normalize ((cc_matWorldIT * vec4 (In.normal, 0.0 )).xyz); vec3 worldTangent = normalize ((cc_matWorld * vec4 (In.tangent.xyz, 0.0 )).xyz); vec3 worldBitangent = cross (worldNormal, worldTangent) * In.tangent.w; v_tbn = mat3 (worldTangent, worldBitangent, worldNormal); return cc_matViewProj * worldPos; } }%
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 CCProgram fragment-shader %{ precision mediump float ; #include <builtin/uniforms/cc-global> #include <builtin/uniforms/cc-shadow> #include <legacy/surface-functions> #include <legacy/lighting-models> #include <builtin/inputs/cc-worldpos> #include <builtin/inputs/cc-worldnormal> #include <builtin/inputs/cc-uv> in mat3 v_tbn; uniform Properties { sampler2D mainTexture; sampler2D normalMap; sampler2D aoMap; sampler2D roughnessMap; sampler2D metallicMap; vec4 mainColor; float roughness; float metallic; float normalScale; float aoStrength; }; void surf (out StandardSurface s) { vec4 baseColor = texture (mainTexture, CC_GET_UV()) * mainColor; vec3 normalTex = texture (normalMap, CC_GET_UV()).rgb * 2.0 - 1.0 ; vec3 worldNormal = normalize (v_tbn * normalTex * vec3 (normalScale, normalScale, 1.0 )); float roughnessValue = texture (roughnessMap, CC_GET_UV()).r * roughness; float ao = texture (aoMap, CC_GET_UV()).r; ao = mix (1.0 , ao, aoStrength); s.albedo = baseColor.rgb; s.normal = worldNormal; s.roughness = roughnessValue; s.metallic = metallicValue; s.ao = ao; s.alpha = baseColor.a; } vec4 frag () { StandardSurface s; surf(s); vec4 color = CC_STANDARD_SURFACE_ENTRY(s); return color; } }%
🔄 数据流分�? 顶点阶段数据�? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 graph TD A[顶点属性输入] --> B[顶点着色器] B --> C[变换计算] C --> D[插值器输出] A1[a_position] --> B A2[a_normal] --> B A3[a_texCoord] --> B A4[a_tangent] --> B B --> C1[位置变换] B --> C2[法线变换] B --> C3[UV处理] C1 --> D1[v_worldPos] C2 --> D2[v_worldNormal] C3 --> D3[v_uv]
片元阶段数据�? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 graph TD A[插值输入] --> B[Surface函数] B --> C[材质属性] C --> D[光照计算] D --> E[最终颜色] A1[v_worldPos] --> B A2[v_worldNormal] --> B A3[v_uv] --> B B --> C1[albedo] B --> C2[normal] B --> C3[roughness] B --> C4[metallic] C1 --> D C2 --> D C3 --> D C4 --> D D --> E[最终渲染结果]
数据传递示�? 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 vec4 vert() { StandardVertInput input; CCVertInput(input); vec4 worldPos = cc_matWorld * input.position; vec3 worldNormal = normalize ((cc_matWorldIT * vec4 (input.normal, 0 )).xyz); CC_TRANSFER_WORLDPOS(worldPos); return cc_matViewProj * worldPos; } vec4 frag() { vec3 worldNormal = CC_GET_WORLDNORMAL(); vec2 uv = CC_GET_UV(); }
🏗�?内置函数和变�? 变换相关函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 CC_TRANSFER_WORLDPOS(vec4 worldPos); vec3 CC_GET_WORLDPOS();CCTransferWorldNormalAndTangent(vec3 normal, vec4 tangent); vec3 CC_GET_WORLDNORMAL();CC_TRANSFER_UV(vec2 uv); vec2 CC_GET_UV();float CC_SHADOW_ATTEN();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 uniform CCGlobal { highp mat4 cc_matView; highp mat4 cc_matViewInv; highp mat4 cc_matProjInv; highp mat4 cc_matViewProjInv; mediump vec4 cc_screenSize; }; uniform CCLocal { highp mat4 cc_matWorld; highp mat4 cc_matWorldIT; }; uniform CCLight { vec4 cc_mainLitDir;
Surface结构定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct StandardSurface { vec3 albedo; vec3 normal; vec3 emissive; float metallic; float ao; float alpha; }; struct CustomSurface { vec3 albedo; vec3 normal; vec3 specular;
⚙️ 编译机制详解 宏展开过程 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 CC_TRANSFER_WORLDPOS(worldPos); cc_worldPos = worldPos.xyz; #endif varying vec3 cc_worldPos; cc_worldPos = worldPos.xyz; ### 条件编译 ```glsl #if CC_USE_LIGHTMAP uniform sampler2D cc_lightMap; vec3 lightmapColor = texture (cc_lightMap, lightmapUV).rgb; s.albedo *= lightmapColor; #endif #if CC_RECEIVE_SHADOW float shadowAtten = CCGetShadowAtten(); s.albedo *= shadowAtten; #endif #if CC_USE_VERTEX_COLOR s.albedo *= v_color.rgb; s.alpha *= v_color.a; #endif
着色器变体生成 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 interface ShaderVariant { name : string ; macros : { [key : string ]: boolean | string | number }; } const variants : ShaderVariant [] = [ { name : "basic" , macros : { CC_USE_LIGHTMAP : false , CC_RECEIVE_SHADOW : false , CC_USE_VERTEX_COLOR : false } }, { name : "with-lightmap" , macros : { CC_USE_LIGHTMAP : true , CC_RECEIVE_SHADOW : true , CC_USE_VERTEX_COLOR : false } }, { name : "with-vertex-color" , macros : { CC_USE_LIGHTMAP : false , CC_RECEIVE_SHADOW : true , CC_USE_VERTEX_COLOR : true } } ];
🔧 高级结构技�? 自定义顶点变�? 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 custom-vertex %{ float time = cc_time.x; position.y += sin (position.x * 2.0 + time) * 0.1 ; position = cc_matWorld * position; #endif return position; } vec4 vert() { StandardVertInput input; CCVertInput(input); CC_TRANSFER_WORLDPOS(pos); return cc_matViewProj * pos; } }%
多Pass渲染结构 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 CCEffect %{ techniques: - name: multi-pass passes: frag: depth-fs:frag rasterizerState: cullMode: back depthStencilState: depthTest: true depthWrite: true blendState: targets: - blend: false colorMask: 0 depthOffset: { value: 0.001 } frag: main-fs:frag rasterizerState: cullMode: back depthStencilState: depthTest: true depthWrite: false properties: &main-props mainTexture: { value: white } mainColor: { value: [1 ,1 ,1 ,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 27 28 CCProgram instanced-vertex %{ #if CC_USE_INSTANCING in vec4 a_matWorld1; in vec4 a_matWorld2; in vec4 a_matWorld3; in vec4 a_instanceColor; return mat4 (a_matWorld0, a_matWorld1, a_matWorld2, a_matWorld3); } #endif vec4 vert() { StandardVertInput input; CCVertInput(input); #if CC_USE_INSTANCING mat4 worldMatrix = getInstanceWorldMatrix(); vec4 worldPos = worldMatrix * input.position; #else vec4 worldPos = cc_matWorld * input.position; #endif CC_TRANSFER_WORLDPOS(worldPos); return cc_matViewProj * worldPos; } }%
💡 最佳实�? 代码组织 *模块化设�? : 将复杂功能拆分为独立的CCProgram�?2. 统一命名 : 使用一致的变量和函数命名规�?3. 注释完整 : 为复杂逻辑添加详细注释 版本控制 : 使用migrations处理版本升级性能优化 精度选择 : 顶点着色器使用highp,片元着色器使用mediump条件编译 : 使用宏开关控制可选功�?3. 变体管理 : 合理控制着色器变体数量资源共享 : 复用常用的着色器代码�?调试技�? 1 2 3 4 5 6 7 if (mode == 0 ) return vec4 (s.albedo, 1.0 ); if (mode == 1 ) return vec4 (s.normal * 0.5 + 0.5 , 1.0 ); if (mode == 2 ) return vec4 (s.roughness, s.roughness, s.roughness, 1.0 ); if (mode == 3 ) return vec4 (s.metallic, s.metallic, s.metallic, 1.0 ); return vec4 (1.0 , 0.0 , 1.0 , 1.0 ); }
理解Surface Shader结构是掌握Cocos Creator着色器编程的关键。通过深入了解其组成和工作原理,你将能够创建更加复杂和高效的着色器效果�? *下一步学�?