从前向渲染到延迟渲染:为什么3A游戏都在用Deferred?

news2026/3/25 8:41:01
两种算账方式两种命运从一个餐厅说起你开了一家餐厅。100桌客人10个厨师。方式一每桌每菜。服务员端着第一桌的菜单走进厨房。第一桌要红烧肉。10个厨师一起做红烧肉。做完了。第一桌还要糖醋鱼。10个厨师一起做糖醋鱼。做完了。第一桌还要宫保鸡丁。做完了。第一桌的菜全部上齐。服务员拿着第二桌的菜单走进厨房。第二桌要红烧肉。等等刚才不是做过红烧肉吗但那是第一桌的。第二桌的红烧肉要重新做。100桌客人每桌3道菜10个厨师。每道菜都要单独做。如果有50桌都点了红烧肉红烧肉就要做50次。方式二每菜每桌。服务员先收集所有桌的菜单。统计一下50桌要红烧肉30桌要糖醋鱼80桌要宫保鸡丁。厨师先做红烧肉。一次做50份。锅热着调料备着流水线作业。50份红烧肉一气呵成。然后做糖醋鱼。一次做30份。然后做宫保鸡丁。一次做80份。方式一是前向渲染。方式二是延迟渲染。桌子 物体。菜 光源。厨师 GPU。一、前向渲染每个物体算所有光1.1 工作方式前向渲染Forward Rendering的逻辑非常直觉for each 物体: for each 影响这个物体的光源: 计算这个光源对这个物体的贡献 输出最终颜色画第一个物体。这个物体受到哪些光的影响主方向光、两个点光源、一个聚光灯。在片段着色器里对每个像素计算这4个光源的贡献加在一起输出颜色。画第二个物体。这个物体受到哪些光的影响主方向光、三个点光源。计算4个光源的贡献输出颜色。画第三个物体。画第四个物体。画第一百个物体。每个物体的每个像素都要在片段着色器里计算所有影响它的光源。1.2 光源少的时候场景里只有1个方向光太阳。物体1计算1个光源 → 输出颜色 物体2计算1个光源 → 输出颜色 物体3计算1个光源 → 输出颜色 ... 物体100计算1个光源 → 输出颜色每个物体的片段着色器只做一次光照计算。很快。没问题。1.3 光源多的时候场景里有1个方向光 100个点光源室内场景到处都是灯。物体1计算101个光源 → 输出颜色 物体2计算101个光源 → 输出颜色 物体3计算101个光源 → 输出颜色 ... 物体100计算101个光源 → 输出颜色每个物体的片段着色器要做101次光照计算。但等等不是每个光源都影响每个物体。一个点光源的照射范围有限可能只影响附近的几个物体。所以实际上物体1受5个光源影响 → 计算5次 物体2受3个光源影响 → 计算3次 物体3受12个光源影响 → 计算12次 ...问题是GPU怎么知道哪些光源影响哪个物体1.4 光源剔除的困境最笨的方法每个物体都计算所有101个光源。大部分光源对大部分像素的贡献为零太远了但GPU不知道它必须算完才知道。100个物体 × 101个光源 10100次光照计算per pixel 实际有效的可能只有500次 浪费率95%稍微聪明一点CPU端做光源剔除。对每个物体判断哪些光源在它的范围内。只把有效的光源传给Shader。物体1附近有5个光源 → Shader里循环5次 物体2附近有3个光源 → Shader里循环3次但这带来了新问题每个物体的光源数量不同Shader里的循环次数不同。GPU喜欢所有线程做同样的事情SIMD。如果一个Warp里的32个像素有的要算5个光源有的要算12个光源GPU要等最慢的那个。其他像素算完了也得等着。更大的问题每个物体一个Draw Call。如果不同物体受不同光源影响它们的Shader参数不同光源列表不同不能合批。100个物体就是100个Draw Call。最大的问题Overdraw。一个像素被三个物体覆盖。前两个物体的像素最终被第三个物体遮挡了。但前向渲染不知道——它画第一个物体的时候不知道后面还有物体会覆盖它。所以前两个物体的光照计算白做了。像素(500, 300) 物体A覆盖计算8个光源 → 写入颜色 → 被覆盖了白算了 物体B覆盖计算5个光源 → 写入颜色 → 被覆盖了白算了 物体C覆盖计算3个光源 → 写入颜色 → 最终可见 总计算量8 5 3 16次光照计算 有效计算量3次 浪费率81%Overdraw 多光源 灾难性的浪费。二、延迟渲染先记录后算账2.1 核心思想延迟渲染Deferred Rendering把渲染分成两个阶段第一阶段几何阶段画所有物体但不做光照计算。只记录每个像素的几何信息位置、法线、材质颜色、金属度、粗糙度等。第二阶段光照阶段对每个像素用记录的几何信息计算所有光源的贡献。阶段一记录信息 for each 物体: for each 像素: 记录位置、法线、颜色、金属度、粗糙度 写入G-Buffer 阶段二计算光照 for each 像素: 读取G-Buffer中的信息 for each 影响这个像素的光源: 计算光照贡献 输出最终颜色光照计算被延迟到了第二阶段。这就是Deferred的含义。2.2 G-Buffer几何信息的快照G-BufferGeometry Buffer是延迟渲染的核心数据结构。它是一组纹理存储了每个像素的几何和材质信息。G-Buffer通常包含 纹理0RGBA8漫反射颜色.rgb 金属度.a ┌────────────────────────────┐ │ R: 漫反射红色分量 │ │ G: 漫反射绿色分量 │ │ B: 漫反射蓝色分量 │ │ A: 金属度 │ └────────────────────────────┘ 纹理1RGB10A2世界空间法线.rgb 粗糙度或其他 ┌────────────────────────────┐ │ R: 法线X │ │ G: 法线Y │ │ B: 法线Z │ │ A: 粗糙度2bit或存别处 │ └────────────────────────────┘ 纹理2RGBA8粗糙度 AO 自发光 ... ┌────────────────────────────┐ │ R: 粗糙度 │ │ G: 环境光遮蔽 │ │ B: 自发光强度 │ │ A: 其他标记 │ └────────────────────────────┘ 深度缓冲D32F深度值可以反推世界坐标G-Buffer就是场景的一张X光片。它记录了每个像素的所有几何和材质信息但没有任何光照信息。2.3 几何阶段画所有物体。片段着色器不做光照计算只输出几何信息到G-Buffer。// 几何阶段的片段着色器极其简单 layout(location 0) out vec4 gAlbedoMetallic; layout(location 1) out vec4 gNormalRoughness; void main() { vec3 albedo texture(albedoMap, uv).rgb; float metallic texture(metallicMap, uv).r; vec3 normal calculateNormal(); // TBN变换 float roughness texture(roughnessMap, uv).r; gAlbedoMetallic vec4(albedo, metallic); gNormalRoughness vec4(normal * 0.5 0.5, roughness); }注意没有光照计算。没有for each light。没有BRDF。没有阴影。片段着色器只做了纹理采样和法线变换。非常轻量。Overdraw的代价大幅降低。一个像素被三个物体覆盖前两个的几何信息被第三个覆盖了。但几何阶段的片段着色器很轻几次纹理采样浪费的计算量很少。前向渲染的Overdraw代价 物体A8次光照计算 → 白算了 物体B5次光照计算 → 白算了 浪费13次光照计算 延迟渲染的Overdraw代价 物体A3次纹理采样 → 白做了 物体B3次纹理采样 → 白做了 浪费6次纹理采样比光照计算便宜得多2.4 光照阶段G-Buffer填充完毕。现在每个像素的几何信息都已经确定了。画一个全屏三角形。对每个像素从G-Buffer读取几何信息计算所有光源的贡献。// 光照阶段的片段着色器 void main() { // 从G-Buffer读取信息 vec3 albedo texture(gAlbedoMetallic, uv).rgb; float metallic texture(gAlbedoMetallic, uv).a; vec3 normal texture(gNormalRoughness, uv).rgb * 2.0 - 1.0; float roughness texture(gNormalRoughness, uv).a; vec3 worldPos reconstructPosition(uv, depth); // 从深度重建位置 // 计算所有光源 vec3 finalColor vec3(0.0); for (int i 0; i numLights; i) { finalColor calculatePBR(albedo, metallic, roughness, normal, worldPos, lights[i]); } fragColor vec4(finalColor, 1.0); }关键光照计算只对最终可见的像素执行。G-Buffer里存的是深度测试之后的结果。被遮挡的像素已经被覆盖了。光照阶段处理的每一个像素都是最终可见的。零Overdraw的光照计算。前向渲染 207万像素 × Overdraw 2.5 × 平均8个光源 4140万次光照计算 延迟渲染 207万像素 × 1无Overdraw× 平均8个光源 1656万次光照计算 节省60%2.5 光源的处理方式延迟渲染对光源的处理方式非常优雅。方向光太阳影响所有像素。画一个全屏三角形对每个像素计算方向光。点光源只影响一定范围内的像素。画一个球体光源的影响范围只有球体覆盖的像素才执行光照计算。点光源A位置(100, 50, 30)半径10米 在屏幕上投影成一个圆形区域 画一个球体Mesh覆盖这个区域 只有被球体覆盖的像素才执行片段着色器 片段着色器从G-Buffer读取信息计算点光源A的贡献100个点光源 100个球体。每个球体只覆盖屏幕的一小部分。大部分像素只被少数几个球体覆盖。光源的开销跟它的屏幕覆盖面积成正比而不是跟场景中物体的数量成正比。一个远处的小灯泡在屏幕上只覆盖几十个像素。它的光照计算只对这几十个像素执行。几乎免费。这就是延迟渲染能支持几百个光源的原因。三、延迟渲染的代价延迟渲染不是免费的午餐。它有自己的代价。3.1 G-Buffer的带宽G-Buffer通常有3-4张纹理加上深度缓冲。纹理0RGBA88.3MB 纹理1RGBA88.3MB 纹理2RGBA88.3MB 深度D32F8.3MB 总计33.2MB几何阶段要写入33.2MB。光照阶段要读取33.2MB。总带宽66.4MB。前向渲染只有一个颜色缓冲8.3MB和一个深度缓冲8.3MB。总带宽16.6MB。延迟渲染的带宽是前向渲染的4倍。在PC上GDDR6X的带宽有1TB/s66.4MB不算什么。在手机上30-50GB/s的带宽66.4MB是一个沉重的负担。这就是为什么移动端很少用传统的延迟渲染。3.2 半透明物体延迟渲染的G-Buffer只能存一层几何信息。每个像素只有一个法线、一个颜色、一个深度。半透明物体怎么办半透明物体需要跟后面的物体混合。但G-Buffer里只有最前面那层的信息。后面那层的信息已经被覆盖了。解决方案半透明物体用前向渲染。阶段一延迟渲染的几何阶段不透明物体 阶段二延迟渲染的光照阶段 阶段三前向渲染半透明物体叠加在延迟渲染的结果上半透明物体是延迟渲染的阿喀琉斯之踵。每个3A游戏都要处理这个问题。通常的做法是不透明物体用延迟渲染半透明物体用前向渲染。两套管线并存。3.3 MSAA的困难前向渲染可以直接用MSAA。每个采样点独立执行片段着色器或者用更高效的方式。延迟渲染的G-Buffer如果要用MSAA每个采样点都要存一份几何信息。G-Buffer的大小翻倍MSAA 2x或翻四倍MSAA 4x。MSAA 4x的G-Buffer 33.2MB × 4 132.8MB132.8MB的G-Buffer。带宽爆炸。所以延迟渲染通常不用MSAA而用后处理抗锯齿TAA、FXAA。3.4 材质多样性的限制G-Buffer的格式是固定的。所有物体的几何信息都要塞进同样的几张纹理里。如果一个物体需要额外的材质参数比如次表面散射的厚度、各向异性的方向G-Buffer里没有地方存。解决方案在G-Buffer里预留一些通道给特殊材质用材质ID标记不同的材质类型光照阶段根据ID选择不同的光照模型用更多的G-Buffer纹理但带宽更大四、为什么3A游戏还是选择延迟渲染因为3A游戏的场景特点决定了延迟渲染的优势远大于劣势。4.1 光源数量3A游戏的室内场景可能有几十到几百个光源。走廊里的壁灯、房间里的台灯、窗外的路灯、爆炸的火光、枪口的闪光。前向渲染处理100个光源每个像素在片段着色器里循环100次。即使做了光源剔除每个像素平均也要算10-20个光源。延迟渲染处理100个光源画100个光源体积。每个光源只影响它覆盖的像素。大部分像素只被3-5个光源覆盖。延迟渲染在多光源场景下的优势是压倒性的。4.2 场景复杂度3A游戏的场景极其复杂。几百万个三角形Overdraw可能达到3-5倍。前向渲染每个被覆盖的像素都要做完整的光照计算。Overdraw 3倍意味着光照计算量翻3倍。延迟渲染几何阶段的Overdraw只浪费轻量的纹理采样。光照阶段零Overdraw。4.3 后处理的需求3A游戏需要大量后处理效果SSAO、SSR、景深、运动模糊。这些效果需要深度、法线、运动向量等信息。延迟渲染的G-Buffer天然提供了这些信息。前向渲染如果要做SSAO需要额外的Pass来输出法线和深度。相当于做了半个延迟渲染。4.4 PC/主机的硬件特点PC和主机的GPU是IMR架构显存带宽大几百GB/s到1TB/s以上。G-Buffer的带宽开销在预算之内。PC和主机的GPU算力强。光照阶段的全屏计算不是瓶颈。延迟渲染的劣势带宽大在PC/主机上不明显。延迟渲染的优势多光源、低Overdraw浪费在3A游戏的复杂场景里非常明显。五、移动端的选择移动端的情况完全不同。带宽小。功耗敏感。TBR/TBDR架构。传统延迟渲染的G-Buffer带宽在移动端是不可接受的。但移动端有一个独特的优势Subpass。在Vulkan/Metal的TBR架构上同一个RenderPass内的多个Subpass可以共享片上内存。G-Buffer不需要写回主内存。RenderPass { Subpass 1几何阶段 输出G-Buffer到片上内存 不写回主内存 Subpass 2光照阶段 从片上内存读取G-Buffer 计算光照 输出最终颜色 }G-Buffer从来没有离开片上内存。带宽开销为零。这就是**移动端延迟渲染Mobile Deferred**的核心思路。用Subpass避免G-Buffer的带宽开销。但有限制Subpass只能读取当前像素的G-Buffer值不能读取邻域像素。所以需要邻域采样的效果SSAO、SSR不能在Subpass里做。六、总结前向渲染 延迟渲染 ───────────────────────────────────────────────────── 光照计算时机 画物体的时候 画完所有物体之后 Overdraw浪费 严重光照白算 轻微只浪费纹理采样 多光源性能 差每像素算所有光源 好每光源只算覆盖的像素 带宽 低 高G-Buffer 半透明 天然支持 需要额外处理 MSAA 天然支持 困难 材质多样性 灵活 受G-Buffer格式限制 适合场景 光源少、移动端 光源多、PC/主机前向渲染像一个全能选手。什么都能做但什么都不极致。光源少的时候很好光源多的时候崩溃。延迟渲染像一个专家。在多光源、复杂场景下无可匹敌。但半透明、MSAA、带宽是它的软肋。3A游戏选择延迟渲染不是因为它完美而是因为在3A游戏的典型场景下它的优势远远大于劣势。100个光源。几百万个三角形。Overdraw 3倍。在这种场景下前向渲染的GPU在燃烧。延迟渲染的GPU在微笑。这就是为什么3A游戏都在用Deferred。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2442725.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…