您的位置:首页 > 产品设计 > UI/UE

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

2016-01-14 20:02 260 查看
http://gad.qq.com/article/detail/7151089

UIWidget的显示顺序

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



但是UIPanel不是继承至UIWidget,Depth是UIPanel自身的一个属性。



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

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

UIPanel A的Depth为x,其上面有一个Depth值为m的UIWideget A

UIPanel B的Depth为y,其上面有一个Depth值为n的UIWideget B

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

NGUI的Draw Call合并

2.1. Draw Call定义

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

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

2.2. NGUI的Draw Call合并原理

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

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



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

渲染优化

3.1. 打包图集

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

3.2. 深度管理

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

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

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

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

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

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

公共UI部分(轻提示,弹窗等)因其特殊性,且可能同时会存在多个,所以交给代码统一管理。比如说一个弹窗,添加至场景时,遍历其身上所有UIPanel和UIWidget,将它们的Depth乘以一个系数再加上一个足够大的基数,然后将弹窗对象保存至一个列表,这样能保证后弹出的窗口会在最上面(详见附件PopUpManager.cs)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: