NGUI 3.x 深度管理及渲染优化

发表于2016-01-12
评论6 2.4k浏览

NGUI 3.x 深度管理及渲染优化

UIWidget的显示顺序

  我们在设置UISpriteUILabel等控件的深度时,都是在设置其Widget样式中的Depth值。其实这些控件都是继承至UIWidget

  但是UIPanel不是继承至UIWidgetDepthUIPanel自身的一个属性。

      NGUI 3.x中每一个UIWidget的显示顺序由Depth值决定,与z轴没关系。但是,这个Depth值并不单纯的是我们设置的值,而是由两部分组成。一个是UIWidget自身的Depth值(即我们设置的值),另一个是UIWidget所在的UIPanel(最内层)的Depth值。

      我们可以理解为是由UIWidget所在UIPanelDepthUIWidget自身的Depth进行加权计算得出来的。并且,UIPanel的权重非常大。举个例子:

UIPanel ADepthx,其上面有一个Depth值为mUIWideget A

UIPanel BDepthy,其上面有一个Depth值为nUIWideget B

      只要x>y,那么不管mn的大小,UIWideget A一定显示在UIWideget B之上。


NGUIDraw Call合并

2.1.Draw Call定义

每次引擎准备数据并通知GPU的过程称为一次Draw Call

Unity(或者说基本所有图形引擎)生成一帧画面的处理过程大致可以这样简化描述:引擎首先经过简单的可见性测试,确定摄像机可以看到的物体,然后把这些物体的顶点(包括本地位置、法线、UV等),索引(顶点如何组成三角形),变换(就是物体的位置、旋转、缩放、以及摄像机位置等),相关光源,纹理,渲染方式(由材质/Shader决定)等数据准备好,然后通知图形API——或者就简单地看作是通知GPU——开始绘制,GPU基于这些数据,经过一系列运算,在屏幕上画出成千上万的三角形,最终构成一幅图像。

 2.2. NGUI的Draw Call合并原理

NGUI为了减少GPU状态切换的消耗(比如切换material),把相同materialUIWidget合并,减少Draw Call的数量。这一步是基于UIPanel做的,合并UIWidget的代码可自行查看UIPanel.csFillAllDrawCalls()函数。

其算法是,先把UIPanel中的UIWidgetDepth从小到大排序,如果Depth相同那按照materialID来排序。然后遍历每个元素,把material相同的UIWidget归类到同一个Draw Call。合并之后的结果如下图:

      最后生成了3DrawCall,并按顺序提交GPU绘制。


渲染优化

3.1.打包图集

每个材质或纹理的渲染一定是会产生Draw Call的,这个Draw Call只能通过打包图集来进行优化。制作图集时应按功能角度进行划分,比如UI可以分为公共部分和每个具体的功能模块(商城、任务等)。

3.2.深度管理

进行深度管理的目的是为了减少DrawCall的数量,从而获得更好的渲染性能。减少Draw Call的规则如下:

1) 由于NGUIDraw Call合并是基于UIPanel的,两个UIPanel间是不会进行合并的。因此,好只用一个UIPanel

2)在一个UIPanel下的texture尽量放在同一个Atlas下。

3)在一个UIPanel下使用多个Atlas,则尽量让使用相同AtlasUIWidget连续,避免不同AtlasUIWidget Depth值交叉。

上述第2点,需要我们合理规划打包图集。上述第3点,需要我们合理规划当前场景中所有渲染对象的渲染顺序(Depth)。 

我们可以通过Excel表来规划和管理每一个场景中所有渲染对象的Depth,规定好后不在代码侧进行设置。

公共UI部分(轻提示,弹窗等)因其特殊性,且可能同时会存在多个,所以交给代码统一管理。比如说一个弹窗,添加至场景时,遍历其身上所有UIPanelUIWidget,将它们的Depth乘以一个系数再加上一个足够大的基数,然后将弹窗对象保存至一个列表,这样能保证后弹出的窗口会在最上面(详见附件PopUpManager.cs)。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论