虚拟几何体
虚拟几何体 (Virtual Geometry) - 以 UE5 Nanite 为例
以 UE5 Nanite 为主要范例,探究"虚拟化几何"这一现代渲染范式。
引言:什么是虚拟几何体
虚拟几何体借鉴了虚拟纹理(Virtual Texturing) 的思想:
虚拟纹理之于贴图,就是虚拟几何之于网格 —— 只加载、只处理当前视角下真正可见、且在该分辨率下真正需要的那部分数据。
传统渲染里,一个模型的全部三角形都要提交给 GPU,再靠离散 LOD 切换降负载。虚拟几何则把网格切成大量小簇(cluster),运行时按屏幕需要动态选择每一簇的精度、流式加载、GPU 驱动剔除,让画面里始终维持"每个三角形约 1 像素"的理想密度 —— 无论模型原始面数是几百万还是几十亿。
一、传统几何管线的瓶颈
虚拟几何要解决的,是传统管线在超高几何密度下的一系列崩溃:
| 瓶颈 | 说明 |
|---|---|
| Draw Call / 提交开销 | 海量物体逐个提交,CPU 端成为瓶颈 |
| 离散 LOD 跳变(Popping) | 美术手工做几档 LOD,切换时肉眼可见跳变;且切换粒度是整个模型 |
| LOD 制作成本 | 每个模型手动/自动生成多档 LOD,占用美术与存储 |
| 内存 / 显存 | 高模全量常驻,装不下真实电影级资产 |
| 微多边形下光栅崩溃 | 三角形小到 ~1 像素时,2×2 Quad 利用率暴跌(详见渲染管线文档) |
| 带宽 | 顶点数据、G-Buffer 写入随几何量线性膨胀 |
核心矛盾:屏幕分辨率是固定的(比如 1080p ≈ 200 万像素),再精细的模型,最终也只能贡献这么多有效像素。传统管线却让 GPU 处理了远超屏幕所需的三角形。虚拟几何的目标就是让处理量与屏幕像素挂钩,而非与模型原始面数挂钩。
二、核心思想:把几何"虚拟化"
虚拟几何的四个支柱:
- 分簇(Clustering):把网格切成固定大小的小簇(Nanite 中每簇约 128 个三角形)。簇是剔除、LOD、流式加载的最小单位。
- 层级 LOD(Hierarchical LOD):把簇逐级合并、简化,构建成一个 LOD DAG(有向无环图),每一层是上一层的简化版本。
- 运行时按屏幕误差选 LOD:根据每一簇投影到屏幕的几何误差是否小于阈值(约 1 像素),在 DAG 上选择一个"切面",得到既够精细又不浪费的簇集合。
- 流式加载(Streaming):只把当前视角需要的簇/LOD 层驻留内存,其余留在磁盘按需加载 —— 这是"虚拟"二字的由来。
三、关键技术拆解
3.1 簇与层级 LOD DAG
网格被切成簇后,通过"分组 → 简化 → 再切分"逐级构建 LOD 层级:
关键设计:简化时会锁定簇组的边界边(boundary edge),保证相邻簇在不同 LOD 下拼接时不产生裂缝(crack)。这让不同区域可以选择不同 LOD 而无缝衔接。
3.2 屏幕空间误差驱动的连续 LOD
运行时不再"整个模型切一档 LOD",而是逐簇判断:把该簇的几何简化误差投影到屏幕,若 < ~1 像素就用这一层,否则下潜到更精细的层。于是:
- 近处的簇用高精度,远处/侧面的簇用低精度 —— 同一模型上 LOD 是空间连续变化的。
- 屏幕上的三角形密度始终维持在"约 1 像素/三角形",既不浪费也不跳变。
下图对比了传统离散 LOD 与虚拟几何连续 LOD 的三角形数量随距离的变化:

3.3 GPU 驱动的两遍遮挡剔除
虚拟几何的剔除几乎全在 GPU(Compute) 上完成,粒度细到每簇,并采用两遍遮挡剔除配合层级 Z 缓冲(HZB, Hierarchical Z-Buffer):
用上一帧深度做保守剔除,再用本帧深度补测,兼顾效率与正确性。
3.4 软件光栅 + 硬件光栅混合
针对"微三角形硬件光栅低效"的问题,虚拟几何按三角形大小分流:
| 三角形大小 | 光栅方式 | 原因 |
|---|---|---|
| 小(~像素级) | 软件光栅(Compute + 64-bit 原子写) | 绕开 2×2 Quad 浪费,比硬件快数倍 |
| 大 | 硬件光栅 | 大三角形硬件仍最优 |
软件光栅用 64-bit 原子操作把「深度 + 可见性 ID」一次性写入可见性缓冲,避开传统深度测试的固定管线开销。
3.5 可见性缓冲 + 延迟材质
光栅结果写入可见性缓冲(每像素一个 深度 + 实例ID + 三角形ID,64-bit),之后在屏幕空间的材质 Pass 里回查几何、重建属性、按材质分类着色,写入 G-Buffer 供光照使用。
3.6 流式加载与压缩
- 几何数据以高度压缩的格式存储,运行时按需把可见簇解压驻留到一个固定大小的内存池。
- 视角移动时动态换入换出,常驻内存与场景总几何量解耦 —— 这才使得"电影级资产直接进引擎"成为可能。
四、Nanite 完整管线总览
五、优势
- 面数近乎无关的开销:处理量与屏幕像素挂钩,而非模型原始面数。上亿三角形的场景也能跑。
- 告别手工 LOD:自动连续 LOD,美术不再逐模型做多档 LOD,也没有 LOD 跳变。
- 微多边形高效:软件光栅 + 可见性缓冲避开 Quad 浪费和 G-Buffer 带宽。
- 精细遮挡剔除:每簇级别的 GPU 剔除,只处理真正可见的部分。
- 内存与场景规模解耦:流式加载让常驻内存可控。
- 电影级资产直接可用:ZBrush 高模、扫描资产无需大改即可入引擎。
六、限制与代价
虚拟几何并非万能,有明确的适用边界(Nanite 也在持续演进,以下为其经典限制,部分已在新版本逐步放开):
| 限制 | 说明 |
|---|---|
| 半透明 | 与延迟/可见性缓冲一样,天生不处理透明物体,需另走前向 |
| 形变/骨骼动画 | 早期仅支持刚性网格;蒙皮、大幅顶点动画支持有限(新版逐步增强) |
| 曲面细分/位移 | 早期不支持硬件细分与置换(后续版本引入可编程光栅/位移) |
| 低模不划算 | 对本就低面数的模型,虚拟几何的固定开销反而不值 |
| 固定内存/常驻开销 | 需要一块常驻内存池与数据结构开销 |
| 细碎/聚集几何 | 大量极细长三角形、聚集型几何历史上表现不佳 |
七、与相关技术的关系
虚拟纹理(Virtual Texturing)
同一思想的两个方向:虚拟纹理按需流式加载贴图的 mip 分块,虚拟几何按需流式加载网格的 LOD 簇。两者常配合使用,共同把"资产总量"与"运行时驻留量"解耦。
Mesh Shader(网格着色器)
Mesh Shader(Turing+/DX12 Ultimate)是一项硬件特性,让 GPU 直接以 meshlet(小簇)为单位做几何处理与剔除,是"GPU 驱动的簇渲染"的硬件基础。但 Nanite 出于跨平台和微三角形性能考虑,主要走Compute + 软件光栅,而非完全依赖 Mesh Shader。游戏也可用 Mesh Shader 自行实现类虚拟几何的簇渲染。
八、传统几何 vs 虚拟几何 对比
| 维度 | 传统几何管线 | 虚拟几何(Nanite) |
|---|---|---|
| LOD | 手工离散档,会跳变 | 自动连续,无跳变 |
| 开销随面数 | 线性增长 | 近乎与屏幕像素挂钩 |
| 剔除粒度 | 物体/子网格级 | 簇级(GPU 驱动) |
| 微多边形 | Quad 崩溃、带宽爆炸 | 软件光栅 + 可见性缓冲 |
| 内存 | 全量常驻 | 流式,规模解耦 |
| 半透明 | 支持 | 需另走前向 |
| 形变几何 | 自由 | 受限(演进中) |
| 制作成本 | 高(手工 LOD) | 低(直接用高模) |
九、总结
虚拟几何通过分簇 + 层级 LOD + GPU 驱动剔除 + 软件光栅 + 流式加载,将几何处理的开销从"模型面数"转移到"屏幕像素",让电影级超高面数资产可以直接在实时渲染引擎中运行。UE5 Nanite 是该范式的代表性实现,它在自动连续 LOD、微多边形高效光栅、可见性缓冲等方面的设计,为实时图形学开辟了新的方向。当然,虚拟几何在半透明、形变几何等场景仍有局限,实用中需要根据资产类型和渲染需求做取舍。
附:核心概念速查
| 概念 | 说明 |
|---|---|
| Cluster / Meshlet | 一小簇三角形(Nanite ~128 个),剔除/LOD/流式的最小单位 |
| LOD DAG | 簇的层级简化结构,运行时在其上选"切面"决定各簇精度 |
| 屏幕空间误差 | 簇的几何误差投影到屏幕的像素量,< ~1px 即可用该 LOD |
| HZB | 层级 Z 缓冲,用于两遍遮挡剔除 |
| 软件光栅 | Compute 着色器 + 64-bit 原子,针对微三角形 |
| 可见性缓冲 | 每像素存 深度+ID,材质延迟到屏幕空间重建 |
| 几何流式 | 只驻留可见簇,常驻内存与场景总量解耦 |
