开启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。
这条跟GPU中的Font End相关。很多时候用不到需要多套无用UV和法线数据的mesh。尽量减少顶点数量,有三种途径:
1. 让美术老铁们手动减少顶点数量重新生成mesh
2. 干脆从场景里删掉,当然这是最坏情况
3. 使用LOD等技术自动对mesh进行culling
通过Geometry Shaders中使用Tessellation技术会带来很大的体验提升,但是也给GPU中Frond End增加了负担。目前对于如和优化Tessellation也没啥好办法,还是尽量优化Frond End中其他地方给Tessllation省出更多空间吧。
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
LOD就是根据物体和摄像机的距离不同可以自动使用不同精度的mesh,因为距离很远的地方根本没必要用精度很高的模型。
LOD的缺点之一是开发起来比较费劲,会需要美术提供不同精度的模型,场景设计师需要生成LOD Groups,还要进行测试效果,这会花费很多时间,当然目前一些插件提供了自动生成LOD Mesh的功能。
LOD另一个缺点是需要消耗更多的磁盘和内存以及CPU,但是优点也非常明显,就可以有效减少顶点数量,降低Draw Call和Fill Rate以及 Memory Bandwidth。
LOD也不是总适用,对于内景以及摄像机是从上往下俯角的游戏,比如RTS和MOBA类游戏,物体和摄像机的距离基本都一致,所以LOD根本不会起到什么效果。
总之是否使用LOD需要视情况而定,如果性能并没有出现瓶颈,没有必要使用。
通过脚本,可以自定义LOD系统,比如可以根据distance使用不同精度的动画,使用不同的shader以及粒子特效等。当然使用Culling Gropus也会带来开发上额外的工作量。https://docs.unity3d.com/Manual/CullingGroupAPI.html
同时能降低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剔除掉了,但是它的阴影依然会被渲染。
粒子系统中,要注意粒子生成的数量以及使用Shader的复杂度,粒子的数量会影响Front End,而shader中使用大量的Texture会
在Back End消耗Fill Rate和Memory Bandwidth。
最直接的优化方法就是减少粒子数量和减少特效数量,使用图集也是优化方法之一。但是经常忽略的一个优化方法是使用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。
Particle System中很多方法都是递归的,调用他们会遍历每一个child并且调用 GetComponent
Start(), Stop(), Pause(), Clear(), Simulate(), and isAlive()都是这种函数。但是这些函数里其实有个withChildren参数,Unity默认设置为true,如果手动设置为false,就可以避免递归行为。