Deferred Rendering延迟渲染
一、先说结论:Three.js 实现 Deferred 需要 3 步
在 Three.js 中实现 Deferred Rendering:
① 创建 GBuffer(MRT)
② Geometry Pass(写入 GBuffer)
③ Lighting Pass(计算光照)
② Geometry Pass(写入 GBuffer)
③ Lighting Pass(计算光照)
我们一步一步来 👇
二、Step 1:创建 GBuffer(MRT)
Deferred Rendering 的第一步:
👉 创建多个 RenderTarget(GBuffer)
在 Three.js 中使用:
THREE.WebGLMultipleRenderTargets
代码:
const width = window.innerWidth;
const height = window.innerHeight;
const gBuffer = new THREE.WebGLMultipleRenderTargets(
width,
height,
3 // 3个贴图
);
// gBuffer.texture[0] → position
// gBuffer.texture[1] → normal
// gBuffer.texture[2] → color
这一步创建了:
| 贴图 | 用途 |
|---|---|
| gBuffer.texture[0] | 位置 |
| gBuffer.texture[1] | 法线 |
| gBuffer.texture[2] | 颜色 |
这就是:
👉 GBuffer
三、Step 2:Geometry Pass
现在我们需要:
👉 渲染场景,但写入 GBuffer
Three.js:
renderer.setRenderTarget(gBuffer);
renderer.render(scene, camera);
renderer.setRenderTarget(null);
但是:
默认材质不会输出:
- position
- normal
- color
所以:
👉 必须写 自定义 ShaderMaterial
四、Geometry Pass Shader
我们创建:
const gBufferMaterial = new THREE.ShaderMaterial({
vertexShader,
fragmentShader
});
Vertex Shader
varying vec3 vPosition;
varying vec3 vNormal;
void main() {
vPosition = (modelMatrix * vec4(position,1.0)).xyz;
vNormal = normal;
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position,1.0);
}
Fragment Shader
关键部分:
layout(location = 0) out vec4 gPosition;
layout(location = 1) out vec4 gNormal;
layout(location = 2) out vec4 gColor;
varying vec3 vPosition;
varying vec3 vNormal;
void main() {
gPosition = vec4(vPosition,1.0);
gNormal = vec4(normalize(vNormal),1.0);
gColor = vec4(1.0,0.5,0.3,1.0);
}
这一步:
👉 把数据写入 GBuffer
五、把 Geometry Material 应用到场景
Three.js 中:
scene.traverse((obj) => {
if(obj.isMesh){
obj.material = gBufferMaterial;
}
});
这样:
所有物体都会写入:
- position
- normal
- color
六、Step 3:Lighting Pass
现在我们已经有:
- gBuffer.texture[0]
- gBuffer.texture[1]
- gBuffer.texture[2]
接下来:
👉 计算光照
Lighting Pass 使用:
👉 Fullscreen Quad
创建 Fullscreen Quad
const quad = new THREE.Mesh(
new THREE.PlaneGeometry(2,2),
lightingMaterial
);
const quadScene = new THREE.Scene();
quadScene.add(quad);
const quadCamera = new THREE.Camera();
Lighting Shader
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gColor;
void main() {
vec2 uv = gl_FragCoord.xy / resolution;
vec3 position = texture(gPosition, uv).rgb;
vec3 normal = texture(gNormal, uv).rgb;
vec3 color = texture(gColor, uv).rgb;
vec3 light = vec3(1.0,1.0,1.0);
float diff = max(dot(normal, light), 0.0);
gl_FragColor = vec4(color * diff,1.0);
}
七、最终渲染流程
渲染代码:
function render(){
// Geometry Pass
renderer.setRenderTarget(gBuffer);
renderer.render(scene, camera);
// Lighting Pass
renderer.setRenderTarget(null);
renderer.render(quadScene, quadCamera);
}
这就是:
👉 Deferred Rendering 在 Three.js 的完整实现 🎯
八、完整流程图
Scene
↓
Geometry Pass
↓
GBuffer (position,normal,color)
↓
Lighting Pass (fullscreen quad)
↓
Final Image
九、为什么 Deferred 更快
因为:
Lighting Pass:
for each pixel
for each light
而不是:
for each object
所以:
👉 光源越多,Deferred 优势越大
十、Three.js Deferred 实际架构
实际项目通常这样:
DeferredRenderer
├─ GBuffer
├─ GeometryPass
├─ LightingPass
└─ CompositePass
现代引擎:
- Unity HDRP
- Unreal Engine
- Frostbite
都是这个架构
十一、重要补充(Three.js 限制)
Three.js WebGL:
MRT 需要:
WebGL2
必须:
const renderer = new THREE.WebGLRenderer({
antialias: true
});
并且浏览器支持 WebGL2
十二、你现在应该理解
Deferred Rendering 在 Three.js:
本质就是:
① 多个 RenderTarget(GBuffer)
② 自定义 shader 写入 GBuffer
③ Fullscreen Quad 计算光照
如果你继续深入建议学习顺序
下一步:
① Deferred Rendering ✔
② Tiled Deferred
③ Clustered Deferred
④ WebGPU Deferred

浙公网安备 33010602011771号