Nanite 虚拟几何体原理与工程实践
Nanite 虚拟几何体原理与工程实践
Nanite 是 UE5 的虚拟几何体系统,它用一套全新的方式存储和渲染网格:导入时把网格拆成分层的三角形群集(Cluster),渲染时按摄像机视图动态切换 LOD、按需流送数据,只把可见细节留在内存里。它跑在自己的渲染通道里,完全绕过传统 draw call。这篇讲清楚它的三个核心思想和一堆落地时会撞上的限制。
核心思想一:Cluster + 一个 DC 画整个场景
思路可以追溯到《刺客信条:大革命》,后来发展成 Cluster based render pipeline 的最佳实践:把可见 Instance、可见 Cluster、可见 triangle 打包进一个超大的 Index Buffer(Index Buffer Compaction),然后用一个 draw call 渲染整个场景。这是 GPU Driven Pipeline 的基础。
核心思想二:View-dependent LOD 与 Cluster Group
三角形数量不该随几何模型数量增长——无论场景多复杂,同一块屏幕上的像素数是固定的,所以可以按屏幕精度来决定三角形数量,这就是 View-dependent LOD transition。
Nanite 的解法是 Cluster Group。这里有个关键设计:LOD 切换时不能出现明显的 popping。Nanite 在合并 Cluster 时总是保留高频的边界——人眼对高频边界很敏感——并让 merge 之后的高频边界较为随机,人眼就看不出 LOD 切换带来的跳变。
遍历也要并行化。如果按 Cluster Group → Cluster 的层级顺序串行遍历来选 LOD,效率极低,得处理成并行,让每个 Cluster 的 LOD selection 自己决定画不画。即便并行还是慢,于是对每一级 LOD 的全部 Cluster Group 的 bound 建 BVH 做初步筛选:玩家站在某视角看模型时能初步定这个模型该在 LOD 几,其他 LOD 的 Cluster Group 直接剔除。
核心思想三:虚拟几何体(虚拟内存)
从虚拟纹理反推虚拟几何体。设想摄像机对准一个很大的房子看不全,渲染这一帧时 GPU 先去显存拿可见的被分割数据来画;摄像机左移后需要的数据显存里没有,就通过 page table 找到物理数据加载进显存。现代游戏场景极复杂,而物理内存有限——显卡厂商这些年一直加显存也不够用。物理内存有限,但虚拟内存可以远大于物理内存,这就是虚拟几何体的立足点。
顺带一个量级感:按 10GB 显存算,满负荷下也仅够支撑接近 500 个 Nanite 物件(还没算贴图等),所以复杂场景仍要靠 Level Streaming / World Partition 把远处或看不见的地方卸载掉。
Nanite 的优势
- 几何复杂度提高数个数量级,实时渲染的三角形/对象数量达到前所未有的高度
- 帧预算不再被多边形数量、draw call、内存使用卡住
- 可直接导入电影级资产(ZBrush 雕刻、摄影测量扫描)
- 用高模实现细节,而非把细节烘焙进法线贴图
- 自动 LOD,不再需要手动设置单个网格的 LOD,且 LOD 过渡时几乎无品质损失
限制与工程权衡
这部分是真正踩过才知道的坑,逐条记:
- DCC 软件扛不住超高模:Nanite 让美术能直接把照扫模型导进引擎,但很多 DCC 软件不支持太高的模型复杂度。实测 30 万顶点的模型就会让建模软件卡到没法工作,所以除照扫资源外最好别超过 50 万点。
- 材质方案的取舍:Nanite 精度上来后传统材质思路不够用了,普遍转向 layer 材质。这里有两条路——一条是宽松、适配各种来源资源的 layer 材质,可自由加贴图打底,美术用着方便、效率高、兼容各种途径的资源,缺点是资源复用率低(引擎端和 SP 等作图工具端都低);另一条是建大量基础材质库(不是 shader,而是各种方法产出的贴图资源)再用大量 mask 混合,复用率很高,但需要大量时间维护 SP 基础材质库。有的项目为了这套方案中途换过三次材质系统。基于实际生产效率,很多团队会先选第一套,等材质库积累起来再谈第二套。
- 不支持形变动画:Nanite 支持刚体动画和 WPO,但不支持形变(蒙皮)动画。角色动画的几条路子:① 传统流程用低面模型蒙皮做动画(不走 Nanite),但低面要靠烘焙高精度贴图才有好效果;② 把 Nanite 物件挂到骨骼插槽上做刚体动画,美术效果好但要拆成刚性动画集合、往往需要很多骨骼,制作繁琐,且做不了形变(能做开关门、旋转这类);③ 把动画转成 WPO 贴图在材质里做,但角色动画太繁琐不可能全用贴图,只适合简单物件——而且 WPO 会让 Nanite 的 view buffer 不断刷新,非常费性能,不能大批量用(比如 Nanite 植被)。
- 不支持半透明:渲染时 Nanite 物件和非 Nanite 物件走两种方式,很多 buffer 要两份。如果项目没有半透明物件,尽量全开 Nanite。
- 细碎/镂空物件支持差:自动 LOD 对细碎的东西处理不好。
- 更容易 popping,需配合 TSR:精度上来后细碎的 popping 边缘概率更高,TAA 大概率抹不掉。UE5 为此推出 TSR,但 TSR 的 bug 更严重——对动态物体(尤其粒子)会造成严重残影,对屏幕空间 GI 有明显停滞。测试下来在 4K HDR 50% 动态分辨率下,DLSS 和 TSR 画质几乎能到真 4K,但 TSR 有残影、DLSS 偶发莫名显示 bug。
- culling 效率高但渲染开销大部分在 culling:这不矛盾——3A 场景太复杂,仍需 Level Streaming / World Partition 配合。
延伸阅读
- GAMES104, Lecture 22: GPU Driven Geometry Pipeline - Nanite
- Nanite Virtualized Geometry(UE 官方文档)
- A Journey to Nanite(HPG 2022)