NGUI中的draw calls
大家好,今天要学习的内容是 NGUI 中的 draw calls,有兴趣的同学可以围观一下,说不定这正是你要找的知识呢。
前置说明
Unity 中 draw call 的定义
在 Unity 里,每次引擎准备数据并通知 GPU 的过程被称为一次 Draw Call。Unity(实际上基本所有图形引擎)生成一帧画面的处理过程大致可以简化描述如下:引擎首先进行简单的可见性测试,确定摄像机能够看到的物体。接着,它会把这些物体的顶点(包含本地位置、法线、UV 等信息)、顶点组成三角形的方式、变换(涵盖物体的位置、旋转、缩放以及摄像机位置等)、相关光源、纹理、渲染方式(由材质/Shader 决定)等数据准备好。之后,通知图形 API(简单来说就是通知 GPU)开始绘制。GPU 基于这些数据,经过一系列运算,在屏幕上绘制出成千上万的三角形,最终构成一幅图像。
NGUI 中 UIWidget 的显示顺序
每一个 UIWidget 的显示顺序由 depth 值决定,与 z 轴无关。而这个 depth 值由两部分组成,是 UIWidget 所在的 UIPanel 的 depth 和 UIWidget 自身的 depth 值进行加权计算得出的。并且,UIPanel 的权重非常大,可以认为,UIPanel 的 depth 大的所有 UIWidget 最终计算得到的 depth 一定比 UIPanel 的 depth 小的所有 UIWidget 的 depth 大。下面举个例子说明:
- UIPanel1 的 depth 为 x,UIPanel2 的 depth 为 y。
- UIWidget1 的 depth 为 m,UIWidget2 的 depth 为 n。
只要 x > y,那么不管 m 和 n 的大小关系如何,UIWidget1 最后的 depth 一定大于 UIWidget2。
减少 draw call 的规则
规则 1:纹理和字体的放置原则
同一个 UIPanel 下的 texture 和 font 尽量放在同一个 atlas 下,换个角度理解,使用同一个 atlas 的元素尽量放在同一个 UIPanel 下面。
规则 1 的前半部分比较容易理解。对于后半部分,结合前面提到的显示顺序问题可知,如果使用同一个 atlas 的元素位于两个不同的 UIPanel 下面,必然会导致它们的 draw call 分离。所以,即便调整它们的 depth 一致,也无法将其合并成一个 draw call。
规则 2:元素连续性原则
如果一个 UIPanel 下面使用了多个 atlas,那么要尽量让使用相同 atlas 的元素连续,尽量避免 atlas 交叉。
下面通过一个例子来详细说明规则 2: 假设同一个 UIPanel 下有 4 个 UIWidget,分别为 w1、w2、w3、w4。其中,W1 和 W2 引用 atlas1,W3 和 W4 引用 atlas2。
情况一:合理的 depth 顺序
当它们的 depth 顺序为 w1 : 1,w2 : 2,w3 : 3,w4 : 4 时,整个渲染需要 2 个 draw call。因为渲染顺序为 w1、w2、w3、w4,而 w1 和 w2 共用一个 atlas,所以可以合并成一个 draw call;同理,w3 和 w4 也可以合并成一个 draw call。
情况二:不合理的 depth 顺序
当它们的 depth 顺序为 w1 : 1,w2 : 3,w3 : 2,w4 : 4 时,整个渲染需要 4 个 draw call。因为渲染顺序为 w1、w3、w2、w4,w1 和 w3 不共用一个 atlas,所以只能分开渲染;同理,w3 和 w2、w2 和 w4 也只能分开渲染。