全局光照探针网格 2026:DDGI、屏幕空间与级联辐射度
全局光照的本质是用有限预算近似一个六维辐射场。本文拆解 DDGI 的八面体辐照度图与切比雪夫可见性矩、Radiance Cascades 的分层辐射度表示以及屏幕空间 GI 的能力边界,逐层对比三者的失真来源、显存开销与动态延迟,并给出横跨 PC 与移动端产品线时如何编排降级链、仲裁多路径贡献并设计可操作的诊断流程。
约 42 分钟阅读12,374 字2 次阅读博主

全局光照的本质是用有限预算近似一个六维辐射场。本文拆解 DDGI 的八面体辐照度图与切比雪夫可见性矩、Radiance Cascades 的分层辐射度表示以及屏幕空间 GI 的能力边界,逐层对比三者的失真来源、显存开销与动态延迟,并给出横跨 PC 与移动端产品线时如何编排降级链、仲裁多路径贡献并设计可操作的诊断流程。

上一篇聊的是帧图与瞬态资源,这篇往回走一步,去看那些真正决定画面"空气感"的东西:光在场景里弹了一圈之后,是怎么被采样、被存下来、再被取出来的。
间接漫反射是渲染管线里最贵、也最容易被"糊过去"的一环。你可以在引擎里把一个角色的 indirect intensity 拉高,让它看起来不那么像贴纸;也可以打开一盏补光,让阴影角落里不至于死黑。但这两种做法都不是全局光照,只是让观众停止质疑。
真正的 GI 系统要回答的是一个几何问题:从一个着色点出发,进入上半球的那些方向上,究竟有多少光能打过来? 这个问题的答案随位置、随方向、随时间变化,还要在 16 毫秒内算完,还要在 10 瓦的移动 GPU 上算完,还要在没有任何光追硬件的设备上"表现得像是算过了"。
围绕这个问题,业界长出了三条截然不同的技术路线:把辐射度烘进探针网格的 DDGI、只在屏幕内追踪的 SSGI、以及把辐射场分层存储的 Radiance Cascades。本文做的事情,是把这三条路线放回它们各自的约束里——数学表示、更新机制、可见性判定——然后回答工程上更实际的那个问题:当你要在一条产品线上同时支持从高端 PC 到千元安卓机的全谱系硬件时,这三条路线该怎么编排成一条降级链。
阴影解决的是"光到不到得了",GI 解决的是"光经过多次弹射之后还剩多少"。两者的计算量差异不在于多几次弹射,而在于弹射把问题的维度从二维升到了六维。
直接光照的经典解法是 shadow map:把光源深度投影到一张纹理上,着色时比较深度。这是一个查表问题,代价是 。而辐照度是半球上的积分:
其中 是从着色点 沿方向 收到的入射辐射度, 是法线半球。注意积分变量是方向——对每一个方向,你还要知道那束光在传播途中被什么挡住了、又被什么颜色染过。光是弹射的,一次弹射就把"沿路径积分"这个问题引了进来,而路径长度是无界的。
工程上通常做两个截断:弹射次数截断(Godot 官方文档对间接漫反射的说法是,游戏引擎里通常限制在 1 到 2 次,烘焙 lightmap 时可以到 16 次)和方向分辨率截断。后者正是探针方法的立足点——既然方向上积分太贵,那就把方向维度积分掉,只留下位置维度。
于是整个 GI 工程问题被重述为:设计一个关于位置的分段常数场 ,让它逼近真实的辐照度场,并且在运行时以可控的代价更新它。 所有主流方案——从最早的 irradiance volume 到今天的 DDGI 与 Radiance Cascades——都是这个句子的不同实例化。差别在于场的表示、更新时机、可见性处理三处。
顺便说清边界:本文讨论的是间接漫反射与间接镜面中的漫射部分。镜面反射(间接高光)的处理路径完全不同——SSR、Radiance Cache、DDGI 的 glossy 变体各有各的问题域,本文只在它们与漫反射共享数据结构的交叉处提及。
一个 GI 方案好不好,取决于它用什么量来表示场。三种主流表示在数学上差异很大。
最经典的做法是在空间中放置探针,每个探针存一个方向相关的辐照度函数。存法上,SH(球谐)基展开是最省事的选择:9 个系数就能把低阶辐照度压得很准。
代价是方向维度的分辨率被死死限制在二阶多项式级别。你永远看不到探针之间的方向性细节——这正是 NVIDIA 自己在 RTXGI-DDGI 文档里承认的局限:"DDGI 组合计算得到的漫反射辐照度本质上属于低频信息,无法捕捉精细的高频细节。"(原文表述为 low frequency global illumination signal,高频辐射度量与几何细节无法复现)
DDGI 的做法不同:它不压成 SH,而是给每个探针配一张二维辐照度图,用八面体映射(octahedral mapping)把方向球面参数化到正方形的两个三角形上。于是方向维度从"9 个数"变成"一张小纹理"。
// 八面体编码:单位方向 <-> 方形域 [0,1]^2
float2 dir_to_oct(float3 n) {
n /= (abs(n.x) + abs(n.y) + abs(n.z));
float2 uv = n.xy;
if (n.z < 0.0) {
uv = (1.0 - abs(uv.yx)) * float2(n.x >= 0.0 ? 1.0 : -1.0,
n.y >= 0.0 ? 1.0 : -1.0);
}
return uv * 0.5 + 0.5;
}
二维纹理带来两个额外好处。第一,可以做多级 mip,每一级对应不同的模糊半径,于是采样时能按"探针离着色点多远"来选模糊程度——远处的探针取模糊层级,近处的取锐利层级。第二,插值不再是 SH 系数空间的插值,而是纹理空间的插值,这可以用硬件双线性过滤白送。
代价是显存。八面体图按探针数乘以纹理分辨率线性膨胀,而探针数本身按场景体积的三次方增长。这也是为什么 RTXGI-DDGI 文档把 "Probe data storage can become memory intensive in large environments" 列为明确限制。
Radiance Cascades(RC)走的第三条路更有意思。它不存辐照度,存的是辐射度——方向与位置都保留,只把空间维度分层。
它的核心观察是:一个位置的辐照度是它周围所有辐射度的角积分,因此天然低频;而直接存储"以某个点为中心、某个方向上的辐射度"保留了高频。RC 用级联解决空间问题:第 0 层的探针间距为 ,第 层间距为 ,每层探针存储的辐射度覆盖自己格子加上一层邻居的范围。
低层负责高频细节(每个格子单独存一份辐射度),高层负责低频(把范围扩大以覆盖更多能量)。层与层之间通过插值与邻域混合衔接。RC 的一个工程优势常被忽略:它没有时域累积,因此天然无噪声,不需要去噪器。DDGI 和大部分路径追踪类 GI 都要面对噪声问题(所以常配 SVGF 之类的时域滤波),RC 直接绕开了。
RC 的代价是层数与探针数的组合开销,以及更复杂的层间混合逻辑。截至 2026 年 10 月,RC 的公开参考实现与生产部署案例主要来自 Path of Exile 2 团队的 arXiv 论文(arXiv:2408.14425),以及社区的交互式复现教程,主流引擎中尚无默认集成——本文引用其作为技术对照组,不假定它已可直接商用。
| 维度 | SH 辐照度探针 | DDGI 八面体图 | Radiance Cascades |
|---|---|---|---|
| 方向分辨率 | 极低(9 系数) | 中(可调纹理分辨率 + mip) | 高(辐射度保留方向细节) |
| 空间分辨率 | 与探针间距同阶 | 与探针间距同阶 | 分层,高层低频低层高频 |
| 噪声 | 无(烘焙或直接积分) | 有(射线估计) | 无(无时域累积) |
| 显存 | 最低 | 中高(纹理随探针数线性增长) | 中高(层数 × 探针数) |
| 动态响应 | 需重烘焙或重积分 | 逐帧增量更新,有最小延迟 | 更新延迟取决于层数 |
这张表也是全文的骨架:三条路线不是在同一个维度上竞争,而是在"方向分辨率 vs 噪声 vs 显存 vs 动态延迟"四个维度上各占一个不同的位置。任何一条降级链,本质上都是在这些维度之间做工程妥协。
有了表示,还剩下位置怎么撒。
DDGI 使用的是相机相对(camera-relative)的规则网格:探针锚定在相机位置上的一组固定偏移上,随相机整体平移。当相机移动超过某个阈值时,网格整体重新锚定并触发一次全量更新;在这个阈值内,探针的采样数据随相机移动而平移,但每个探针仍然在它自己的位置上做射线追踪。
这个设计有一个容易被忽略的性质:同一个探针会先后服务多个世界位置。 探针数据存的是"在这个位置能收到多少光",而相机移动改变了哪些世界位置被照亮。做法上常见两种:一种是让探针跟着相机跑并持续更新(DDGI 的做法),另一种是让探针钉在世界空间不动、只更新可见区域(静态烘焙探针的思路)。
DDGI 文档明确提到了它的一个根本性约束:"辐照度在探针中时域累积,因此当光照发生变化时,存在不可避免的最小延迟。" 换句话说,你在探针里看到的间接光,永远是若干帧之前的世界的间接光。这不是实现缺陷,而是时域累积本身的性质。
设探针间距为 ,场景中平均遮挡体厚度为 。当 时,相邻探针之间的可见性差异无法被分辨,会出现典型的漏光(light leak);当 时,探针数量按 爆炸。
实践中常见的经验值是让 大致等于场景中主要遮挡结构的典型尺寸。这不是能靠数学算出来的量,通常要靠观察漏光位置来调——这也解释了为什么 GI 的调试总是从"看漏光"开始而不是从"看性能"开始。
把三条路线放到一条产品线上,编排问题就变成了这样:
图表加载中…
这张图有一个关键性质:降级链是叠加而非替换。 高精度路径通常只覆盖一部分空间(硬件 RT 有 BVH 覆盖不到的区域,屏幕空间追踪天然看不到屏幕外的物体),低精度路径负责把剩余的洞补上。所以引擎里真正要实现的不是"选一个 GI 算法",而是"多个 GI 结果的融合与仲裁"。
Godot 的做法提供了一个反面参照。Godot 4.7 官方文档列出的 GI 技术包括 ReflectionProbe、LightmapGI、VoxelGI、SSIL 与 SDFGI,每种都有独立的可用性与限制;文档同时指出 VoxelGI、SSIL 与 SDFGI 在 Mobile 与 Compatibility 渲染器中不可用。文档对烘焙方案的判断也很直接:"如果你的目标是最大化视觉质量,烘焙方案通常看起来更好,而且漏光更少。" 这句话点破了核心权衡——实时方案换来的是灵活性,代价是漏光与噪点。
而在 Godot 的社区里,DDGI 仍是一个开放提案。godot-proposals 的讨论 #15115(2026 年 7 月 3 日由 thXor05 发起)提议把 DDGI 作为引擎的额外 GI 技术加入,理由是它"不需要预烘焙"且能解决光泄漏与阴影泄漏;维护者 Calinou 在回复中把它引向 godot#119869 "Upgrade SDFGI to HDDAGI",并提到曾有人在贡献者频道里尝试把屏幕空间 pass 集成进 HDDAGI 以改善质量、降低漏光,代价是明显的性能开销。截至 2026 年 10 月 25 日实测,godotengine/godot 主仓库最近的推送为 2026-10-23,MIT 许可,星标数约 11.9 万;讨论 #15115 在 2026 年 10 月 24 日仍有新回复,社区里也有人指向 godot#122999 这个分支做局部 GI 的尝试。
DDGI 的核心是一套"用统计量近似可见性"的机制。理解它,就理解了 DDGI 相对普通辐照度探针的全部增量价值。
对每个探针,DDGI 向场景投出一组射线。每条射线返回三个信息:沿射线返回的辐射度贡献、命中点背后的 backface 距离(backface hit distance),以及用于加权的一组矩。
朴素做法是"射线打中就是可见",这会导致探针从被遮挡的位置收不到光,产生硬阴影式的暗斑。DDGI 的做法是记录 backface 距离的一阶矩与二阶矩:
这两个矩构成一个单侧切比雪夫(Chebyshev)不等式的输入。对任意 :
在着色点做可见性测试时,我们知道着色点到探针的距离 ,也希望知道"探针到着色点这条连线被遮挡的概率"。取 为连线距离,若 说明探针那一侧的平均可见面比当前点更近,也就是当前点很可能被遮挡,把该方向的辐照度权重压低:
// 单侧切比雪夫可见性权重
float visibility_weight(float mean, float variance, float dist) {
if (dist <= mean) return 1.0f; // 比平均可见面还近,基本畅通
float d2 = (dist - mean) * (dist - mean);
return variance / (variance + d2); // 越远越可能被遮挡
}
这就是"统计遮挡测试"的实际形态:不做几何求交,用矩来近似。代价是它只能给出统计意义上的可见性,无法区分"被一面墙挡住"和"被一块布挡住",这正是漏光与暗斑同时可能出现的根源。
一帧内对所有探针投全部射线是不可能 budgets。DDGI 的做法是把更新摊销到多帧:每帧只更新一部分探针,探针的采样则通过时域抖动复用。
一个直观的实现是让每帧更新一个 8x8 的探针块,未更新的探针沿用上一次的辐照度与矩。探针数量多、更新周期长时,单帧成本就压到了可接受区间;但更新周期越长,"光照变化的最小延迟"就越长——这与前面提到的时域累积性质是同一枚硬币的两面。
射线在探针周围的球面上按黄金角(golden angle)分布生成,位置做逐帧抖动以在时间上均匀覆盖球面。命中点返回的辐射度按余弦加权累积进探针的辐照度图,这是蒙特卡洛估计半球积分的标准做法。
最终采样时,一个像素的辐照度由周围若干探针插值得到。权重通常由两部分相乘:空间距离权重(近的探针权重大)与方向权重(探针的法线与像素法线接近时权重大)。
float3 sample_probe_grid(ProbeGrid grid, float3 worldPos, float3 normal) {
float3 acc = 0.0f; float wsum = 0.0f;
for (int i = 0; i < kProbeCount; ++i) {
float3 d = grid.probePos[i] - worldPos;
float dist = length(d);
float spatial = saturate(1.0f - dist / grid.probeSpacing);
float dirW = saturate(dot(normal, -d / max(dist, 1e-4)) * 0.5f + 0.5f);
float w = spatial * spatial * dirW;
acc += grid.irradiance[i] * w;
wsum += w;
}
return acc / max(wsum, 1e-4f);
}
距离权重用二次方衰减是有讲究的:平方让影响范围收缩得更快,避免远处探针把近处的阴影"抹平",这正是漏光的第二个来源。
截至 2026 年 10 月 25 日实测,NVIDIA GameWorks 的两个公开仓库状态如下:NVIDIAGameWorks/RTXGI-DDGI 星标约 875,最后推送为 2024-02-05;NVIDIAGameWorks/RTXGI 星标约 368,最后推送为 2026-10-13,其仓库描述明确提到 "RTXGI v2.0 Update including Neural Radiance Cache and Spatial Hash Radiance Cache"。
这个组合透露出一个信息:NVIDIA 把重心从 DDGI 单一算法,转移到了更通用的一组 radiance cache 结构上。DDGI 仓库的文档还提到一个对工程部署很关键的性质——"探针可以被预先计算并加载到不支持 GPU 光追的平台上,从而为所有平台提供统一的光照路径"。这条性质让 DDGI 有了一个别的实时 GI 方案不具备的定位:它可以是唯一的运行时 GI 路径,而烘焙只是它的离线预计算形态,而不是与之并列的第二套系统。
SSGI(Screen Space Indirect Lighting)走的是完全不同的思路:不追踪场景几何,只在屏幕空间里追踪。
典型流程是:给定当前像素的法线与深度,沿半球方向在屏幕空间步进;每一步用前一帧的深度缓冲与颜色缓冲判断是否被遮挡,命中则取其辐射度作为间接光贡献。由于所有数据都来自上一帧的屏幕纹理,实现成本极低,且天然受益于 TAA/TSR 的时域累积。
它的问题也是结构性的,有四条:
Godot 的 SSIL 在这一点上做得很直白:官方文档说明它的渲染"默认在半分辨率下执行(然后线性放大)以保证合理的性能水平",并且明确标注在 Mobile 与 Compatibility 渲染器中不可用。一个半分辨率、无去噪、屏幕空间的间接光通道,在定位上只能是"有总比没有好"的补充,而不是主力。
工程结论很明确:SSGI 是降级链的末端,不是起点。 它最适合的位置是当硬件光追不可用、DDGI 需要光追支持而软件光追又太贵时,作为一个零硬件门槛的兜底。
Radiance Cascades 值得单独一节,因为它的思路转变足够彻底。
传统的探针方法存的是辐照度——方向维已经被积分掉,代价是高频信息永久丢失。RC 存的是辐射度:一个 probe 不只记"我总共收到多少光",而是记"从我周围某个空间单元的某个方向上来的是多少光"。
实现上,每个 probe 存一个小型的三维辐射度体(常见做法是 4x4x4 的 RGB 体素),每个体素对应探针周围空间的一个格子。层级之间通过上采样衔接:第 k 层的探针覆盖的范围是第 k-1 层的两倍宽。
这带来三个直接后果:
代价集中在两点:内存(层数乘以每层探针数)和层间混合的复杂度。而且它并非万能——RC 同样需要解决"屏幕空间之外"的可见性,只是处理方式不同(用高层的粗粒度辐射度场来近似远场)。
RC 的公开材料截至 2026 年 10 月主要来自 Path of Exile 2 团队的 arXiv:2408.14425,以及社区基于该论文的交互式复现(如 jason.today/rc 的实现指南)。社区文章中把它描述为"一种高效、无噪声、面向 2D 与 3D 全局光照的新方法"。本文把它当作技术对照组:它提示了一个重要方向——不是改进辐照度探针,而是干脆不存辐照度。
现在到了工程上最实际的部分:一条产品线要覆盖高端 PC、主机与移动端,GI 层应该怎么搭。
一个可用的预算框架是按"每帧 GPU 毫秒"和"显存 MB"两个维度给每一层分配额度,然后让降级链在预算内自动选路。
| 层 | 触发条件 | 典型代价 | 显存 |
|---|---|---|---|
| 硬件 RT 路径 | 设备支持 DXR/Vulkan RT/Metal RT | 依赖场景复杂度,波动极大 | BVH + 命中点着色 |
| DDGI(硬件 RT 供射线) | 支持光追但不追求路径追踪 | 中等,可摊销到多帧 | 探针网格 + 辐照度图 + 矩 |
| DDGI(软件 RT 供射线) | 无光追但有 compute | 较高,需距离场等表示 | 同上 + 距离场 |
| SSGI | 兜底 | 低,但受半分辨率拖累 | 低 |
| 烘焙辐照度探针 | 离线流程存在 | 运行时几乎为零 | 探针数据(可流式加载) |
引擎要提供的是运行时探测 + 动态选路的能力。Godot 社区在讨论 #15115 时提出的设想正是这个形态:运行时检测显卡是否具备 RT 核心,据此在 DDGI 与 HDDAGI 之间切换;拥有对应硬件的玩家用 DDGI,其余玩家用替代方案。
当多个路径同时生效且结果重叠时,需要明确的仲裁规则,否则会出现亮度叠加或接缝。
一个务实的规则集是:
这三条规则背后的判断是:不同路径的误差是不相关的,因此加权融合的收益通常高于择一使用。这也是很多自研引擎在实践中发现的经验——哪怕各路径单独看都有明显瑕疵,叠在一起反而比任何单一路径都稳定。
如果要研究一个把 GI 做进生产管线的完整样本,Unreal Engine 5.8 的 Lumen 文档是目前公开信息最全的之一(Epic 的 EpicGames/UnrealEngine 仓库并未公开,GitHub API 拉取返回 404,因此以下全部依据官方文档)。
文档里几个值得单独拎出来的设计决策:
Surface Cache 与卡片化参数化。 Lumen 为场景表面生成自动参数化(Surface Cache),用于在射线命中点快速查光照。每个 mesh 的捕获位置称为 Cards,默认放 12 张,可在 Static Mesh 编辑器的 Build Settings 里用 Max Lumen Mesh Cards 调整。没有 Surface Cache 覆盖的区域在视图模式里显示为粉色,这些区域"不会弹射光,在反射中显示为黑色"。骨骼网格因为动态特性没有离线生成的卡片,默认在其预蒙皮包围盒周围放置 6 张,导致覆盖与质量相当有限。
软件光追的距离场策略。 默认对每条光线的前两米追踪单个 mesh 的距离场以保证精度,其余部分走合并后的 Global Distance Field 以加速。项目设置里的 Software Ray Tracing Mode 提供 Detail Tracing 与 Global Tracing 两种。
屏幕追踪优先的求值顺序。 文档明确:Lumen 先做 Screen Traces,找不到命中或光线穿过表面之后,再退回到更可靠的方法。文档也诚实指出屏幕追踪的代价——它"极大限制了美术调控的能力",因为像 Indirect Lighting Scale 这类调参只作用于间接光,而屏幕追踪无法正确支持它。
最终聚集包含天空光。 文档指出天空光被作为 Lumen 最终聚集过程的一部分来求解,包含天空遮蔽,这让室内比室外暗得多,效果更自然。
性能基线。 文档给出的 Epic 质量档位目标是:在次世代主机上以 1080p 内部分辨率跑 GI 与反射约 8 毫秒,并依赖 Temporal Super Resolution 输出接近原生 4K 的画质。
与烘焙的互斥。 文档说明启用 Lumen 后,预先计算的静态光照贡献会被禁用、所有 lightmap 被隐藏。这是一条重要的架构分界线:Lumen 不是"烘焙的补充",而是"替代烘焙"。这与 DDGI 文档里"探针可预计算以统一所有平台光照路径"的思路形成了两种不同的整合哲学。
Godot 4.7 的文档给出了一个模块化的 GI 谱系,每种技术的视觉代价被逐项列出:VoxelGI 因体素化特性,在墙与地板薄于一个体素时会出现漏光,官方建议所有实体表面至少厚于一个体素,且斜面上可能出现条纹状伪影,可通过调整 bias 属性或旋转 VoxelGI 节点改善;SDFGI 会有漏光与可见的级联切换(visible cascade shifts),其 GI 细节层次随相机到表面的距离变化。
这些描述很有价值——它们把"GI 看起来不对"从玄学变成了可操作的检查项。漏光先查遮挡体厚度,条纹先查空间表示的各向异性,色阶断层先查精度。 这条诊断顺序比任何单一算法的实现细节都更实用。
综合前面的分析,落地一条 GI 降级链时反复出现的坑大致是这六个。
陷阱一:探针间距与遮挡体厚度失配。 症状是隔墙漏光。诊断方法是把探针间距临时减半,看漏光是否消失——若消失,就是间距问题;若不消失,是遮挡体太薄或距离场表示有洞。
陷阱二:方向维度被过度压缩。 症状是墙角、物体边缘的间接光完全没有方向性,一片死平。SH 探针是典型受害者,八面体图可以缓解但不能根除。
陷阱三:把统计可见性当成几何可见性。 切比雪夫权重是概率估计,"遮挡 60%"不等于"遮挡 60% 的区域"。表现是间接阴影边界出现半透明的灰雾,而不是清晰的软阴影。
陷阱四:时域累积带来的延迟被误判为 bug。 探针里的光是几帧前的世界的光。表现是快速移动的光源(比如玩家手里的火把)照亮墙面有肉眼可见的滞后。解法是缩短更新周期或提高受影响探针的更新优先级,而不是去调混合权重。
陷阱五:多路径叠加导致能量超发。 硬件 RT 与探针覆盖同一像素时若简单相加,会出现比任何单一路径都亮的区域,且亮度随相机移动跳变。必须用遮罩做互斥。
陷阱六:把 GPU 侧成本当成唯一成本。 探针网格的 CPU 侧维护(网格锚定、可见性剔除、更新列表维护)在大型场景里可能比 GPU 更新还贵。一个纯粹的"GI 团队只管 GPU"的分工方式在这里会失效。
关于工具链,值得单独提一句 GPU 验证:Khronos 官方的 KhronosGroup/Vulkan-ValidationLayers(实测约 1047 星,Apache-2.0,2026-10-24 有推送)与 KhronosGroup/SPIR-V-Cross(约 2536 星)这类工具,能帮你确认着色器在跨平台编译后语义一致;而 microsoft/DirectXShaderCompiler(约 3674 星,2026-10-23 推送)是 DXC 本身,在着色器变体与跨平台编译上的排查经常用到。GI 路径之所以难调,很大程度上是因为它同时跨了数学、GPU 与内容三个层,工具链的断裂会让人误判问题所在。
如果要在一个自研或基于现有引擎的项目里搭一条可维护的 GI 降级链,可以按这个顺序推进。
第一步,先定测量口径。 在写任何 GI 代码之前,先能回答:"当前这一帧的间接光贡献是多少?"没有这个测量能力,后面所有的优化都是盲改。
第二步,把烘焙路径做成一等公民。 烘焙方案在静态内容上的画质与漏光表现都优于实时方案,且运行时成本接近零。RTXGI-DDGI 文档里"探针可预计算并加载到所有平台"的说法,值得作为架构目标而不是技巧。
第三步,明确每条路径的不变量。 硬件 RT 路径应该追求物理正确,探针路径应该追求覆盖与低延迟,屏幕空间路径应该追求零硬件门槛。给每条路径写清楚它保证什么、不保证什么,美术与程序才知道什么时候该用哪一个。
第四步,建立融合的仲裁规则,并把它们写进文档。 遮罩互斥、优先级、基底叠加——这些规则如果只存在于某个人的脑子里,每次重构都会重新踩一遍。
第五步,做诊断工具,而不是调参。 Godot 文档里那些具体到"墙至少厚于一个体素""调整 bias 属性或旋转 VoxelGI 节点"的建议,才是 GI 调试应有的颗粒度。引擎应该提供可视化探针网格、可视化可见性权重、可视化各路径贡献分量的开关。
第六步,把降级做成产品决策而非技术兜底。 一个只有 8% 玩家用得上的高端路径,和一个覆盖 100% 玩家的低端路径,是两种完全不同的美术指导方向。降级链的每一级都应当对应一个明确的用户群与画质预期。
回到开头的问题。DDGI 走的是"用统计量补足可见性"的路线,RC 走的是"换一种辐射度表示"的路线,屏幕空间走的是"干脆只求近似"的路线。它们看起来分岔,但底层都在回答同一个问题:在有限预算下,如何近似一个六维的辐射场。
有几个可能的收敛方向值得留意。
其一,硬件光追的普及会重排优先级,但不改变数据结构问题。 光追让"射线追踪"变便宜了,但"把结果存到哪里、怎么插值"的问题一个都没少。NVIDIA 从 DDGI 转向包含 Neural Radiance Cache 与 Spatial Hash Radiance Cache 的 RTXGI v2.0,看起来正是这个方向——用可学习的结构替换手工设计的探针网格。
其二,学习型 radiance cache 正在成为第三条路线的补充而非替代。 它的思路是用神经网络直接预测辐照度场,跳过显式的射线积分与探针存储。这是本轮 GPU 光追浪潮里最有希望压缩成本的方向之一,但截至 2026 年 10 月,其在生产项目中的大规模可复现质量数据仍然缺乏公开验证。
其三,降级链本身可能会被更少的技术吃掉。 如果一台设备能以可接受代价跑硬件 RT 命中点着色,那么"没有烘焙、没有探针、没有屏幕空间"的全动态路径是最简洁的架构。Lumen 走向"替代烘焙"而非"补充烘焙",正是这种趋势的具体体现。
但对绝大多数同时面对 PC 与移动端的团队来说,"多条路径 + 明确仲裁 + 逐级降质"仍然是唯一现实的选择。差别只在于,你是把这套东西设计出来的,还是每次逐帧手搓。
一句话摘要:全局光照的本质是用有限预算近似一个六维辐射场,DDGI 用统计可见性补足探针、Radiance Cascades 用分层辐射度保住高频、屏幕空间 GI 兜底无硬件场景,而把它们编排成一条可维护的降级链,才是横跨 PC 与移动端的引擎真正要解的题。
Conversation
0 条