Banner

现代实时 GI 技术全景

笔记持续更新中

一、 导论:为什么实时 GI 如此艰难?

1.1 渲染方程:一切的起点

Kajiya 1986 年提出的渲染方程定义了所有 GI 技术必须近似的物理目标:

其中:

  • 是从点 沿方向 出射的辐射度
  • 是该点的自发光项
  • 是 BRDF
  • 是入射辐射度——这里是 GI 的难点:它本身又是某个其他点的

这意味着求解严格的 需要递归地积分整个场景,理论上是无穷次反弹的积分嵌套,是一个 Fredholm 第二类积分方程

1.2 实时渲染的帧预算悖论

在 60 fps 下,每帧只有 16.67 ms。如果按照 1080p × 4 SPP 的路径追踪估算:

即使在 RTX 4090 上,也只能勉强支撑这个量级。整个行业的 GI 发展史,本质上就是在固定预算下,用各种巧妙的近似与时空复用,逼近无穷次弹射真值的过程。

GI 技术演进时间轴
点击或悬浮时间节点查看技术详情,感受算力与算法的博弈
请选择上方的时间节点

二、 屏幕空间时代的妥协:SSGI

Screen Space Global Illumination 是最”廉价”的 GI 近似,同时也是最常被误用的技术之一。

SSGI 效果示意:屏幕空间的漫反射近似与接触阴影

上图展示了屏幕空间 GI 对接触遮蔽和短程漫反射的改善效果。

2.1 核心机制

在深度缓冲与颜色缓冲已有的前提下,对每个像素做屏幕空间 Ray March,把命中像素作为”代理光源”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 简化版 SSGI 核心循环
half3 SSGI_Trace(float2 uv, float3 viewPos, float3 normal)
{
half3 indirect = 0;

[loop] for (uint i = 0; i < SAMPLE_COUNT; i++)
{
float3 dirVS = SampleHemisphere(uv, i, normal); // 重要性采样半球
float3 rayEndVS = viewPos + dirVS * MAX_DIST;

// 投影到屏幕空间
float4 rayEndCS = mul(_ProjMatrix, float4(rayEndVS, 1.0));
float2 rayEndUV = rayEndCS.xy / rayEndCS.w * 0.5 + 0.5;

// 沿屏幕空间步进比较深度
float2 hitUV;
if (RayMarchScreenSpace(uv, rayEndUV, viewPos, dirVS, /*out*/ hitUV))
{
indirect += SAMPLE_TEXTURE2D(_GBuffer0, sampler_Point, hitUV).rgb;
}
}
return indirect / SAMPLE_COUNT;
}

2.2 屏幕空间综合症

  • 摄像机视野外、被遮挡的几何体的间接光完全缺失
  • 摄像机转动时,间接光发生剧烈跳变(Temporal Instability)
  • 无法模拟多次反弹

💡SSGI

SSGI 在现代管线中的正确生态位是,而非主力 GI 方案。建议与 DDGI/Lumen 类低频间接光叠加,并强制要求管线已有充分的 TAA/TSR 颞部稳定。


三、 预计算时代的优雅数学:PRTGI

Precomputed Radiance Transfer GI 是 GPU 时代最具数学优雅性的 GI 方案,由 Peter-Pike Sloan 于 2002 年 SIGGRAPH 提出。

PRT 球谐

球谐函数(Spherical Harmonics)的前三阶共 9 个基函数。PRT 将光照的”传输关系”投影到这组基底上。

3.1 核心思想:存的是”关系”而非”光”

Unity Light Probes(常规烘焙) PRTGI
存储内容 某点最终的辐照度结果(已含光源信息) 几何体对光的遮蔽与反弹关系(不含光源)
光源动态性 ❌ 必须静态 ✅ 完全动态
几何体动态性 静态提供 GI / 动态接收 GI ❌ 必须完全静态

PRT 运行时计算:

其中 是离线烘焙的转移系数实时动态的光源球谐表示。二者点积即可得到最终光照——这在 GPU 上几乎是零开销的操作。

3.2 SH 基函数交互可视化

下面这个组件实时渲染了 SH 前 3 阶(共 9 个基函数)的三维图像。每个基函数 用极坐标曲面表示:半径 = 函数值的绝对值红色 = 正瓣,蓝色 = 负瓣。这就是 PRT 用来近似任意光照分布的”基底字典”。

⚡ Three.js · SH 基函数实时渲染 (可交互)
拖拽旋转视图 · 滚轮缩放
上排:l=0 (1 系数) 中排:l=1 (3 系数) 下排:l=2 (5 系数)
共 9 个 RGB 系数 = 27 floats / 探针
Fig 1.

球谐函数(Spherical Harmonics)前三阶基函数的空间分布可视化。

3.3 SH 在 Shader 中的实际重建代码

下面是在 c 中采样 9 系数 SH 重建漫反射辐照度的标准实现:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 9 个 SH 系数(每个分量 RGB),共需 27 floats
struct SHCoeffs
{
float3 c[9];
};

// 给定法线方向 N,重建漫反射辐照度 E(N)
// 这是 PRT/Light Probe 系统在 PS 阶段的核心数学
float3 EvaluateSH9(float3 N, SHCoeffs sh)
{
// ---- Band 0 (constant) ----
float3 result = sh.c[0] * 0.282095; // Y_0^0

// ---- Band 1 (linear) ----
result += sh.c[1] * (0.488603 * N.y); // Y_1^-1
result += sh.c[2] * (0.488603 * N.z); // Y_1^0
result += sh.c[3] * (0.488603 * N.x); // Y_1^+1

// ---- Band 2 (quadratic) ----
result += sh.c[4] * (1.092548 * N.x * N.y); // Y_2^-2
result += sh.c[5] * (1.092548 * N.y * N.z); // Y_2^-1
result += sh.c[6] * (0.315392 * (3.0 * N.z * N.z - 1.0)); // Y_2^0
result += sh.c[7] * (1.092548 * N.x * N.z); // Y_2^+1
result += sh.c[8] * (0.546274 * (N.x * N.x - N.y * N.y)); // Y_2^+2

return max(result, 0.0);
}

// Unity / URP 中的实际写法(已乘 cos lobe 卷积系数)
// 详见 ShaderLibrary/EntityLighting.c 的 SampleSH9 函数
half3 SampleProbeSH(half3 normalWS)
{
real4 SHCoefficients[7] = { unity_SHAr, unity_SHAg, unity_SHAb,
unity_SHBr, unity_SHBg, unity_SHBb,
unity_SHC };
return max(half3(0,0,0), SampleSH9(SHCoefficients, normalWS));
}

3.4 工业落地:Enlighten 的兴衰

Unity 内置的 Enlighten Realtime GI 是最接近 PRT 思想的产品化实现:它在烘焙阶段把场景切分为簇(Clusters),预计算簇之间的辐射度传输矩阵(Form Factors),运行时允许动态光源驱动这个传输矩阵产生实时间接光。

但它在 Unity 中经历了”被废弃 → 因 SaaS 授权问题回归 → 在新管线中又被 APV 替代”的曲折历史,是 GI 工业化的一个典型缩影。


四、 体素时代的探索:VXGI / SVOGI

在 SSGI 与 PRT 之间,Voxel Cone Tracing 是一个不可忽视的里程碑。Cyril Crassin 2011 年提出,NVIDIA 在 2014 年通过 VXGI 推广,CryEngine 用其变种 SVOGI 在《孤岛危机 4》和《狩猎对决》中落地。

VXGI 体素化场景与锥追踪示意

VXGI 把场景体素化进 3D 纹理,每帧从着色点向半球发射”圆锥”做追踪,圆锥沿途累积体素辐射度。

4.1 核心算法三步曲

  1. 场景体素化(Voxelization):通过几何着色器或 Compute Shader 把场景写入一个稀疏 3D 纹理(Sparse Voxel Octree, SVO),每个体素存储反照率、法线、自发光与不透明度。
  2. 辐射度注入(Light Injection):把直接光照结果”注入”对应体素,并生成多级 Mipmap,作为不同尺度间接光的近似。
  3. 锥追踪(Cone Tracing):从着色点向半球投射 ~6 个圆锥,每个圆锥沿途按距离从粗糙到精细 Mip 采样体素,等效于一次低成本的”模糊路径追踪”。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 简化版体素锥追踪 - 单个圆锥
float4 ConeTrace(float3 startPos, float3 dir, float coneAperture)
{
float4 accum = 0;
float dist = VOXEL_SIZE; // 起步距离避免自相交

while (dist < MAX_DIST && accum.a < 0.95)
{
float diameter = max(VOXEL_SIZE, 2.0 * tan(coneAperture * 0.5) * dist);
float mipLevel = log2(diameter / VOXEL_SIZE);

float3 samplePos = (startPos + dir * dist) * INV_SCENE_BOUNDS;
float4 voxel = _VoxelTex3D.SampleLevel(s_linear, samplePos, mipLevel);

// Front-to-back 累积,符合 Beer-Lambert 透射
accum += (1.0 - accum.a) * voxel;
dist += diameter * 0.5;
}
return accum;
}

4.2 优劣总结

优势: 全动态、单次反弹质量高、与几何复杂度解耦、不依赖硬件光追。
劣势: 体素分辨率受显存限制(典型 256³ 已占数百 MB),导致漏光细节丢失;二次反弹需多 Pass 重新注入,成本陡增;在大世界场景中难以维护稀疏 SVO 的更新。

VXGI 的思想现在依然活跃:UE5 Lumen 的 Global Distance Field + Surface Cache 体系本质上是 VXGI 思路的现代演化版本,只是把”体素”换成了”距离场 + 卡表面缓存”。


五、 面元时代的工业实践:Surfel GI

Surfel-based Global Illumination(面元 GI)是 EA Frostbite 在 2018 GDC 推出、被 Massive 的 Snowdrop 引擎用在《阿凡达:潘多拉边境》等 3A 项目中的方案。

Surfel GI:用面元替代探针,把几何信息显式地"撒"到表面上

Fig 5.1.

Surfel GI 把场景表面采样为大量持久化的圆盘”面元”,每个面元缓存自己的辐照度并跨帧累积,从根本上绕开了探针网格的漏光与密度问题。

5.1 核心数据结构:面元(Surfel)

每个面元是一个圆盘代理,存储:

  • 世界空间位置 、法线 、半径
  • 累积的多次反弹辐照度(球谐表示)
  • 命中计数器(用于自适应分裂/合并)
1
2
3
4
5
6
7
8
9
struct Surfel
{
float3 position;
float3 normal;
float radius;
float accumulatedHits;
SH9_RGB irradiance; // 累积辐照度 SH
uint cellIndex; // 加速结构 cell 索引
};

5.2 面元 GI vs 探针 GI 的本质区别

🔴 DDGI 探针
  • 规则空间网格 → 空间冗余
  • 探针不感知几何 → 薄壁漏光
  • 密度由网格决定 → 大世界爆炸
💖 Surfel 面元
  • 仅存在于可见表面上 → 0 空间浪费
  • 面元贴合几何 → 天然遮挡感知
  • 自适应密度 → 大世界友好

5.3 时间复用:Frostbite 的”持久化辐照度”

Surfel GI 的真正威力来自时间累积:每帧只需对一小部分面元发射光线(通常是数千条/帧),结果以指数滑动平均的方式合并进面元缓存。这意味着——

整个场景的 GI 可以在 1~2 秒内逐步收敛到接近 Path Tracing 的质量,但每帧只花费几毫秒。

这套思路深刻影响了后来的 UE5 Lumen Surface Cache,二者在哲学上是同一脉的”屏幕空间外的持久缓存”。


六、 RTX 时代的破局者:DDGI

Dynamic Diffuse Global Illumination 是基于硬件光追的主流落地方案,由 Morgan McGuire 团队提出并被 NVIDIA RTXGI SDK 产品化。

DDGI 探针网格:每个探针用八面体映射存储辐照度和深度矩

Fig 6.1.

DDGI 在场景中分布探针网格,每帧从探针发射实时光线,捕获动态间接光照。

6.1 核心数据结构:八面体映射

每个 DDGI 探针存储两张**低分辨率八面体(Octahedral)**纹理:辐照度(8×8)和深度矩(16×16)。八面体映射把整个球面无奇异点地展开到一个正方形 UV 空间,是探针数据的关键编解码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// === 八面体编码:3D 方向 → 2D UV ===
float2 OctEncode(float3 n)
{
n /= (abs(n.x) + abs(n.y) + abs(n.z));
float2 oct = n.xy;
if (n.z < 0.0)
{
// 折叠下半球到外角
oct = (1.0 - abs(oct.yx)) * sign(oct);
}
return oct * 0.5 + 0.5; // 映射到 [0, 1]
}

// === 八面体解码:2D UV → 3D 方向 ===
float3 OctDecode(float2 uv)
{
uv = uv * 2.0 - 1.0;
float3 n = float3(uv.x, uv.y, 1.0 - abs(uv.x) - abs(uv.y));
if (n.z < 0.0)
{
n.xy = (1.0 - abs(n.yx)) * sign(n.xy);
}
return normalize(n);
}

6.2 防漏光魔法:Chebyshev 不等式

DDGI 最巧妙的设计在于用 深度 + 深度平方 的两阶矩配合 Chebyshev 不等式给出软可见性概率——这是它优于传统三线性插值探针的核心:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// DDGI 软可见性测试
// 返回该方向上探针对采样点的可见性概率(0 = 被遮挡, 1 = 完全可见)
float ChebyshevVisibility(float3 probePos, float3 surfacePos,
Texture2D depthMomentsTex, SamplerState s)
{
float3 toSurface = surfacePos - probePos;
float distToSurface = length(toSurface);
float2 octUV = OctEncode(normalize(toSurface));

// 采样该方向上探针记录的 (mean_depth, mean_depth²)
float2 moments = depthMomentsTex.Sample(s, octUV).xy;
float mean = moments.x;
float variance = max(moments.y - mean * mean, 1e-6);

// 表面在探针记录的"前方",肯定可见
if (distToSurface <= mean) return 1.0;

// Chebyshev 不等式给出可见性概率上界:
// P(X >= t) <= variance / (variance + (t - mean)^2)
float d = distToSurface - mean;
float pMax = variance / (variance + d * d);

// Light-bleed reduction:经验性的硬阈值,避免软阴影下的漏光
pMax = smoothstep(0.2, 1.0, pMax * pMax);
return pMax;
}

6.3 探针网格 + 漏光交互演示

下面这个交互组件用 Canvas 模拟了一个 2D 房间:拖动光源可以看到探针每帧捕获的辐照度变化,勾选 Chebyshev 测试可以观察到薄墙漏光被消除的过程:

⚡ DDGI 探针网格 · 漏光交互演示
🔴 拖拽红色光源 · 灰色矩形为障碍墙 · 圆点为探针,光环颜色等于其捕获的辐照度

观察提示: 关闭 Chebyshev 后,把光源拖到 Room A,可以看到 Room B 内的探针错误地”亮起来”——这就是著名的探针漏光问题。开启 Chebyshev 后,探针会根据自己记录的最近墙壁深度矩信息,软性地拒绝穿墙能量。

6.4 局限性

痛点 说明
薄壁漏光 墙体厚度小于探针间距时,Chebyshev 测试也会失效。生产中常用 Probe Backface Hit Threshold 强制裁剪
大世界爆炸 8×8×8 的均匀网格在 1km × 1km 场景中需要数百万探针。RTXGI 推出了 Volume 分级方案缓解
更新带宽 每帧每探针发射 64256 条光线,1080p 60fps 下光追开销可达 24 ms

七、 迈向完全路径追踪的桥梁:ReSTIR GI

Reservoir-based Spatiotemporal Importance Resampling for GI(Ouyang et al., EGSR 2021)是目前通向实时路径追踪最重要的算法突破。

ReSTIR GI:通过蓄水池采样在时空邻域复用高质量光路

Fig 7.1.

左侧为 1 SPP 原始路径追踪(大量噪点),右侧为 ReSTIR GI 复用时空邻域样本后的结果(接近收敛)。

7.1 核心概念:蓄水池采样

传统路径追踪每像素只有 1~4 SPP,噪点严重。ReSTIR 的关键观察是:

相邻像素的”好样本”对你也是高质量样本——只要做一次重要性重采样校正。

每个像素维护一个蓄水池(Reservoir),里面装着当前最优的样本及其权重 。算法在三个维度复用样本:

  1. Initial Sampling —— 每像素追踪少量初始路径(通常 1 SPP)
  2. Spatial Reuse —— 从邻近像素的 Reservoir 中借用样本,加权合并
  3. Temporal Reuse —— 从上一帧的 Reservoir 中复用未失效的历史样本
1
2
3
4
5
6
7
8
9
10
// Reservoir 加权合并的核心 update 算法
void Reservoir::Update(Sample s, float weight, float rng)
{
wSum += weight;
M += 1;
if (rng < weight / wSum)
{
sample = s; // 以 weight/wSum 的概率接受新样本
}
}

经过空间 + 时间复用后,单帧内的等效 SPP 可放大 100 倍以上,几乎无视频路径追踪的代价开辟了一条实时落地的捷径。

7.2 Ghosting 与 Boiling 的工程挑战

⚠️ 时空复用的代价

当几何或光源剧烈变化(爆炸、瞬移、武器切换),历史 Reservoir 数据失效,单帧来不及刷新就出现:

  • Ghosting(残影) —— 光源关闭后画面残留幽灵般的间接光
  • Boiling(沸腾) —— 高方差区域产生跳变的“沸腾”噪点

常见对策: 基于运动矢量的 Reservoir 重投影 + 历史长度限制(通常 M_max = 20~30)+ 专用降噪器(NRD / OptiX Denoiser)。


八、 工业界双雄:引擎级 GI 管线深度对比

8.1 Unreal Engine 5:Lumen 的混合哲学

Lumen 的核心是多层光追源 + 表面缓存的混合系统:

Tier 1: 屏幕空间
SSGI 类似的 Screen Trace,处理细节接触阴影与近距离反射
Tier 2: SDF 软光追
Mesh Distance Field + Global Distance Field,无硬件光追时的主力路径
Tier 3: 硬件光追
Hardware RT 提供精确远距离命中,吸收 ReSTIR 思想做样本复用
🔑
关键解耦:三层光追的命中结果统一写入 Surface Cache(贴图空间持久化辐照度,类似 Surfel 思想),下游的 Final Gather 阶段只采样 Surface Cache,与具体追踪手段完全解耦。

Lumen 调试视图实战

UE5 提供了一套强大的 r.Lumen.* Debug 视图,对 TA 排查问题至关重要:

调试视图 排查问题
ShowFlag.VisualizeLumenScene 查看 SDF/Surface Cache 是否覆盖目标几何,缺失会导致黑斑
r.Lumen.Visualize.CardPlacement 1 显示 Surface Cache Card 分布,过密区域意味着多余开销
r.Lumen.Visualize 2 (Reflections) 反射的命中分布;噪点严重通常意味着该处需要更高 RT 预算
stat GPU + LumenScene Lumen Scene Update 占用 ms,PS5 上的预算约为 1.5~2.5 ms

典型性能预算(PS5/Xbox Series X,1080p 内部分辨率,Quality 档):

步骤 耗时
LumenSceneUpdate ~ 1.8 ms
LumenScreenProbeGather ~ 2.4 ms
LumenReflections ~ 1.6 ms
Total Lumen ~ 5.8 ms (35% of 16.67ms budget)

8.2 Unity:从 Light Probes 到 APV

Unity APV 自适应探针体积:八叉树 Brick 自适应分布

Unity 当前主推的 Adaptive Probe Volumes (APV) 在 HDRP 与 URP 中已成为推荐方案。它放弃了手动摆放 Light Probe,改用:

  • 基于 Brick 的八叉树分级 —— 几何复杂区域自动加密,开阔区域稀疏
  • 流式加载 —— 大世界场景按需加载 Brick
  • Sky Occlusion —— 烘焙阶段计算每点对天空的可见性,运行时动态天光直接驱动间接光(这是 PRT 思想的延续)

APV 调试视图实战

调试视图 排查问题
Probe Volume → Display Probes 直接看探针位置与系数颜色,漏光最常见原因是 Brick 跨墙
Probe Validity 显示无效探针(位于几何内部),Dilation Threshold 调节自动有效化
Sampling Debug → Sampling Position 给定一个像素,可视化它从哪 8 个探针做三线性插值
Reflection Probes Debug 验证 Reflection Probes 与 APV 的混合是否平滑

APV 与 Lumen 的本质差异:

维度 Unity APV UE5 Lumen
数据形态 Brick 网格 + SH 系数 Surface Cache(贴图空间)
运行时光照 天光驱动 SH 重建(无 RT) 每帧 RT/SDF 追踪更新
动态几何 ❌ Brick 烘焙时固定 ✅ MDF 完全动态
移动端可用 ✅ URP 有适配版本 ❌ 需主机/PC 算力
主要适用场景 大世界、移动端、动态天光 写实 3A、室内复杂光照

💡注:

  • Unity APV (Adaptive Probe Volumes) 更倾向于一种“高性能、半动态”的方案,本质上是光照探针的进化版,非常适合移动端大世界。
  • UE5 Lumen 则是真正的“全动态”全局光照,对硬件性能要求极高,更适合追求极致画面效果的 PC 或主机平台。

九、 项目 GI 方案快速决策

🎯 基于平台与动态需求的 GI 选型方案
1. 目标平台
2. 场景动态性

十、 总结与未来展望

10.1 技术演进的内在逻辑

回顾整条进化轴,可以看到一条清晰的工程哲学迁移:

从”信息越多越好”(PRT/VXGI 存所有传输关系),到”复用越聪明越好”(Surfel/ReSTIR 用时空缓存逼近真值)。

每一代 GI 方案都是在”预计算精度 vs 运行时灵活性 vs 硬件成本”的三角形中,找到当前硬件代际下的最优平衡。

10.2 下一代:Neural Radiance Caching

NVIDIA 在 2021 年提出的 Neural Radiance Caching (NRC) 把 GI 缓存的形态推到了新的高度:用一个小型在线训练的 MLP 替代 Surfel/Probe 缓存,在场景中的每个着色点查询神经网络得到无偏的辐射度估计。

关键特征:

  • 在线训练:每帧用少量路径追踪样本反向传播更新 MLP 权重
  • 自适应:网络容量自动分配给高频区域
  • 首次让纯神经网络在实时光追中战胜了显式缓存方案

这指向了一个有趣的未来:GI 的下一阶段可能不再是”算法”而是”小模型”,每个游戏运行时都在背地里训练自己的辐射度网络。


📚 参考文献与延伸阅读
[1]

DDGI 原理 | “Dynamic Diffuse Global Illumination with Ray-Traced Irradiance Fields” — Majercik, McGuire et al., JCGT 2019

[2]

ReSTIR GI 论文 | “ReSTIR GI: Path Resampling for Real-Time Path Tracing” — Ouyang et al., EGSR 2021

[3]

ReSTIR DI 原始论文 | “Spatiotemporal reservoir resampling for real-time ray tracing with dynamic direct lighting” — Bitterli et al., SIGGRAPH 2020

[4]

PRT 原始论文 | “Precomputed Radiance Transfer for Real-Time Rendering in Dynamic, Low-Frequency Lighting Environments” — Sloan et al., SIGGRAPH 2002

[5]

Surfel GI | “Global Illumination Based on Surfels” — EA Frostbite, GDC 2018

[6]

VXGI / SVOGI | “Interactive Indirect Illumination Using Voxel Cone Tracing” — Crassin et al., 2011

[7]

Neural Radiance Caching | “Real-time Neural Radiance Caching for Path Tracing” — Müller et al., SIGGRAPH 2021

[8]
[9]

Unity APV 文档 | Unity Docs > Adaptive Probe Volumes