UE5 Nanite特性
1. Nanite是虚幻引擎5全新推出的虚拟几何体系统(Virtualized Geometry System)。它采用全新的内部网格体格式和渲染技术来渲染像素级别的细节以及海量的物体对象。它足够智能,可以仅处理能够观察到的细节。Nanite采用高度压缩的数据格式,并且支持高细节流送和自动LOD。
2. Nanite优势
(1) 几何体形状的复杂度提高了数个数量级;三角形和对象的实时渲染数量达到了前所未有的高度。
(2) 帧预算(Frame Budget)不再会因为多边形数量、绘制调用和内存使用情况而受限。
(3) 可以直接导入电影级品质的美术资源,例如ZBrush雕刻模型和摄影测量扫描数据
(4) 通过高模实现细节,而非通过烘培法线贴图来实现
(5) 自动处理LOD,不再需要手动设置
(6) 品质损失极少或没有损失,特别是在LOD发生过渡时
3. Nanite网格体本质上仍属于三角形网格体,但对其数据进行了大量压缩以及LOD设置。此外,Nanite使用了一种全新系统,能以极高效的方式来渲染这种数据格式。
4. 静态网格体只需通过一个选项就能转换成Nanite网格体。编辑Nanite网格体和传统网格体没什么不同,区别就在于相比使用传统方法渲染的几何体,Nanite能够渲染的三角形和实例要多出数个数量级。将摄像机移到足够近的位置后,Nanite就会绘制出你导入的原始三角形。
5. Nanite网格体支持多重UV和顶点颜色。材质可以被指定给网格体的不同部分,并且这些材质可以使用不同的着色模型和动态效果(在着色器中完成)。材质的指定可以动态切换,就像其他静态网格体一样。Nanite也不需要任何烘焙材质的过程。
6. 虚拟纹理并不要求与Nanite一起使用,但推荐这么做。虚拟纹理是一个单独的虚幻引擎功能,它与纹理数据的关系类似于Nanite对网格体数据的关系。
7. 一般来说,能启用时应该尽量启用Nanite。启用了Nanite的静态网格体通常可以更快渲染,占用的内存和磁盘空间也更少。
具体而言,网格体如果满足以下条件,将很适合使用Nanite:
(1) 包含很多三角形,或屏幕上三角形非常小
(2) 场景中有很多实例
(3) 作为其他Nanite几何体的主要遮挡物
这些规则有一个例外的情形,那就天空球体之类的元素:它的三角形在屏幕上显得很大,不会遮挡任何东西,并且场景中只有一个。
受支持的Nanite特性
下文介绍了如何在虚幻引擎5抢先体验版中最充分利用Nanite的特性。
几何体
静态网格体和几何体集合可以选择启用Nanite。
启用Nanite的网格体可以搭配使用以下组件类型:
(1) 静态网格体
(2) 实例化的静态网格体
(3) 分级实例化静态网格体
(4) 几何体集合
1. Nanite目前仅限于刚性网格体。在常见项目场景中,90%以上的几何体都是刚性网格体,这也是Nanite开发的最初重点。Nanite支持刚性网格体的动态平移、旋转和非均匀缩放,但不支持一般的网格体变形,无论是动态还是静态的。这意味着,Nanite网格体的任何一个位置,都无法简单的通过用一个4x3矩阵乘以整个网格体来表示。
变形(Deformation)不支持且不限于以下几点:
(1) 骨骼动画(Skeletal animation)
(2) 变形目标(Morph Targets)
(3) 材质的世界位置偏移(World Position Offset in materials)
(4) 样条网格体(Spline meshes)
此外,Nanite目前还不支持:
(1) 自定义深度或模板
(2) 在实例上绘制顶点
* 特指使用编辑器网格体绘制(Mesh Paint)模式进行的逐实例绘制的颜色。
支持在原始网格体上导入的顶点颜色
2. 场景中的最大实例数量不会大于200万。这包括场景中的所有流送进来的实例,而不仅仅是为Nanite启用的那些。只有流送进来的实例会被计算在内。未来UE会对此进行优化。
3. 静态网格体启用Nanite时,不会保存静态网格体每个顶点切线的信息。相反,切线空间会在像素着色器中隐式继承。目前没有存储切线数据(为了减少数据大小),但在未来的引擎版本中,我们将提供存储功能。使用这种方法的切线空间会有所不同,可能会导致边缘不连续。但是,这点还未被证明是一个重要问题。虚幻引擎未来版本将计划支持顶点切线。
材质
以下材质以及以下设置 无法 被分配给Nanite网格体。它们要么被禁止使用,要么对Nanite网格体没有影响。
不受支持的材质会转而使用一个默认材质,并在 输出日志 中输出警告信息以及信息细节。
(1) 所有 混合模式,除了**不透明(Opaque)**。
这包括遮罩和半透明混合模式。
(2) 延迟贴花(Deferred Decal)
例如,将Nanite网格体用于网格体贴花
支持将贴花网格体投射到Nanite网格体上
(3) 线框
(4) 像素深度偏移
(5) 世界位置偏移
(6) 自定义的逐实例数据(ustom Per-Instance Data)
(7) 双面材质
使用以下内容的材质在应用到Nanite网格体上后将无法正确渲染:
(1) 顶点插值器节点
(2) 自定义UV
渲染
以下渲染功能目前不支持:
(1) 视图相关的对象筛选:
-使用以下方法进行的场景捕获:
隐藏的组件
隐藏的Actor
仅显示组件
仅显示Actor
-最小屏幕半径
-距离剔除
-FPrimitiveSceneProxy::IsShown() 筛选的任何内容
(2) 正向渲染
(3) 虚拟现实的立体渲染
(4) 分屏
(5) 多重采样抗锯齿(MSAA)
(6) 光照通道
(7) 针对完整Nanite细节的光线追踪
-支持光线追踪功能,但光线会与代理网格体(Nantie网格体的简化表现)交互,而不是具体的Nanite网格体
(8) 某些可视化视图模式还不支持显示Nanite网格体
-在查看细节十分丰富的几何体时,在静态网格体编辑器中使用某些可视化模式时应该谨慎。查看法线和UV可能会导致编辑器的性能出现问题。
支持的平台
Nanite目前支持PlayStation 5、Xbox Series X、以及符合以下显卡要求的PC(需要使用最新显卡驱动并支持DirectX 11/12):
NVIDIA: Maxwell显卡或更新版本
AMD: GCN显卡或更新版本
Nanite代理网格体及其精度设置
静态网格体中包含其他属性,可以控制Nanite的精度;基于原始Nanite网格体生成的简化版网格体称之为代理网格体。
这些设置可以在静态网格体编辑器的 细节(Details) 面板的 Nanite设置(Nanite Settings) 下找到。
顶点精度
1. Nanite会对网格体顶点位置进行量化,以便最大化内存密度以并最小化磁盘足迹(disk footprint)。量化的步长是二的幂,步长的大小可以通过 位置精度(Position Precision) 属性并根据各个网格体的要求来单独确定。默认设置是 自动(Auto),它允许Nanite根据网格体尺寸以及三角形密度挑选合适的精度。你也可以手动设置精度,以便提升精度效果或优化磁盘足迹。
2. 一般而言,手动微调精度总能带来一些增益。我们的想法是通过 自动(Auto) 设置来提供一个合理的折衷方法,在单独设置各个网格体时,它能充当一个良好的基准。
3. 量化(Quantization)是一种有损压缩形式,而在处理模块化网格体或使用共享边界的网格体时,使用有损压缩特别具有挑战性——特别是当这些边界需要完全对齐,避免产生孔洞或裂缝时。为了确保一致性,量化发生在以网格体原点为中心的非标准化对象坐标上。这能确保当网格体使用相同的精度设置,而网格体中心之间的平移是该精度的倍数时,量化不会导致裂缝的出现。
代理网格体
(1) 虚幻引擎的很多模块都需要访问传统的顶点缓冲数据(由传统方式渲染的网格体提供)。为静态网格体启用Nanite之后,将生成一个较为简单的代理网格体,以便在Nanite数据无法使用的情况下作为备选。比如在需要复杂的碰撞时,或者当平台不支持Nanite时,就会使用 代理网格体(Proxy Mesh)。如果网格体烘焙了静态光照,代理网格体也可以在Lightmass中使用。
(2) 代理三角形百分比(Proxy Triangle Percent) 表示从原来的Nanite网格体中抽出百分之多少的三角形来生成代理网格体(相对简陋)。接受从0到100的数值,百分比越大,保留的原始网格体细节越多。数值100表示没有删减。代理网格体将限制为最少2000个三角形,无论代理百分比有多小。
(3) 如果在三角形数量少于2000个的网格体上启用了Nanite,将自动把原始网格体用作代理网格体,因为代理网格体最低需要2000个三角形。
(4) 在静态网格体编辑器中,可以使用 显示(Show) > Nanite代理(Nanite Proxy) 或热键 Ctrl + N,在原始Nanite网格体和代理网格体之间切换。
(5) 在抢先体验版中,静态网格体编辑器中显示的 三角形 和 顶点 统计数据仅表示代理网格体是否启用了Nanite。要查看原始三角形数量,必须在网格体上暂时禁用Nanite。
(6) 增加"代理三角形百分比"这个数值能一定程度上在代理网格体上还原Nanite网格体原有的细节。在平台或功能不支持Nanite渲染时,你可能需要用到此功能
为Nanite网格体设置自定义代理网格体LOD
代理网格体在引擎的许多功能中都有用到,例如复杂的逐多边形碰撞、光线追踪、光照烘培等等。当在不支持Nanite的平台上运行时,它也可以用于直接绘制。在某些情况下,可能需要使用手动指定的代理网格体,或者一组传统LOD来实现这些目的,而不是使用自动生成。例如,在不支持的平台上运行时,或者涉及基于光线追踪的反射时,这能允许你直接控制看到的几何体。
Nanite网格体会固定将自动生成的代理设置在LOD0插槽中。如需使用自定义代理,或一组LOD,请遵循以下步骤:
(1) 将 代理三角形百分比(Proxy Triangle Percent) 设置为 0,这样代理会尽可能小;使用此方法时代理将被忽略。
(2) 使用传统LOD设置方法为网格体添加一个或多个LOD。
(3) 在静态网格体编辑器的 细节(Details) 面板的 LOD设置(LOD Settings) 中,将 最小LOD(Minimum LOD) 设置为 1。这将导致Nanite生成的代理被忽略。
(4) 复杂碰撞是一种特殊情况,需要单独设置。使用 用于碰撞的LOD(LOD for Collision) 属性来指定用于碰撞的LOD。它可以是任何一个LOD,包括LOD 0。
使用这种方法后,可能无法让Nanite项目自动兼容不支持Nanite的平台。Nanite可以高效处理大量实例,但如果Nanite被禁用,那么可能会出现大量绘制调用,以至于传统渲染管线可能无法应对。你可以使用 r.Nanite 0 来尝试全局禁用Nanite。
性能以及内容相关的问题
1. 大多数情况下,Nanite能很好地随屏幕分辨率的变化而变化。能这样做是基于两个关键技术:精细的LOD和遮蔽剔除。通常这意味着,无论场景中的源数据的几何复杂度如何,Nanite试图绘制到屏幕上的三角形的数量会保持不变,并且与像素的数量成正比。Nanite遵循的设计理念是——三角形的绘制数量超过像素数量就是一种浪费。
2. 某些情况下,有些类型的内容会破坏Nanite采用的缩放技术,但这不意味着不应将Nanite用于这些内容,或者说它在渲染时可能无法比传统管线快得多。对于这类内容,这意味着基于像素缩放而非基于场景复杂度缩放可能不再适用于它们。请使用虚幻引擎的性能分析功能来监控这类情况。
聚合几何体
1. 聚合几何体(Aggregate geometry)是指许多微小、不连贯的对象在远处被合并成单个体积(volume),例如毛发、树叶和草。这打破了原有的LOD和遮挡剔除方式。
2. 首先,Nanite网格体本身是一种HLOD结构,它依赖的方法是将小三角形简化为大三角形;在差异小到无法感知时,Nanite会选择较粗糙的三角形。这在连续的表面上效果很好,但在聚合体(aggregate)上效果不佳——从远处看时它们更像是部分不透明的云,而不是固体类型的表面。因此,Nanite可能认为它无法像处理常见的固体表面那样大幅度减少聚合体,从而导致相同像素区域内,有更多的三角形被绘制。
3. 会受到聚合几何体影响的第二种优化技巧是遮挡剔除(occlusion culling)。尽管遮挡剔除非常精细,但它的精细度(granularity)还无法达到像素级。充满孔洞的几何体(更糟的是多个相互重叠、充满孔洞的几何体)会导致大量的过度绘制,这是因为屏幕区域在遮挡背后的内容前,首先需要建立许多个深度层。想象一下,屏幕上有一片8x8像素的区域,在每个像素被填充之前需要绘制多个深度层。这种过度绘制就意味着,对于相同大小的像素区域,Nanite会尝试绘制更多的三角形,导致它的渲染速度变慢。
4. 植被是一个最明显的例子;不过即便如此,这也不意味着Nanite不应用于植被网格体。假如是充满树冠的森林,并且树冠由单独建模的树叶构成,那么Nanite的效果肯定不会很好;但将Nanite用于树干和树枝时,效果可能会好一些。建筑物表面的藤曼应该效果也还不错,因为理想情况下,它与底下固体表面之前只有一层。你可以试验一下,看看哪些情况适合你的项目,并使用性能分析功能来确认Nanite在处理这些网格体时的性能情况。
紧密堆叠的表面
由于各种实际存在的限制,传统网格体的遮挡剔除使得大规模的模型搭建(kitbashing)流程几乎不可能实现。Nanite的高精细遮挡剔除有助于减少开发流程中的麻烦。
正如在聚合几何体中解释的,导致过度绘制的一种情况,是可见表面与底部隐藏表面的距离过于接近近。如果某个几何体被妥当地隐藏在可见表面之下,Nanite检测并清除它的成本是相当低的,甚至可以认为没有开销。然而,如果有一些相互堆叠的几何体,并且都位于最顶部的表面上,Nanite可能无法确定哪个位于上面或下面,导致两个几何体都被绘制出来。这种情况通常最糟糕,因为Nanite不知道哪个表面在最上层,导致绘制出所有内容。像这样的精度误差会随着屏幕尺寸和距离的变化而变化,所以,尽管10厘米的距离足够分开各个层,并且在近处看起来很好,但在更远的位置,距离差可能会小于一个像素,从而导致过度绘制。
这种可视化选项是发现类似问题的最佳方法。尽管一定程度的过渡绘制是可以接受的,但过量的过渡绘制会导致Nanite的剔除和光栅化开销变大,并且Nanite的缩放功能也会更加容易受到场景复杂度的影响。
面片法线和硬边法线
1. 导入高精度网格体时,很容易导入相应的面片法线(faceted normals)。要注意避免使用面片法线,因为网格体中少量的顶点共享会导致渲染性能和数据大小的开销变得非常大。理想情况下,一个网格体的顶点数量要少于三角形数量。如果这个比例是2:1或更高,那就可能有问题了,尤其是当三角形数量较多时。如果比例为3:1,意味着网格体完全是面状的(facet),每个三角形都有单独的三个顶点,没有一个顶点是和其他三角形共享的。大多数情况下,这是因为法线不一样,因为它们没有被平滑化。
2. 考虑到这一点,更多的顶点意味着更多的数据。这也意味着更多的顶点转换工作,而高于2:1的比率会陷入一些缓慢的路径。在硬表面建模中有意使用不应该导致问题,没有理由不使用它们。然而,意外的100%切面的非常密集的网格要比预期的昂贵得多。另外,在其他DCC软件包中生成的密集有机型表面的导入法线,其硬法线阈值在低多边形网格上可能是合理的,但在Nanite中会增加不必要的费用。
以左图中的两个网格体为例。左边的网格体有面片法线,右边的网格体有平滑法线。Nanite的 三角形 可视化功能显示了两者在绘制时所用的三角形数量存在明显差异。
常见内容的性能
为了便于比较,我们以PS5的虚幻5技术演示Lumen in the Land of Nanite为例,记录了它的GPU耗时信息:
(1) 平均渲染分辨率为1400p,时序上采样到4K
(2) 剔除和光栅化Nanite网格体的时间约为~2.5毫秒(演示中几乎所有网格体都是Nanite网格体)
几乎所有几何体都是Nanite网格体
几乎没有CPU开销,因为场景100%由GPU驱动
(3) 计算所有Nanite网格体材质的时间为2毫秒
场景中每个材质都只产生一次绘制调用,因此CPU开销很小
把这些GPU耗时叠加后大约是4.5毫秒,相当于虚幻4中深度预通道(depth prepass)加上基础通道(base pass)的时间。这使得Nanite可用于那些以60 FPS为目标的游戏项目。
控制台变量和命令
使用以下统计数据和控制台变量对Nanite进行调试和配置。
在运行时,你可以使用控制台变量 r.Nanite 0 来全局启用和禁用Nanite渲染。禁用Nanite能让你模拟那些不支持Nanite的平台。
Nanite代理渲染模式
当Nanite被禁用或不被平台支持时,Nanite还提供了代理网格体渲染模式。你可以通过控制台变量 r.Nanite.ProxyRenderMode 来控制使用哪种模式。
0是默认模式,如果设置为1,就会回退为渲染代理网格体,或采用基于屏幕空间 的 LOD(参见上文代理网格体一节)。
1禁止渲染所有启用Nanite的网格体。
2与模式1类似,但允许在静态网格体编辑器中通过 显示 > Nanite代理 来渲染Nanite代理。
假如场景的实例数量非常大,远超过普通情况下的数量(即未启用Nanite时),则代理渲染模式1和2都很有用。它们使你能够在不支持Nanite的平台上用编辑器打开该场景。例如,在虚幻引擎5的示例项目"远古山谷"中,禁用Nanite会导致产生数以万计的常规绘制调用,导致那些不支持Nanite的平台甚至难以打开该地图。
Nanite统计数据命令
在控制台输入 Nanitestats 命令后,屏幕右侧会出现当前视图的Nanite剔除统计数据( Nanite culling statistic)。
命令参数可用于指定Nanite在屏幕上显示哪些统计信息。当没有提供参数时,将使用主视图。
Nanitestats List 会在调试输出中生成一个可用视图列表:
Primary
VSM_Directional
VSM_Perspective
ShadowAtlas0
ShadowAtlas1
ShadowAtlas2
需要选择某个视图时,输入 Nanitestats,后面是你要使用的参数。例如,Nanitestats VSM_Directional。
对于使用双通道遮挡剔除(two-pass occlusion culling)的视图,统计数据会被划分成 主(Main) 和 后(Post) 两种情况。
统计数据名称:
PRE-CULL 剔除阶段前的实例数量。
POST-CULL 可视性测试完成后的剩余实例数量。
NODEVISITS 剔除过程中访问的层级节点总数。
CANDIDATES 层级遍历过程中发现的、需要进行可见性测试的群集数量。
CLUSTERSSW 使用软件光栅化渲染的可见群集的数量。
CLUSTERSHW 使用硬件光栅化渲染的可见群集的数量。
控制Nanite流送池的大小
控制台变量 r.Nanite.Streaming.StreamingPoolSize 可用于指定使用多少内存来保存Nanite的流送数据。使用较大的内存池可以减少在场景中移动时产生的IO和解压缩工作量,但代价是内存占用较大。
就抢先体验版而言,如果内存池不够大,无法容纳一个视图所需的所有数据,就会发生"缓存抖动(Cache thrash)",导致即便是静态视图,流送也无法解决。
这个控制台变量无法在运行时改变,必须在配置(.ini)文件中指定。
设置单个通道的最大群集数
控制台变量 r.Nanite.MaxCandidateClusters 和 r.Nanite.MaxVisibleClusters 可以指定单个通道(single pass)中的最大候选群集和可见群集数量。它们的值可用于确定中间缓冲区的大小,它们的默认值(分别为8388608和2097152)已经被用于常见的游戏渲染场景中。
在抢先体验版中,没有动态调整这些缓冲区大小的机制,也无法在溢出时自动降低品质。如果这些数值过小,无法满足场景复杂度,就可能会导致渲染瑕疵,通常表现为几何体丢失或闪烁。当这类渲染瑕疵发生时,请使用 Nanitestats 来确定候选群集和可见群集的合适数值(查看CLUSTERSSW和CLUSTERSHW统计数据)。目前一个候选群集的内存开销是12字节,一个可见群集是16字节。
这个控制台变量无法在运行时更改,必须在配置(.ini)文件中指定。