Deferred Rendering延迟渲染

一、先说结论:Three.js 实现 Deferred 需要 3 步

在 Three.js 中实现 Deferred Rendering:

 
① 创建 GBuffer(MRT)
② 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

 

posted @ 2026-04-07 17:02  SimoonJia  阅读(3)  评论(0)    收藏  举报