Nanite植被是一套利用Nanite虚拟化几何体渲染技术的集成系统,旨在以极高的密度和细节大规模呈现植被效果。 Nanite植被结合了新旧技术,利用实例化、蒙皮网格体、体素化、动画及材质特性,仅需原始帧成本的一小部分,即可高质量地渲染出拥有广阔远景的开放世界。 综上所述,这使得构建拥有逼真动态植被的宏大世界成为可能。
要了解Nanite植被的目标及未来方向,首先必须明白,过去的有效做法在构建未来这种完全拟真的世界时已不再高效。
下面我们来看看,为何开发植被的“旧方式”无法很好地适配Nanite植被:
Alpha遮罩:Nanite对其支持不佳,因为这不仅会导致大量过度绘制,还需要执行开销可能很大的遮罩函数。 使用Alpha遮罩还意味着植被将继续以平面片的形式渲染,而无法展现其本应具备的几何复杂度。
全Nanite三角形表示:这会导致集群剔除效果不佳,且远距离下的简化质量较差。 此外,存储每种植被类型都需要占用大量的磁盘空间。
世界位置偏移(WPO):在材质中使用WPO来移动叶片和树枝,是过去模拟风力效果的常用手段。 这与Nanite的渲染机制并不契合,因为它增加了每顶点计算量,且由于无法预知材质如何移动顶点,系统被迫采用更保守的计算方式,从而导致集群边界处于次优状态。
既然你已了解哪些方法行不通,接下来让我们看看Nanite植被渲染管线是如何渲染完整建模的树木,细致到每一片树叶和针叶。 该技术需要能够支持大型开放世界,在无需细节级别(LOD)网格体的情况下,依然保持内存高效、性能卓越且高度动态。
为此,Nanite植被由以下三个系统构成:
Nanite程序集即构成植被上高细节实例的“部件”。 这能显著减少内存占用和磁盘空间。
Nanite体素是一种接近像素大小的聚合体素,可根据相机距离保留三角形细节、动画及材质属性。 三角形可无缝切换为体素,从而解决了渲染密集且动态的聚合三角形网格体这一难题。 由于这些体素在屏幕上的尺寸极小,肉眼几乎无法察觉。
Nanite蒙皮定义了植被在风力等系统作用下的动态行为,其中的风力效果是通过骨骼层级来模拟的。 由于不使用WPO来模拟风力动画,Nanite植被能够利用最佳的集群边界。
在下方的示例中,你可以观察到这些系统是如何协同工作的。 随着相机拉近细节,你可以看到树木的密度和细节程度极高,甚至连每一根松针都清晰可见。 随着相机拉远,一旦Nanite集群小到一定程度,便会无缝过渡为体素。 这使得树木能够在远距离保持其体积感,而无需退化为广告牌(Billboard)或LOD。
这棵树包含以下数据:
4100万个三角形
12个唯一程序集部件
2160个程序集部件实例
850根骨骼
启用Nanite植被
若要为项目启用Nanite植被,请在项目设置>渲染中勾选Nanite植被选项。 你需要重启编辑器以使更改生效。
Nanite程序集
Nanite程序集是Nanite植被的核心组件,专为高效渲染复杂且可重复的几何体(例如树木的枝干和叶片)而设计。 程序集同时支持静态网格体和骨骼网格体资产,允许对小型高精度部件进行“微实例化”。 它们最多可包含65,000个其他网格体的实例。 这些实例被称为程序集的“部件”。 这种方法能显著节省磁盘空间和流送空间,代价仅是集群剔除期间微小的性能损耗。
Nanite程序集的工作原理是获取部件网格体的集群,将其编码进最终网格体的层级结构中,且在构建时不会复制部件的几何体。 运行时,Nanite会在层级遍历和集群剔除过程中,按需计算部件实例的最终变换。
在非Nanite平台上,部件的三角形会被变换并合并为一个单一网格体。 随后,系统会对其进行简化以生成回退网格体,其机制与常规Nanite回退网格体一致。
程序集是Nanite植被不可或缺的一部分。 若没有程序集,我们将因磁盘空间或流送内存不足,而无法渲染如Unreal Fest《巫师4》技术演示中那样的大型森林场景。 例如,演示中那棵最大的树,其UASSET磁盘占用从3.5 GB骤降至约29 MB。 经测量,仅单一视图下的一棵树,其流送内存占用就从约36 MB降至2.7 MB左右。 正是这种程度的资源节省,使得在场景中渲染50万个包含数十种变体、且每棵树都拥有100万至1000万多边形的高精度树木实例成为可能。
关于在虚幻引擎中使用及创建程序集的更多详情,请参阅Nanite程序集。
Nanite体素
Nanite体素是接近像素大小的体素,它能保留所代表几何体的细节、动画及材质属性,并维持物体(如树叶)的体积感外观。 之前的Nanite技术为了防止叶片在远距离变形,会在简化过程中当三角形被剔除时,将其表面积补回给幸存三角形,这种方法效果尚可。 这主要是为了缓解在远距离渲染简化集群时,植被几何体变得稀疏的问题。
光栅化过程
Nanite体素能够在远距离简化过程中,保留聚合几何体(不相连的碎片)的整体轮廓。 在简化这些启用体素的网格体集群时,Nanite构建器会将其体素化为最多包含128个4x4x4体素砖的集群。 体素表示的简化误差是决定是否从三角形切换为体素的关键因素——如果切换为体素能比使用三角形产生更低的误差,系统便会执行切换。
运行时,体素集群会与三角形集群分开归类,并使用特殊的着色器排列进行光栅化。 集群被分类至深度桶中,并按从前向后的顺序进行光栅化,以利用提前Z测试的优势,因此在远距离渲染时,其表现通常优于光栅化三角形。
以下是在独立资产及《巫师4》技术演示中,体素化过程的可视化示例。
着色过程
单个体素覆盖的区域内可能包含许多法线各异的表面。 因此,系统存储的是法线分布,而非每个体素存储单一法线。 这在概念上类似于材质粗糙度控制用于着色的微表面分布。
写入GBuffer时,系统会从该分布中为每个像素随机选择一个法线。 在未来版本中,该分布将像粗糙度一样直接纳入着色计算。 这能有效减少噪点,该技术已应用于《巫师4》的演示中。
这需要额外的数据来存储分布的统计信息。 目前该数据存储在顶点颜色的Alpha通道中,这意味着它会覆盖原有的任何内容。 将来,这部分数据可能会移至独立于顶点颜色的单独数据区。
启用Nanite体素
若要启用Nanite体素化:
在静态网格体的细节(Details)面板中,找到Nanite设置类别。
找到形状保留(Shape Preservation)并通过下拉菜单选择体素化(Voxelize)。
设置为体素化(Voxelize)后,下拉菜单下方的部分属性将变为可编辑状态。 这些选项并不建议大多数用户(乃至任何用户)在项目中随意调整。 部分功能尚在开发中,可能还无法按预期工作,甚至完全不可用。 随着Nanite植被技术的成熟,这些设置在虚幻引擎的未来版本中可能会被更改或移除。
使用Nanite蒙皮制作Nanite植被动画
提到游戏中的植被风力动画,许多都是通过材质中的世界位置偏移(WPO)来实现的,这通常应用于树枝和树叶的材质。 由于给定顶点的WPO可能是任意偏移量,美术师需要在所有材质上针对最坏情况设置最大WPO位移(Max WPO Displacement),以扩大剔除边界,确保植被资产被正确剔除。 然而,这会导致边界设置过于保守,从而产生极高的过度绘制成本。
由于WPO会改变顶点着色器逻辑,每种材质都会被归入单独的调度中,这会带来相应的GPU开销。 当材质数量众多时,这些可编程光栅化容器可能会引发性能问题。
Nanite蒙皮解决了这一问题,因为它可以根据蒙皮矩阵计算出更精确的边界,并且所有内容均可使用固定函数光栅器。
在下方的截图中,十万根骨骼同时更新仅需约0.1毫秒的GPU时间,这使得Nanite植被具有极高的速度和可扩展性。 当树木的屏幕尺寸小于特定值时,动画将自动停止,而风力依然可以影响极远处的树木,从而带来更好的沉浸感。 这一点同样适用于使用骨骼网格体的草地和灌木。
动态风插件
该插件目前处于实验阶段。
动态风(Dynamic Wind)插件专为配合Nanite植被而设计。 它需要针对特定的骨骼网格体,使用动态风骨骼数据资产进行一些额外设置。 该资产中的数据将骨骼分类为不同的模拟组,这不仅有助于系统在模拟中逻辑化地识别骨骼链,还能让你按组调整风力影响。
你可以在插件浏览器的渲染类别下启用该插件。
该插件提供了一项校本化资产操作功能,用于从.JSON文件导入必要数据。 此外,它还包含一个通用场景描述(USD)模式,可应用于骨骼以提供相关数据,并附带一个用于从USD创建JSON文件的示例Python脚本。
动态风变换提供者数据
包含动态风数据资产中用户数据的骨骼网格体,可利用该数据来驱动实例化蒙皮网格体组件中的风力动画。
若要在蒙皮网格体上启用风力效果,请在组件的变换提供者下拉菜单中选择变换提供者数据。 系统将为该网格体创建一个新的动态风数据资产。
该资产用于将组件注册到风力系统中。 目前它除此之外别无他用。
目前存在的限制包括:
添加更多基于物理的模拟,例如近场效应。
仅支持全局风向。
与玩家或物体之间没有碰撞。
动态风骨骼数据必须通过JSON文件导入。
未来计划实现导入USD时自动导入该数据。
允许对动态风变换提供者数据进行调整。
Nanite蒙皮性能提示
尽管Nanite蒙皮在处理植被风力动态方面优于WPO,但仍需关注性能表现。
请注意以下几点:
虚拟阴影贴图(VSM)的性能要求你必须禁用远处的动画。
被禁用的实例化网格体会切换至未蒙皮光栅化容器,并像固定函数静态网格体那样(以绑定姿势)进行渲染。
使用组件上的动画最小屏幕尺寸,当物体在屏幕上的尺寸较小时禁用蒙皮。
这与设置世界位置偏移禁用距离类似,不同之处在于它始终处于激活状态。 如果保留为0,将使用全局默认值,即动画停止时的阈值为物体屏幕尺寸的1/10 (0.1)。 你可以通过控制台命令
r.Skinning.DefaultAnimationMinScreenSize来更改此设置。