☰
RE Engine实时路径追踪改造:从光栅化到电影级画面的工程实践
2026/11/1 5:42:43 网站建设 项目流程

《识质存在》和《生化危机:复仇》这两个项目摆到一起,最初是挺分裂的——一个偏科幻悬疑,一个是已经有大量镜头级特写的CG电影衍生项目,怎么看都不像能用同一套渲染方案处理。但两边美术把参考素材往桌上一摊,问题完全一致:光栅化那套东西做到极限,画面仍然有“假”的痕迹,而后期修图修到吐也修不干净那层塑料感。最后我们的选择收敛到同一个方向——在RE Engine 的渲染链路里实现实时路径追踪,不是局部开个光追反射那种,而是把整条光照求解链路换掉。

这篇文章是这次改造的项目复盘,重点讲我们实际动了哪些地方、哪些模块被推翻重建、哪些坑是文档里根本不会写的,以及最后怎么把帧时间压回可用范围。后面涉及到的细节不一定能直接抄到你们的项目里,但思路、取舍逻辑和容易忽略的环节,应该能省下你不少试错时间。

1. 先说清楚:两个项目为什么同时倒向路径追踪

1.1 光栅化方案做到最后,剩下的全是“看上去不对”

《生化危机:复仇》的镜头感非常电影化,角色经常怼脸,背景里有大量冷色调的暗部、湿漉漉的地面、玻璃、金属、皮肤,这些材质对光照正确性极其敏感。用传统光栅化做,金属反射要靠SSR(屏幕空间反射),屏幕外的反射信息一断,金属上就会出现生硬的黑色条带;皮肤上的间接光要靠烘焙或者探针,角色一转头,GI光斑死死定在原地,灰度变化一下就露馅了。地面上的雨水反光,SSR只能反射屏幕里已经画出来的东西,反射源一超出屏幕就全灭,美术只能手动补环境贴图,最后出来的效果跟“实时渲染”这四个字没多大关系。

《识质存在》则是另一种问题。场景里面有大量自发光面板、玻璃、悬浮物体,灯光数量多而且经常以群体方式出现。光栅化处理这种环境时,光照计算基本就是“几个主光源+阴影贴图+一堆反射探针”的排列组合。光源一多,阴影锯齿就开始打架;玻璃后面的物体和反射叠在一起,shader里各种手动分支写得比业务逻辑还复杂;那些大面积自发光面板在旧管线里就只能当死光源,明明该照亮周围却完全没有任何光贡献。

1.2 电影级画面带来的“像素级对位”压力

《生化危机:复仇》虽然不是按传统光栅化流程去做的电影,但它的渲染风格非常接近离线渲染结果。当我们尝试把它往RE Engine的实时流程里搬的时候,最大的矛盾在于:光栅化阴影、SSR、探针补光的误差是系统性的,画面上一定会出现“有一层对比度增强之后的脏感”。你很难通过调参数去消除它,因为问题出在算法近似本身,而不是某个反射率数值没调对。

路径追踪在这件事上有一个特别大的优势:它的误差是统计上的收敛误差,不是系统性误差。意思是,只要采样数提到足够高,画面会精确收敛到我们期望的物理正确结果,不会出现“反射突然消失”“接触阴影位置错误”这类根本性错误。实时情况下虽然每帧只能采少量样本,但配合时间累积和降噪,画面会稳定在一个正确方向上,而不是在错误方向上修修补补。

1.3 路径追踪不是炫技,而是收束复杂度

我们做技术选型时有个判断标准:方案能不能降低整个渲染链路的长期维护成本。

光栅化管线下,每一个新的材质特效,都需要美术和TA去理解“当前光照模型下怎么写才不出错”,遇到金属、皮肤、半透明、次表面散射,全都是一套专用逻辑。路径追踪则把这堆特殊情况统一成一个逻辑——所有材质都变成BSDF采样,光源变成可采样的发光体,剩下的交给光线的概率分布去解决。对于只有一两个项目的团队来说,前期改造工作量巨大,但一旦跑通,后续材质接入反而变简单。

这个判断在《识质存在》那边得到了验证:它的大量自发光面板,在路径追踪里不需要再去写“虚拟光源注入”的作弊逻辑,直接把自发光材质标记为可采样光源,物理上就自动正确了。

2. 改造前,先给RE Engine的光照体系做了个家底盘点

2.1 RE Engine原生光照工作方式

RE Engine并不是一个渲染实验性很强的引擎,它的实时渲染路线图偏保守和务实。从我们接手的版本看,它大致走的是延迟着色框架:几何阶段输出GBuffer,光照阶段在屏幕空间内做逐光源计算,阴影靠阴影贴图,间接光靠探针或者烘焙,反射靠SSR加反射探针。这套机制放在传统游戏里没有问题,但对于我们要做的这两种项目,几个关键环节全都到不了“电影感”标准。

举个例子,RE Engine在角色皮肤的实时渲染上,用了多层高光近似和简单的透光模拟,这在游戏镜头里够用,但一旦镜头怼到脸部,皮肤下面的次表面散射效果就暴露了“平面感”。其实很多引擎都有这个问题,不是RE Engine特有,只是电影项目对材质的逼真度要求远高于普通游戏。

2.2 哪些能复用,哪些必须推翻

我们建了一张表,把原管线里每个模块挨个标了一遍:保留、改造、替换。

模块处理方式原因
网格资源、顶点属性、蒙皮数据保留路径追踪需要的是几何信息,这些可以直接复用到BLAS构建
材质参数(基础色、粗糙度、金属度、法线等)保留并扩展作为路径追踪BSDF的输入,但要补充自发光标记、SSS参数
GBuffer逻辑改造路径追踪虽然不依赖GBuffer做直接着色,但primary ray和降噪都需要世界坐标、法线、深度
光源管理、光源数据保留复用光源强度/颜色/衰减,但要改成可被重要性采样
阴影贴图不再做最终贡献路径追踪自己产生接触阴影和远距离阴影,阴影贴图只在特殊版本里做辅助
SSR、反射探针关闭路径追踪已经包含反射,再开反而叠加错误能量
烘焙GI关闭路径追踪自带全局光照,烘焙数据不再有意义
半透明渲染改造路径追踪下需要按真实折射/透射处理,不能再用老的混合排序

做这一步的时候我很大的一个感触是:不要舍不得关掉那些老系统。很多人改造光追时,喜欢留着SSR做“万一光追采不到信息时的兜底”,结果两个系统在能量上互相叠加,画面反而更脏。路径追踪是一个完整的光照模型,你要么信任它,要么不信任它,半信半疑的结果一定是两边互相拆台。

2.3 加速结构的接入代价:BLAS/TLAS的构建与更新

路径追踪的光线求交依赖加速结构,也就是底层加速结构(BLAS)和顶层加速结构(TLAS)。RE Engine原本没有这部分数据,我们需要把引擎的网格系统桥接到DXR或Vulkan光线追踪接口上。

工程上最主要的坑是动态网格更新。角色蒙皮动画每一帧都在改变顶点位置,对应的BLAS如果不更新,光线打到的模型还停在上一个姿势。这里有几个方案:每帧完整重建BLAS(成本高)、refit(便宜但极端姿势下包围盒会膨胀)、或者按骨骼影响范围拆成多个子网格分层更新。我们最终用了一个混合策略:静态场景物体只在加载或移动时refit,动态角色每帧对变形部分做局部重建,其余子网格完整保留。

TLAS的更新相对便宜,但也不能掉以轻心。TLAS里要正确标记每个实例的mask和transform,尤其是那些半透明物体、可破坏场景物体,mask配错会出现“光线穿透完全不存在的物体”这种诡异问题。我们的做法是把所有物体按语义分组:opaque、transparent、character、decal,然后让不同的光线类型去匹配不同的mask,这套分组是从早期测试就定下来的,越早越好,后期改起来会非常痛苦。

3. 光照求解链路替换:把离线渲染的思路压进16.6毫秒

3.1 路径追踪的基本逻辑:从“逐个光源算”到“沿路径积分”

经典光栅化假设每个像素的颜色 = 材质颜色 × 所有光源的加权贡献,光源多了就遍历,光源少了就补环境项。路径追踪走的是另一条路:从相机通过像素发射射线,让射线与场景求交,在交点上根据材质BSDF采样一个新的出射方向,再继续发射,反复弹射,最终把所有弹射点的radiance贡献累计起来。它在数学上逼近渲染方程本身,所以能自然处理多次弹射、焦散、反射、全局光照,而不需要单独加载一大堆探针数据。

实时情况下,一个像素不可能发射几百条路径。我们两个项目最终的标准是每像素1条主路径(1 spp),配合后续的降噪和时间累积来稳定画面。在某些重要区域,比如《生化危机:复仇》的角色脸部特写,我们会细分采样密度,但这已经是后话了。

3.2 直接光与间接光的采样分配,以及ReSTIR的价值

纯路径追踪在实时环境里的一个大问题是:如果场景里灯光数量很多,每条路径随机挑一盏灯去采样,方差会大得吓人。一帧画面可能出现这里亮那里暗、同一面墙上一块一块的噪斑。

我们的做法是引入ReSTIR(Reservoir-based SpatioTemporal Importance Resampling)思路来做直接光的采样。简单说,ReSTIR用一小部分候选样本,配合历史帧信息和周围像素的空间复用,把“从二百盏灯里找出真正对这像素贡献最大的那几盏”这个问题的代价大幅压缩。它不直接提高spp,但能显著降低同spp下的方差。

以下是当时采样策略的大致结构,写出来给大家参考:

  • 直接光:尝试从光源列表里做重要性采样,候选光源数量在几个到几十个之间,使用ReSTIR在空间与时间上复用样本;
  • 间接光:从相机路径的primary hit点采样半球方向作为一轮bounce,本轮之后的弹射点数视预算选择1~2次;
  • 环境光:把IBL拆成可采样的分布,作为背景辐射参与路径采样;
  • 自发光物体:单独抽取发光体列表,用基于发光体面积和辐射通量的分布做采样,避免小面积高亮发光体被漏掉。

这套组合在理论正确性上肯定不如离线那种每条路径弹射七八次的完整路径追踪,但对于实时画面,它把最关键的能量分布给还原了,剩下的误差交给降噪去掩盖。

3.3 我们的光线预算与硬件分配

最终我们在PC版两个项目上保留了这样几个档位:

档位每像素光线数直接光采样方式间接弹射降噪
画质优先2 spp + 1轮间接ReSTIR DI半球采样,单次弹射高精度空间+时间降噪
平衡1 spp + 1轮间接ReSTIR DI半球采样,单次弹射标准降噪管线
性能1 spp,直接光1次光源重要性采样无单独间接,依赖环境项快速降噪模式

实际执行下来,平衡档在RTX 3070级别硬件上,1080p分辨率能稳定在30fps以上;画质优先档需要RTX 4070级别才比较从容。要注意的是,这个帧数并不是光追单独占掉的,整条链路里加速结构更新、路径生成、求交、降噪、后处理都算在了里面。

4. 材质系统翻译层:从RE Engine的材质节点到路径追踪BSDF

4.1 材质图数据怎么换算成标准PBR参数

RE Engine的材质体系有自己的历史包袱,它有基础色、金属度、粗糙度、法线这些标准PBR输入,但同时又混合了自家的一些特殊参数,比如“反射强度”、“高光色调偏移”之类。路径追踪对材质输入的要求很严格:如果粗糙度语义不统一,微表面高光就会出现奇怪的亮圈;如果基础色里已经加了光照烘焙的内容,路径追踪一跑,能量就翻倍了。

我们在两个项目里统一建立了转换层,把引擎材质参数映射成标准PBR输入:

  • 基础色:只保留反照率,丢弃其中隐含的环境光遮蔽或烘焙阴影;
  • 粗糙度、金属度:直接映射到GGX微表面模型的粗糙度、金属度参数;
  • 高光偏移类参数:合并进微表面模型或作为法线扰动处理;
  • 次表面参数:单独提取给皮肤/蜡烛等材质;
  • 自发光:从emissive通道提取强度与颜色,登记为可采样光源。

这个映射表看起来简单,实际排查起来花了很长时间。比如某些角色头发的基础色贴图里,为了旧管线的表现,在贴图里预画了很强烈的烘焙光影,路径追踪一上来,头发看起来就像刷了一层漆,直到我们把贴图里的烘焙光全部拆干净,才恢复正常。

4.2 皮肤、眼球、头发的特判:电影感的来源

《生化危机:复仇》这种项目,角色近景几乎是不可避免的,皮肤、眼球、头发的处理直接决定画面有没有“电影感”。

皮肤方面,实时路径追踪我们用了一种类随机游走的次表面散射近似:在路径命中皮肤表面时,以一定概率让光线向表面内侧偏移,传播一小段距离后重新出射,模拟光线在皮下组织里的散射。这个近似不便宜,所以只对皮肤材质开启,其他材质一律关闭。另一个更经济的做法是用两层高光叠加+面积光采样来伪造皮肤质感,但近距离特写下还是能看出平面感,所以我们在《复仇》里坚持打开了类次表面散射。

眼球是单独的shading model。角膜产生镜面反射,虹膜本身有次表面散射和透射效果。这里不能简单地把眼球当成一个普通金属球,否则高光和虹膜的层次根本出不来。我们把眼球拆成角膜层和虹膜层,角膜用带IOR的折射路径,虹膜用漫反射+贴片式的SSS,这样在特写镜头下眼球才有“湿的玻璃球包着一片有色膜”的感觉。

头发是另一个大坑。旧管线的头发基本是各向异性高光改成多层卡片,路径追踪里这样不行——光线穿过卡片成面片时,不透明Alpha裁剪会造成边缘硬伤。我们给发丝加了一个基于曲线的薄片模型,用近似Marschner模型的各向异性BSDF去处理,重点不是严格物理,而是让头发的三根高光带(主反射、透射、二次反射)在实时路径追踪里能出现。这里如果性能不够,可以砍掉透射项,只保留主反射和二次反射,画面观感损失有限。

4.3 自发光表面如何参与光照,而不是等着被照亮

在光栅化时代,一块发光的显示面板只是“自己亮”,不会对周围产生任何影响,所以美术需要在面板旁边手工摆光去模拟面板对环境的照明。路径追踪里,我们希望自发光表面成为真正意义上的光源。

这个转变需要在数据上做配套改造:所有自发光物体要打进一个独立的发光体列表里,实时维护它们的发光强度、颜色、面积以及位置。路径生成时,发光体会参与重要性采样,面积大、亮度高的发光体更容易被光线命中。这一步做好,《识质存在》那些大面积的面板就能自动照亮周围金属结构、玻璃和地面,画面真实度立刻上一个台阶,而且完全不需要美术手动布光。

不要忽视发光体列表的更新频率。有些发光体会闪烁(比如故障灯、屏幕内容变化),如果发光体列表的更新和帧率不同步,画面会出现“紫边闪烁”这种很难排查的伪影。我们的做法是把发光体的提取合并在材质上传阶段,每帧强制刷新一次,确保光源变化与画面状态一致。

5. 降噪与时间累积:真正决定整个方案能不能用的一环

5.1 1spp的路径追踪图像是什么观感

如果你直接看1spp的输出,那画面基本不能用:到处是颗粒噪点,暗部尤甚。原因在于路径追踪的方差和信号的幅度有关,亮度越高的区域,光线的能量贡献越集中,噪点相对不明显;暗部区域,少量光线偶尔碰到亮光源就产生一个闪亮的采样点,大部分时候是黑的,于是出现那种“暗部冒星星”的噪声。

这种噪声是路径追踪固有的统计性质,不能靠简单的高斯模糊去掉,因为直接糊掉会把边缘和高光细节也抹掉。你必须上专门的降噪方案——空间域上利用法线、深度、材质ID保护边缘,时间域上利用历史帧信息持续累积有效样本。

5.2 空间与时间降噪的配合方式

我们参考了SVGF和NRD一类降噪设计,自己拼了一条适合RE Engine的降噪管线,分为两段:

第一段是空间降噪。对当前帧的路径追踪输出做边缘保持滤波,权重同时考虑法线夹角、深度差、材质ID变化和辐照度差异。材质ID这一点非常关键,墙面和地面交界处即使法线接近,漫反射颜色差异也很大,没有材质ID的约束会出现“颜色脏水互相渗”的问题。

第二段是时间累积。把当前帧的路径追踪结果与前几帧重新投影后的历史结果做指数移动平均。理论上,静态场景下时间累积相当于把spp堆到几十上百,画面质量能显著提升。

这两段执行顺序不能颠倒,必须先空间后时间,否则时间累积会把历史帧的噪点错误地传播到当前帧,闪烁会越来越严重。

5.3 恐怖场景里的特殊处理:手电光和暗部闪烁

两个项目都有大量低照度场景,《生化危机:复仇》更是有手电筒这种极端光源。手电筒产生的光束边界非常强烈,一边是被照亮的高亮区域,另一边几乎是纯黑,这对降噪算法是极端压力测试。

我们遇到的问题主要有两类:

第一,动态范围跨度太大导致的时间累积残影。当手电筒快速扫过墙面,前几帧的高亮像素被重投影到新的暗部位置,历史缓冲里还残留着亮度信息,画面上就会拖出一条光弧。我们的处理是引入一个基于帧间差异的置信度权重:当前像素和重投影后的历史像素差异过大时,自动降低历史权重,让画面快速遗忘旧信息。

第二,暗部噪声的可见度。人类视觉对暗部区域的噪声非常敏感,尤其在几乎全黑的场景里,一点亮斑都会非常显眼。我们专门做了一个亮度自适应的降噪强度调整:在暗部区域提高时间累积比例,在亮部区域则更依赖空间降噪来保留细节。这套逻辑在《识质存在》里也适用,因为它的室内场景同样有大量低照度环境。

5.4 一个容易被忽视的问题:运动向量

时间累积依赖运动向量做重投影,RE Engine的原有运动向量是基于光栅化深度和前一帧矩阵算出来的。路径追踪改造之后,运动向量的计算仍然可以复用深度信息,但有几类物体容易出错:半透明物体、粒子、被物理改变的可破坏物体。这些物体在旧管线上可以靠人为隐藏或近似处理,在路径追踪下直接会让历史重投影成功产生拖影。

我们的办法是给这些特殊物体单独输出遮挡标签,在降噪阶段把这些区域强行切换到只用当前帧空间降噪,牺牲一部分平滑度来换取干净边缘。这是典型的“眼前亏可以吃,长远脏不能忍”的思路。

6. 实测数据、调优记录与两套交付标准

6.1 两个项目各自的帧时间拆解

改造完成一轮之后,我们在同配置显卡上对两个项目做了帧时间拆解。以下数据是当时多次采集后的平均比例,不代表所有场景,但能反映瓶颈分布:

阶段占帧时间比例(平衡档)说明
几何处理和BLAS/TLAS更新约20%静态场景还好,角色多时占比上升
路径追踪光线遍历与求交约35%实际上一次路径的求交数量非常大
ReSTIR直接光采样约10%光源数量越多占比越高
空间+时间降噪约20%降噪不是免费的,反而非常贵
后处理、Tone Mapping等约15%包括原引擎的一部分后处理链

从表格能看出一个容易被忽视的事实:降噪占了整整两成帧时间,并不是一个廉价的“滤镜”。很多人以为路径追踪性能瓶颈全在光线求交,实际上在室内多光源场景里,降噪的占比往往会超过光线求交本身。如果你想优化性能,第一个要考虑的往往不是减少路径弹射次数,而是简化为你场景专门定制的降噪策略。

6.2 针对《生化危机:复仇》的角色近景优化

《复仇》大量镜头是角色近景,GPU资源更应该花在脸部和上半身,而不是地板上那些没人看得清的细节。我们做了一版按屏幕区域分配采样率的机制:通过面部网格的包围盒投影到屏幕空间,把角色脸部区域标记为高优先级采样区,每像素可以跑到2~4条路径,而背景区域保持1spp。

代价是背景可能会稍微软一点,但人类的视觉注意力天然集中在人脸,这种有偏分配在电影镜头里实际观感提升非常明显。同时,脸部区域我们打开类随机游走SSS和特殊皮肤高光模型,确保特写镜头下皮肤不是“塑料”。这个思路在其他游戏里也通用:屏幕区域自适应采样永远比全局均匀采样划算。

6.3 针对《识质存在》的场景大范围切换优化

《识质存在》更麻烦的是场景空间跨度大,从狭窄舱室到开阔外景都在一个关卡里。开阔场景里,路径追踪的光线会打到极远处的几何体,这些几何体的BLAS如果不够精细,会在墙面或地面上映射出错误的反射形状。我们后期把远景环境做了代理几何体处理:超过一定距离的场景自动替换成简化碰撞体,保证光线的求交结果在视觉上正确,但内存和求交开销大幅降低。

另一个专门为大场景做的优化是距离衰减的间接光采样深度。近距离的路径继续保留完整的多弹射,远距离物体命中后就提前终止,用环境辐射做近似。实践中,这种距离阈值设在10米到30米之间,具体取决于场景密度,墙面上的反射连续性和噪点控制都还不错。

6.4 最终交付时的画质档位与硬件要求

最后的交付标准,我们分成三档面向不同硬件:

画质档位分辨率每像素光线数降噪强度推荐硬件
电影质(质量)2560×14402 spp高精度双段式降噪RTX 4070及以上
平衡1920×10801 spp标准降噪RTX 3070级别
性能1920×10801 spp,降低弹射深度快速降噪RTX 3060级别

到这里并不是说项目就万事大吉了。光照换成了路径追踪之后,很多原引擎的后处理需要重新校准:Bloom强度、Tone Mapping曲线、镜头光晕这些全是基于老的光照分布去调的,拿到路径追踪的线性输出上一看,要么过曝,要么对比度不对。我们花了整整两周去重新调整色彩管线和曝光,这部分工作可能在技术分享里很少被人提到,但在项目里它和写shader一样重要。

最后再分享一个实操里的经验

如果让我重来一次,我会把降噪管线的搭建提前到和路径追踪本体同步进行,而不是等光追出图了再回头补。第一次接入路径追踪时,我们先用最简单的球体场景验证加速结构与基础BSDF,结果没问题后就把时间全砸在提高光线数量上,结果画面依然脏得没法看。后来才反应过来——实时路径追踪真正跑通的关键,是1spp下依然能输出稳定、干净、不闪的画面,而不是能追踪多少条光线。

这个项目之后,我对实时渲染的看法变了很多。以前觉得物理正确是最高目标,现在觉得在实时场景里,物理正确只是地基,能在这个地基上做稳定、可调、不穿帮的输出,才是真正困难的部分。希望这篇复盘能帮正在往这个方向走的朋友少踩几个我们踩过的坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询