第11.1章:Cocos Creator渲染管线概览

理解渲染管线是掌握着色器优化的基础。本教程将详细介绍Cocos Creator的渲染管线架构,帮助你从底层理解图形渲染的工作原理。

🎯 学习目标

通过本教程,你将能够�?

  • 理解Cocos Creator渲染管线的整体架�?- 掌握各个渲染阶段的作用和性能特点
  • 学会分析渲染性能瓶颈
  • 理解着色器在渲染管线中的位置和作用

📋 前置知识

  • 已完成前面章节的学习
  • 了解基本的计算机图形学概�?- 熟悉Cocos Creator的基本使�?

🎨 渲染管线基础架构

渲染管线概述

Cocos Creator采用现代化的渲染管线架构,主要包含以下几个阶段:

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
场景遍历 �?视锥剔除 �?批次合并 �?材质排序 �?Draw Call �?
顶点着色器 �?图元装配 �?光栅�?�?片段着色器 �?深度测试 �?混合 �?帧缓�?```

### CPU端处理流�?
```typescript
// 渲染流程管理�?class RenderFlow {
public render(camera: Camera) {
// 1. 场景遍历
const allNodes = this.traverseSceneGraph();

// 2. 视锥剔除
const visibleNodes = this.frustumCulling(allNodes, camera);

// 3. 距离剔除
const culledNodes = this.distanceCulling(visibleNodes, camera);

// 4. 批次合并
const batches = this.batchMerging(culledNodes);

// 5. 材质排序
const sortedBatches = this.sortByMaterial(batches);

// 6. 提交渲染命令
this.submitRenderCommands(sortedBatches);
}
}

🔍 详细渲染阶段分析

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
// 场景图遍�?class SceneTraversal {
public traverseSceneGraph(): RenderableNode[] {
const renderables: RenderableNode[] = [];

// 深度优先遍历场景�? this.traverseNode(scene.root, renderables);

return renderables;
}

private traverseNode(node: Node, renderables: RenderableNode[]) {
// 检查节点是否可�? if (!node.active) return;

// 检查渲染组�? const renderer = node.getComponent(Renderer);
if (renderer && renderer.enabled) {
renderables.push({
node: node,
renderer: renderer,
worldBounds: this.calculateWorldBounds(node, renderer)
});
}

// 递归遍历子节�? for (const child of node.children) {
this.traverseNode(child, renderables);
}
}
}

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
// 视锥剔除系统
class FrustumCulling {
public cullObjects(objects: RenderableNode[], camera: Camera): RenderableNode[] {
const frustum = this.calculateFrustum(camera);

return objects.filter(obj => {
return this.isInFrustum(obj.worldBounds, frustum);
});
}

private calculateFrustum(camera: Camera): Frustum {
const viewMatrix = camera.viewMatrix;
const projMatrix = camera.projectionMatrix;
const vpMatrix = mat4.multiply(projMatrix, viewMatrix);

return this.extractFrustumPlanes(vpMatrix);
}

private isInFrustum(bounds: AABB, frustum: Frustum): boolean {
// 检查包围盒是否与视锥体相交
for (const plane of frustum.planes) {
if (this.distanceToPlane(bounds, plane) < 0) {
return false; // 完全在平面外�? }
}
return true;
}
}

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
// 批次合并系统
class BatchingSystem {
public createBatches(renderables: RenderableNode[]): RenderBatch[] {
const batches: RenderBatch[] = [];

// 按材质分�? const materialGroups = this.groupByMaterial(renderables);

for (const [material, objects] of materialGroups) {
if (this.canStaticBatch(objects)) {
// 静态批次合�? batches.push(this.createStaticBatch(material, objects));
} else if (this.canDynamicBatch(objects)) {
// 动态批次合�? batches.push(this.createDynamicBatch(material, objects));
} else if (this.canInstance(objects)) {
// GPU实例�? batches.push(this.createInstanceBatch(material, objects));
} else {
// 单独渲染
batches.push(...this.createIndividualBatches(material, objects));
}
}

return batches;
}

private canStaticBatch(objects: RenderableNode[]): boolean {
// 检查是否可以进行静态批次合�? return objects.every(obj =>
!obj.transform.isDynamic &&
obj.mesh.vertexCount < MAX_BATCH_VERTICES
);
}
}

4. GPU渲染阶段

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
// 顶点着色器阶段
CCProgram vertex %{
precision highp float;

// 顶点属�? in vec3 a_position;
in vec3 a_normal;
in vec2 a_texCoord;

// 输出到片段着色器
out vec3 v_worldPos;
out vec3 v_worldNormal;
out vec2 v_uv;

// 变换矩阵
uniform mat4 cc_matWorld;
uniform mat4 cc_matViewProj;
uniform mat3 cc_matWorldIT;

void main() {
// 顶点变换
vec4 worldPos = cc_matWorld * vec4(a_position, 1.0);
v_worldPos = worldPos.xyz;

// 法线变换
v_worldNormal = normalize(cc_matWorldIT * a_normal);

// UV传�? v_uv = a_texCoord;

// 最终投�? gl_Position = cc_matViewProj * worldPos;
}
}%
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 fragment %{
precision highp float;

// 插值输�? in vec3 v_worldPos;
in vec3 v_worldNormal;
in vec2 v_uv;

// 输出颜色
layout(location = 0) out vec4 fragColor;

// 材质参数
uniform sampler2D mainTexture;
uniform vec4 mainColor;

void main() {
// 纹理采样
vec4 baseColor = texture(mainTexture, v_uv);

// 简单光照计�? vec3 normal = normalize(v_worldNormal);
float lighting = max(0.0, dot(normal, vec3(0.0, 1.0, 0.0)));

// 最终颜�? vec3 finalColor = baseColor.rgb * mainColor.rgb * lighting;
fragColor = vec4(finalColor, baseColor.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
// 渲染状态管�?class RenderStateManager {
private currentState: RenderState;

public applyState(newState: RenderState) {
// 深度测试状�? if (this.currentState.depthTest !== newState.depthTest) {
this.device.setDepthTest(newState.depthTest);
}

// 深度写入状�? if (this.currentState.depthWrite !== newState.depthWrite) {
this.device.setDepthWrite(newState.depthWrite);
}

// 混合状�? if (this.currentState.blendMode !== newState.blendMode) {
this.device.setBlendMode(newState.blendMode);
}

// 剔除模式
if (this.currentState.cullMode !== newState.cullMode) {
this.device.setCullMode(newState.cullMode);
}

this.currentState = newState;
}
}

// 渲染状态定�?interface RenderState {
depthTest: boolean;
depthWrite: boolean;
depthFunc: DepthFunc;
blendMode: BlendMode;
cullMode: CullMode;
stencilTest: boolean;
}

材质排序策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 材质排序�?class MaterialSorter {
public sort(batches: RenderBatch[]): RenderBatch[] {
return batches.sort((a, b) => {
// 1. 按渲染队列排�? const queueDiff = a.material.renderQueue - b.material.renderQueue;
if (queueDiff !== 0) return queueDiff;

// 2. 透明物体按距离排序(远到近)
if (a.material.isTransparent) {
return b.distanceToCamera - a.distanceToCamera;
}

// 3. 不透明物体按材质ID排序
const materialDiff = a.material.id - b.material.id;
if (materialDiff !== 0) return materialDiff;

// 4. 按纹理ID排序
return a.material.mainTexture.id - b.material.mainTexture.id;
});
}
}

🔧 性能监控工具

渲染统计系统

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
// 渲染统计收集�?class RenderStats {
private stats = {
frameTime: 0,
drawCalls: 0,
triangles: 0,
vertices: 0,
stateChanges: 0,
textureBinds: 0,
shaderSwitches: 0
};

public recordDrawCall(mesh: Mesh) {
this.stats.drawCalls++;
this.stats.vertices += mesh.vertexCount;
this.stats.triangles += mesh.indexCount / 3;
}

public recordStateChange() {
this.stats.stateChanges++;
}

public recordTextureBinding() {
this.stats.textureBinds++;
}

public getFrameStats() {
const result = { ...this.stats };
this.resetStats();
return result;
}

private resetStats() {
Object.keys(this.stats).forEach(key => {
this.stats[key] = 0;
});
}
}

GPU性能分析

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
// GPU性能分析�?class GPUProfiler {
private timerQueries: Map<string, WebGLQuery> = new Map();

public beginProfile(name: string) {
const gl = this.device.gl;
const query = gl.createQuery();
gl.beginQuery(gl.TIME_ELAPSED_EXT, query);
this.timerQueries.set(name, query);
}

public endProfile(name: string) {
const gl = this.device.gl;
gl.endQuery(gl.TIME_ELAPSED_EXT);
}

public getProfileResult(name: string): number {
const query = this.timerQueries.get(name);
if (!query) return 0;

const gl = this.device.gl;
const available = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);

if (available) {
const time = gl.getQueryParameter(query, gl.QUERY_RESULT);
return time / 1000000; // 转换为毫�? }

return 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
27
28
29
30
31
32
33
34
// 优化前:每个物体独立渲染
class UnoptimizedRenderer {
public render(objects: GameObject[]) {
objects.forEach(obj => {
// 每个物体都需要设置材质和网格
this.setMaterial(obj.material);
this.setMesh(obj.mesh);
this.setTransform(obj.transform);
this.draw(); // 一个Draw Call
});
// 总Draw Call�?= 物体数量
}
}

// 优化后:批次合并渲染
class OptimizedRenderer {
public render(objects: GameObject[]) {
// 创建批次
const batches = this.createBatches(objects);

batches.forEach(batch => {
this.setMaterial(batch.material);

if (batch.isInstanced) {
// GPU实例化渲�? this.setInstanceData(batch.instances);
this.drawInstanced(batch.instanceCount);
} else {
// 静态批次渲�? this.setMesh(batch.combinedMesh);
this.draw();
}
});
// 总Draw Call�?= 批次数量(大幅减少)
}
}

案例2:视锥剔除效�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 剔除效果对比
class CullingComparison {
public measureCullingEffect(scene: Scene, camera: Camera) {
// 剔除�? const allObjects = scene.getAllRenderables();
console.log(`剔除前物体数�? ${allObjects.length}`);

// 执行视锥剔除
const visibleObjects = this.frustumCulling(allObjects, camera);
console.log(`剔除后物体数�? ${visibleObjects.length}`);

// 计算剔除�? const cullingRate = (1 - visibleObjects.length / allObjects.length) * 100;
console.log(`剔除�? ${cullingRate.toFixed(1)}%`);

// 性能提升估算
const drawCallReduction = allObjects.length - visibleObjects.length;
console.log(`减少Draw Call: ${drawCallReduction}`);
}
}

🎯 渲染管线优化要点

1. CPU端优化策�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class CPUOptimization {
// 1. 空间分割加速剔�? public spatialPartitioning(objects: GameObject[]) {
const octree = new Octree(objects);
return {
query: (frustum: Frustum) => octree.queryFrustum(frustum),
raycast: (ray: Ray) => octree.raycast(ray)
};
}

// 2. LOD系统
public applyLOD(objects: GameObject[], camera: Camera) {
objects.forEach(obj => {
const distance = vec3.distance(camera.position, obj.position);
const lodLevel = this.calculateLOD(distance, obj.lodSettings);
obj.setLODLevel(lodLevel);
});
}

// 3. 异步资源加载
public async preloadResources(scene: Scene) {
const resources = scene.getRequiredResources();
await Promise.all(resources.map(res => res.load()));
}
}

2. GPU端优化策�?

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
// GPU优化示例
CCProgram optimized_vertex %{
// 1. 减少计算复杂�? void main() {
// 预计算MVP矩阵
vec4 position = cc_matViewProj * cc_matWorld * vec4(a_position, 1.0);

// 快速法线变换(假设无非等比缩放�? vec3 normal = mat3(cc_matWorld) * a_normal;

gl_Position = position;
v_worldNormal = normal;
}
}%

CCProgram optimized_fragment %{
// 2. 减少复杂的分支和循环
void main() {
vec4 color = texture(mainTexture, v_uv);

// 使用step函数替代if语句
float factor = step(0.5, someValue);
color.rgb = mix(color.rgb, highlightColor.rgb, factor);

fragColor = 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
// 性能监控UI组件
@ccclass('PerformanceMonitor')
export class PerformanceMonitor extends Component {
@property(Label)
statsLabel: Label = null!;

private frameTimeHistory: number[] = [];
private readonly HISTORY_SIZE = 60; // 60帧历�?
public update() {
const stats = renderer.getFrameStats();
this.updateFrameTime(game.deltaTime);
this.updateDisplay(stats);
}

private updateFrameTime(deltaTime: number) {
this.frameTimeHistory.push(deltaTime * 1000);
if (this.frameTimeHistory.length > this.HISTORY_SIZE) {
this.frameTimeHistory.shift();
}
}

private updateDisplay(stats: RenderStats) {
const avgFrameTime = this.frameTimeHistory.reduce((a, b) => a + b, 0) / this.frameTimeHistory.length;
const fps = Math.round(1000 / avgFrameTime);

const displayText = `
FPS: ${fps}
帧时�? ${avgFrameTime.toFixed(2)}ms
Draw Calls: ${stats.drawCalls}
三角�? ${stats.triangles}
顶点: ${stats.vertices}
状态切�? ${stats.stateChanges}
`.trim();

this.statsLabel.string = displayText;
}
}

🔍 调试工具与技�?

1. 渲染调试�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 渲染调试系统
class RenderDebugger {
public showWireframe(enabled: boolean) {
this.setGlobalMacro('WIREFRAME_MODE', enabled);
}

public showOverdraw(enabled: boolean) {
this.setGlobalMacro('OVERDRAW_DEBUG', enabled);
}

public highlightBatches(enabled: boolean) {
this.setGlobalMacro('BATCH_DEBUG', enabled);
}

public showDepthBuffer(enabled: boolean) {
if (enabled) {
this.setDebugRenderTarget(this.depthTexture);
} else {
this.clearDebugRenderTarget();
}
}
}

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
// 着色器热重载系�?class ShaderHotReload {
private watchedShaders: Map<string, Shader> = new Map();

public watchShader(path: string, shader: Shader) {
this.watchedShaders.set(path, shader);
this.startWatching(path);
}

private startWatching(path: string) {
// 监听文件变化
fileWatcher.watch(path, () => {
this.reloadShader(path);
});
}

private async reloadShader(path: string) {
try {
const shader = this.watchedShaders.get(path);
if (shader) {
const newSource = await this.loadShaderSource(path);
shader.recompile(newSource);
console.log(`着色器已重新加�? ${path}`);
}
} catch (error) {
console.error(`着色器重载失败: ${path}`, error);
}
}
}

🎯 最佳实践建�?

1. 设计阶段

  • 性能预算: 提前设定Draw Call、三角形等性能目标
  • LOD策略: 为复杂模型准备多级细节版�?- 材质规划: 设计可复用的材质系统

2. 开发阶�?- 批次优化: 优先考虑静态批次合�?- *状态管�?: 减少不必要的渲染状态切�?- 资源管理: 合理管理纹理和网格资�?

3. 优化阶段

  • 性能分析: 使用Profiler找出性能瓶颈
  • 迭代优化: 逐步优化,避免过早优�?- 测试验证: 在目标设备上验证优化效果

📝 本章小结

通过本教程,你应该掌握了�?

  1. 渲染管线架构: 理解Cocos Creator完整的渲染流�?2. 性能监控: 学会收集和分析渲染性能数据
  2. 优化策略: 掌握CPU和GPU端的基本优化方法
  3. *调试技�?: 了解渲染调试工具的使�?

🚀 下一步学�?

继续加油!🎮✨