【UE5 C++进阶 01】Nanite基础

介绍虚幻引擎5中的虚拟几何体系统——一种可以获得像素级细节和海量对象的系统。
UE5-Nanite官方文档

Nanite是什么?

Nanite是虚幻引擎5的虚拟化几何体系统,它采用全新的内部网格体格式和渲染技术来渲染像素级别的细节以及海量对象。它可以智能地仅处理你能够感受到的细节。另外,Nanite采用高度压缩的数据格式,并且支持具有自动细节级别的细粒度流送。

流程上,Nanite允许用户先使用最高分辨率的资产来构造数字内容,然后再针对download size(云端)或者disk size(本地)来优化package的大小,而不是一开始就限制资产制作精度。

Nanite的设计与实现思路

设计的目的

  • 类似于virtual texture一样支持virtual geometry(支持LOD,且可以快速stream in/out,支持超出显存/内存的数据规模)
  • 不需要考虑budget问题,包括:
  1. 多边形数量
  2. draw call数量
  3. 内存用量
  • 允许直接使用电影级别质量的艺术资产
  • 直接可用
  1. 不需要大量的手工优化渲染工作
  • 质量上没有损失

实现的困难

比virtual texturing困难多了

  • 不只是需要很好的内存管理
  • 几何细节直接会影响渲染开销
  • 几何并不是很简单的就可以过滤的(相比于纹理可以很容易构造mip-map)

使用哪种几何表达?

研究了几种潜在的几何表达方式:

  • 体素(voxel)
  • 细分曲面(subdivision surface)
  • 置换贴图(displacement map)
  • 几何图像(geometry image)
  • 基于点的图形(point based)

三角形cluster剔除

cluster sampling (整群抽样)
整群抽样是指整群地抽选样本单位,对被抽选的各群进行全面调查的一种抽样组织方式。例如,检验某种零件的质量时,不是逐个抽取零件,而是随机抽若干盒 (每盒装有若干个零件),对所抽各盒零件进行全面检验。如果全及总体划分为单位数目相等的R个群,用不重复抽样方法,从R群中抽取r群进行调查。

先将总体分为i个群,然后从i个群中随机抽取若干个群,对这些群内所有个体或单元均进行调查。

  • 把三角形组织为cluster
  1. 对每个cluster构造包围数据
  • 基于包围数据剔除cluster
  1. frustum剔除
  2. 遮挡剔除

cluster层级

  • 对每个cluster决定LOD级别
  • 构造LOD的层级
  1. 最简单的就是cluster的二叉树
  2. 父节点是子节点的简化版本

LOD Runtime

  • 在LOD树中根据需要的精度,寻找cut
  • 基于感知差异的视角依赖方法

流处理

  • 整颗树不需要一次性载入内存
  • 可以把树中的任意cut标记为叶子节点,然后扔掉剩下的数据
  • 在渲染的时候按照需要请求数据
  1. 和virtual texturing类似

像素级别的细节

  • 大于一个像素的三角形能否达到像素级别精度
  1. 依赖于三角形有多光滑
  2. 一般不行
  • 我们需要绘制像素大小的三角形

细微的三角形

  • 对于典型的光栅化来说非常难处理
  1. 通常是基于像素并行,而不是基于三角形
  • 软件光栅化在这种情况能不能打败硬件光栅化?
    【UE5 C++进阶 01】Nanite基础_第1张图片
  • 软件光栅化
  1. 比硬件光栅化快3倍
  • 硬件光栅化
  1. 对于大的三角形还是用硬件光栅化
  2. 基于每个cluster选择是使用软件光栅化还是硬件光栅化
    【UE5 C++进阶 01】Nanite基础_第2张图片
    (红色的区域是硬件光栅化,蓝色的区域是软件光栅化)

Virtual shadow maps(虚拟阴影贴图)

  • Nanite允许了新的一些技术
  • 每盏灯都可以用16k x16k的shadow map
  • 基于1 texel = 1 pixel选择mip level
  • 只渲染shadow map上需要被看到的像素
  • Nanite裁剪并且处理LOD来保证足够的细节水平
  • 缓存了(shadow map)所以只有移动的部分需要重新绘制

代理网格体

虚幻引擎的很多模块都需要访问传统的顶点缓冲数据(由传统方式渲染的网格体提供)。为静态网格体启用Nanite之后,将生成一个较为简单的代理网格体,以便在Nanite数据无法使用的情况下作为备选。比如在需要复杂的碰撞时,或者当平台不支持Nanite时,就会使用 代理网格体(Proxy Mesh)。如果网格体烘焙了静态光照,代理网格体也可以在Lightmass中使用。
Nanite特性

思想相似之处 JPEG算法

JPEG利用人眼感知的局限性,剔除掉不容易被人眼感知的部分(色彩信息),
从而节约存储空间。

Nanite网格体和传统静态网格体的不同之处

Nanite网格体是一种启用了Nanite的特殊静态网格体。Nanite网格体本质上仍是三角形网格体,但对其数据进行了大量细节和压缩处理。此外,Nanite使用了一种全新系统,能以极高效的方式来渲染这种数据格式。

要让静态网格体利用Nanite,只需一个标记来启用它即可。编辑Nanite网格体的内容和传统网格体没太大不同,区别就在于相比使用传统方法渲染的几何体,Nanite能够渲染的三角形和实例要多出数个数量级。将摄像机移到足够近的位置后,Nanite就会绘制出导入的原始源三角形。

Nanite网格体支持多重UV和顶点颜色。材质可以被指定给网格体的不同分段,并且这些材质可以使用不同的着色模型和动态效果(在着色器中完成)。材质指定可以动态切换,就像其他静态网格体一样。Nanite也无需任何烘焙材质的过程。

虚拟纹理并不要求与Nanite一起使用,但非常推荐这么做。虚拟纹理是正交虚幻引擎功能,它与纹理数据的关系类似于Nanite与网格体数据的关系。

使用Nanite前,你应该首先熟悉静态网格体的工作流程,但是目前还有很多内容尚不支持。有关更多细节,请参阅本页面的支持功能小节。

Nanite如何工作?

Nanite可最大限度地与现有的引擎工作流无缝集成,可使用前所未有的方法来存储和渲染网格体数据。

  • 导入期间 — 分析网格体,并将其拆分成由三角形组构成的分层群集。

  • 渲染期间 — 根据摄像机视图以不同LOD,随时切换群集,并且可以在不破坏同一对象中相邻群集的情况下完美连接。数据会根据需求流送,因此只有可见细节才会保存在内存中。Nanite在自己的渲染通道中运行,该通道完全绕过了传统的绘制调用。你可以使用可视化模式来检视Nanite管线。

由于Nanite需要从磁盘快速流送网格体数据,因此建议使用固态硬盘(SSD)来存储运行时数据。

为什么使用Nanite?

Nanite 遵循的设计理念是——三角形的绘制数量超过像素数量就是一种浪费。
— 摘自 UE5 Nanite 官方文档

Nanite的优势

  • 几何体形状的复杂度提高了数个数量级,三角形和对象的实时渲染数量达到了前所未有的高度

  • 帧预算不再会因为多边形数量、绘制调用和内存使用情况而受限

  • 现在可以直接导入电影级品质的美术资源,例如ZBrush雕刻模型和摄影测量扫描数据

  • 通过高模实现细节,而非将细节烘培到法线贴图纹理

节省了制作法线贴图的步骤,而且细节在VertexShader上得以更正确的显示

  • 自动处理细节级别(LOD),不再需要手动设置单个网格体的LOD

传统方式是要对每个几何体降低面数,再设置不同的LOD,放入引擎处理

  • 品质损失极少或没有损失,特别是在LOD发生过渡时

  • 数据大小

Nanite能够实现大量微观细节,这可能会让人认为几何体数据会大量增加,导致玩家的游戏包大小和下载数据增加。然而,现实并没有那么可怕。事实上,Nanite的网格体格式要比标准的静态网格体格式小得多,因为Nanite有专门的网格体编码格式。

例如,对虚幻引擎5示例《古代山谷》来说,平均而言,Nanite网格体的每个输入三角形会消耗14.4字节。这意味着平均一个拥有100万三角形的Nanite网格体,在磁盘上需要约13.8兆字节(MB)。

例如,注意Nanite网格体的大小(约150万个三角形)要小于4k法线贴图纹理的大小(19.64MB)。

将一个传统的低模网格体(带有法线贴图)和一个高模Nanite网格体相比较,你会看到以下效果:
【UE5 C++进阶 01】Nanite基础_第3张图片

Nanite支持什么

  • 首先专注在刚体几何
  1. 大于90%的几何体都是刚体
  2. 物体的移动是支持的
  • 还不支持:
  1. 半透明或者带遮罩的材质
  2. 非刚体形变,骨骼动画等
  3. 细分和置换
  • 对于Aggregate类型的几何体(聚合几何体)不友好
  1. 很多很小的物体组成了一个带孔的体积
  2. 草地、树叶、毛发(因为这种情况,LOD很难建立的很有效)

聚合几何体

  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 过度绘制(Overdraw) 可视化可以显示这些堆叠的表面是如何渲染的。区域越亮,表明在该区域发生的过度绘制越多。
过度绘制可视化能够最有效地发现过度绘制的问题。尽管一定程度的过度绘制是可以接受的,但过量会导致Nanite的剔除和光栅化开销变大,并且Nanite的缩放功能也会更加容易受到场景复杂度的影响。

Nanite不支持的设备条件

  • Nanite现在要求在Windows系统上使用DX12。不再支持DX11。
  1. 只有DX12支持和兼容的GPU上可以运行Nanite。

应该将Nanite用于哪些类型的网格体?

一般来说,能启用时应该尽量启用Nanite。启用了Nanite的静态网格体通常可以更快地渲染,占用的内存和磁盘空间会更少。

具体来说,如果网格体满足以下条件,则尤其适合使用Nanite:

  • 包含很多三角形,或屏幕上三角形非常小

  • 场景中有很多实例

  • 是其他Nanite几何体的主要遮挡物

  • 使用虚拟阴影贴图投射阴影

不过有一个例外,那就是天空球之类的对象:它的三角形在屏幕上显得很大,不会遮挡任何东西,并且场景中只有一个。通常,这种例外很少见,并且让它们启用Nanite导致的性能损失很小,所以只要Nanite支持,就不必过度担心是否应该不开启Nanite。

目前Nanite不支持某些情况。有关更多细节,请参阅本页面的支持功能小节。

如何使用Nanite?

在网格体上启用Nanite支持

以下方法可在几何体上启用Nanite(前提是几何体支持):

  • 导入时

  • 使用单独的网格体编辑器

  • 在内容浏览器中的批量选择

将几何体转换为Nanite时,在每个网格体上都需要花费一些处理时间。在大型项目上,如果有很多Nanite资产,使用共享的派生数据缓存(DDC)将非常有帮助。如需更多信息,请参阅共享DDC文档。

导入静态网格体

在导入要启用Nanite的网格体时,勾选 构建Nanite(Build Nanite) 复选框。
【UE5 C++进阶 01】Nanite基础_第4张图片

假如不使用Lightmass预计算光照,建议禁用 生成光照贴图UV(Generate Lightmap UVs) 属性。

启用此属性后,高精度网格体会显著增加静态网格体数据的导入和构建时间。此属性还将增加一个额外的UV通道,这对于非常密集的网格体而言将会产生大量的数据。因此,如果你的项目不使用烘焙光照,则没有必要产生这两项开销。

预计算的全局光照

虚幻引擎中的光照烘焙系统使用Lightmass全局光照系统在CPU或GPU上计算光照数据。使用此方法预计算光照旨在获得高质量结果,可以将信息存储在将要应用至几何体的纹理中,不受实时限制因素的影响。使用此方法,光照无法动态修改,对于那些无需改变光照效果的项目来说十分理想,对于动态光照受限的移动平台项目也是非常好的选择。

  • 基于CPU的Lightmass 使用CPU和名为Unreal Swarm的独立进程来计算和生成光照数据。此方法可使用单个机器或将光照分配到构建场。

  • 基于GPU的Lightmass 使用当前计算机上支持DirectX 12和光线追踪的NVIDIA GPU来计算和生成光照数据。

在资产上启用Nanite

假如你的项目已经有了大量内容,并且你希望启用Nanite,那么有两种办法:一是使用内容浏览器批量启用资产,二是在每个资产的编辑器中单独启用。

在网格体上批量启用Nanite

对于你要启用Nanite的批量静态网格体资产,使用 内容浏览器(Content Browser) 选择它们,然后,右键点击 并从上下文菜单选择 Nanite > 启用(Enable)
【UE5 C++进阶 01】Nanite基础_第5张图片

在单独的网格体上启用Nanite

打开支持Nanite的网格体的编辑器,例如静态网格体和几何体集合(Chaos物理驱动的破裂网格体),并通过 细节(Details) 面板启用Nanite。

在静态网格体编辑器(Static Mesh Editor)中,找到 Nanite设置(Nanite Settings) 并选中 启用Nanite支持(Enable Nanite Support) 复选框。

【UE5 C++进阶 01】Nanite基础_第6张图片
在几何体集合编辑器(Geometry Collections Editor)中,找到 Nanite 分段,并选中 启用Nanite(Enable Nanite) 复选框。

【UE5 C++进阶 01】Nanite基础_第7张图片

Nanite可视化

Nanite包含多种可视化模式来检查其在当前场景中的数据。

在关卡视口中的 查看模式(View Modes) 下拉菜单中,将鼠标悬停在 Nanite可视化(Nanite Visualization) 上,然后从选项中进行选择。

管理启用Nanite的内容

Nanite尚不支持虚幻引擎的某些功能,而某些功能可能永远不会受支持。Nanite工具(Nanite Tool) 使你能够在单个窗口中对支持Nanite的可用资产进行审核。你可以在内容浏览器(Content Browser)中审核资产,通过禁用或启用Nanite来查找错误或找到优化机会。

选择 工具(Tools)>Nanite工具(Nanite Tools) ,即可从主菜单打开Nanite工具(Nanite Tools)窗口。

【UE5 C++进阶 01】Nanite基础_第8张图片
打开Nanite工具(Nanite Tools)窗口时,有两个选项卡:

  • 错误(Errors) 选项卡将显示所有启用了Nanite且已知当前会导致Nanite相关错误的资产,例如应用了不支持Nanite的材质的资产。

  • 优化(Optimize) 选项卡将根据它们拥有的三角形数量以及是否排除使用不受支持的材质的资产,筛选项目中支持Nanite的资产。

要评估Nanite应该如何处理错误或优化,你需要对每个资产进行审核。

你可能感兴趣的:(UE,C++进阶,虚幻,游戏引擎)