前向渲染和延迟渲染

前向渲染(Forward Rendering)和延迟渲染(Deferred Rendering)——详细阶段对比

这两种渲染方式都属于 基于光照的渲染管线(Lighting Pipeline),区别在于光照和材质的计算时机不同


一、前向渲染(Forward Rendering)

✅ 渲染阶段流程:

每个物体 → 顶点着色器 → 光栅化 → 片元着色器(计算光照) → 合并输出到帧缓冲

每个阶段解释:

阶段 作用 关键点
1️⃣ 顶点着色器 处理模型的顶点(变换到屏幕空间) 模型、视图、投影矩阵
2️⃣ 光栅化 将三角形“填充”为像素(片元) GPU自动处理
3️⃣ 片元着色器 每个像素计算颜色,此时就计算光照! 光照开销大
4️⃣ 合并输出 输出最终像素颜色到帧缓冲 支持透明效果

优点:

  • 实现简单,渲染路径直接

  • 透明对象支持良好

  • 所有硬件都支持

缺点:

  • 每个像素都要重复计算光照

  • 光源多了性能急剧下降

  • 阴影、全局光照不易扩展


二、延迟渲染(Deferred Rendering)

✅ 渲染阶段流程(更复杂):

1. 几何阶段(Geometry Pass):
   所有物体 → 写入 G-buffer(记录位置、法线、材质信息)

2. 光照阶段(Lighting Pass):
   每个像素 → 读取 G-buffer → 统一计算光照

3. 合成阶段(Composition Pass):
   合成所有信息输出最终颜色

阶段细节:

阶段 内容 数据
1️⃣ 几何阶段 所有物体写入 G-buffer 位置、法线、反射率、颜色、深度等
2️⃣ 光照阶段 使用 G-buffer 对每个像素统一处理光照 高效统一处理多个光源
3️⃣ 合成阶段 最终输出颜色图像 可加后期处理(Bloom、HDR等)

优点:

  • 多光源性能优越(只计算一次像素)

  • 易于集成后处理效果(如Bloom、SSA0)

  • 几何与光照解耦,灵活

缺点:

  • 不支持透明对象(因为没有透明顺序)

  • 消耗显存大(多个G-buffer)

  • 不适合低端设备


对比总结表:

比较项 前向渲染 延迟渲染
光照计算 每物体每像素 每像素一次,统一光照
光源数量 少则性能好,多则急剧下降 多光源效率更高
支持透明 ✅ 支持良好 ❌ 支持差
后处理 较困难 容易扩展
内存占用 高,需要多个缓冲区
实现复杂度 高,需要设计G-buffer

初学者建议:

建议 说明
学习顺序 先理解前向渲染,再学习延迟渲染
推荐路径 Cocos / Unity 都默认使用前向渲染,也提供延迟渲染(在高质量设置中)
着重点 理解 为什么延迟渲染更适合多光源环境,以及 它为何不适合处理透明物体

附加资料(可选)

  • G-buffer 通常包含哪些内容:

    • worldPos: 世界坐标

    • normal: 法线向量

    • albedo: 反照率

    • specular: 镜面系数

    • depth: 深度信息

新的一篇==========================================

延迟渲染的几何阶段和光照阶段(Lighting Pass)都会走顶点着色器和片段着色器,不过有区别

下面我给你详细拆解这个流程,作为初学者能快速理解:


延迟渲染的两个主要阶段回顾:

阶段 用途 使用顶点/片元着色器? 说明
1️⃣ 几何阶段(G-Buffer Pass) 把场景信息写入 G-Buffer(颜色、法线、深度等) ✅ 会走顶点和片元着色器 每个物体一个一个画
2️⃣ 光照阶段(Lighting Pass) 利用 G-buffer 数据,逐像素计算光照 ✅ 也会走顶点和片元着色器 通常只画一个全屏 Quad

光照阶段怎么还用“顶点着色器”?

这个顶点着色器非常简单!

我们会画一个 全屏幕的矩形(Full-Screen Quad),通常由 2 个三角形组成。

顶点着色器的作用是:

  • 把这 4 个顶点从局部坐标变换到屏幕坐标。

  • 输出纹理坐标(UV)给片元着色器。

示例顶点着色器代码(伪代码):

attribute vec2 a_position;
varying vec2 v_uv;

void main() {
    v_uv = a_position * 0.5 + 0.5; // [-1,1] → [0,1]
    gl_Position = vec4(a_position, 0.0, 1.0); // 投影到屏幕空间
}

光照阶段的“片元着色器”做了什么?

片元着色器会:

  • 从 G-buffer 读取当前像素的:

    • 位置(或重建位置)

    • 法线

    • 材质属性(如颜色、金属度、粗糙度)

  • 对每个光源进行逐像素光照计算

  • 输出最终颜色

示例片元着色器伪代码:

varying vec2 v_uv;

uniform sampler2D u_albedo;
uniform sampler2D u_normal;
uniform sampler2D u_position;

void main() {
    vec3 albedo = texture2D(u_albedo, v_uv).rgb;
    vec3 normal = texture2D(u_normal, v_uv).xyz;
    vec3 position = texture2D(u_position, v_uv).xyz;

    vec3 lightDir = normalize(lightPos - position);
    float NdotL = max(dot(normal, lightDir), 0.0);
    vec3 color = albedo * lightColor * NdotL;

    gl_FragColor = vec4(color, 1.0);
}

✅ 所以总结:

阶段 顶点着色器作用 片元着色器作用
几何阶段 变换物体顶点位置 输出 G-buffer 数据
光照阶段 绘制全屏 Quad(输出 UV) 从 G-buffer 获取数据进行光照计算

延伸补充:

  • 全屏 Quad 不需要复杂变换,所以光照阶段的顶点着色器很简单,不涉及模型矩阵。

  • 光照阶段最关键的是片元着色器中的光照计算

  • 多数延迟渲染框架中,光照阶段只有一个 Draw Call,但可以处理成百上千个光源。

新的一篇==========================================


✅ 回答:

是的,延迟渲染中的 Lighting Pass 也会走一遍顶点着色器和片段(片元)着色器。

但:

  • 顶点着色器很简单,仅用于生成一个全屏四边形(Full-Screen Quad)。

  • 真正进行光照计算的是片元着色器


延迟渲染两个阶段对比(都走完整管线):

Pass 阶段 走顶点着色器? 走片元着色器? 用途
Geometry Pass(几何阶段) ✅ 是 ✅ 是 渲染场景所有物体,写入 G-Buffer(颜色、法线、深度等)
Lighting Pass(光照阶段) ✅ 是 ✅ 是 渲染一个全屏 Quad,从 G-Buffer 读取数据,计算光照

Lighting Pass 为何还要走顶点着色器?

因为我们需要在屏幕上画一个全屏幕矩形(Full-Screen Quad)

这个矩形也有顶点,需要送进 GPU 渲染:

过程如下:

1️⃣ 顶点着色器:
   - 输入全屏 Quad 的顶点位置(例如左下角 [-1,-1])
   - 输出到裁剪空间(gl_Position)
   - 生成对应 UV 坐标传给片元着色器

2️⃣ 光栅化:
   - 把这个矩形切成像素

3️⃣ 片元着色器:
   - 对每个像素,从 G-Buffer 取出该点的材质、法线、深度信息
   - 计算光照结果(如漫反射、镜面反射、PBR 等)

✅ Lighting Pass 的顶点着色器通常如下(GLSL 示例):

attribute vec2 a_position;  // [-1,-1] ~ [1,1]
varying vec2 v_uv;

void main() {
    gl_Position = vec4(a_position, 0.0, 1.0);
    v_uv = a_position * 0.5 + 0.5;  // 映射到 [0,1] UV
}

✅ Lighting Pass 的片元着色器通常如下:

varying vec2 v_uv;

uniform sampler2D u_albedo;
uniform sampler2D u_normal;
uniform sampler2D u_position;

void main() {
    vec3 albedo = texture2D(u_albedo, v_uv).rgb;
    vec3 normal = texture2D(u_normal, v_uv).xyz;
    vec3 position = texture2D(u_position, v_uv).xyz;

    vec3 lightDir = normalize(lightPos - position);
    float NdotL = max(dot(normal, lightDir), 0.0);
    vec3 color = albedo * lightColor * NdotL;

    gl_FragColor = vec4(color, 1.0);
}

总结:

内容 是否走 说明
Geometry Pass 顶点着色器 每个物体都跑一次,做变换
Geometry Pass 片元着色器 输出材质信息到 G-Buffer
Lighting Pass 顶点着色器 只画一个全屏 Quad,输出 UV
Lighting Pass 片元着色器 每像素从 G-Buffer 取数据并计算光照

你可能感兴趣的:(渲染管线)