一个场景的亮度分成两部分:直接光(从光源直达表面)与间接光(光在物体间弹跳后到达)。直接光好算,间接光则是"真实感"的分水岭——没有 GI 的画面像舞台布光,有了 GI 才像自然世界。实时渲染的 GI 难题在于:精确 GI 需要路径追踪(见 光线追踪与混合渲染),成本太高。于是业界用一条光谱:烘焙 Lightmap(最便宜)、光照探针(动态物体)、屏幕空间 SSGI(临场感)、直到 Lumen/RTGI(全动态)。本文按成本从低到高梳理这条谱系,并给出选型决策。
一、直接光与间接光
一句话:直接光是光源直达的光,间接光是弹过一圈的光;间接光让阴影区不再死黑、让色彩在场景中"洇染",是真实感的主要来源。
1.1 渲染方程视角
渲染方程把出射辐射度写成直接项与间接项之和:
L_o = L_e + ∫ f_r · L_i · cosθ dω
├─ 直接项:光源直接照射
└─ 间接项:来自场景其他表面的反射光
实时渲染对直接项做得非常好(Shadow Map、级联阴影见 光照与阴影),间接项则靠近似——因为它的积分维度是"全场景所有表面"。
1.2 间接光的两种形态
| 形态 | 物理来源 | 视觉表现 | 实时常用方案 |
|---|---|---|---|
| 漫反射间接光 | 表面向半球均匀反射 | 阴影泛光、暗部色彩 | Lightmap、探针、SSGI |
| 镜面间接光 | 高光/镜面方向反射 | 环境倒影、光泽反射 | 反射探针、SSR、RTGI |
1.3 天光(Sky Light)
没有天光时"天空"也可以被视为一个巨大光源:环境贴图(Cubemap/HDRI)提供从上到下的辐射,是最廉价的 GI 起点。天光 + 环境光遮蔽(AO)就能让静态场景立起"空间感",是 GI 的第一步。
二、烘焙光照与 Lightmap
一句话:烘焙(Baking)用离线渲染器把间接光预计算进 Lightmap(逐纹素光照图),运行时只是采样——代价全在制作期,运行时几乎为零。
2.1 烘焙流程
- 建模 → 拆 UV2(第二套光照 UV,与纹理 UV 分离);
- 离线光照(Radiosity / 路径追踪 / 辐射度)计算每个纹素的漫反射间接光照;
- 输出 Lightmap 纹理(通常
RGBA16F或 RGBM 编码); - 运行时采样 Lightmap,乘以反照率即可。
// GLSL:Lightmap 采样
vec3 gi = texture(lightmap, vUv2).rgb * texture(albedoTex, vUv).rgb;
color = directLight * albedo + gi; // 直接 + 间接
2.2 优缺点
| 优点 | 缺点 |
|---|---|
| 运行成本极低 | 只能用于静态物体/静态光源 |
| 画质高(离线精确) | 光照无法动态变化(日夜/开关灯) |
| 低端设备可用 | 大场景 UV 展开与烘焙时间长 |
| 天然平滑 | 移动物体需要探针补偿 |
2.3 烘焙的变体
- Baked GI(纯 Lightmap):静态场景专用;
- 间接 Lightmap + 动态直光:Lightmap 只存间接光,直光实时算——灯光变暗时间接光同步变暗;
- Lightmass / 烘焙工具:Unreal Lightmass、Unity GPU Progressive Lightmapper 等。
编码上 Lightmap 常用 RGBM(RGB + Multiplier)或 LDR+AO,降低带宽;高动态范围需求用 RGB9_E5 或 RG11B10F。
三、Irradiance Volume:光照探针
一句话:Lightmap 覆盖静态表面,动态物体没法采样表面光照,于是用"体素网格里的探针"记录周围空间的光照,运行时按位置插值——这是动态物体 GI 的标准方案。
3.1 探针(Light Probe)
在场景空间布点(网格、球体、随机),每个探针预计算该点的辐照度(irradiance)——来自各个方向的天光/间接光入射的积分结果,常存为:
- 球谐(Spherical Harmonics,SH)系数:通常 3 阶 9 个系数,压缩方向信息;
- 或预滤波 Cubemap:存方向辐射,采样更直接但更占空间。
// 探针数据:一阶/二阶/三阶 SH 系数
struct SH9 {
float r[9], g[9], b[9]; // 每通道 9 个 SH 系数
};
3.2 运行时插值
动态物体按位置取周围 8 个探针(网格布局)或 K 近邻,做权重插值,把 SH 系数按法线方向求值得到漫反射 GI:
// GLSL:SH 采样漫反射间接光
vec3 shEval(const in SH9 sh, vec3 n) {
// 用法线方向对 SH 基函数求值(省略系数展开)
float r = dot(sh.r, basis(n));
float g = dot(sh.g, basis(n));
float b = dot(sh.b, basis(n));
return vec3(r, g, b);
}
3.3 探针布置与陷阱
- 间距:探针太疏会插值出错误光照,太密成本高;按场景几何复杂度自适应;
- 遮挡:探针无法感知墙体,跨墙插值会"漏光"(light leaking)——用可见性/体积烘焙修正;
- 动态探针:可用低分辨率反射(上一帧深度)实时更新少数探针,支撑部分动态 GI。
四、反射探针与镜面 GI
一句话:漫反射 GI 解决"暗部泛光",镜面 GI 解决"高光与环境倒影"——反射探针把场景快照进 Cubemap,运行时按粗糙度做 mipmap 采样,一帧搞定大部分镜面间接光。
4.1 反射探针(Reflection Probe)
在场景位置放一个探针,渲染周围 6 面 Cubemap(或等距柱状),预过滤成带 mip 的粗糙度序列:mip0 最锐(镜面),mipN 最糊(粗糙)。运行时 PBR 采样:
采样级别 ≈ 粗糙度(roughness)→ mip level
粗糙度低 → 采 mip0(锐利倒影)
粗糙度高 → 采高 mip(糊成漫反射色)
// GLSL:PBR 镜面间接光采样(split-sum 近似)
vec3 R = reflect(-V, N);
float lod = roughness * MAX_MIP_LEVEL;
vec3 prefiltered = textureLod(envCube, R, lod).rgb;
vec2 brdf = texture(brdfLUT, vec2(NdotV, roughness)).rg;
vec3 specularGI = prefiltered * (F0 * brdf.x + brdf.y);
4.2 探针的局限与修补
- 一个探针只能覆盖小范围,大场景需要多个探针 + 混合;
- 探针快照是静态的,移动物体不会反映在倒影里;
- 补偿:**SSR(屏幕空间反射)**用上一帧深度/颜色重建反射,见 后处理管线;
探针 + SSR是 AAA 的标准镜面 GI 组合。
4.3 平面反射(Planar Reflection)
水平面/地面常用专门的平面反射:以平面镜像相机渲染一次场景。成本高(全场景重渲),但水面这类大面积镜面效果远超探针。实时方案常把"探针 + SSR + 平面反射"按表面类型分配。
五、SSGI:屏幕空间全局光照
一句话:SSGI 把屏幕空间当成场景的"代理"——用上一帧的颜色与深度,在屏幕空间做光线步进采样,把远处理深处隐藏的间接光"带回来",零额外场景数据却有临场感。
5.1 原理
SSGI(Screen-Space Global Illumination)与 SSR 同源:
- 对每个像素,随机采样半球若干方向;
- 用深度缓冲沿方向做光线步进(Ray March),找到命中点;
- 把命中点上一帧的颜色当作该方向的入射光;
- 累加所有命中样本,得到漫反射/镜面间接光。
SSGI 一次命中:
像素 P → 采样方向 D → 步进到表面 S → 取 S 的上一帧颜色作为入射光
多个方向命中 → 平均 → 叠加到 P 的 GI
5.2 优点与硬伤
- 优点:无需任何场景预处理;动态(几何、灯光、动画都实时反映);可与探针/Lightmap 叠加补足动态细节。
- 硬伤:只能看到屏幕内的信息——被屏幕边缘切掉或藏在背面的间接光永远采不到,常配合"上一帧颜色 + 时域累积"减轻空洞。
// GLSL:SSGI 单方向步进示意
vec3 march(inout vec3 dir) {
vec3 p = worldPos + normal * bias;
for (int i = 0; i < STEPS; ++i) {
p += dir * stepLen;
vec4 sp = projView * vec4(p, 1.0);
vec2 uv = sp.xy / sp.w * 0.5 + 0.5;
float depth = texture(depthTex, uv).r;
if (uv 越界) break;
if (sp.z < depth) {
return texture(prevColorTex, uv).rgb; // 命中 → 取色
}
}
return vec3(0.0); // 未命中
}
5.3 时域降噪
SSGI 的随机采样噪声很大,必须配时域累积 + 降噪(原理见 抗锯齿与时序):用运动向量把上帧结果重投影,与当前帧混合,把噪声平均成平滑的间接光。
六、动态 GI 思路:Lumen 与 RTGI
一句话:全动态 GI 是终极形态——Lumen 用"软件光追 + 全局表面缓存"模拟多次弹跳,RTGI 用硬件光追直接算间接光;前者省显存、通用,后者物理准确但成本高。
6.1 Lumen(UE5)的核心思路
Lumen 不追求逐像素精确,而是:
- 全局表面缓存(Global Surface Cache):把场景网格的材质与光照缓存到低分辨率图集,供光线命中时查色;
- 软件光追 + Mesh SDF:用有向距离场(SDF)做光线步进,命中表面缓存取色;
- 屏幕空间优先:能命中屏幕空间的先用(快),命中不到才查 SDF/表面缓存;
- 多次弹跳:把一次弹跳的结果再当作光源做第二次弹跳,模拟漫反射扩散。
Lumen 一次弹跳:
屏幕空间命中(快) → 有则取屏幕颜色
↓ 未命中
SDF 步进(Mesh SDF) → 命中 → 查 Global Surface Cache
↓
反射/漫反射间接光 → 再次弹跳迭代
6.2 RTGI(硬件光追 GI)
用 RT Core(见 光线追踪)对间接光发射少量光线,命中后采样材质与光照,再经时域降噪。物理上更准确,但每像素几十根间接光线 + 降噪的开销仍高于 Lumen 的软光追。
6.3 动态 GI 对比
| 方案 | 动态几何 | 多次弹跳 | 成本 | 硬件需求 |
|---|---|---|---|---|
| 烘焙 Lightmap | 否 | 是(离线) | 极低 | 无 |
| 光照探针 | 部分 | 部分 | 低 | 无 |
| SSGI | 是 | 单次近似 | 中 | 无 |
| Lumen | 是 | 多次近似 | 中高 | 中端 |
| RTGI | 是 | 精确 | 高 | 光追硬件 |
七、混合方案与工程选型
一句话:现实中几乎没人只用一种 GI——主流的混合是"烘焙打底、探针管动态物体、SSGI/光追补细节",按平台预算把成本阶梯排列。
7.1 经典混合架构
| 层 | 负责 | 技术 |
|---|---|---|
| 底层 GI | 静态场景漫反射 | Lightmap |
| 动态物体 | 角色/载具漫反射 | Irradiance Volume |
| 镜面 GI | 金属/水面/光泽 | 反射探针 + SSR |
| 细节补充 | 动态光遮挡/弹跳 | SSGI |
| 终极(高端) | 全动态 | Lumen / RTGI |
7.2 按平台选型
- 低端移动:Lightmap + 探针 + 天光,几乎零动态 GI;
- 中端移动/PC:Lightmap + 探针 + SSGI + 反射探针;
- 高端 PC / 主机:Lumen 或 RTGI + 屏幕空间降噪;
- 影视/离线:路径追踪全精确。
7.3 工程决策清单
- 场景几何是否静态?(静态多 → 烘焙收益大)
- 是否有日夜/开关灯?(有 → 需要动态 GI 至少管灯光)
- 移动物体多吗?(多 → 探针/动态探针必需)
- 高端平台还是全平台?(预算决定是否上 Lumen/RTGI)
// 概念:运行时 GI 组合的开关策略
struct GISettings {
bool useBaked; // Lightmap 采样
bool useProbes; // SH 探针插值
bool useSSGI; // 屏幕空间步进
bool useRT; // 硬件光追(可选)
};
// 高端平台开 RT,中端降级到 SSGI,低端只剩 baked + probes
八、移动端与降级策略
一句话:移动端几乎没有实时多次弹跳的预算,工程重点是把 GI 质量"做进烘焙、省在运行时",并把动态部分限制在探针与廉价 SSGI 上。
8.1 移动端预算特征
- 带宽与功耗是硬约束,全屏 GI Pass 每秒几十 MB 读写不可接受;
- 无光追硬件,Lumen 式软光追也不现实(除非降分辨率 + 降采样率);
- 结论:烘焙优先,动态 GI 用探针与低分辨率 SSGI。
8.2 移动端 GI 组合
| 层 | 移动端方案 | 备注 |
|---|---|---|
| 漫反射 GI | Lightmap(低分辨率/压缩) | RGBM 编码省带宽 |
| 动态物体 | SH 探针(3 阶 9 系数) | 每探针仅 108 字节 |
| 镜面 | 单个环境 Cubemap + 粗糙度 mip | 不额外开销 |
| 动态补充 | 半分辨率 SSGI + 时域 | 只在需要处启用 |
8.3 降级技巧
- 半分辨率 GI 缓冲 + 上采样(GI 是低频信号,低分辨率感知损失小);
- 帧交替计算 SSGI(隔帧更新,配合时域累积);
- 探针网格稀疏化 + 可见性预计算,减少运行时插值错误;
- 用
GL_EXT_texture_compression压缩 Lightmap,降带宽。
总结
实时全局光照是一张"成本—精度"光谱:烘焙 Lightmap 用离线算力换运行零成本,光照探针让动态物体共享空间光照,SSGI 用屏幕空间信息补充临场细节,Lumen 用软件光追 + 表面缓存逼近全动态多次弹跳,RTGI 则用硬件光追换取物理精确。工程上几乎总是混合使用,把各层的成本按平台预算阶梯排列。
| 技术 | 类型 | 成本 | 动态性 | 适合 |
|---|---|---|---|---|
| Lightmap | 离线烘焙 | 极低 | 静态 | 静态场景、移动端 |
| Irradiance Volume | 探针 | 低 | 动态物体 | 角色/动态物体 |
| 反射探针 + SSR | 镜面 | 中低 | 半动态 | 金属/水面 |
| SSGI | 屏幕空间 | 中 | 全动态 | 桌面/主机细节补充 |
| Lumen / RTGI | 动态 GI | 高 | 全动态 | 高端主机 / PC |
实践路径:先搭"天光 + Lightmap + SH 探针 + 反射探针"的最小 GI 栈,用 RenderDoc 检查静态场景的暗部不再死黑;再加 SSGI 观察动态物体的临场感;高端平台再评估 Lumen/RTGI。每一步都以"间接光是否可信"为验收标准,避免叠加过多 GI 层导致光影打架。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。