Nanite — Virtual Geometry & Software Rasterizer
What Problem Does Nanite Solve?Nanite 解决什么问题?
Traditional rendering has a hard constraint: the number of triangles drawn per frame is bounded by the GPU's rasterization throughput and the CPU's draw call overhead. Artists must create multiple LOD levels and spend significant time on polygon budgets. Nanite eliminates this constraint by virtualizing geometry: it renders only the triangles that are pixel-sized or larger on screen, streaming and culling the rest automatically. The result: imported CAD/ZBrush meshes with 100M+ triangles render at the same cost as a 10K polygon game mesh, because Nanite never renders more detail than the screen can display.
传统渲染有一个硬性约束:每帧绘制的三角形数量受 GPU 光栅化吞吐量和 CPU 绘制调用开销的限制。美术必须创建多个 LOD 级别,并在多边形预算上花费大量时间。Nanite 通过虚拟化几何体消除了这一约束:它只渲染屏幕上像素大小或更大的三角形,其余自动流送和剔除。结果:拥有 1 亿以上三角形的 CAD/ZBrush 网格与 10K 多边形游戏网格的渲染成本相同,因为 Nanite 永远不会渲染超出屏幕显示能力的细节。
Cluster Hierarchy — The BVH/DAG StructureCluster 层级 — BVH/DAG 结构
Every Nanite mesh is preprocessed (offline, during cook) into a cluster hierarchy — a directed acyclic graph (DAG) of triangle groups called clusters, each containing 64–128 triangles. This hierarchy is the key data structure that enables GPU-side LOD selection and culling.
每个 Nanite 网格在预处理期间(离线,在 Cook 时)被处理成Cluster 层级——一个由三角形组(称为 Cluster)组成的有向无环图(DAG),每个 Cluster 包含 64–128 个三角形。这个层级结构是实现 GPU 侧 LOD 选择和剔除的关键数据结构。
The hierarchy enables the GPU culling shader to select the finest level of detail that results in sub-pixel triangles being skipped. The LOD error metric is a screen-space projected error: if the error a cluster introduces (vs its parent) projects to less than 1 pixel, the parent is chosen instead.
层级结构使 GPU 剔除着色器能够选择最细的细节级别,同时跳过投影为亚像素的三角形。LOD 误差度量是屏幕空间投影误差:如果某个 Cluster 引入的误差(相对于其父节点)投影小于 1 像素,则选择父节点。
FPackedCluster — GPU Memory LayoutFPackedCluster — GPU 内存布局
Each cluster is stored as a tightly packed GPU struct FPackedCluster (NaniteResources.h:94). The packing is extremely aggressive — positions are stored as integer deltas from a cluster-local origin, with variable precision determined per-cluster to minimize bandwidth.
每个 Cluster 存储为紧密打包的 GPU 结构体 FPackedCluster(NaniteResources.h:94)。打包极为紧凑——位置存储为从 Cluster 本地原点的整数偏移量,精度按 Cluster 可变以最小化带宽。
// FPackedCluster — GPU layout (NaniteResources.h:94) struct FPackedCluster { uint32 NumVerts_PositionOffset; // NumVerts:14, PositionOffset:18 uint32 NumTris_IndexOffset; // NumTris:8, IndexOffset:24 uint32 ColorMin; uint32 ColorBits_GroupIndex; // R:4, G:4, B:4, A:4 + debug group index FIntVector PosStart; // cluster-local origin (integer world position) uint32 BitsPerIndex_PosPrecision_PosBits_NormalPrecision_TangentPrecision; // Culling data: FSphere3f LODBounds; // sphere for LOD error test FVector3f BoxBoundsCenter; uint32 LODErrorAndEdgeLength; FVector3f BoxBoundsExtent; uint32 Flags_NumClusterBoneInfluences; // Material data: uint32 AttributeOffset_BitsPerAttribute; uint32 DecodeInfoOffset_HasTangents_Skinning_NumUVs_ColorMode; uint32 UVBitOffsets; // UV sets encoded as bit offsets // ... };
Two-Pass Occlusion Culling两阶段遮挡剔除
Nanite's culling runs in two passes, enabled by r.Nanite.Culling.TwoPass=1 (default). This is a hierarchical GPU-driven approach that achieves near-perfect occlusion culling without any CPU involvement:
Nanite 的剔除分两个通道运行,由 r.Nanite.Culling.TwoPass=1(默认)启用。这是一种分层 GPU 驱动的方法,无需 CPU 参与即可实现近乎完美的遮挡剔除:
The culling is performed by a Compute shader dispatched with persistent threads. Each thread processes one BVH node, testing it against frustum planes and the HZB. If a node passes, its children are pushed into a GPU-side work queue for further processing. This fully GPU-resident traversal avoids the CPU-GPU readback that was necessary with traditional occlusion queries.
剔除由持久线程分发的 Compute 着色器执行。每个线程处理一个 BVH 节点,针对视锥面和 HZB 进行测试。如果节点通过,其子节点被推送到 GPU 侧工作队列以进一步处理。这种完全 GPU 驻留的遍历避免了传统遮挡查询所需的 CPU-GPU 回读。
Software vs Hardware Rasterizer软件 vs 硬件光栅器
Nanite uses a dual rasterizer strategy. Hardware rasterization (the fixed-function GPU pipeline) is efficient for large triangles but has significant overhead for small (sub-pixel or sub-4x4 quad) triangles due to quad overshading. Nanite's software rasterizer is a Compute shader that avoids this overhead entirely:
Nanite 使用双路光栅器策略。硬件光栅化(固定功能 GPU 管线)对大三角形高效,但由于四边形过度着色,对小(亚像素或亚 4x4 四边形)三角形有显著开销。Nanite 的软件光栅器是一个完全避免这种开销的 Compute 着色器:
| Hardware Raster硬件光栅 | Software Raster (Compute)软件光栅(Compute) | |
|---|---|---|
| Best for适用于 | Triangles > ~8px (fills hardware pipeline well)三角形 > ~8px(充分利用硬件管线) | Triangles < ~8px (avoids quad overshading waste)三角形 < ~8px(避免四边形过着色浪费) |
| Output输出 | Depth + Visibility Buffer via traditional RT通过传统 RT 输出深度 + 可见性缓冲 | Atomic writes to Visibility Buffer (64-bit: depth | cluster_id | tri_id)原子写入可见性缓冲(64 位:深度 | cluster_id | tri_id) |
| Materials材质 | Supports all material features支持所有材质特性 | Only position/depth; material shading done separately via shading bins仅位置/深度;材质着色通过着色 Bin 单独完成 |
Visibility Buffer & Shading Bins — Deferred Material Evaluation可见性缓冲与着色 Bin — 延迟材质评估
After rasterization, Nanite has a Visibility Buffer: a 64-bit per-pixel texture encoding (depth, cluster_id, triangle_id). Material shading is completely decoupled from rasterization. Instead of running a pixel shader per triangle as it rasterizes, Nanite groups visible pixels by material (into shading bins) and dispatches one Compute shader per material type, processing only the pixels that use that material. This is fundamentally more efficient than traditional mesh shading because it avoids material shader launch overhead for occluded geometry.
光栅化后,Nanite 拥有一个可见性缓冲:每像素 64 位纹理,编码 (depth, cluster_id, triangle_id)。材质着色与光栅化完全解耦。Nanite 不是在光栅化时为每个三角形运行像素着色器,而是按材质将可见像素分组(进入着色 Bin),并为每种材质类型分发一个 Compute 着色器,只处理使用该材质的像素。这从根本上比传统网格着色更高效,因为它避免了为被遮挡几何体启动材质着色器的开销。
BuildShadingCommands() — CPU-Side Bin PreparationBuildShadingCommands() — CPU 侧着色 Bin 准备
// NaniteShading.cpp:445 — called early in the frame to prepare shading commands void BuildShadingCommands(FRDGBuilder& GraphBuilder, FScene& Scene, ENaniteMeshPass::Type MeshPass, FNaniteShadingCommands& ShadingCommands) { // Launch a setup task (runs in parallel with other CPU work) ShadingCommands.SetupTask = GraphBuilder.AddSetupTask([&ShadingCommands, &Pipelines, ...] { // Iterate all shading pipelines (one per unique material × render pass) for (const auto& Iter : Pipelines) { // Each shading bin gets an entry in MetaBufferData // GPU fills .XYZ (dispatch args) during shade binning pass // CPU sets .W = MaterialBitFlags ShadingCommands.MetaBufferData[Entry.BinIndex].W = Entry.ShadingPipeline->MaterialBitFlags; } // Build shader bundle for bindless dispatch (if platform supports) if (UseShadingShaderBundle(ShaderPlatform) && ShadingCommands.NumCommands > 0) { ShadingCommands.ShaderBundle = RHICreateShaderBundle(CreateInfo); } }); // Sort commands: by BoundTargetMask → by shader → by ShadingBin // This improves GPU cache coherency during material dispatch }
Nanite Limitations — What It Cannot RenderNanite 的限制 — 它不能渲染什么
r.Nanite.AllowWPO=1 使用硬件路径重新光栅化 WPO 网格,有性能代价)。半透明材质不支持(可见性缓冲仅用于不透明)。双面植被和遮罩材质可用,但需要硬件光栅器回退。可变形/蒙皮网格需要特殊的 Nanite 皮肤缓存集成。
Art Guidelines for Nanite面向 Nanite 的美术规范
- No polygon budget: Import source meshes at full resolution (ZBrush, CAD). Nanite handles LOD automatically. However, keep mesh count reasonable — each instance still has a draw call even if Nanite removes triangles.
- 无多边形预算:以完整分辨率导入源网格(ZBrush、CAD)。Nanite 自动处理 LOD。但请保持合理的网格数量——即使 Nanite 移除了三角形,每个实例仍有绘制调用。
- Texture density over triangles: With Nanite, the bottleneck shifts from polygons to texture sample count. Optimize texel density ratios, not polygon counts.
- 纹理密度优于三角形:有了 Nanite,瓶颈从多边形转移到纹理采样数量。优化纹素密度比,而不是多边形数量。
- Avoid WPO on large geometry: WPO forces hardware rasterization fallback. For vegetation, use Nanite + World Position Offset only on close-up foliage, and disable Nanite for wind-affected large trees.
- 避免在大型几何体上使用 WPO:WPO 强制回退到硬件光栅化。对于植被,仅在近景植被上使用 Nanite + WPO,并为受风影响的大树禁用 Nanite。
- Fallback meshes: Always provide a non-Nanite fallback LOD for platforms that don't support Nanite (mobile, older consoles).
- 回退网格:始终为不支持 Nanite 的平台(移动端、旧主机)提供非 Nanite 回退 LOD。
Source Navigation源码导航
- Nanite/NaniteCullRaster.cpp:3164FRenderer — main Nanite renderer class, DrawGeometry, two-pass cullingFRenderer — Nanite 主渲染器类,DrawGeometry,两阶段剔除
- Nanite/NaniteCullRaster.cpp:37
CULLING_PASS_*defines — pass type enum valuesCULLING_PASS_*定义 — 通道类型枚举值 - Nanite/NaniteShading.cpp:445BuildShadingCommands() — prepares per-material Compute dispatchBuildShadingCommands() — 准备每材质的 Compute 分发
- Engine/Public/Rendering/NaniteResources.h:50FPackedHierarchyNode — BVH node for GPU traversalFPackedHierarchyNode — GPU 遍历的 BVH 节点
- Engine/Public/Rendering/NaniteResources.h:94FPackedCluster — tightly packed GPU cluster layoutFPackedCluster — 紧密打包的 GPU Cluster 布局
- Shaders/Private/Nanite/NaniteCulling.usfGPU culling compute shader — persistent threads, HZB testGPU 剔除计算着色器 — 持久线程、HZB 测试