导语:上个月评审会,我放了两段视频。同一张灰盒医院地图:A 组三件套全开,但植被挂了顶点动画风场,走廊里 24 帧,全场沉默;B 组全关回传统方案,帧率翻倍,画面退回 2014 年,全场也沉默。总监问:"为什么不能两个都要?"能,前提是你知道这三件套各自的账单长什么样、哪类资产碰了哪条红线。这一章就来算这笔账。
本章地图
mindmap
root((渲染三件套))
Nanite 虚拟几何
收益:LOD 消失
边界:变形体 风场植被 半透明
簇管线:三角形到层级
Lumen 动态全局光照
软 RT:距离场加表面缓存
硬 RT:三角形追踪
妥协:镜面反射 薄墙漏光
VSM 虚拟阴影
16K 虚拟页表 按需渲染
显存账:物理池与页失效
联开预算
RTX 3070 4070 5090 三档
自动降级策略
回响峡谷实操
废弃医院走廊布光
对标 TLOU 室内光影7.1 Nanite:LOD 是引擎的事了,但三类资产别硬塞
传统渲染管线里,美术有三笔手工账:每个资产做 3 到 4 级 LOD 减面、每个场景守着多边形预算不敢喘气、每改一次灯光就等一夜光照烘焙。UE5 这代三件套,本质是把这三笔账从人工转给引擎:Nanite 接管 LOD,Lumen 接管烘焙,VSM 接管阴影分辨率。本章逐个算账,先从小三角形开始。
Nanite 的收益一句话说透:美术把摄影测量扫描件、ZBrush 高模直接拖进场景,不用减面,不用做 LOD 链,引擎按像素精度自动决定画多少三角形。官方演示里同屏几十亿三角形是营销数字,不用记;要记的是工程数字——一个中型项目里,LOD 制作与返工的工时以人月计,Nanite 把这笔钱全省了。废墟、岩石、混凝土碎块这类硬表面资产,是 Nanite 的主场,「回响峡谷」的末日质感有一半靠它。
但 Nanite 从来不是"所有网格都开"。它的虚拟化管线吃的是静态、不透明、不变形的几何,越出这条线就要付代价,有些资产干脆进不了门。开工前把这张表贴在墙上:
| 资产类型 | 开不开 Nanite | 原因与去处 |
|---|---|---|
| 静态硬表面(opaque / masked) | 开 | 主力场景,放心用 |
| 骨骼网格 / 变形体 | 别开 | 蒙皮与 morph target 不在虚拟化管线内;近期版本有实验性支持,5.8 下以官方文档 Nanite 词条的支持清单为准 |
| WPO 风场植被(草、树叶摇摆) | 慎重 | 顶点动画改走可编程光栅逐实例求值,密草皮一开帧率就跳水 |
| 半透明材质 | 不能 | Nanite 只接 opaque 与 masked,玻璃、水面走传统 |
| 样条网格 SplineMesh | 不能 | 沿路变形的网格不在支持列表 |
| Landscape 地形 | 看版本核对 | 地形支持在 5.x 后期版本逐步进入,开前核对文档与地形材质 |
| 双面板状草卡 | 能开但别 | masked 能进门,但多层叶片的像素填充率才是真账单 |
表看完,手上还要有排查工具。下面这段是控制台三连加资产自查清单,评审前对着扫一遍,能挡掉八成"Nanite 背锅"事故:
; 控制台排查三连:看 Nanite 到底在渲染什么
stat Nanite ; 三角形/簇/实例计数与光栅耗时
r.Nanite 0 ; 一键回退传统管线,做 A/B 对比
r.Nanite.Visualize triangles ; 可视化三角形密度,模式名以 5.8 编辑器为准
; 资产自查清单(命中任意一条,这个资产就别硬开 Nanite)
; [ ] 骨骼网格 / morph target 变形 → 走传统 LOD
; [ ] 半透明材质 → Nanite 只接 opaque/masked
; [ ] WPO 顶点动画(风场、旗帜) → 可开但按实例求值,密集慎用
; [ ] 样条网格 SplineMesh → 不在支持列表
; [ ] 面板状草卡多层堆叠 → masked 填充率爆表,改传统除了进门资格,还有两笔隐性账。一笔是流送:Nanite 的几何数据按簇组从磁盘按需加载,大体量场景要给它留磁盘带宽与显存预算,这部分和第 16 章的流送策略是同一本账。另一笔是材质着色:Nanite 免掉的是"每三角形"的钱,"每像素"的钱照付——masked 植被七八层叶片叠在一个像素上,着色成本一层不少。
我的取舍判断:影视感室内加硬表面废墟,Nanite 全开;大草皮配低端机,树用 Nanite、草回传统方案,或者整片植被回退。别为了"全项目技术栈统一"这种洁癖让帧率陪葬——评审会上那 24 帧就是这么来的。
三件套的项目级开关集中在渲染设置里。下面是「回响峡谷」的 DefaultEngine.ini 片段,默认值后面各节会逐项解释:
; DefaultEngine.ini 片段:三件套的项目级开关
[/Script/Engine.RendererSettings]
r.Nanite.ProjectEnabled=True ; Nanite 项目级总开关,单资产还可独立关
r.DynamicGlobalIlluminationMethod=1 ; 动态全局光照:1 = Lumen
r.ReflectionMethod=1 ; 反射方案:1 = Lumen
r.Shadow.Virtual.Enable=1 ; 虚拟阴影贴图 VSM
r.Lumen.HardwareRayTracing=0 ; 默认软 RT,高配机由运行时档位再开
r.GenerateMeshDistanceFields=True ; 软 RT 的前置条件:逐网格距离场改完记得:这些键名在 5.8 的项目设置面板里都有对应勾选项(Project Settings → Rendering),团队协作时走 ini 版本控制,别靠口口相传。
7.2 簇管线:Nanite 省掉 LOD 的代价是什么
Nanite 凭什么不做 LOD?答案是 LOD 没有消失,而是被细化了:从"资产级"细化到"簇级",从"离线做死"变成"运行时现算"。传统 LOD 是美术提前备好 3 个粗糙版本,引擎按距离整只切换;Nanite 是把网格切成约 128 个三角形一簇(Cluster),簇再聚成簇组,自底向上聚出一棵有向无环图(DAG),每个父节点是子节点们的简化版,带着自己的误差度量。
flowchart TD
A[美术导入高模
数十万到数百万三角形] --> B[构建期:三角形切分与聚类]
B --> C[簇 Cluster
约 128 三角形]
C --> D[簇组 Cluster Group
流送与剔除的基本粒度]
D --> E[层级 DAG
父节点 = 子节点的简化版]
E --> F{运行时逐帧}
F --> G[实例与视锥剔除]
G --> H["沿 DAG 切一刀
屏幕误差小于 1px 即停"]
H --> I[双 Pass 遮挡剔除
第一遍借上一帧 HZB]
I --> J[可见性缓冲
小三角软光栅 大三角硬光栅]
J --> K[按材质 ID 逐像素着色]运行时沿 DAG 切的那一刀,就是 LOD 选择:某个簇在屏幕上缩到误差不足一个像素,就用它的父节点代替,一层不够再上一层。一份资产,等价于无穷级 LOD,且切换粒度细到屏幕上根本看不见跳变——传统 LOD 在切换瞬间的"胖瘦突变",在 Nanite 里不存在。
把这层聚合关系画成剖面,比文字更直观:
图里有三个工程事实值得单独拎出来。簇组是流送单位:相机走近,簇组才从磁盘拉进来,所以 Nanite 场景的磁盘带宽要进预算表。父节点是简化版:整棵 DAG 占的磁盘空间比原始网格大约多三成(数量级),这是"不做 LOD"真正付出的存储代价。切面是逐帧现算的:镜头从 1 米拉远到 100 米,同一块废墟用哪层细节,每帧都在变,美术完全无感。
Nanite 还有一半性能藏在实例化里。同一个 mesh 摆一千份(Instanced Static Mesh),簇数据在磁盘和显存里只存一份,流送只付一次钱,剔除和绘制都按实例批处理——这是"废墟里铺两千块混凝土碎块"敢成立的真正原因。所以「回响峡谷」的散件规矩是:能用实例化绝不摆独立 Actor,第 17 章 PCG 程序化生成散件时,输出默认就是实例,别摆一千个 StaticMeshActor。
渲染侧的另一半是可见性缓冲(Visibility Buffer)。传统前向着色是"画一个三角形就着色一片像素",Nanite 反过来:先以簇为单位做双 Pass 遮挡剔除——第一 Pass 借上一帧的层级深度缓冲(HZB)剔掉绝大部分,漏网的簇第二 Pass 补——然后小三角形走内置软件光栅、大三角形走硬件光栅,把"哪个簇、哪种材质"写进一张可见性缓冲,再统一按像素上的材质 ID 着色。
这套设计的后果要会背:材质着色成本从"每三角形"变成"每像素"。同屏一亿三角形,只要它们挤在不同像素里,着色账单和一万三角形一样——这是 Nanite 敢堆面数的底气。反过来说,masked 植被七八层叶片叠在同一个像素上,每层都要着色,面数免单、像素照付,所以 7.1 那张表里草卡才被标成"能开但别"。
Nanite 资产其实随身带着一份"低保":回退网格(Fallback Mesh)。构建时引擎会自动生成一个传统三角形版本,用在三处——不支持 Nanite 的平台直接渲染它;硬件光追拿不到全精度几何时,追的是它或更高精度的代理;部分物理与拾取查询也走它。两个工程后果:磁盘上每个 Nanite 资产都比源文件更占地方;回退网格的精度可以在资产编辑器里调(Fallback Triangle Percent 等字段,以 5.8 面板为准),调太低,光追反射里的世界会先露馅——你在 Lumen 硬 RT 反射里看到的"远处建筑变豆腐",多半就是它。
7.3 Lumen:软 RT 是主力,硬 RT 是选修
第 6 章讲过延迟渲染把直接光照做便宜了,但直接光只回答"灯照到哪",回答不了"光弹到哪"。现实里一个房间之所以柔和,是因为光在墙面、地板、天花板之间弹了无数轮。传统方案两条路:Lightmass 离线烘焙,质量好但光源一动就得重烘,完全静态;屏幕空间 GI(SSGI)便宜,但屏幕外的世界不存在,镜头一转就露馅。Lumen 走了第三条路:给场景建一份"低配分身",在这份分身上实时追踪光线。
软 RT 路径下,这份分身由三样东西拼成:每个网格的距离场(Mesh Distance Field,记录"离表面还有多远")、把周边距离场合成的全局距离场(Global Distance Field)、以及表面缓存(Surface Cache)——把每个 mesh 的表面辐射亮度捕进若干张卡片,相当于给场景拍了一套"带光照信息的低清照片"。屏幕探针按 BRDF 重要性采样往分身里打射线,命中表面缓存就拿到那一面的间接光,再经时空滤波降噪输出。
硬 RT 路径不打距离场,直接对真实三角形追踪,反射射线还能自己算光照(Hit Lighting)。质量上限明显更高,代价也明码标价:要 RT Core 显卡,要为场景建加速结构(BLAS,一笔显存账),帧时间在软 RT 基础上再多吃 1.5 到 3 毫秒(数量级,视场景密度)。两条路怎么选,照这张决策树走:
flowchart TD
A[项目启用 Lumen] --> B{目标平台有 RT Core
且显存不低于 10GB}
B -- 否 --> C[软件 RT
距离场 + 表面缓存]
B -- 是 --> D{画面有大量镜面
或高精度反射需求}
D -- 是 --> E[硬件 RT
三角形追踪 + Hit Lighting]
D -- 否 --> F{帧预算还吃得下
1.5 到 3 毫秒}
F -- 吃得下 --> E
F -- 吃不下 --> C
C --> G[妥协清单
镜面反射粗糙 / 薄墙漏光 / 细节走样]
E --> H[代价清单
BLAS 显存 / 帧时间 / 平台收窄]软 RT 最出名的短板是镜面反射。磨石地板那种模糊反射,表面缓存的低清照片足够以假乱真;但一面正镜子会立刻露馅——反射里细节糊、远处物体直接缺席,因为屏幕探针拿不到的只能退回屏幕空间追踪或全局距离场近似。对策三选一:上硬 RT;给那面镜子单独放平面反射(Planar Reflection),让它离线式地精确;或者美术层面回避正镜。「回响峡谷」的废弃医院里几乎找不到完好的镜子——巧合?不,这是选型:软 RT 配漫反射为主的室内,短板被场景藏起来了。
第二个坑是漏光。表面缓存的卡片分辨率有限,一堵 5 厘米的薄墙,两侧房间的光会互相渗过去。规范写死在灰盒阶段:墙体厚度不低于 20 厘米;某面墙还是漏,就提这个 mesh 的距离场分辨率(Distance Field Resolution Scale,墙和门给到 1.5 至 2.0),或增加它的 Lumen 卡片数(网格 Build Settings 里的 Max Lumen Mesh Cards,字段名以 5.8 面板为准)。
还要认清 Lumen 的适用半径。表面缓存和最终聚集都围着相机转,远距离的间接光会被逐步裁掉,改由天光兜底——所以 Lumen 场景里 Sky Light 不是配角,它是远处世界的"基础保险",关掉它,远山和城市轮廓会像黑洞一样吸光。室内小场景把 Max Trace Distance 收紧换精度,室外大场景则反过来:放宽追踪距离、接受更粗的远处反弹。「远港 Online」那种无缝大地图另有全套难题(流送中的距离场缝合、跨格子的表面缓存),第 16 章讲大世界时会接着这本账往下算。
补课:本节补全局光照的美术基础——美术补课第一块砖。程序出身可借此理解美术在吵什么,美术出身可跳过。
先立三个名词。直接光:从光源出发、中间不经过任何反弹就落在表面上的光,正午阳光投下的那块亮斑就是。间接光(反弹光):光打到表面后弹起来再照亮别处——阳光落在白墙上,白墙把光弹到天花板,天花板再弹进房间深处。漏光(Light Leaking):间接光算错了地方,隔壁房间的光"渗"进本该是暗部的区域,是烘焙分辨率和 Lumen 表面缓存不足时的常见病。
开一个灰盒走廊对照看。开 Lumen 前:阳光从窗口进来,地板上一条亮带,亮带之外是死黑——墙角、桌底、走廊尽头全部纯黑,因为引擎里没有任何东西负责"反弹"。美术的补救只能是塞假补光:挂一盏不投影的点光假装反弹光。假灯没有影子、方向不对,玩家一眼看穿"这光是贴的"。
开 Lumen 后,同一条亮带变成了光源本身:阳光打在地板上,地板把光弹到天花板和白墙,白墙再把光渗进走廊深处;拐角不再是纯黑,而是一直衰减到刚好能看清轮廓的灰。墙上一盏红色应急灯没开投影,却把左右两个门洞染成了暗红——那是它的光在墙面上弹了一次的结果。美术第一次拥有了"光的体积":场景里每块被照亮的表面,都自动加入照明阵营。这就是 TLOU 式室内光影的物理基础,也是 7.6 节布光参数要复现的东西。
一帧之内,这套系统在 GPU 上是这样交接的:
sequenceDiagram
participant G as Game 线程
participant R as 渲染线程
participant V as "GPU(Lumen)"
G->>R: 提交场景变化(新入视锥/移动/材质改)
R->>V: 更新 Lumen Scene 实例与变换
V->>V: 为新出现的 mesh 捕获卡片
(Surface Cache Atlas)
V->>V: 增量更新网格/全局距离场
V->>V: 屏幕探针按 BRDF 重要性采样追踪
V->>V: Final Gather:探针插值 + 时空滤波
V->>V: 辐射缓存更新(镜面方向)
V-->>R: 间接光与反射结果送 Base Pass 合成注意"为新出现的 mesh 捕获卡片"这步:表面缓存按需增量更新,从不每帧全量重拍,默认图集大小有限(4096 量级),场景塞太多不同 mesh 会挤爆图集,表现为远景光照闪烁——这是 Lumen 场景要控制"独特资产数量"的原因。
还要知道谁进不了这份分身。软 RT 的追踪世界里没有半透明,也没有蒙皮角色:玻璃、水面、皮肤只能接收间接光(走探针与辐射缓存采样),不贡献反弹。工程后果很直白——"红色的水面把光弹到天花板"这种效果,软 RT 的 Lumen 给不了;角色站在红墙边,身上能染上反弹的红,但角色自己不会把光弹回给环境。需要角色参与反弹的场景,要么上硬 RT(蒙皮几何在硬 RT 下可被追踪,支持细节以 5.8 的 Lumen 词条为准),要么当作不存在来设计布光。
屏幕探针那一步值得用数学看一眼。屏幕上每个像素并不自己追踪光线,而是从周围 2×2 的探针里插值借光。第 i 个探针的权重由三项相乘:
三项各管一件事。 是双线性空间权重:像素离哪个探针近,就多听谁的。法线项 要求朝向一致:地板像素不该偷听天花板探针的话——否则天花板就漏到了地板上。深度项按 指数衰减:探针所在表面和像素所在表面纵深差太多,权重归零——这一挡的正是"墙另一侧的探针",是抗漏光的数学闸门。分母做归一化,保证四项权重凑不齐时画面不整体变暗。真实实现还叠了射线命中距离等更多项,这套简化模型足够解释你调参时看到的大部分现象:为什么探针密度(r.Lumen.ScreenProbeGather.DownsampleFactor,默认每 16 像素一探针)减半,漏光和噪点一起退潮,帧时间一起上涨。
7.4 VSM:阴影从"每帧重画"变成"按变化付费"
传统阴影的两笔旧账。方向光用级联阴影贴图(CSM):3 到 4 级级联,每级固定 2048²,近处嫌糊、远处浪费,bias 调参是地狱——调小了漏光,调大了阴影飘离脚底(Peter Panning)。局部光更惨:点光的立方体阴影贴图六个面每帧重画,第 6 章那场 120 盏灯的评审事故,一半罪在阴影。
虚拟阴影贴图(Virtual Shadow Maps, VSM)换了思路。每盏灯配一张 16K×16K 的"虚拟"阴影贴图,切成 128×128 纹素的页;屏幕上真正覆盖到的页才在页表里分配、才渲染几何;渲染过的页在物理池里缓存,场景没变就跨帧复用。阴影成本从"每帧全量重画"变成"按变化付费"——静态场景里,第二帧起阴影几乎白送,而且逐像素都锐利,大光源还能按距离给出自然的半影。
VSM 和 Nanite 是咬合设计的:Nanite 的簇直接投影进 VSM 的页,不用为阴影再跑一遍传统几何管线。这就是为什么三件套要当成一套系统理解——三者是并联咬合的整体,而非三个独立开关。
一页的生命周期就是 VSM 全部的成本逻辑:
stateDiagram-v2
[*] --> 空闲: 物理池中的页
空闲 --> 已分配: 视锥内首次覆盖到该页
已分配 --> 已渲染: 本帧向页内投影几何
已渲染 --> 缓存复用: 几何与灯光均未变化
缓存复用 --> 已渲染: 变化触发页失效
缓存复用 --> 空闲: 长期未访问被回收
已分配 --> 空闲: 移出视锥 页表项被裁显存账要算清楚。虚拟的 16K² 只是地址空间,真占显存的是物理池里那些被分配的页。每页 128×128 纹素、32 位深度:
代入一个常见量级:4096 页 ≈ 256MB。物理池默认总量在数百 MB 量级(具体默认值与调参项以 5.8 的 r.Shadow.Virtual 系列 CVar 和官方文档 Virtual Shadow Maps 词条为准),用带 VirtualShadowMap 关键字的 stat 命令可以实时看占用。这笔显存是固定支出,买断了"阴影不用每帧重画"的收益。
VSM 的红线有三条,全是"缓存失效"的变种。一,大量会动的 masked 植被:风一吹,覆盖那些页的几何变了,页每帧失效重画,缓存形同虚设——又是风场植被,它在三件套里处处交税。二,几十盏投影局部光同屏:每盏灯都在抢物理池,页被反复踢出再分配(thrashing),阴影开始闪烁、帧时间出现尖峰——局部光的投影预算要写进设计规范,同屏投影灯先按个位数控制。三,低端机与移动平台:VSM 和 Nanite 同代,老硬件直接回传统阴影方案,不要在低配档里硬撑。
给卷三埋个伏笔。「远港 Online」的大世界里,方向光 VSM 配合流送是天然搭档——玩家走近,对应区域的页才分配渲染,走远回收——但 MMO 的同屏密度会放大第二条红线:一个主城广场几十个玩家自带投影技能特效,加上建筑灯海,物理池压力远比单机竖切大。等做到卷三性能章,你会回来感谢现在记下的这条经验:MMO 的投影灯预算,要从设计文档阶段就掐死,不是上线前再砍。
7.5 三件套联开:三档显卡的帧率区间表
先把测试条件钉死,没有条件的帧率数字都是耍流氓。下表基于这样的场景:灰盒建筑加中密度资产的室内外混合关卡,含一片废墟街区加两层室内;原生 1440p,TSR 不开超分;画质 Epic 档;Windows 11 配当代驱动。数字是数量级估算,误差按 ±20% 看,你的场景以实测为准——测量方法见第 6 章帧剖析,系统化的性能工作流在第 18 章。
| 开关组合 | RTX 3070 | RTX 4070 | RTX 5090 |
|---|---|---|---|
| 三件套全开(软 RT) | 40–55 | 65–85 | 110–140 |
| 三件套全开(硬 RT) | 28–38 | 50–65 | 85–110 |
| Nanite + VSM,无 Lumen(SSGI 替代) | 55–70 | 90–115 | 140–175 |
| 全关(传统 LOD + CSM + SSAO) | 70–90 | 105–130 | 165–200 |
表要横着读。3070 这一行:软 RT 全开落在 40 到 55,能上但谈不上从容;硬 RT 直接掉进 30 帧的 PPT 区——这张卡的时代,硬 RT 只适合截图赏画,别拿来跑 gameplay。4070 是软 RT 的甜点档:65 到 85 帧,画质革命完整落袋。5090 才有资格把硬 RT 当默认选项,而且多出来的帧率余量通常会被 4K 或超分关掉立刻吃回去。
再补一条不在表里的账:美术工时。Nanite 省 LOD 制作,Lumen 省烘焙等待和假补光摆放,VSM 省 bias 调参。哪怕某台目标机器帧率打平,省下的也是真金白银的人月。开三件套从来不只是帧率决策,还是产能决策。反过来,"开错是 PPT"的标准画像也要记住:风场草皮加 Nanite、几十盏投影灯配 VSM、3070 强开硬 RT——三个坑任意踩中一个,评审会就等着沉默。
用四象限把五种组合摆进"开销—画质"坐标,选型直觉一眼就有:
quadrantChart
title "三件套组合:画质收益 vs GPU 开销(1440p 数量级)"
x-axis "开销低" --> "开销高"
y-axis "画质低" --> "画质高"
quadrant-1 "土豪专属"
quadrant-2 "甜点区"
quadrant-3 "别停在这"
quadrant-4 "只配截图"
"全关传统方案": [0.15, 0.25]
"只开 Nanite": [0.30, 0.45]
"Nanite 加 VSM": [0.45, 0.62]
"软 RT 全开": [0.62, 0.85]
"硬 RT 全开": [0.90, 0.95]这张表还有第三个变量没展开:超分。表里是原生 1440p 的数字;打开 TSR(或 DLSS/FSR,档位名以 5.8 对应插件为准)从 1080p 左右上采样,整张表大约上移一个档位——3070 配超分能摸到 4070 原生的体验。代价是运动中的鬼影与细节闪动,而 Lumen 的时空滤波恰好也吃历史帧,两套时序重建叠在一起,植被边缘和高速甩镜头时最先露破绽。所以超分是"预算实在不够时的止痛药",不是默认选项;定目标帧率时先按原生算,算不过来再上。
落到工程上,档位不该让玩家自己在设置菜单里猜。启动时按 GPU 跑分(或设备档案 Device Profile)选好组合,写进日志备查。下面这段伪代码是「回响峡谷」的启动逻辑骨架:
# 伪代码:启动时按 GPU 档位选择三件套组合
TIER_50, TIER_40 = 9000, 5000 # 以引擎 GPU 跑分或设备档案为准
def apply_rendering_tier(gpu_score, vram_mb):
set_cvar("r.Nanite.ProjectEnabled", 1) # 三档都开 Nanite:硬表面成本相近
set_cvar("r.Shadow.Virtual.Enable", 1)
if gpu_score >= TIER_50 and vram_mb >= 20000: # 5090 档
set_cvar("r.Lumen.HardwareRayTracing", 1)
set_cvar("r.Lumen.ScreenProbeGather.DownsampleFactor", 16)
elif gpu_score >= TIER_40 and vram_mb >= 12000: # 4070 档:软 RT 甜点
set_cvar("r.Lumen.HardwareRayTracing", 0)
set_cvar("r.Lumen.ScreenProbeGather.DownsampleFactor", 16)
else: # 3070 及以下:软 RT 减配
set_cvar("r.Lumen.HardwareRayTracing", 0)
set_cvar("r.Lumen.ScreenProbeGather.DownsampleFactor", 32) # 探针密度减半
set_cvar("r.Lumen.Reflections.MaxRoughnessToTrace", 0.3) # 反射只留近镜面
log_tier_choice() # 线上问题排查全靠这行日志要点三个:Nanite 与 VSM 三档全开,因为它们在硬表面场景的成本与档位关系不大;降级先砍 Lumen 的探针密度与反射追踪粗糙度,这是性价比最高的两刀;无论选了什么,写日志——线上反馈"画面黑"时,这行日志能省你半天。
7.6 「回响峡谷」实操:废弃医院走廊的 Lumen 布光参数
对标目标先翻译成工程语言。TLOU 式室内光影拆开来是三条规律:一盏主光打天下,七成照明靠反弹;暗部永远是衰减到刚好保住轮廓的灰——绝对不能让暗部黑成一片;颜色靠一两个色源点题——一盏红色应急灯、一块绿色出口牌,比十盏白炽灯更有叙事。这三条恰好全是 Lumen 的强项:反弹、衰减、自发光染色。用烘焙做这套东西,光一挪全重来;用 Lumen,挪灯即所见。
场景设定:走廊长 24 米、宽 2.4 米、高 3.2 米;右侧三扇破窗,窗外是黄昏方向光;走廊尽头是安全出口;中段侧墙挂一盏红色应急灯;天花板垂一根断掉的灯管。灰盒阶段墙厚统一 25 厘米,先堵死漏光的物理可能。
参数组合如下,全部按软 RT、4070 档目标调,字段名以 5.8 编辑器面板为准:
| 项 | 参数组合(示例起点) |
|---|---|
| Directional Light | Intensity 10 lux,色温 5200K,Source Angle 约 1.5°,从破窗 35° 斜射;开 VSM 投影;Indirect Lighting Intensity 1.0 |
| Sky Light | Real Time Capture,Intensity 0.4,只铺一层冷色天光底,不抢戏 |
| 应急灯 PointLight | 红色,强度从 6(坎德拉档,单位以项目 Light Units 设置为准)起调,衰减半径 500cm,不投影——让 Lumen 的反弹替它做戏 |
| 出口牌 | 材质 Emissive 颜色乘 20 到 50,Lumen 拾取自发光,把绿晕染到地面 |
| 后处理体积 | GI 方法 Lumen(软 RT);Final Gather Quality 1.0 到 1.5;Max Trace Distance 收到 100 至 200 米量级,压远处漏光与噪声;反射走 Lumen,Max Roughness To Trace 0.4,Screen Traces 开 |
| 曝光 | 自动曝光 Min/Max Brightness 锁 0.3 / 1.5;或手动 EV100 约 11,目标暗部不死黑 |
| 体积雾 | ExponentialHeightFog 开 Volumetric,Density 约 0.015,让窗口光轴显形;成本不低,低配档第一个砍它 |
| 距离场精度 | 墙体与门框 Distance Field Resolution Scale 1.5 至 2.0 |
这套参数的核心思路是"少即是多":真正投影的光只有方向光一盏,应急灯和出口牌都不投影,靠反弹和自发光染色撑起氛围——阴影质量全押在那一盏灯的 VSM 上,16K 虚拟页表给足锐利度,破窗的光斑边缘能看清窗棂的分叉。
自发光照明有一个使用纪律:Emissive 照进 Lumen 的光是低分辨率的,适合"染色",不适合"照明"。出口牌的绿光晕开一米是氛围,指望它照亮半个走廊就会看到闪烁和色块——需要真照明的地方,还是老实放一盏小强度点光,让 Lumen 管它的反弹。
验收用眼睛加控制台双重过。眼睛看四件事:光轴落地成亮带;亮带把天花板弹亮,白墙把光渗进走廊深处;红色应急灯染红左右两个门洞,但没有生硬的"光贴";尽头出口牌的绿光在地面晕开,走廊中段暗部保得住门洞轮廓。控制台按下面的清单走:
; 走廊验收控制台(PIE 内逐条敲)
stat gpu ; 读出 Lumen / Nanite / ShadowDepths 各 Pass 毫秒数
stat Nanite ; 簇与三角形计数,异常资产一眼现形
r.Lumen.HardwareRayTracing 0 ; 确认本次走软 RT
; 视图菜单:Lit → Lumen → Lumen Scene / Surface Cache,检查表面缓存覆盖(菜单名以 5.8 为准)
; 阴影显存:stat 列表里搜 VirtualShadowMap 相关项,确认物理池没爆4070 档上这条走廊的 GPU 时间应落在 8 到 12 毫秒(1440p 原生,数量级)。超了先查三样:体积雾密度、探针 DownsampleFactor、有没有美术偷偷给应急灯开了投影。光的故事讲到这里,"亮不亮"解决了;但灰盒的墙还是灰的——"像不像"是第 8 章材质系统的事。更完整的布光方法论(三点布光、色温叙事、构图引导)在第 10 章,本章先把物理正确性立住。
小结
Nanite 把 LOD 从资产级细化到簇级:硬表面放心全开,变形体、半透明、风场植被三类资产别硬塞,进门资格表贴在墙上。Lumen 软 RT 是 60 帧主力,硬 RT 买的是反射质量;镜面是软 RT 的短板,用平面反射补,或者像「回响峡谷」一样在场景设计里回避正镜。VSM 让阴影按变化付费,静态场景近乎白送;代价是数百 MB 量级的显存池,以及"会动的植被"和"投影灯海"两类缓存失效场景。三档显卡的常识线:3070 走软 RT、4070 是软 RT 甜点、5090 才谈硬 RT 默认开——数量级估算,你的场景永远以实测为准。开三件套不只是帧率决策,还是产能决策:Nanite 省 LOD 工时,Lumen 省烘焙与假灯工时,VSM 省 bias 调参工时;算总账时把人月放进等式。
上手任务
- 三件套 A/B 对照:用任意灰盒室内场景,按 7.1 的 ini 片段全开三件套,截一张图;再
r.Nanite 0、GI 方法改 None、阴影改回传统,截同机位一张图。验收:两张图并排,用一句话说清间接光差在哪,并记录两种方案的stat gpu总毫秒数。 - 走廊复刻:按 7.6 的参数表搭出废弃医院走廊。验收:眼睛验收四条全过;4070 档机器 1440p 原生 GPU 时间 < 12ms;Lumen Scene 视图下表面缓存无明显空洞。
- 边界试探:找一个骨骼网格尝试开 Nanite,再给一片植被同时开 Nanite 与 WPO 风场。验收:记录编辑器对前者给出的行为(拒绝或回退),记录后者开关前后的帧率差,用一句话写出"为什么贵"。
- 档位落地:把 7.5 的伪代码翻成你工程里的启动逻辑(蓝图或 C++ 均可,读设备档案即可,不必真做跑分)。验收:在两台不同档位机器上启动,日志各打印出正确的档位与 CVar 组合。
下一章
光到位了,墙还是一块灰色的墙。第 8 章进材质系统:PBR 四参数的物理直觉、材质函数的组织学,以及「回响峡谷」末日质感材质包——让灰盒长出锈迹、霉斑和被水泡过的墙皮。
延伸阅读
- 官方文档:Unreal Engine → Rendering → Nanite Virtualized Geometry(支持特性清单以词条为准)
- 官方文档:Unreal Engine → Rendering → Lumen / Virtual Shadow Maps
- SIGGRAPH 2021, Advances in Real-Time Rendering:Epic 的 Nanite 深度分享(簇管线与可见性缓冲的第一手讲解)
- 源码:Engine/Source/Runtime/Renderer/Private/Nanite 与 Private/Lumen 目录,配合第 6 章的 RDG 知识食用
- 官方文档:Unreal Insights 词条(帧剖析工具,第 18 章会系统使用)