�?.3章:Surface Shader执行流程详解 理解Surface Shader的执行流程对于优化性能和调试问题至关重要。本章将详细解析Surface Shader在渲染管线中的执行过程,包括前向渲染和延迟渲染的差异,以及各个阶段的具体工作原理�?
🎯 学习目标 通过本章学习,你将掌握:
Surface Shader在渲染管线中的完整执行流�?- 前向渲染和延迟渲染的执行差异 光照计算的时机和方式 多Pass渲染的执行顺�?- 性能优化的关键点 🔄 渲染管线概览 Cocos Creator渲染管线架构 1 2 3 4 5 6 7 8 9 graph TD A[Scene Objects] --> B[Culling] B --> C[Sorting] C --> D[Batching] D --> E[Vertex Processing] E --> F[Rasterization] F --> G[Fragment Processing] G --> H[Output Merger] H --> I[Frame Buffer]
Surface Shader在管线中的位�? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 executeRenderPass (objects: RenderObject[] ) { for (let obj of objects) { this .executeVertexShader (obj); } this .executeFragmentShader (fragment); } this .blendAndOutput (); } }
🏃♂�?顶点处理阶段 顶点着色器执行流程 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 precision highp float ; #include <builtin/inputs/cc-position> #include <builtin/inputs/cc-normal> #include <builtin/inputs/cc-texcoord> vec4 vert() { CCVertInput(input); vec4 localPos = input.position; vec4 worldPos = cc_matWorld * localPos; vec4 viewPos = cc_matView * worldPos; vec4 clipPos = cc_matProj * viewPos; vec3 worldNormal = normalize ((cc_matWorldIT * vec4 (input.normal, 0 )).xyz); CC_TRANSFER_WORLDPOS(worldPos); CC_TRANSFER_WORLDNORMAL(worldNormal); CC_TRANSFER_UV(input.texCoord); return clipPos; } }%
顶点变换详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 vec4 worldPosition = cc_matWorld * input.position; vec3 worldNormal = normalize ((cc_matWorldIT * vec4 (input.normal, 0 )).xyz); vec4 viewPosition = cc_matView * worldPosition; vec3 viewNormal = normalize ((cc_matViewInvTrans * vec4 (worldNormal, 0 )).xyz); vec4 clipPosition = cc_matProj * viewPosition; #if CC_USE_TANGENT vec3 worldTangent = normalize ((cc_matWorld * vec4 (input.tangent.xyz, 0 )).xyz); vec3 worldBitangent = cross (worldNormal, worldTangent) * input.tangent.w; mat3 tbnMatrix = mat3 (worldTangent, worldBitangent, worldNormal); CC_TRANSFER_TBN(tbnMatrix); #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 33 CCProgram surface-fs %{ precision mediump float ; #include <builtin/inputs/cc-worldpos> #include <builtin/inputs/cc-worldnormal> #include <builtin/inputs/cc-uv> vec4 frag() { surf(surface); vec4 finalColor = calculateLighting(surface); } vec2 uv = CC_GET_UV(); vec4 albedoTex = texture (mainTexture, uv); vec3 normalTex = texture (normalMap, uv).rgb * 2.0 - 1.0 ; s.normal = normalize (CC_GET_WORLDNORMAL() + normalTex); s.roughness = roughness; s.metallic = metallic; s.alpha = albedoTex.a * mainColor.a; } }%
光照计算流程 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 vec3 worldPos = CC_GET_WORLDPOS(); vec3 normal = surface.normal; vec3 viewDir = normalize (cc_cameraPos.xyz - worldPos); vec3 finalColor = vec3 (0.0 ); vec3 ambient = calculateAmbientLighting(surface); finalColor += ambient; finalColor += mainLight; #if CC_ENABLE_ADDITIONAL_LIGHTS for (int i = 0 ; i < cc_additionalLightCount; i++) { vec3 additionalLight = calculateAdditionalLight(i, surface, normal, viewDir, worldPos); finalColor += additionalLight; } #endif return vec4 (finalColor, surface.alpha); } vec3 lightDir = normalize (-cc_mainLitDir.xyz); vec3 lightColor = cc_mainLitColor.rgb * cc_mainLitColor.w; vec3 diffuse = surface.albedo * lightColor * NdotL; vec3 halfDir = normalize (lightDir + viewDir); float NdotH = max (dot (normal, halfDir), 0.0 ); float NdotV = max (dot (normal, viewDir), 0.0 ); vec3 specular = calculatePBRSpecular(surface.roughness, surface.metallic, surface.albedo, NdotL, NdotV, NdotH); #if CC_RECEIVE_SHADOW float shadowAtten = CC_SHADOW_ATTEN(); diffuse *= shadowAtten; specular *= shadowAtten; #endif return diffuse + specular; }
🔀 前向渲染 vs 延迟渲染 前向渲染执行流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 graph TD A[Object 1] --> B[Vertex Shader] B --> C[Rasterization] C --> D[Fragment Shader + Lighting] D --> E[Blend to Frame Buffer] F[Object 2] --> G[Vertex Shader] G --> H[Rasterization] H --> I[Fragment Shader + Lighting] I --> J[Blend to Frame Buffer] K[Object N] --> L[Vertex Shader] L --> M[Rasterization] M --> N[Fragment Shader + Lighting] N --> O[Blend to Frame Buffer]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CCProgram forward-fs %{ vec4 frag() { StandardSurface surface; surf(surface); color += calculateLightContribution(surface, lights[i]); } return vec4 (color, surface.alpha); } }%
延迟渲染执行流程 1 2 3 4 5 6 7 8 9 10 11 graph TD A[All Objects] --> B[Geometry Pass] B --> C[G-Buffer] C --> D[Lighting Pass] D --> E[Final Frame Buffer] B1[Vertex Shader] --> B B2[Fragment Shader<br/>Output to G-Buffer] --> B D1[Screen Quad] --> D D2[Lighting Shader<br/>Read from G-Buffer] --> D
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 CCProgram deferred-geometry-fs %{ struct GBufferOutput { vec4 albedoAO; vec4 normalRoughness; vec4 motionMetallic; }; GBufferOutput frag() { StandardSurface surface; surf(surface); GBufferOutput output; output.albedoAO = vec4 (surface.albedo, surface.ao); output.normalRoughness = vec4 (surface.normal * 0.5 + 0.5 , surface.roughness); output.motionMetallic = vec4 (calculateMotionVector(), surface.metallic, 0.0 ); return output; } }% CCProgram deferred-lighting-fs %{ vec4 frag() { vec4 albedoAO = texture (gBufferAlbedoAO, screenUV); vec4 normalRoughness = texture (gBufferNormalRoughness, screenUV); vec4 motionMetallic = texture (gBufferMotionMetallic, screenUV); StandardSurface surface; surface.albedo = albedoAO.rgb; surface.ao = albedoAO.a; surface.normal = normalRoughness.rgb * 2.0 - 1.0 ; surface.roughness = normalRoughness.a; surface.metallic = motionMetallic.b; vec3 worldPos = reconstructWorldPosition(screenUV, depthTexture); vec3 finalColor = calculateAllLighting(surface, worldPos); return vec4 (finalColor, 1.0 ); } }%
🔄 多Pass渲染执行顺序 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 CCEffect %{ techniques: - name: multi-pass-demo passes: - vert: depth-vs:vert frag: depth-fs:frag phase: depth-prepass rasterizerState: cullMode: back depthStencilState: depthTest: true depthWrite: true blendState: targets: - blend: false colorMask: 0 - vert: main-vs:vert frag: main-fs:frag phase: forward rasterizerState: cullMode: back depthStencilState: depthTest: true depthWrite: false depthFunc: equal blendState: targets: - blend: false - vert: effect-vs:vert frag: effect-fs:frag phase: forward-add rasterizerState: cullMode: none depthStencilState: depthTest: true depthWrite: false blendState: targets: - blend: true blendSrc: one blendDst: one }%
Pass间数据传�? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 vec4 data = calculateSomeData(); gl_FragColor = data; } }% CCProgram pass2-fs %{ uniform sampler2D pass1Result; vec4 previousData = texture (pass1Result, uv); vec4 finalResult = processData(previousData); gl_FragColor = finalResult; } }%
�?性能优化关键�? 顶点着色器优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 vec4 pos1 = cc_matWorld * a_position; vec4 pos2 = cc_matView * pos1; vec4 pos3 = cc_matProj * pos2; return pos3; } vec4 vert_fast() { float complexValue = texture (lookupTable, a_position.xy).r; vec4 clipPos = cc_matViewProj * (cc_matWorld * a_position); return clipPos; }
片元着色器优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 vec4 color1 = texture (tex1, uv); vec4 color2 = texture (tex1, uv + offset1); vec4 color3 = texture (tex1, uv + offset2); vec4 color4 = texture (tex1, uv + offset3); return (color1 + color2 + color3 + color4) * 0.25 ; } vec4 result = baseColor * somePrecomputedValue; return result; }
光照计算优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 vec3 optimizedLighting(StandardSurface surface) { float lightDistance = length (lightPos - worldPos); if (lightDistance > lightRange) return vec3 (0.0 ); int lightingLOD = determineLightingLOD(cameraDistance); if (lightingLOD == 0 ) { return calculateSimpleLighting(surface); } else { return calculateFullPBRLighting(surface); } }
📊 性能分析和调�? 渲染统计收集 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class RenderStats { vertexShaderTime : number = 0 ; fragmentShaderTime : number = 0 ; drawCalls : number = 0 ; trianglesRendered : number = 0 ; measureShaderPerformance ( ) { const query = gl.createQuery (); gl.beginQuery (gl.TIME_ELAPSED , query); this .executeRenderPass (); gl.endQuery (gl.TIME_ELAPSED ); gl.getQueryParameter (query, gl.QUERY_RESULT ); } }
瓶颈识别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #define PERFORMANCE_TEST 1 #if PERFORMANCE_TEST == 1 vec4 frag() { return vec4 (1.0 , 0.0 , 0.0 , 1.0 ); #elif PERFORMANCE_TEST == 2 return texture (mainTexture, uv); } #elif PERFORMANCE_TEST == 3 return vec4 (calculateLighting(), 1.0 ); } #else vec4 frag() { return fullRenderingPipeline(); } #endif
💡 最佳实�? 执行流程优化 合理安排Pass顺序 : 深度预Pass �?不透明物体 �?天空�?�?透明物体*批处理优�? : 相同材质的物体合并渲�?3. 状态变更最小化 : 减少渲染状态切换次�?4. early-Z优化 : 利用深度测试减少overdraw 内存访问优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 vec4 badMemoryAccess() { vec4 c1 = texture (tex, uv); vec4 c2 = texture (otherTex, uv); } vec4 goodMemoryAccess() { vec4 c1 = texture (tex, uv); vec4 c3 = texture (tex, uv + offset ); vec4 c2 = texture (otherTex, uv); return c1 + c2 + c3; }
理解Surface Shader的执行流程是优化渲染性能的基础。通过掌握这些知识,你可以更好地设计着色器,避免性能瓶颈,创建高效的渲染效果�? 下一步学习