Rendering performance enhancements
Enable/Disable GPU Skinning
开启GPU Skinning可以减轻CPU或GPU中Front End部分中某一个的负担,但是会加重另一个的负担。Skinning是mesh中的顶点根据动画中骨骼的当前位置进行计算,从而让角色摆出正确的姿势。
顶点的计算过程可以放在CPU也可以放在GPU的Front End 部分,取决于是否开启GPU Skinning。可以通过 Edit | Project Settings | Player Settings | Other Settings | GPU Skinning 设置。
开启后,计算会放在GPU的Frond End中,但是CPU并不是一点工作都不做了,还是需要传输数据到GPU并且要在Command Buffer中生成指令。如果不开启,则计算在CPU中完成,传输到GPU中的数据已经是计算好的顶点数据。所以根据CPU和GPU的状态可以灵活决定应该在哪算,如果CPU负担太重就开启,把工作丢给GPU。
Reduce geometric complexity
这条跟GPU中的Font End相关。很多时候用不到需要多套无用UV和法线数据的mesh。尽量减少顶点数量,有三种途径:
1. 让美术老铁们手动减少顶点数量重新生成mesh
2. 干脆从场景里删掉,当然这是最坏情况
3. 使用LOD等技术自动对mesh进行culling
Reduce Tessellation
通过Geometry Shaders中使用Tessellation技术会带来很大的体验提升,但是也给GPU中Frond End增加了负担。目前对于如和优化Tessellation也没啥好办法,还是尽量优化Frond End中其他地方给Tessllation省出更多空间吧。
Employ GPU Instancing
GPU Instancing比Dynamic Batching更高效,因为它并不需要mesh的合并,但是也要求更严格,material引用和mesh必须都一致才可以。GPU Instancing可以有效的减少Draw Call,但是它在Unity中默认是没有被开启的,在每个material中可以设置是否开启。
-
Unity automatically picks MeshRenderer components and
Graphics.DrawMesh
calls for instancing. Note that SkinnedMeshRenderer is not supported. -
Unity only batches GameObjects that share the same Mesh and the same Material in a single GPU instancing draw call. Use a small number of Meshes and Materials for better instancing efficiency. To create variations, modify your shader scripts
to add per-instance data (see next section to learn more about this).
https://docs.unity3d.com/Manual/GPUInstancing.html
Use mesh-based Level Of Detail(LOD)
LOD就是根据物体和摄像机的距离不同可以自动使用不同精度的mesh,因为距离很远的地方根本没必要用精度很高的模型。
LOD的缺点之一是开发起来比较费劲,会需要美术提供不同精度的模型,场景设计师需要生成LOD Groups,还要进行测试效果,这会花费很多时间,当然目前一些插件提供了自动生成LOD Mesh的功能。
LOD另一个缺点是需要消耗更多的磁盘和内存以及CPU,但是优点也非常明显,就可以有效减少顶点数量,降低Draw Call和Fill Rate以及 Memory Bandwidth。
LOD也不是总适用,对于内景以及摄像机是从上往下俯角的游戏,比如RTS和MOBA类游戏,物体和摄像机的距离基本都一致,所以LOD根本不会起到什么效果。
总之是否使用LOD需要视情况而定,如果性能并没有出现瓶颈,没有必要使用。
Culling Groups
通过脚本,可以自定义LOD系统,比如可以根据distance使用不同精度的动画,使用不同的shader以及粒子特效等。当然使用Culling Gropus也会带来开发上额外的工作量。https://docs.unity3d.com/Manual/CullingGroupAPI.html
Make use of Occlusion Culling
同时能降低Fill Rate和Overdraw最好的途径之一就是使用Occlusion Culling(遮挡剔除)。Frustum Culling(视锥剔除)只能剔除掉摄像机外的物体,但是对于摄像机内的物体,常规渲染的顺序是从远到近,因此当近物体挡住了远处物体时,就会产生OverDraw。
Occlusion Culling可以有效剔除摄像机内被近处物体挡住的部分。Unity把场景切割成cells,在Editor中烘焙生成Cells间遮挡剔除关系的数据,在运行时,加载烘焙好的数据到内存中,对于设置了遮挡剔除的摄像机,就会根据这个数据来进行渲染。
只有标记为Occluder Static 或者 Occludee Static的物体可以产生遮挡剔除数据。对于普通物体,既能遮挡其他物体又能被其他物体遮挡,使用Occluder。对于透明物体,并不能遮挡其他物体,使用Occludee。
开启Occlusion Culling会增加存储空间和内存以及CPU的开销,因此对于Occlusion Culling烘焙时要设置合适的Cell参数。
特别注意的是即使物体被Occlusion Culling剔除掉了,但是它的阴影依然会被渲染。
Optimizing Particle Systems
粒子系统中,要注意粒子生成的数量以及使用Shader的复杂度,粒子的数量会影响Front End,而shader中使用大量的Texture会
在Back End消耗Fill Rate和Memory Bandwidth。
最直接的优化方法就是减少粒子数量和减少特效数量,使用图集也是优化方法之一。但是经常忽略的一个优化方法是使用Particle System Culling。
Make use of Particle System Culling
关于 Particle System Culling 的一篇blog:https://blogs.unity3d.com/2016/12/20/unitytips-particlesystem-performance-culling/.
Particle System Culling的基本原理就是是否可以预测,如果一个粒子特效的各种参数设置使得其行为样式时可以被预测的,当它并不在视野中时就可以自动的被剔除掉,当重新进入视野中时,因为是可以预测的,所以可以计算出此时此刻应有的样子。
但是一些设置可能会导致粒子系统变得不可预测或者非程序化,那就无法使用Particle System Culling,无论是否可见,粒子系统必须一直保持运行。比如Particle System 使用world-space坐标,使用碰撞,或者复杂的动画曲线等等,都可能会使得其不可预测。Unity提供了一个非常有用的Warning,来提示改设置是否会导致粒子系统无法自动 Culling。
Avoid recursive Particle System calls
Particle System中很多方法都是递归的,调用他们会遍历每一个child并且调用 GetComponent<ParticleSystem>()函数,当层级非常复杂时,会有比较大的开销。
Start(), Stop(), Pause(), Clear(), Simulate(), and isAlive()都是这种函数。但是这些函数里其实有个withChildren参数,Unity默认设置为true,如果手动设置为false,就可以避免递归行为。