作为开发者,我们总是会关注CPU和GPU的性能。对于更广阔更复杂的场景要想获得一个好的执行效率始终是布满挑战的,特别是当我们添加了更多的角色时。Unity技术支持团队在帮助、支持客户的时候经常会遇到类似的问题,所以我们决定专注去解决这个问题,提高渲染大规模角色的效率。我们把此技术称为Animation Instancing。
我们经常利用GPU Instancing 去实现室外场景,比如草地和树木。但是对于SkinnedMeshRenderer,例如角色,我们是使用不了instancing技术的。因为角色的蒙皮计算通常是在CPU计算的,然后一个一个的提交给GPU渲染。通常来说,我们没办法通过一次提交来画出多个角色。那么当场景中有大量角色时,就会有相应的大量Drawcall和动画计算存在。

Animation Instancing是一种大大减少CPU消耗并且对GPU Instancing的一种补充。你可以在GitHub上获取代码。

下载地址:https://github.com/Unity-Technologies/Animation-Instancing

特别说明:这是一个实验性质的解决方案,在此之前仅分享给个别企业级服务的客户。但是现在我们希望获得更多的反馈以便改进此方案,如果你有任何问题请在GitHub或者后台留言给我们。

目标
我们对这个实验项目最初的目标是:
Instancing SkinnedMeshRenderer 尽可能实现动画特性 LOD 支持移动平台 Culling


由于时间关系,不是所有的目标我们都已经实现的。例如动画特性已经支持的有:Root Motion,Attachment,Animation Events,还不支持的有:Transitions, Animation Layer。同时,对于移动平台的支持需要平台支持OpenGL ES 3.0及以上。

通过我们的测试,这个实验性的解决方案已经可以获得很有趣的结果。让我们来深入了解一下细节。

动画处理
在我们instancing角色之前,我们需要预处理角色的动画。我们把角色动画存储为了texture,以便于在GPU上蒙皮。这些texture我们称其为Animation Texture。
001527qn3ggnd9godrf3f9.png

这个处理器会收集角色的动画以及动画事件。从Mecanim系统转化到Animation instancing是十分方便的。如果你想挂接某些东西,例如:挂接武器到你的角色上,你需要在Attachment settings中设置要挂接的骨骼节点。

当我们处理完成Animation Texture,名字为Animation Instancing的脚本在运行时会自动读取对应的动画信息。请注意这里的动画信息并不是指animation clip文件。

Instancing
启用Animation Instancing是十分简单的。我们只需添加Animation Instancing脚本到我们已经处理过的Game Object。Bone Per Vertex选项控制着每顶点受影响的骨骼数目。更少的数目带来更高的性能但是更低的动画精确度。
001529vhy199yjh9vhjzu8.png

之后,我们需要修改角色所用的shader来支持instancing。基本上,你所需要做的就是把下面二句话加到你的shader中。它不会影响你的shader的着色,只是添加了一个顶点着色器来蒙皮。
[C#] 纯文本查看 复制代码 #include “AnimationInstancingBase.cginc” #pragma vertex vert

性能分析
我们使用了Mecanim Example Scenes中的一个场景来做测试,这个测试结果是在iPhone 6上的,在pc上你可以获得更好的测试结果。让我们来看一下测试结果对比。

CPU
原始的测试场景我们生成了300个角色后FPS大概在15帧左右。由此我们可以粗略的推断要达到30帧大概要生成150个角色。在Animation Instancing版本我们生成了900个角色后,可以稳定在30帧。
001546oxv6pvy4iovii9z4.png
001555yw0u69w1v6cy007x.png

通过下图我们可以发现,瓶颈主要在CPU端。而通过Animation Instancing,我们降低了在CPU上的动画计算与蒙皮。所以,我们相对于原始版本可以生成5、6倍的角色。
001556fljr7srlx55ntrer.png
001558l9re5dk8akavmvmn.png

下面让我们来看一下Drawcall。在这个测试场景中,环境的Drawcall大概在80左右。由于我们的测试角色有3个材质,所以我们需要3个Drawcall去渲染一个角色。

原始版本中,我们生成了250个角色,这产生了1100个左右的Drawcall(3*250个角色+阴影)。

在Animation Instancing版本中,我们生成了800个角色,但是Drawcall的增加量只有50左右。同时,在instancing这一行中,你可以看到有Batched drawcall4800个(3*8角色+3*8阴影)。这是因为我们把100个角色作为一个批次来提交的。
001559lkbe4ny4wkwj4kaw.png
001559gli70ijls792f0i9.png

GPU
这个技术会提升一点GPU的消耗,因为我们把蒙皮放在了GPU上。如果角色还有阴影的话,我们在shadow pass中会再次计算蒙皮。然后,总体来看它提升了整体的帧率,因为我们大大降低了CPU的消耗。通常在有大量角色存在的场景中,CPU的消耗是主要的瓶颈。

内存
我们需要的额外的内存占用是Animation Textures。这些纹理保存了蒙皮矩阵。我们使用了RGBAHalf格式的纹理(对于不支持此纹理的平台我们还在找寻解决方法)。让我们假设一个角色有N个骨骼且每骨骼保存4个像素作为一个矩阵。我们预处理一个动画为M帧。所以一个动画会花费N * 4 * M * 2 = 8NM bytes。如果一个角色有50个骨骼且我们生成30帧的动画的话,一个动画会消耗50 * 4 * 30 = 6000 像素。所以一张1024x1024的纹理可以存储174个动画。

结论
通过验证我们发现如果你的场景中有大量的SkinnedMeshRenderers,Animation Instancing可以有效的降低CPU的消耗。它十分适合那些有大量角色存在的游戏中,比如僵尸、战争模拟等类型游戏。

我们系统此实验性的方案可以抛砖引玉,使开发者可以实现更为宏大的游戏场景。当然,还有工作需要我们来完善,比如支持transitions, animation layer等。请在下载我们的代码,欢迎在GitHub或Unity中文官方论坛(UnityChina.cn) 反馈给我们!
本主题由 admin 于 2018-5-8 02:07 解除置顶锐亚教育

锐亚教育 锐亚科技 unity unity教程