Unity Labs : AutoLOD自动化性能提升的实验
作为数字3D内容的创造者,我们总希望自己的作品能呈现出它们最好的一面。在某些情况下,我们对目标硬件具有完全的控制,因此我们可能会试图将渲染能力推向极限。在其它情况下,我们可能会尝试创建一个在各种硬件上都能运行的体验效果。
动机
无论哪种情况,我们都可以通过选择一些性能优化方式,改善CPU和GPU的运行时间。例如:遮挡剔除(Occlusion Cullings)、纹理图集(Texture atlasing)、静态和动态批处理、GPU实例化(GPU instancing)、着色器回退(Shader fallbacks)、多线程、光照贴图、优化垃圾回收、脚本等。在3D图形方面,对网格使用多个不同的LOD(Levels of detail)是一种已经过验证且可靠的技术。
LOD0是传统的原始网格,每一个增加的LOD是对上一层LOD的多边形数量进行抽取或减少后所得
如果LOD技术已经被验证是可靠的,那么为什么还要讨论它呢?因为可用的LOD还不够完美。
以下任何一个原因,都可能使无法充分利用LOD的优势。
需要艺术家来处理LOD。例如:单独处理,通过UI来对它们进行批量处理等。
需要建立一个自定义管线。
涉及一些软件工程方面的工作。
涉及对工作流程的不同阶段中使用的各种产品进行评估,但是各种生产方式之间可能各不相同。
最终可能需要权衡使用LOD的利弊。
综合上述原因,我们在Unity Labs中尝试了一个实验性项目AutoLOD,用来挑战针对LOD使用的障碍。
你可以访问Github来获取试验中所使用的代码:
https://github.com/Unity-Technologies/AutoLOD
从上面的动态图中,我们可以看见左边是传统LOD的渲染,平均30 fps,与右边SceneLOD的渲染,平均42fps的对比。放大到最大时,传统LOD需要大约9 ms / 7 ms (CPU/GPU),而SceneLOD只需要大约1 ms / 0.5 ms (CPU/GPU)。
注意:颜色的不一致是由于当前SceneLOD所使用的着色器的关系,这个着色器是可以自定义创作的。
在每个回放窗口的下面是分析器的一个录制窗口。在左边,你可以看到渲染开销随着显示的场景内容的增多而激增。在右边,当Hierarchical LOD开始起作用,渲染成本就维持在了一个相对恒定的水平。你可以关注右侧的最小CPU使用率,这是绘制调用次数减少的结果。
愿景
我们希望AutoLOD项目是探索出Unity中一个自动化可扩展和可插入的LOD系统的轮廓,以支持渲染密集型项目并作为继续LOD研究的一个实验平台。
首先让我们来定义这些术语:
自动化是指使用合理的默认值来自动生成LOD,通常能使项目以更好的性能运行。
可扩展是指默认的LOD生成器和运行时可被扩展和/或重写(例如:离散与连续)。
可插入是指第三方可以创建他们自己的LOD生成器,用以替代默认的LOD生成器。
目标
实验项目最初的目标是:
基于导入模型生成具有合理默认值。
项目范围或单个模型的LOD导入设置。
GPU加速的默认LOD生成器*1。
异步可插入的LOD生成框架。
SceneLOD支持下的层级式LOD*。
能与使用其它LOD技术的LOD生成器匹配的可扩展运行时。例如连续LOD,视角依赖LOD等。
允许进行LOD生成器比较的“工作台”场景。
由于时间关系,我们并没有完全达成所有目标。但是这个实验是成功的,因为部分愿景已获得实现。现在让我们深入了解其中的一些细节。
LOD生成
要实现LOD生成自动化,就需要找出适用于绝大多数项目的合理默认值。所有专业的LOD包都带有大量的滑动条和开关按钮,理想情况下,这些仅在自动生成的LOD太糟糕时才会使用到。在Edit ->Preferences中,可以进行项目范围的设置。
如果生成的LOD不正确,还可以针对每个模型文件进行重写。
我们可以为某个文件单独更改Simplifier/Batcher下拉框的设置,或者直接关闭导入时自动生成,并手动提供LOD。甚至可以在LOD链中添加额外的LOD。这个LOD链会被包含到项目中导入版本的模型文件之中,因此无需使用额外的预制件来设置LODGroup。
SceneLOD
SceneLOD的灵感来自于Erikson, C., D. Manocha和 W. Baxter发表的论文2001 I3D Paper。我们决定创建一个与Unity中现有的LODGroup组件一起工作的实现,这样就不需要一个定制版本的Unity了。LODGroup组件的体积包围盒层级(目前是一个八叉树),控制着使用哪个LOD被用于渲染场景。
2001 I3D Paper:
https://www.cs.unc.edu/Research/ProjectSummaries/hlods.pdf
为什么是HLOD?
HLOD(Hierarchical Level of Detail)作为一种性能优化,将场景中的独立网格进行分区,以便能使用一个组合的网格进行代替。
传统的LOD会根据屏幕大小、距离、视角或其它指标来选择一个合适的网格表示物。无论选择哪个LOD,每个渲染的网格都需要增加一次额外的绘制调用。传统LOD的局限性是绘制调用次数在总量上没优化,因为每个对象的LOD链是单独评估的。静态批处理只解决了部分问题,因为它只根据共享材质进行合批。绘制调用通常会加重CPU的负担,所以减少绘制调用次数能提高CPU的性能。
HLOD通过将特定空间内的所有对象组合成一个单一网格,并利用纹理图集来实现单一材质,可以减少绘制调用的数量。对于希望在整个场景中显示全景视图的游戏,HLOD能极大的提高性能。在其他情况下,当被作为一组组合网格时,HLOD可能会胜过单个LOD的质量。HLOD的缺点是,在BVH每个节点上的每个HLOD网格都需要额外的内存开销。
性能分析
我们对Asset Store商店中POLYGON-CITY资源包中提供的一个演示场景略作修改。在5秒内,使用Timeline将一个摄像机从一个近距离视图放大到整个城市的视图。测试是在笔记本上进行的。
让我们看看传统LOD和HLOD的分析器视图。
随着摄像机镜头的放大和更多场景的显示,上图传统的LOD显示了CPU和渲染开销的增长。
对于HLOD,在回放刚开始时传统的LOD是激活的,这就解释了刚开始时的渲染开销。最终一旦HLOD被充分利用后,性能就会接近恒定的CPU和GPU消耗。BVH评估即决定哪些HLOD需要渲染也需要一些CPU消耗。
下面实验是在整个场景呈现在游戏视图中(摄像机固定)和统计窗口打开的情况下进行的。
静态批处理需要将场景中所有对象标记为静态,然后点击播放
实例化需要在所有使用的材质中启用GPU实例化
正如我们看到的,针对一个大型城市场景,HLOD比传统的LOD在性能上提高了1425%,并且将绘制调用数量从1487次减少到仅仅只有6次!
然而,当构建的场景是传统LOD通常无法处理的时候,HLOD才会真正发挥巨大作用。
这个示例场景中包含了原始场景的4个副本,总共有620万个三角形和11655个批处理。渲染速率为83.3ms/43.9ms(CPU/GPU),低于交互响应率。
现在将它与相同场景的HLOD版本进行比较。
尽管三角形的个数增加到700万,但仍然以1ms/0.4ms(CPU/GPU)的速率进行渲染,并且只有6个批处理。请记住:尽管副本来源于原始场景,即使城市的每个部分都是独一无二的,你也可以期待有同样的性能表现。
存储成本
在一次构建中,SceneLOD将增加静态网格和纹理的大小,但如果BVH深度也同时减少,这可以相应减少。
HLOD:纹理 36.3 mb 5.3% 网格 599.9 mb 87.6%
LOD:纹理 20.3 mb 20.9% 网格 30.0 mb 30.8%
磁盘上未压缩的HLOD网格的大小是1.1GB。
时间成本
一次性
HLOD实现在BVH生成和每个从LOD生成分离出的HLOD生成都有一次性的成本。在场景中添加、移动或删除对象时,这些一次性计算成本将会随时产生。但是SceneLOD会跟踪这些变化,并在后台自动的更新BVH和HLOD。
动态
每当一个摄像机渲染时,遍历BVH并决定在渲染前启用哪些LODGroup组件是非常必要的。
结语
我们发现自动化LOD能够解决一部分在数字化内容创作中存在的痛点。合理的默认值能满足项目的大部分需求,而如果发现有任何网格存在问题,可以根据具体情况对它们进行单独修改。SceneLOD提供了一个HLOD的示例实现,可以用于当前Unity版本的大场景中。如果你愿意用存储成本来换取性能,对一个拥有很多静态元素的特大场景来说,你有可能可以将其渲染性能提高一个数量级。
我们希望这个实验项目能使开发者对自己项目中遇到的性能问题有一些深入的见解,并且让你有能力去构建更为复杂的场景。未来的工作方向还有很多,比如支持动态对象、更好地压缩磁盘上的HLOD、默认的LOD生成器、用于HLOD渲染的不同的着色器配置以及性能优化!
更多Unity最新技术内容尽在Unity中文官方论坛(Unitychina.cn )!
技术直播
5月30日晚,新一期的Unity官方技术直播课程,Unity技术经理成亮将带你了解Unity 2018中的渐进光照贴图Progressive Lightmappe。
直播地址:https://connect.unity.com/events/unitychina-progressivelightmapper
推荐阅读
点击“阅读原文”访问Unity官方中文社区