Lumen 默认走软件光追:用全局距离场(Global SDF)做 ray march,配合 Surface Cache 取辐射。但这并不是唯一选项——开了 r.Lumen.HardwareRayTracing 1 且显卡支持 DXR,Lumen 会切到硬件光追路径,直接用 BVH 遍历和真实三角形求交。两条路径的数据结构、覆盖内容、质量特性完全不同,切换条件也不是一个开关那么简单。代码在 Engine/Source/Runtime/Renderer/Private/Lumen/LumenHardwareRayTracing.cpp(UE5.3)。
两条路径的本质差异
软件路径(SWRT)的追踪目标是距离场,不是三角形。全局 SDF 是 FGlobalDistanceField(DistanceFieldAtlas.cpp)里的一张三维纹理:把场景里所有静态网格的 mesh SDF 用 clipmap 层级铺在世界空间里,密度随离相机距离递减。Ray march 步长直接取当前点的 SDF 值 $d(x)$——理论上步进永远不会穿透表面,因为距离场保证了半径 $d(x)$ 内没有几何:
$$x_{i+1} = x_i + \omega \cdot d(x_i)$$
$\omega$ 是射线方向。空区域一步跨过巨大距离,这是 SDF march 快的根本原因。但代价有两个:距离场是有损的(mesh SDF 本身是低分辨率烘焙,多个物体融合时细节互相抹平,薄墙可能被完全吞掉);而且 SDF 天然管不了形变和带动画的物体——动态物体走的是逐实例 instance SDF,精度和成本都受限。
硬件路径(HWRT)的追踪目标是 FRayTracingScene 里的 BVH,求交结果是真实的三角形,法线、UV、材质索引都是精确的。覆盖内容上 HWRT 可以纳入更多对象类别(骨骼网格、植被实例化的完整支持),精度上没有 SDF 的体素化损失。
切换条件:不是一个布尔
实际项目里影响路径选择的变量(UE5.3):
r.Lumen.HardwareRayTracing:总开关,但开它还要求r.RayTracing可用、项目设置里 Lumen 的 Hardware Ray Tracing 勾选;r.Lumen.HardwareRayTracing.LightingMode:开 HWRT 后还有子模式——SurfaceCache(HWRT 只负责追踪,辐射仍从 Surface Cache 取,本质是"更准的遮挡查询")和HitLighting(命中三角形后直接算直接光照,不再经过 Surface Cache,质量最高也最贵);- 资产粒度:
r.Lumen.HardwareRayTracing.SupportedMaterialModes一类开关决定哪些材质纳入 HWRT 场景,不透明/masked 的覆盖度需要权衡 BVH 构建成本。
关键认知:HWRT 的 SurfaceCache 模式并没有抛弃 Surface Cache。Final Gather 的探针采样、辐射缓存、时域累积全部保留,HWRT 只是把"沿射线找最近表面"这一步从 SDF march 换成 BVH 求交。所以 HWRT-SurfaceCache 和 SWRT 的差异集中在遮挡和命中位置的精度上,不在着色模型上。要绕开 Surface Cache 的低频特性(它 8×8 纹素一块的辐射缓存会让小尺度间接光细节糊掉),得上 HitLighting 模式,那是另一档成本。
质量差异到底在哪
实测对比(同样的室内场景,SWRT 与 HWRT-SurfaceCache 各截帧):
- 薄几何和细结构。SDF 的 clipmap 分辨率决定了小于一个 clipmap 纹素的结构在距离场上不存在。栏杆、窗棂、线缆这类东西在 SWRT 下对间接光近似透明——光线直接穿过去。HWRT 下它们正确遮挡,间接光的方向性(比如百叶窗的光斑)会明显不同;
- 自遮挡漏光。SDF 是保守距离估计,贴墙发射的射线起点略在墙外,SWRT 会在墙脚处漏光;HWRT 命中位置精确,这类 leak 基本消失;
- 动态物体。骨骼网格、布料不烘焙进全局 SDF(或只走 instance SDF 的粗粒度),SWRT 下动态物体的间接光照射和受光都弱一截;HWRT 把骨骼网格实例纳入 ray tracing scene 后正常参与。
但也不是 SWRT 全面落败。SWRT 对远景更稳:全局 SDF 的 clipmap 天然给出离相机越远越粗的 LOD,远处间接光的成本有界;HWRT 的 BVH 对密集实例场景(大片植被)遍历成本随三角形数线性增长,远处不做 LOD 时 HWRT 反而更贵。所以混合策略(近处 HWRT、远处 SDF)在 r.Lumen.HardwareRayTracing.MaxRayDistance 里留了口子——射线超过这个距离就 fallback 到 SDF march,一次追踪可以跨两种数据结构。
性能账本
以一台 RTX 3070 级显卡的中等场景为参照(数量级估算,随内容波动很大):
| 项目 | SWRT | HWRT (SurfaceCache) |
|---|---|---|
| 预处理 | 烘焙 mesh SDF + 全局 SDF clipmap 更新 | BVH 构建(每帧增量,RTScene 管理) |
| Trace 单位成本 | SDF 纹理采样 march,填充率受限 | BVH 遍历 + 三角形求交,RT core 专用单元 |
| 主要瓶颈 | 高密度几何的 clipmap 更新 | 高实例数场景的 BVH refit |
| 典型开销占比(Final Gather 段) | ~3-5 ms | ~2-4 ms(RT core 卸载了 SM) |
注意 HWRT 的数字只有在 RT core 空闲时才好看——如果场景同时开了 RT 反射和 RT 阴影,三路共用 RT core,Lumen 的 HWRT 会和其他特性抢同一份专用吞吐。Stat GPU 里 LumenHardwareRayTracing 一项单独计时,和反射的阴影的耗时对照着看。
怎么选
给一个决策顺序,按项目实际情况截断:
- 目标平台没有 DXR(Switch、多数手机、老显卡)——没得选,SWRT,把精力放在 mesh SDF 的烘焙质量上;
- 场景薄结构少、以大体块为主( typical 室外场景)——SWRT 的质量差距肉眼难辨,留预算给别的地方;
- 室内+大量细结构、或者强依赖动态物体参与 GI —— 开
r.Lumen.HardwareRayTracing,先用SurfaceCache模式,遮挡精度问题直接解决,成本可控; - 美术仍抱怨间接光细节糊(小物件的影子、辐射的锐利边界)—— 上
HitLighting,但接受它是 Lumen 全管线里最贵的单项,预算要从别处抠。
源码入口
- 路径选择的判定:
LumenSceneRendering.cpp里Lumen::UseHardwareRayTracing系列函数(项目设置 + cvar + 平台能力三重判断); - HWRT 追踪实现:
LumenHardwareRayTracing.usf的Lumen::RayTrace入口,注意它内部还分了 inline raytracing 和 raytracing pipeline 两种执行模式; - BVH 场景管理:
RayTracing/RayTracingScene.cpp,重点看每帧哪些 primitive 触发 refit(增量更新的脏标记逻辑)。
两条路径共用下游、只换追踪原语——这个分层让 Lumen 的实验成本很低:今天用 SDF 跑通管线和调优预算,明天显卡换代了切 HWRT 只动数据结构不动架构。