前言:
CPU和GPU是相辅相成的,一般CPU准备好渲染的数据、状态、发出指令以及处理电脑软件中的大数据,而GPU执行实际的绘制、复杂的数学和集合运算。现在引擎可以通过良好的排序,将渲染状态一样的对象连续绘制出来,这样CPU可以仅做一次准备工作就把一组对象连续画出来,这样开销大大减少。优化drawcall可以从很多的方面入手,就比如3D模型、图集、遮罩、UI、粒子特效等等,今天我们主要来讲述一些我们游戏开发中我们可以用到的优化方法!
1.mask的多次使用
mask对于drawcall的影响应该是所以组件里面最大的。每存在一个mask,就把mask以内和以外的UI分割成两个“世界”,依次计算两个“世界”的drawcall,然后再相加。原因是mask以内和以外的UI不能通过unity3d一次渲染(batch)。所以在使用mask的时候要仔细思考,能不用就不用,实在要用可以考虑用带通道的图片代替mask的遮罩功能。
2.图集整理不规范
影响drawcall数量的根本是batch(批处理数),而batch是根本一个一个图集来进行批处理的。简单来说,两张image重叠在一起,当两张image的sprite是一个图集里的时候,这两张image就是一个batch;当不是一个图集的时候,两张image就变成了两个batch。所以在处理图集的时候,通常做法是,常用图片放在一个共有图集,然后独立界面的图片放在一个图集,一个UI最好控制在2-3个图集。
制作图集一般遵循几个规则:
从功能角度进行划分,例如UI可以划分为公共部分,以及每个具体的界面,功能上,显示上密切相关的图片打包到一起
不要一股脑把所有东西打包到一个图集里,特别是那些不可能同时出现的东西,它们就不应该在一个图集里,这样的图集意义不大,减少不了DrawCall,并且一个你不需要显示的图片,会一直占用你的内存
注意控制图集的大小,不要让图集太大,一个超级大图集的DrawCall消耗或许顶的上十几个小图集的消耗
3.图文交叉
unity3dUGUI的batch规则除了依赖于图集之外,还依赖于组件关系,当2张图片(同一图集),1个文字进行重叠时,处理不好会发生一些drawcall的多余。比如:image->image->text,这样的话,drawcall就是两个,但是当:image->text->image的时候,就算两张image的sprite是一个图集的,这样的drawcall都会有3个。所以尽量不要出现image->text->image,图文交叉的情况。根据原因2,也有个小tips,当存在两个图集的三张image的时候,也尽量不要出现image1->image2->image1这样的操作。
4.UI层级的深度
在不必要的情况下,我们尽量减少UI层级的深度,在UGUI中Hierarchy面板,节点的的深度,表现的就是UI层级的深度,我们UI中有N层,N越大越靠前,会遮住后面的组件。当深度越深,不处在同一层级的UI就越多,drawcall就会越大。二. 动静分离的优化 当我们在制作UI的时候,我们应该考虑到整个UI,哪个部分处于经常变化的部分,哪个部分属于不常变化的部分。把常变化的归到动态区域,把不常变化的归到静态区域。
5.动静分离
几乎每一秒都在变化,我们放在一个节点以下。这样就可以达到动静分离的效果。 动静分离,可以减少UIMesh动态更新,在某些比较复杂,常驻的界面可以这样优化。小的界面就没必要了,因为不必要的节点有可能会造成drawcall的增加的。
6.特效
U3D提供了非常便捷的方法让我们很轻易地使用美术给过来的特效,懒惰的U3D程序猿会直接放入U3D,甚至不去看这是个什么特效,我们的特效一般都是一瞬间的事情,例如技能特效,或者其他什么特效,那么特效播放完,这个特效我们就看不到了,但假设这个特效在播放结束的时候,没有将自身的Active属性设置为false,那么它就会继续占用你的DrawCall,消耗你设备的计算能力,所以程序需要保证当一个特效播放完之后,能够被消耗,或者设置为非激活的状态,可以使用一些公共方法来完成特效播放完之后的清理工作(自己实现2个静态函数,一个播放完销毁,一个播放完设置未激活)
7.细节小优化
这部分主要有两个可以优化的点。效果可能没有前面两种优化大,但是积少成多,在项目后期,有优化瓶颈的时候,也是非常有必要的。
1>.image和text组件的Raycast Target属性 在组件不需要射线检测的时候,我们可以尽可能的把射线检测去掉,在运行的时候,就可以减少不必要的性能开销。
2.>text组件的Rich Text属性
当text组件勾选了Rich Text的时候,其实每次文本显示都会先判断一下是否拥有这个属性,然后做正则匹配,如果不勾选的话,其实还是有一些优化的。但是这个优化点其实是最小的,不必要的话,其实没必要优化这个,事实证明,在通常情况下如果我们不勾选,但是后期又要对text进行富文本的操作,容易引出一些不必要的bug。
注意:
这几个点我们在实际开发中一定要注意
1、 批处理动态物体需要在每个顶点上进行一定的开销,所以动态批处理仅支持小于900顶点的网格物体。
2、 如果你的着色器使用顶点位置,法线和UV值三种属性,那么你只能批处理300顶点以下的物体;如果你的着色器需要使用顶点位置,法线,UV0,UV1和切向量,那你只能批处理180顶点以下的物体。
3、请注意:属性数量的限制可能会在将来进行改变。
4、 不要使用缩放尺度(scale)。分别拥有缩放尺度(1,1,1)和(2,2,2)的两个物体将不会进行批处理。
5、 统一缩放尺度的物体不会与非统一缩放尺度的物体进行批处理。
使用缩放尺度(1,1,1)和 (1,2,1)的两个物体将不会进行批处理,但是使用缩放尺度(1,2,1)和(1,3,1)的两个物体将可以进行批处理。
6、 使用不同材质的实例化物体(instance)将会导致批处理失败。
7、拥有lightmap的物体含有额外(隐藏)的材质属性,比如:lightmap的偏移和缩放系数等。所以,拥有lightmap的物体将不会进行批处理(除非他们指向lightmap的同一部分)。
8、 多通道的shader会妨碍批处理操作。比如,几乎unity中所有的着色器在前向渲染中都支持多个光源,并为它们有效地开辟多个通道。
9、预设体的实例会自动地使用相同的网格模型和材质。