12个优化Unity /Gear VR 应用的小技巧
比起那些普通应用,VR 应用所需的计算性能要求大了太多,让优化成为了一项重要工作。如果你的目标是类似 Gear VR 这种移动设备,那么要求会更高。
以下为一些基本的标杆:
单眼 50 个绘制调用(Draw Call)。Unity5 更加精准地称之为口令设置程序调用(setpass calls)。
少于50000-100000个顶点数以及50000-100000个多边形数量。
确实是很严苛的要求,而这里有一些小技巧来助你一臂之力。
静态批处理(Static batching)
在一个场景里,可能有一堆静态几何图形,比如墙、椅子、灯光和各种静止网格(Mesh)。你可以在编辑器里把它们标成静态。确保将他们标记成静态光源映射来得到烘焙好的光照贴图纹理。被标记成静态的物品会被组成一个网格,而非每绘制一次物品就产生调用。
但静态批处理有一个关键的要求:所有物品必须都有同样的材质。如果你有木头材质的静态墙壁和一个钢铁材质的静态椅子,所有墙壁会被组合成一个网格,消耗一个绘制调用,而椅子则也会有一个网格,占用另一个绘制调用。
纹理集合(Texture atlasing)
如上文所提及,每个不同材质都会至少导致一个新的绘制调用。你可能会以为一个木头门和铁椅子必须使用不同的材质,毕竟它们使用了不同的纹理。但是,如果它们 能使用同样的着色器,你可以使用纹理集合来创建一个材质,同时兼容这两个物体。一个纹理集合其实就是一个大纹理贴图,里面包含了各种各样的小纹理。你可以让某个材质加载几个纹理而非让一堆材质加载一堆纹理。每个物体都能通过不同的纹理映射来加载这个纹理集合上不同坐标位置的一小片纹理。
你可以手动去做这个事,而作为参考,在Unity 官方资源商店Asset Store上的内容:Juan Sebastian’s Pro Draw Call Optimizer (https://www.assetstore.unity3d.com/en/#!/content/16538)非常有用。它能自动生成纹理集合,而不会干扰你的 assets。
动态批处理(Dynamic batching)
移动的非静态物品也能被动态批处理为单一绘制调用。这对于 CPU 来说开销较大,每帧都经过计算,但就优化最终结果来说还是不错的。不过要注意的是这只对低于 900 个顶点并有着同样材质的物体才有用。使用纹理集合来为你的动态物体创建一个单一材质,然后你就能得到简单的动态批处理了。
多细节层次(Level Of Details)
多细节层次组也是一个提升表现的简单方式。使用拥有多个细节层次的 assets 并让离镜头远的物体采取低细节的几何图形进行渲染。Unity 能自动在摄像头和物体之间的距离发生改变时过渡到不同的细节层次。
填充率,重复绘制,过滤剔除(Fillrate, overdraw, and culling)
降低重复绘制,不要让远处物体的像素被绘制了后,近处覆盖在这个像素的物体上面时又绘制了一次。对于平时在 PC 上倒不是太所谓,但对于移动 VR,这也是性能开销啊。大量的重复绘制和高分辨率影响了你的填充率,而纹理填充率也是 GPU 的限制之一。
目前,解决方式包括遮挡剔除(occlusion culling)和层次视锥剔除(frustum culling)。层次视锥剔除能将摄像机视野锥体外的东西都剔除掉,毕竟渲染那些你根本没有看着的东西毫无意义。而遮挡剔除则是不渲染那些被挡住的物体,比如门后的房间,沙发后的桌子什么的。默认状态下遮挡剔除能搞定你的整个当前场景,但如果关卡设计得当,整个关卡都能被剔除掉。
多细节层次组也能剔除掉离摄像头太远的物体,以进一步降低填充率。
关卡设计(Level design)
如果你的游戏是让玩家从一个房间(地区)到另一个房间(地区),那基本的方式就是让整个游戏只在一个关卡中。但这样会导致过高的内存开销 – 每一个房间内的物体和材质都会被加载到内存里,即便在当前场景中是不可见的。所以,将房间放到不同的关卡里,通过代码设计来分开加载。
异步加载(Asynchronous loading)
当玩家快要接近到加载下一关的门时,加载下一关。不要同时使用 Application.LoadLevel(),这会导致游戏暂停卡顿。即便头部跟踪只停了一小会,也会让玩家非常不适。
因此,采取 Application.LoadLevelAsync()。关于范例你可以查看 Oculus Mobile SDK 下的 BlockSplosion 样品,在 StartupSample.cs 里。
光照烘焙(Baked lighting)
关闭实时阴影!得到实时阴影的物体将无法被批处理,导致大量的额外绘制调用的开销。
在 PC 上,你能仅通过单一的即时方向光来得到不错的动态阴影,但在移动端,请用烘焙好的光照,不要用实时阴影。
阴影(Shadows)
尤其对于移动体验来说,请对立体物品的阴影使用以前那些小技巧。半真实的阴影可以通过在物体下面增加一个平面模糊灰色图形来达到。
比如,千万不要用这种高端电脑才能搞定的 GTA V 实时动态阴影。
但你可以用类似这种:下图来自2002年的 GTA 罪恶城市,只是在人物下面增加了一个模糊的小圆球而已。
光照探针(Light probes)
当使用烘焙好的光线后,那些动态物品看起来违和感很高,而静态物品非常好。解决的办法之一就是通过光照探针来模拟动态光源。光照探针是烘焙好的立方体贴图,在场景中的各个点上存储直接、间接乃至发散的光线。当动态物体移动时,它会提取最近的几个光照探针的样品来模拟在那个部位的光线。这是模拟真实光线而无需依赖昂贵的实时光线的方式。
Unity 官方提供了一个专业的文档来告诉你应该如何放置光照探针(点击阅读原文可查看)。
避免透明和多材质物品
使用透明着色器的物品,比如玻璃,窗户等都非常昂贵。还有一些专门让物品看起来更真实的方式,比如在墙壁前增加一层锈色或灰尘透明的材质等等。这类多材质 alpha 融合都非常昂贵,每个材质都会增加一个绘制调用。但是,多纹理就无所谓了,所以你应该只用一个材质,并用一个可以通过 alpha 融合来融合多个纹理的着色器。
蒙皮网格渲染器(Skinned mesh renderers)
蒙皮网格渲染器在渲染那些有动画部件的角色时比较常用,能通过物理或定制的动画来达到真实的网格变形。不幸的是,蒙皮网格渲染器不会被整合进批处理中去。每个场景里的角色都会导致单眼增加几个绘制调用。