在《堡垒之夜:大逃杀》第四章中引入Nanite技术

2023年1月26日
你好,我是Epic Games图像部门的工程研究员Graham Wihlidal。为了能够在《堡垒之夜:大逃杀》第四章中将Nanite投入使用,我们今年为它提供了一系列激动人心的功能和改进,我现在就在这里展示它们。虚幻引擎5.1中已经提供了这些功能的测试版本,你可以试用看看。

在虚幻引擎5.0发布时,Nanite就已处于生产就绪状态。开发者可以用它取得许多惊人的成就,但最初的版本无法完整支持非Nanite网格体中的各种丰富功能。我们将时间集中在了对Nanite核心功能的打磨上。

展望未来,我们希望将支持扩展到Nanite尚未涉及的众多领域。用户一直在强烈要求我们提供对全局位置偏移、像素深度偏移、自定义UV、双面材质和遮罩材质等功能的支持。今年年初,我们的团队接受了这项挑战,尝试支持其中一些功能,这要求我们解决大量难题。

GPU最开始采用的是所谓的固定函数管线,几何体的变换方法以及深度和颜色的编写方法都被内置到了硬件中,只能通过有限的预定义函数集进行配置。后来,这些硬件具备了“可编程”的特性,允许人们编写着色器代码,这为图形学带来了新的发展潜力,也使固定函数管线难以实现或无法实现的功能成为了可能。

从开始到现在,Nanite始终支持“可编程”的材质图表着色器,它们控制着输出的颜色。但实际上,这个光栅化程序(决定如何在屏幕上定位顶点以及三角形将覆盖哪些像素)本身采用的是“固定函数”模式:尽管它是以着色器代码的形式实现的,但内容创建者无法控制这些逻辑。

为了在Nanite中支持上述功能,我们需要为这个光栅化程序本身添加可编程特性。

最初的原型

为了让这个光栅化程序支持材质图表逻辑,我们着手为其所需的架构设计了原型。我们将这个架构称为“Nanite可编程光栅化程序”。

下方图片展示了遮罩材质在Nanite垃圾车网格体上的动画效果,这就是我们最初的原型。
 
该原型成功地证明了可编程的光栅化程序具备实现的可能性,但若要将它用于生产并提高它的效率,我们仍有许多工作要做。

为了推进这项工作,我们制定了一些明确的目标:
  • 维持现有“固定函数”快速路径的性能概况,引入可编程的光栅化程序不应拖慢现有内容的处理速度。
  • 出于可维护性方面的考虑,必须确保固定函数路径和可编程的光栅化程序路径基本上共享了相同的代码路径。
  • 考虑到可编程的光栅化程序将被大量内容频繁使用,所以简易求值器提供的性能应该只比固定函数光栅化程序慢一点。
  • 只执行一次实例剔除和群集剔除工作。
  • 将对GPU场景和Nanite的额外内存影响降到最低。
  • 以产品功能的形式将它交付到UE 5.1中。

最初的原型是硬编码的,在场景中只支持单个可编程材质,所以下一步是实现一个正确的光栅化程序“分仓”通道,这将让我们能够支持由数百种材质组成的真实游戏场景,进而对实际内容做出测试。
“中世纪游戏”场景中的镜头,其中的内容几乎都被转换成了Nanite!
Nanite三角形的可视化
场景中独特的光栅化程序“仓”(材质)
即使中世纪游戏测试场景表现正常,要向游戏交付Nanite可编程光栅化程序框架,我们在优化和功能方面还有许多工作要做。

很明显,在最早期的阶段,就有人希望在《堡垒之夜:大逃杀》第四章中利用Nanite技术,但那时我们缺乏必要的功能,无法支持大规模采用。即使是看似简单的网格体(例如不透明的建筑物碎片),也需要全局位置偏移才能在遭到破坏时产生标志性的“反弹”效果。《堡垒之夜》成了第一款使用可编程光栅化程序的游戏。

《堡垒之夜》用例

动画道具

第一个要支持的用例是使用全局位置偏移产生次级动画的不透明静态网格体道具。虽然我们不需要使用Nanite技术就能高效渲染这些道具,但虚拟阴影贴图的性能在Nanite网格体上得到了大幅增强,所以尽可能使用Nanite渲染场景是很重要的。


 

 

建筑物

各种各样的建筑物是《堡垒之夜》中的一大特色,而Nanite已被证明可以轻松处理大型城市的建造。因此我们选择使用Nanite创造所有建筑物网格体也就不足为奇了,这样做可以提高视觉保真度,避免细节层次级别被突然切换,还能获得更出色的性能。
构造方式
《堡垒之夜》中的建筑物网格体实在太多,我们无法在所有平台上从头开始重新构建它们,因此我们创建了一套离线流程,它可以采用美术师创建的置换纹理和规则生成高质量的置换Nanite网格体,并且其中的三角形数量远多于传统版本的网格体。

注:这不是“运行时”置换,它是《堡垒之夜》专用的离线工作流程,用于创建置换网格体。

除了对主要视图渲染做出视觉改进外,置换的Nanite网格体也大幅提升了虚拟阴影贴图的质量,因为像砖块这样的几何细节现在被置换到了真正的3D空间中(之前是在纹理上以2D绘制区域的形式表现的)。这增加了表面的深度和细节,使实现恰当的自身阴影和轮廓成为了可能。
 
《堡垒之夜》中的建筑物都是不透明的静态网格体,可以完全使用虚幻引擎5.0中提供的Nanite功能集进行渲染,但在建筑物遭到破坏时(例如玩家使用镐斧击打它)暂时出现的“颤动”效果除外。

颤动效果是一条简单的动画轨道,通过材质中的全局位置偏移实现,而且即使没有发生可见的颤动(使用的权重为零),也始终会执行计算。

由于《堡垒之夜》中建筑物网格体的数量太过庞大,我们通过优化实现了这样一种范式:你可以启用一种特殊的模式(r.OptimizedWPO),不管材质是否具有驱动全局位置偏移的逻辑,只有在给定的Primitive组件启用了“计算全局位置偏移”(这是默认设置)时,Nanite才会计算这种逻辑。
当Nanite执行上述的“光栅化程序分仓”通道时,任何通常会采用可编程路径但禁用了“计算全局位置偏移”的Primitive都将转向采用标准的固定函数光栅化程序路径。

这种优化在很多地方都被证明有用(包括在远处禁用全局位置偏移时),但在实现建筑物的颤动时,它尤为重要。我们默认在所有建筑物网格体上禁用了“计算全局位置偏移”,并调整了《堡垒之夜》中的游戏代码,根据建筑物目前是否正在遭到破坏(并发生颤动),以编程方式设置这个值。
在执行这项新优化的同时,我们添加了一个名为“计算WPO”的Nanite调试视图(r.Nanite.Visualize EvaluateWPO),如果一个网格体目前正在计算全局位置偏移,则显示绿色,否则就显示红色。
通过这项优化,几乎所有建筑物网格体都会采取固定函数路径,只有少数网格体会在必要时偶尔采用可编程的颤动路径。
示例镜头
r.OptimizedWPO关闭后与开启后的对比

树木

我们的大部分原型设计时间和开发时间都花在了树木上。在第四章中,我们希望有郁郁葱葱的森林区域,所以我们需要一种可预测其性能的高效解决方案。树木依赖于我们以前从未在游戏中使用过的全新Nanite功能,因此我们执行了大量原型设计和优化工作,验证我们最终将要使用的方法。
构造方式
我们最初的实验版本使用遮罩材质和卡片创造树木。
对于《堡垒之夜》中的内容,我们发现,避免使用遮罩材质,依靠增加网格体的三角形数量并确保材质不透明通常会取得更快的速度,在树木和草地中尤其明显。这主要是因为Nanite中的遮罩材质在基础通道着色期间会产生很高的成本,因为我们必须逐个像素地重新计算三角重心,而且Alpha贴图中的负空间会增加额外的过度绘制成本。

保留区域

转为采用Nanite实现《堡垒之夜》中的树木之后,我们注意到在简化过程中,远处的树木会失去树冠:树叶变稀疏,或者在某些情况下,突然变得光秃秃。某些时候,每片树叶都被简化成了一个三角形,无法进一步精简,因此需要移除树叶,降低三角形计数。像这样移除树叶会使树冠在视觉上变得稀疏。

为了纠正这个问题,我们在Nanite构建程序中添加了新的逻辑(一个名为“保留区域”的选项,可以在网格体的“Nanite设置”中启用),通过扩张开放的边界边缘,将丢失的区域重新分配给剩余的三角形。就树叶来说,这就相当于剩余的树叶变大了。如果在近处执行这种操作会得到很奇怪的外观,但在远处,当激活区域保留时,看起来反而是将密度保持在了应有的水平。

只应该在表现出了该问题的植被网格体上启用这项功能,不应该在其他地方启用。
 
没有保留区域

 
拥有保留区域
风的动画
如果所有树木都以高保真度渲染,但却没有随风摆动,看起来就会很奇怪。以往,《堡垒之夜》会通过复杂的逻辑驱动全局位置偏移,完成风的动画。由于Nanite树木的顶点(约30-50万个)远多于非Nanite树木的顶点(约1-2万个),而且全局位置逻辑现在将在Nanite光栅化程序的内部进行计算,我们探索了其他方法,希望以减少每个顶点计算成本的方式制作树木动画。
在实验的最后,我们将对风的复杂模拟烘焙到了纹理中,这使我们能够在驱动全局位置偏移时进行采样,无需针对每个顶点执行大量复杂的数学运算。
从树木几何体中,我们可以提取每根树枝的枢轴点、它在层级中的级别,以及它的上一级分支。我们可以根据这些信息制作骨架,并输入到Houdini Vellum的模拟中。
我们能够以像素值的形式将模拟中每根树枝的枢轴点和方向编码到图像中,然后在材质着色器中进行采样,并使用编码到网格体资产中的自定义UV值生成索引(该资产用于为树枝挑选合适的像素行)。

因此,Nanite光栅化程序只需要查找一个位置和四元数就能计算偏移量,不需要读取多个依赖的纹理。这种方法目前只支持刚性动画,因为每根树枝都有相同的UV值。
距离剔除
在靠近摄像机的地方,模拟风吹树木是非常重要的,但在远处则很难分辨这种效果。作为一项性能优化,我们允许Nanite在美术师定义的距离内禁用全局位置偏移计算。这项优化建立在“计算全局位置偏移”模式的基础上。

草地

我们新添加了为地形中的草地生成Nanite网格体实例的功能,并在这个系统中添加了对距离剔除的支持。这里采用的资产处理方法与处理树木时类似。我们使用不透明的材质以及实际的几何体制作草叶,但全局位置偏移动画仅仅是通过简单的数学运算驱动的。
 

 

地形(Nanite)

大型非Nanite网格体渲染阴影的速度比同等大小的Nanite网格体慢得多,这是由虚拟阴影贴图的工作原理决定的。这个问题对地形的造成影响尤其大,因为它是一个巨大的非Nanite网格体,将耗费大量GPU时间。我们在UE 5.1中针对地形实现并发布了一个实验性的Nanite渲染功能,可在构建时将地形高度场转换为Nanite网格体。这种转换并不会提高视觉质量,但在渲染基础通道或虚拟阴影贴图时,它能够提供Nanite剔除和栅格化所具备的全部性能特征。
Lumen有专门的路径用来追踪地形高度场,而Nanite目前不支持渲染运行时虚拟纹理,所以在这些情况下不会使用Nanite表示地形,只会使用高度场。
 

未来的工作

剩下最重要的事情或许就是实现精确到帧的实例和与全局位置偏移动画相匹配的群集包围体计算。为了确保Nanite的遮蔽剔除在栅格化之前尽可能多地移除群集,这是是至关重要的。
 

目前,我们正在使用参考姿势边界(忽略全局位置偏移),这样做显得过于保守(栅格化的群集比需要的更多),当全局位置偏移动画将群集彻底移到参考姿势边界之外(网格体的部件消失)时,这样也会导致视觉伪影。我们在Primitive组件上实现了对边界缩放的粗略支持(与非Nanite网格体处理这个问题的方式类似),你可以任意扩大边界,但这会使剔除变得更加保守,不必要地耗费性能。

我们还计划继续优化Nanite的材质系统,为遮罩材质和具有像素深度偏移的材质降低成本。

我们很高兴地在虚幻引擎5.1中发布了这些新的Nanite功能,并期待看到开发者用它创建出各种令人惊叹的内容!
如需进一步了解这篇文章中所提到的功能,请访问Nanite文档

    立即获取虚幻引擎!

    获取全球最开放、最先进的创作工具。
    虚幻引擎包罗万象,并提供完整的源代码访问权限,开箱即用,诚意十足。