drawcall影响的是CPU的效率,而且也是最知名的一个优化点。对DrawCall的优化,主要就是为了尽量解放CPU在调用图形接口上的开销。所以针对drawcall我们主要的思路就是每个物体尽量减少渲染次数,多个物体最好一起渲染。
一个Draw Call 是CPU为GPU准备渲染数据,并指令GPU进行一次渲染的过程。
过程是:引擎首先经过可见性测试,确定摄像机可以看到的物体,把这些物体的顶点(包括本地位置、法线、UV等),索引(顶点如何组成三角形),变换(就是物体的位置、旋转、缩放、以及摄像机位置等),相关光源,纹理,渲染方式(由材质/Shader决定)等数据准备好,然后通知图形API——或者就简单地看作是通知GPU——开始绘制,GPU基于这些数据,经过一系列运算,在屏幕上画出成千上万的三角形,最终构成一幅图像。
影响CPU性能主要有一下几个方面:DrawCalls,物理组件,GC,代码质量。
》DrawCalls
优化方法:
- 使用Draw Call Batching,也就是描绘调用批处理。 分为静态批处理和动态批处理。动态批处理机制是引擎自动进行的,无需像静态批处理那样手动设置static。如果scale不同,竟然也不会去做批处理优化。静态批处理来说,好消息是自由度很高,限制很少,坏消息是可能会占用更多的内存,而且经过静态批处理后的所有物体都不可以再移动了。
- 通过把纹理打包成图集来尽量减少材质的使用.
- 尽量少的使用反光啦,阴影之类的,这些会使物体多次渲染。
batching的物体必须使用相同材质;
静态bathing:只要这些物体不移动,并且拥有相同的材质,静态批处理就允许引擎对任意大小的几何物体进行批处理操作来降低描绘调用。
只需要在检测器(Inspector)中将Static复选框打勾即可;
静态批处理比动态批处理约束少;
Unity3D的draw call动态批处理机制是引擎自动进行的,无需像静态批处理那样手动设置static。
如果动态物体共享相同的材质,则引擎会自动对draw call优化,也就是使用批处理。
》动态批处理的约束:
- 批处理动态物体需要在每个顶点上进行一定的开销,所以动态批处理仅支持小于900顶点的网格物体。
- 如果你的着色器使用顶点位置,法线和UV值三种属性,那么你只能批处理300顶点以下的物体;如果你的着色器需要使用顶点位置,法线,UV0,UV1和切向量,那你只能批处理180顶点以下的物体。
- 不要使用缩放。分别拥有缩放大小(1,1,1) 和(2,2,2)的两个物体将不会进行批处理。
- 统一缩放的物体不会与非统一缩放的物体进行批处理。
- 使用缩放尺度(1,1,1) 和 (1,2,1)的两个物体将不会进行批处理,但是使用缩放尺度(1,2,1) 和(1,3,1)的两个物体将可以进行批处理。
- 使用不同材质的实例化物体(instance)将会导致批处理失败。
- 拥有lightmap的物体含有额外(隐藏)的材质属性,比如:lightmap的偏移和缩放系数等。所以,拥有lightmap的物体将不会进行批处理(除非他们指向lightmap的同一部分)。
- 多通道的shader会妨碍批处理操作。比如,几乎unity中所有的着色器在前向渲染中都支持多个光源,并为它们有效地开辟多个通道。
- 预设体的实例会自动地使用相同的网格模型和材质。
》物理组件(Physics) 优化方法
- 设置一个合适的Fixed Timestep。
- 不要使用网格碰撞器(mesh collider);
- 从性能优化的角度考虑,物理组件能少用还是少用为好。
》GC(GC用来处理内存,但是由CPU来控制)
》代码质量
- 不要调用GetComponent,保存成成员引用;
- 使用OnBecameVisible()和OnBecameVisible(),来控制物体的update()函数的执行以减少开销。
- 使用内建的数组,比如用Vector3.zero而不是new Vector(0, 0, 0);
- 对于方法的参数的优化:善于使用ref关键字。
- 优化数学计算。比如,如果可以避免使用浮点型(float),尽量使用整形(int),尽量少用复杂的数学函数比如 Sin 和 Cos 等等。