UE5 Graphics Deep Insights From Japan
UE5 Graphics Deep Insights From Japan | Unreal Fest Bali 2025
引言:UE在日本的应用
-
核心观点: Unreal Engine在日本不仅用于传统的写实风格游戏 (如《最终幻想7》、《王国之心》),也广泛用于非真实感渲染 (NPR) 风格的作品。
-
日本市场特性:
-
偏好风格化: 受到漫画和动画的启发,风格化图形非常流行。
-
主流平台: 主要是游戏主机 (特别是 Switch 和 PlayStation),而非PC或移动设备。
-
开发需求: 这意味着严格的性能优化是刚需。
-
一、 GPU性能分析 (GPU Profiling)
ProfileGPU 的准确性问题
演讲的核心问题:ProfileGPU 命令在多大程度上能准确反映真实的GPU工作负载?
答案是:不一定准确,主要干扰因素是异步计算 (Async Compute)。
异步计算 (Async Compute) 带来的挑战
-
UE5的趋势: UE5越来越多地使用异步计算(例如Lumen)来并行处理图形管线和计算管线,以提高GPU的利用率和效率。
-
分析的困难:
-
任务重叠: 异步计算导致多个渲染过程在时间上重叠。这使得在分析器中很难孤立地查看单个任务的GPU工作负载。
-
隐藏的等待: 某个Pass(如Lumen Scene Lighting)在分析器上可能显示占用了2毫秒,但实际上其中包含了很多GPU等待时间,并非纯粹的计算开销。
-
UE中分析工具的演进 (5.6版本为分水岭)
1. Unreal Insights (UE Insights)
-
UE 5.5及之前: 无法显示异步计算任务。例如,Lumen中重要的
ScreenProbePass在Insights中是不可见的。 -
UE 5.6及之后: 默认支持显示计算管线 (Compute Pipe),无需额外设置。
2. ProfileGPU 命令
-
UE 5.5及之前:
-
为了测量,它会强制关闭异步计算。
-
这导致其结果不准确,因为它没有体现异步计算带来的性能优势,测得的开销可能高于实际运行情况。
-
-
UE 5.6:
-
ProfileGPU开始支持异步计算,结果会同时显示图形和计算管线。 -
但专用的UI窗口被移除,结果只显示在输出日志 (Output Log) 中。
-
-
UE 5.7:
-
ProfileGPU的UI窗口回归。 -
重要注意: 此版本不会禁用异步计算(因此总时间更准),但它不会显示计算管线的活动详情。
-
性能分析工具的正确使用层级
讲者强调,不应只依赖UE内置工具。
-
UE内置工具 (
ProfileGPU,Unreal Insights)-
用途: 适用于日常分析,快速了解性能瓶颈在哪里 (Where)。
-
优点: 轻量级,易于使用。
Unreal Insights是获取完整管线图(图形+计算)的首选。
-
-
平台/硬件方分析器 (PIX, Razor, NSight等)
-
用途: 深入理解为什么某个Pass开销很高 (Why)。
-
优点: 可以捕获单帧,详细分析是计算密集、纹理访问过多、寄存器使用率高,还是异步计算重叠问题。
-
结论: 使用这些工具可以避免进行无效或不必要的优化。
-
核心洞察:如何正确进行GPU分析
-
异步计算使分析更难: 由于任务重叠,理解单个Pass的开销变得更加困难。
-
UE 5.6是转折点: 此版本后,UE的工具才开始原生支持异步计算的可视化。
-
隔离Pass的方法: 如果你想微调和优化某个特定的渲染过程(如Lumen),建议在分析时暂时禁用该过程的异步计算,以便观察其独立的开销。
-
工具组合拳:
-
日常使用
Unreal Insights查看整体负载和瓶颈分布。 -
深入分析使用平台方工具 (PIX, NSight等) 来定位开销的根本原因。
-
二、 着色器复杂度视图 (Shader Complexity View Mode)
核心误区:指令数量 ≠ 性能开销
-
视图功能:
Shader Complexity视图的目的是可视化每个像素执行的着色器指令总数。 -
核心问题: 在此视图中显示为红色(昂贵)或白色(极度昂贵)的材质,是否_一定_需要修复?
-
答案:否。 讲者强调:指令数量 (Instruction Count) 绝对不等于性能开销 (Performance Cost)。
-
经典类比:
-
请求1:“给我做点煎饼。”
-
请求2:“在GDC上做两个技术演讲。”
-
两个请求写下来都只是一行字(指令数少),但实际工作量(性能开销) 天差地别。
-
-
材质中的同理:
-
不同的指令开销不同(例如,纹理访问通常比简单的数学计算昂贵得多)。
-
你可以在材质编辑器的 平台统计 (Platform Stats) 窗口查看指令数。
-
误导性的"陷阱"
陷阱1:智能的着色器编译器 (Alpha Test 示例)
-
Alpha Test (Masked Material): 材质中可能包含一个
discard指令。 -
编译器优化: 现代GPU编译器非常智能。它可能会首先评估Alpha条件,如果一个像素被
discard,后续所有其他计算(即使指令数很多)都将被跳过。 -
导致的结果: 一个材质可能指令总数非常高,但在
Shader Complexity视图中看起来很昂贵,而实际GPU开销却极低,因为大部分指令都被跳过了。
“魔鬼”示例:
低指令数 (39),高开销 (20ms):一个故意构造的材质,可能涉及复杂的依赖或纹理读取。
高指令数 (2533),低开销 (0.04ms):一个包含大量分支或
discard的材质,大部分计算被跳过。
陷阱2:视图无法反映 Masked 材质的真实开销
-
最大的问题:
Shader Complexity视图完全不反映被discard(裁剪) 掉的透明区域的开销。 -
Masked Material (遮罩材质): 即使是透明区域,GPU仍然运行了着色器(至少运行到
discard指令)。这部分是有开销的。 -
视图的误导: 该视图会将 Masked 材质显示得如同完全不透明 (Opaque) 材质一样廉价,这是严重误导。
核心洞察:如何正确优化材质
-
Shader Complexity 的推荐用途: 讲者认为,这个视图现在最适合的用途可能只剩下检查半透明 (Translucent) 元素的重叠 (Overlapping) 情况。
-
最危险的事: 投入时间去做无效的优化 (ineffective optimizations)。仅仅因为视图是红/白色就去削减指令数,很可能在牺牲了视觉质量后,并不能带来任何实际的性能提升。
-
正确的优化路径:
-
使用详细的GPU分析器 (PIX, NSight等)。
-
精确定位真正的性能瓶颈(例如,是ALU计算受限,还是纹理读取或带宽受限)。
-
基于实际瓶颈进行优化。
-
三、 TSR (Temporal Super Resolution - 时间超分辨率)
TSR的核心价值与挑战
-
核心价值: TSR是一种升采样 (Upscaling) 技术。它能以极低的性能开销(例如从57ms降至16ms)将低分辨率渲染(如1080p)提升至高分辨率(如4K),而视觉差异很小。
-
对UE5的意义: Nanite 和 Lumen 的性能都依赖于内部渲染分辨率。TSR能极大地降低它们的负载。
-
工作原理: TSR通过引用过去的多个帧来重建("收集样本")当前帧,以达到目标分辨率。
TSR的"阿喀琉斯之踵":Ghosting (鬼影)
TSR的质量高度依赖于能否从足够近的过去帧中收集到足够的样本。当它被迫引用_太久远_的帧时,就会出现鬼影。
导致引用过往旧帧的两个主要原因:
-
过低的内部渲染分辨率:
-
原理: 内部渲染分辨率越低,TSR需要“拼凑”的像素就越多,因此需要回溯的帧数也越多。
-
极端示例: 当内部仅以25%分辨率渲染时,摄像机移动会导致整个画面非常模糊。
-
-
过低的帧率 (FPS):
-
原理: 假设TSR需要引用4帧前的样本。
-
在 60 FPS 下,这4帧发生在 ~66ms 内。
-
在 30 FPS 下,这4帧发生在 ~133ms 内。
-
-
结论: 帧率降低一倍,TSR就必须引用时间上“老”一倍的帧。这就是为什么帧率下降会导致鬼影。
-
CPU与GPU的共同责任: 即使GPU成本很低,但如果CPU成为瓶颈(例如动画、加载)导致FPS下降,同样会引发TSR鬼影。
-
TSR的另一个关键:速度向量 (Velocity Vectors)
-
TSR的依赖: TSR使用速度向量来预测前一帧的像素在当前帧的位置。
-
问题: 如果一个物体(例如一个旋转的轮子)没有被正确地写入速度向量,TSR就无法正确地"重投影"它的历史信息。
-
结果: 在该物体上出现严重的模糊和鬼影。
-
解决方案: 必须确保所有运动物体(包括通过WPO或着色器实现的运动)都生成了正确的速度向量。
核心洞察:如何用好TSR
-
TSR的优点: 在摄像机静止时,TSR表现极佳。由于前后帧完全一致,它甚至能从25%的分辨率完美重建出高清图像。
-
TSR的敌人:
-
过低的内部渲染分辨率。
-
不稳定的帧率(无论是GPU还是CPU引起的)。
-
错误的速度向量。
-
-
重要资源: 从 UE 5.4 开始,TSR的官方文档得到了极大扩展,内容详尽,强烈推荐阅读。
四、 MegaLights (实验性功能)
-
核心概念: 一项正在开发中的实验性 (Experimental) 新功能,旨在高效支持海量的光源,即使在主机平台上。
-
工作原理: 它通过固定每像素每帧的最大采样数来实现。
-
关键优势: 即使场景中的光源数量增加,处理成本也不会显著增加。
-
与传统渲染的对比:
-
传统光照: 为每个光源执行光照计算。成本随光源数量线性增长,优先保证准确性。
-
MegaLights: 优先保证性能优化。即使放置再多光源,开销也不变,但这可能会导致轻微的渲染错误或瑕疵。
-
核心洞察:关于MegaLights的警告
-
状态: 纯粹的实验性功能。
-
讲者建议: 讲者非常严肃地建议,在"实验性"标签被移除之前,不要在任何生产项目中使用MegaLights。
五、 虚拟纹理 (VT) 与 虚拟阴影贴图 (VSM)
1. 虚拟纹理 (Virtual Textures)
-
重要变更: 从 UE 5.6 开始,虚拟纹理默认开启。
-
核心问题: Moiré (摩尔纹) / 远处闪烁
-
根本原因: 虚拟纹理有一个最小MipMap尺寸,即其Tile Size (图块大小,默认为 128x128)。
-
后果: VT不会生成比 128x128 更小的MipMap。当一个高频纹理被拉到很远时,由于缺乏足够小的Mip,会导致严重的摩尔纹和闪烁。
-
当前限制: 这是VT目前的固有局限。
-
-
部分缓解方案:
-
TSR的一个控制台变量
r.TSR.Shading.Rejection.Flickering(在高/电影级设置下默认开启)可以极大改善这个问题。 -
但这并非完美的解决方案,某些摄像机角度下摩尔纹仍可能出现。
-
2. 虚拟阴影贴图 (Virtual Shadow Maps - VSM)
-
常见痛点: 开发者经常因为VSM的默认显存池 (512MB) 和高GPU负载而放弃使用它。
-
重要更新: VSM的显存和性能在近期版本中已显著改善。
优化技巧 1: 动态页面LOD (UE 5.4)
-
功能: 允许VSM根据你设置的显存池大小,自动动态调整页面的LOD。
-
CVAR:
r.Shadow.Virtual.MaxPagePoolLoadFactor -
示例: 即便将显存池从512MB强制降至60MB,VSM也能通过自动提升Page LOD(降低远处阴影质量)来适应这个预算,并依然渲染出效果不错的阴影。
优化技巧 2: 动态负载调整 (UE 5.6)
- 功能: 引入了一项新功能,可以根据实时性能表现来动态降低VSM的处理负载,类似于动态分辨率。
核心洞察:VSM/VT总结
-
VT: 默认已开启(5.6+),但要警惕它因最小Mip限制 (128x128) 导致的远处摩尔纹。
-
VSM: 性能和显存已大为改善(5.4+)。如果你曾因显存占用过高而放弃它,请尝试调小显存池并配合使用
MaxPagePoolLoadFactor。
六、 Lumen (动态全局光照)
Lumen的优缺点
-
优点:
-
实时动态GI: 无需烘焙光照,允许在运行时实现动态光照。
-
节省时间: 极大减少了光照烘焙的工作流程和时间成本。
-
-
缺点:
-
时间性瑕疵 (Temporal Artifacts): 和TSR类似,Lumen在运行时需要累积多帧的信息,这可能导致鬼影 (Ghosting) 或拖尾。
-
性能开销: 性能开销相对较高(尽管在持续改进中)。
-
核心问题:模块化资产 vs. Lumen卡片 (Modular Assets vs. Lumen Cards)
这是讲者在日本支持开发者时遇到的最大问题,尤其是在使用Megascans或Fab导入的模块化资产时。
1. 关键概念定义
-
模块化资产 (Modular Assets):
- 指将复杂的物体(如房屋)拆分为独立的、简单的部件(如墙壁、窗户、地板)。开发者通过组合这些部件来搭建场景。
-
Lumen卡片 (Lumen Cards):
-
为了快速计算GI,Lumen并不会直接处理高精度的模型。
-
相反,它会为每个静态网格体预先计算并生成一组极其简单的平面(卡片) 来代表这个物体,Lumen的GI计算是基于这些卡片进行的。
-
2. 模块化带来的"三难困境" (Catch-22)
问题1:单一复杂网格体 → GI计算错误
-
场景: 如果你将一个复杂的物体(比如带完整内部房间的房屋)制作成一个单独的Mesh。
-
后果: Lumen无法为这个复杂形状创建出贴合的、简单的Lumen卡片。
-
结果: 导致GI计算出错,在无法正确计算的区域出现粉色瑕疵。
-
基本规则: 因此,使用Lumen的基本规则是:复杂形状应由简单的模块化资产拼搭而成。
问题2:海量的Lumen卡片 → 性能崩溃
-
场景: 你遵循了规则1,使用了大量模块化资产(墙、窗等)来搭建建筑。
-
后果: 如果每一个模块化部件都有自己的Lumen卡片,那么一栋建筑就会生成海量的Lumen卡片。
-
结果: 导致处理负载和显存占用激增,无法实时运行。
问题3:合并卡片的副作用 → 内部GI失效
-
解决方案 (合并): UE允许将Lumen卡片进行合并。你只需为一组模块化资产(如一整栋建筑的所有部件)的静态网格体组件设置一个相同的
Ray Tracing Group ID。 -
City Sample 示例: 在City Sample中,整栋建筑的所有部件共享一个ID,它们被合并为一个单一、简化的Lumen卡片。
-
结果 (优点): 极大地减少了Lumen卡片的数量,显著降低了性能开销。
-
结果 (缺点): 这又带回了问题1。被合并后的卡片过于简单(例如只是一个方盒),完全丢失了建筑的内部结构。
-
关键事实: 这就是为什么在City Sample中,你无法进入任何一栋建筑的原因。
核心洞察:Lumen的当前权衡
-
现状: 目前Lumen没有一种机制能同时完美地处理建筑的外部和内部。
-
建议方案:
-
将建筑的外部和内部分开创建。
-
通过游戏逻辑来动态切换可见性。例如,当玩家在室外时,关闭内部模型的可见性和GI计算,反之亦然。
-
-
关键建议: 在使用模块化资产构建场景时,积极使用
Ray Tracing Group ID来分组Lumen卡片,这有助于优化性能。但你必须充分理解这会对内部空间的GI计算带来的副作用。
七、 Nanite (虚拟化几何体)
核心问题:Nanite对低多边形场景是否有效?
这是一个常见疑问:对于轻量级、低多边形的场景,使用Nanite是否反而会更慢?
测试1:轻量级场景 (Sol City)
-
条件: 将一个轻量级场景 (Sol City) 的所有网格体(平均约1,000面)转换为Nanite。测试时Lumen 和 VSM 均开启。
-
结果:
-
Nanite 开启 ≈ 9.9 ms
-
Nanite 关闭 ≈ 7.85 ms
-
-
结论: 在这个特定的轻量级场景中,禁用Nanite反而快了约 1.4 ms。
-
原因: Nanite 本身带有一定的 基础开销 (Baseline Overhead)。在这个场景中,Nanite的这点基础开销超过了它优化渲染所带来的收益。
测试2:高多边形场景 (PCG 树林)
-
条件: 使用PCG放置300棵树,每棵树约146万面。
-
结果:
-
Nanite 开启 ≈ 10.28 ms
-
Nanite 关闭 > 100 ms
-
-
结论: 在高多边形场景下,Nanite 带来了巨大的性能优势。
核心洞察:如何评估Nanite
-
Nanite的性能曲线:
-
Nanite 确实存在基础开销。
-
但一旦启用,其性能开销随多边形数量的增长极其缓慢。
-
(对比测试1和2:多边形数量增加了数千倍,但总帧时仅从9.9ms增加到10.28ms,几乎持平)。
-
-
Nanite的隐性价值: 最大的好处之一是它自动处理了所有LOD,极大地节省了美术和开发者的优化时间。
-
最终建议:
-
不要轻易下结论说“Nanite对简单场景太重了,我们关掉吧”。
-
一定要仔细测量 (Measure Carefully)。在做出决定之前,请在你的具体场景中进行评估和测试。
-