�?3.3章:多Pass渲染技�?

多Pass渲染是实现复杂视觉效果的重要技术,通过多次渲染同一几何体来实现描边、阴影、反射等高级效果。本教程将深入讲解多Pass渲染的原理和实际应用�?

🎯 学习目标

  • 理解多Pass渲染的基本原�?- 掌握Cocos Creator中的多Pass实现
  • 学会设计高效的多Pass渲染管线
  • 了解多Pass渲染的性能优化技�?

🔧 多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
47
48
// 多Pass着色器示例
CCEffect %{
techniques:
- name: multi-pass
passes:
# Pass 1: 深度预处�? - vert: depth-pre-vs:vert
frag: depth-pre-fs:frag
phase: depth-pre
rasterizerState:
cullMode: back
depthStencilState:
depthTest: true
depthWrite: true
depthFunc: less
blendState:
targets:
- blend: false

# Pass 2: 主要渲染
- vert: main-vs:vert
frag: main-fs:frag
phase: forward
rasterizerState:
cullMode: back
depthStencilState:
depthTest: true
depthWrite: false
depthFunc: equal
blendState:
targets:
- blend: false

# Pass 3: 描边效果
- vert: outline-vs:vert
frag: outline-fs:frag
phase: forward
rasterizerState:
cullMode: front
depthStencilState:
depthTest: true
depthWrite: false
depthFunc: less_equal
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
}%

TypeScript多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
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// 多Pass渲染管理�?class MultiPassRenderer {
private gl: WebGL2RenderingContext;
private renderPasses: Map<string, RenderPass> = new Map();
private passQueue: RenderPass[] = [];

interface RenderPass {
name: string;
shader: Shader;
phase: 'depth-pre' | 'shadow' | 'forward' | 'post-process';
priority: number;
renderState: RenderState;
enabled: boolean;
objects: RenderObject[];
}

interface RenderState {
cullMode: 'none' | 'front' | 'back';
depthTest: boolean;
depthWrite: boolean;
depthFunc: 'never' | 'less' | 'equal' | 'less_equal' | 'greater' | 'not_equal' | 'greater_equal' | 'always';
blendEnabled: boolean;
blendSrc: string;
blendDst: string;
}

public addRenderPass(passConfig: Partial<RenderPass>): void {
const pass: RenderPass = {
name: passConfig.name || 'unnamed',
shader: passConfig.shader!,
phase: passConfig.phase || 'forward',
priority: passConfig.priority || 0,
renderState: passConfig.renderState || this.getDefaultRenderState(),
enabled: passConfig.enabled !== false,
objects: passConfig.objects || []
};

this.renderPasses.set(pass.name, pass);
this.sortPassQueue();

console.log(`�?添加渲染Pass: ${pass.name}, 阶段: ${pass.phase}, 优先�? ${pass.priority}`);
}

private sortPassQueue(): void {
this.passQueue = Array.from(this.renderPasses.values())
.filter(pass => pass.enabled)
.sort((a, b) => {
// 先按阶段排序,再按优先级排序
const phaseOrder = ['depth-pre', 'shadow', 'forward', 'post-process'];
const aPhaseIndex = phaseOrder.indexOf(a.phase);
const bPhaseIndex = phaseOrder.indexOf(b.phase);

if (aPhaseIndex !== bPhaseIndex) {
return aPhaseIndex - bPhaseIndex;
}

return a.priority - b.priority;
});
}

public renderAllPasses(camera: Camera, renderList: RenderObject[]): void {
console.log(`🎨 开始多Pass渲染,Pass数量: ${this.passQueue.length}`);

for (const pass of this.passQueue) {
this.renderPass(pass, camera, renderList);
}

console.log(`�?多Pass渲染完成`);
}

private renderPass(pass: RenderPass, camera: Camera, renderList: RenderObject[]): void {
console.log(`🔸 渲染Pass: ${pass.name}`);

// 设置渲染状�? this.applyRenderState(pass.renderState);

// 绑定着色器
this.gl.useProgram(pass.shader.program);

// 设置相机相关uniform
this.setCameraUniforms(pass.shader, camera);

// 过滤适用于当前pass的对�? const passObjects = this.filterObjectsForPass(pass, renderList);

// 渲染对象
passObjects.forEach(obj => {
this.renderObject(obj, pass.shader);
});
}

private applyRenderState(state: RenderState): void {
// 设置剔除模式
if (state.cullMode === 'none') {
this.gl.disable(this.gl.CULL_FACE);
} else {
this.gl.enable(this.gl.CULL_FACE);
this.gl.cullFace(state.cullMode === 'front' ? this.gl.FRONT : this.gl.BACK);
}

// 设置深度测试
if (state.depthTest) {
this.gl.enable(this.gl.DEPTH_TEST);
const depthFuncs = {
'never': this.gl.NEVER,
'less': this.gl.LESS,
'equal': this.gl.EQUAL,
'less_equal': this.gl.LEQUAL,
'greater': this.gl.GREATER,
'not_equal': this.gl.NOTEQUAL,
'greater_equal': this.gl.GEQUAL,
'always': this.gl.ALWAYS
};
this.gl.depthFunc(depthFuncs[state.depthFunc]);
} else {
this.gl.disable(this.gl.DEPTH_TEST);
}

// 设置深度写入
this.gl.depthMask(state.depthWrite);

// 设置混合
if (state.blendEnabled) {
this.gl.enable(this.gl.BLEND);
// 这里需要解析blendSrc和blendDst字符串到GL常量
} else {
this.gl.disable(this.gl.BLEND);
}
}
}

🎨 经典多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
// 描边多Pass着色器
CCProgram outline-vs %{
precision highp float;

in vec3 a_position;
in vec3 a_normal;

uniform CCGlobal {
mat4 cc_matViewProj;
};

uniform CCLocal {
mat4 cc_matWorld;
mat4 cc_matWorldIT;
};

uniform vec4 outlineParams; // x: width, y: z_offset, z: unused, w: unused

void vert() {
// 沿法线方向扩展顶�? vec3 worldPos = (cc_matWorld * vec4(a_position, 1.0)).xyz;
vec3 worldNormal = normalize((cc_matWorldIT * vec4(a_normal, 0.0)).xyz);

// 扩展位置
worldPos += worldNormal * outlineParams.x;

vec4 clipPos = cc_matViewProj * vec4(worldPos, 1.0);

// 轻微前移,避免z-fighting
clipPos.z -= outlineParams.y * clipPos.w;

gl_Position = clipPos;
}
}%

CCProgram outline-fs %{
precision highp float;

layout(location = 0) out vec4 fragColor;

uniform vec4 outlineColor;

void frag() {
fragColor = outlineColor;
}
}%

阴影映射多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
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
// 阴影映射多Pass系统
class ShadowMappingSystem {
private shadowMapFBO: WebGLFramebuffer;
private shadowMapTexture: WebGLTexture;
private shadowMapSize: number = 2048;

interface ShadowCaster {
lightViewMatrix: mat4;
lightProjMatrix: mat4;
shadowBias: number;
shadowRadius: number;
}

public setupShadowMapping(): void {
// 创建阴影贴图FBO
this.shadowMapFBO = this.gl.createFramebuffer()!;
this.shadowMapTexture = this.gl.createTexture()!;

this.gl.bindTexture(this.gl.TEXTURE_2D, this.shadowMapTexture);
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.DEPTH_COMPONENT24,
this.shadowMapSize, this.shadowMapSize, 0,
this.gl.DEPTH_COMPONENT, this.gl.UNSIGNED_INT, null);

this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);

this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.shadowMapFBO);
this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT,
this.gl.TEXTURE_2D, this.shadowMapTexture, 0);

console.log(`🌑 阴影映射初始化完成,贴图尺寸: ${this.shadowMapSize}x${this.shadowMapSize}`);
}

public renderShadowPass(shadowCaster: ShadowCaster, renderList: RenderObject[]): void {
console.log(`🌑 渲染阴影Pass`);

// 绑定阴影贴图FBO
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.shadowMapFBO);
this.gl.viewport(0, 0, this.shadowMapSize, this.shadowMapSize);

// 清除深度缓冲
this.gl.clear(this.gl.DEPTH_BUFFER_BIT);

// 设置渲染状�? this.gl.enable(this.gl.DEPTH_TEST);
this.gl.depthFunc(this.gl.LESS);
this.gl.depthMask(true);
this.gl.disable(this.gl.BLEND);
this.gl.enable(this.gl.CULL_FACE);
this.gl.cullFace(this.gl.FRONT); // 前面剔除减少阴影瑕疵

// 使用阴影着色器
const shadowShader = this.getShadowShader();
this.gl.useProgram(shadowShader.program);

// 设置光源矩阵
const lightViewProjMatrix = mat4.multiply(mat4.create(), shadowCaster.lightProjMatrix, shadowCaster.lightViewMatrix);
this.setMatrix4(shadowShader, 'u_lightViewProjMatrix', lightViewProjMatrix);

// 渲染所有投射阴影的对象
renderList
.filter(obj => obj.castShadow)
.forEach(obj => {
this.renderObjectShadow(obj, shadowShader);
});

// 恢复默认FBO
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
}
}

🌟 高级多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
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// G-Buffer Pass (几何阶段)
CCProgram gbuffer-vs %{
precision highp float;

in vec3 a_position;
in vec3 a_normal;
in vec2 a_texCoord;
in vec4 a_tangent;

out vec3 v_worldPos;
out vec3 v_normal;
out vec2 v_uv;
out vec3 v_tangent;
out vec3 v_bitangent;

uniform CCGlobal {
mat4 cc_matViewProj;
};

uniform CCLocal {
mat4 cc_matWorld;
mat4 cc_matWorldIT;
};

void vert() {
vec4 worldPos = cc_matWorld * vec4(a_position, 1.0);
v_worldPos = worldPos.xyz;

v_normal = normalize((cc_matWorldIT * vec4(a_normal, 0.0)).xyz);
v_tangent = normalize((cc_matWorld * vec4(a_tangent.xyz, 0.0)).xyz);
v_bitangent = cross(v_normal, v_tangent) * a_tangent.w;

v_uv = a_texCoord;

gl_Position = cc_matViewProj * worldPos;
}
}%

CCProgram gbuffer-fs %{
precision highp float;

in vec3 v_worldPos;
in vec3 v_normal;
in vec2 v_uv;
in vec3 v_tangent;
in vec3 v_bitangent;

// 多个渲染目标 (MRT)
layout(location = 0) out vec4 gBuffer0; // rgb: albedo, a: metallic
layout(location = 1) out vec4 gBuffer1; // rgb: normal, a: roughness
layout(location = 2) out vec4 gBuffer2; // rgb: emission, a: ao
layout(location = 3) out vec4 gBuffer3; // rgba: velocity/motion vector

uniform sampler2D albedoTexture;
uniform sampler2D normalTexture;
uniform sampler2D pbrTexture; // r: metallic, g: roughness, b: ao
uniform sampler2D emissionTexture;

void frag() {
// 采样纹理
vec4 albedo = texture(albedoTexture, v_uv);
vec3 normalMap = texture(normalTexture, v_uv).xyz * 2.0 - 1.0;
vec3 pbrData = texture(pbrTexture, v_uv).rgb;
vec3 emission = texture(emissionTexture, v_uv).rgb;

// 计算世界空间法线
mat3 TBN = mat3(v_tangent, v_bitangent, v_normal);
vec3 worldNormal = normalize(TBN * normalMap);

// 编码法线到[0,1]范围
vec3 encodedNormal = worldNormal * 0.5 + 0.5;

// 填充G-Buffer
gBuffer0 = vec4(albedo.rgb, pbrData.r); // albedo + metallic
gBuffer1 = vec4(encodedNormal, pbrData.g); // normal + roughness
gBuffer2 = vec4(emission, pbrData.b); // emission + ao
gBuffer3 = vec4(0, 0, 0, 1); // motion vector (这里暂时�?)
}
}%

// 光照Pass (着色阶�?
CCProgram lighting-vs %{
precision highp float;

in vec2 a_position; // 全屏四边�?
out vec2 v_uv;

void vert() {
v_uv = a_position * 0.5 + 0.5;
gl_Position = vec4(a_position, 0.0, 1.0);
}
}%

CCProgram lighting-fs %{
precision highp float;

in vec2 v_uv;

layout(location = 0) out vec4 fragColor;

// G-Buffer纹理
uniform sampler2D gBuffer0;
uniform sampler2D gBuffer1;
uniform sampler2D gBuffer2;
uniform sampler2D depthTexture;

// 光照数据
uniform CCForwardLight {
vec4 cc_mainLitDir;
vec4 cc_mainLitColor;
vec4 cc_ambientSky;
};

uniform CCGlobal {
mat4 cc_matViewInv;
mat4 cc_matProjInv;
vec4 cc_cameraPos;
};

// 从深度重构世界位�? vec3 reconstructWorldPos(vec2 uv, float depth) {
vec4 clipPos = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
vec4 viewPos = cc_matProjInv * clipPos;
viewPos /= viewPos.w;
vec4 worldPos = cc_matViewInv * viewPos;
return worldPos.xyz;
}

void frag() {
// 采样G-Buffer
vec4 gb0 = texture(gBuffer0, v_uv);
vec4 gb1 = texture(gBuffer1, v_uv);
vec4 gb2 = texture(gBuffer2, v_uv);
float depth = texture(depthTexture, v_uv).r;

// 解析材质数据
vec3 albedo = gb0.rgb;
float metallic = gb0.a;
vec3 normal = gb1.rgb * 2.0 - 1.0;
float roughness = gb1.a;
vec3 emission = gb2.rgb;
float ao = gb2.a;

// 重构世界位置
vec3 worldPos = reconstructWorldPos(v_uv, depth);
vec3 viewDir = normalize(cc_cameraPos.xyz - worldPos);

// 执行PBR光照计算
vec3 lightDir = normalize(-cc_mainLitDir.xyz);
vec3 lighting = calculatePBRLighting(albedo, normal, viewDir, lightDir, metallic, roughness);

// 添加环境光和自发�? vec3 finalColor = lighting + cc_ambientSky.rgb * albedo * ao + emission;

fragColor = 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
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
// 智能Pass合并系统
class PassMergeOptimizer {
interface MergeCandidate {
passes: RenderPass[];
compatibility: number;
performance_gain: number;
merge_complexity: number;
}

public analyzeMergeOpportunities(passes: RenderPass[]): MergeCandidate[] {
const candidates: MergeCandidate[] = [];

// 检查所有可能的Pass组合
for (let i = 0; i < passes.length - 1; i++) {
for (let j = i + 1; j < passes.length; j++) {
const compatibility = this.calculateCompatibility(passes[i], passes[j]);

if (compatibility > 0.7) { // 兼容性阈�? candidates.push({
passes: [passes[i], passes[j]],
compatibility: compatibility,
performance_gain: this.estimatePerformanceGain(passes[i], passes[j]),
merge_complexity: this.calculateMergeComplexity(passes[i], passes[j])
});
}
}
}

// 按性能收益排序
return candidates.sort((a, b) => b.performance_gain - a.performance_gain);
}

private calculateCompatibility(passA: RenderPass, passB: RenderPass): number {
let compatibility = 1.0;

// 检查渲染状态兼容�? if (passA.renderState.cullMode !== passB.renderState.cullMode) compatibility *= 0.5;
if (passA.renderState.depthTest !== passB.renderState.depthTest) compatibility *= 0.3;
if (passA.renderState.blendEnabled !== passB.renderState.blendEnabled) compatibility *= 0.6;

// 检查着色器兼容�? if (this.shadersCompatible(passA.shader, passB.shader)) compatibility *= 1.0;
else compatibility *= 0.1;

// 检查对象列表重叠度
const overlap = this.calculateObjectOverlap(passA.objects, passB.objects);
compatibility *= overlap;

return compatibility;
}

public createMergedPass(candidate: MergeCandidate): RenderPass {
console.log(`🔀 合并Pass: ${candidate.passes.map(p => p.name).join(' + ')}`);

const mergedPass: RenderPass = {
name: `Merged_${candidate.passes.map(p => p.name).join('_')}`,
shader: this.createMergedShader(candidate.passes),
phase: candidate.passes[0].phase,
priority: Math.min(...candidate.passes.map(p => p.priority)),
renderState: this.mergeRenderStates(candidate.passes),
enabled: true,
objects: this.mergeObjectLists(candidate.passes)
};

return mergedPass;
}

private createMergedShader(passes: RenderPass[]): Shader {
// 这里需要实现着色器合并逻辑
// 可以使用条件编译或uniform分支来处理不同效�? console.log(`🔨 创建合并着色器,合�?{passes.length}个Pass`);

// 简化示例:返回第一个Pass的着色器
// 实际实现需要更复杂的着色器生成逻辑
return passes[0].shader;
}
}

📝 本章小结

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

  1. 多Pass原理: 理解多Pass渲染的基本概念和应用场景
  2. 效果实现: 学会实现描边、阴影等经典多Pass效果
  3. 延迟渲染: 掌握基于多Pass的延迟渲染技�?4. 性能优化: 了解多Pass渲染的优化策略和合并技�?

🚀 下一步学�?

继续学习渲染纹理技术!🎮�?