NGUI 渲染顺序概述

Wesley13
• 阅读 574

以下内容基于 NGUI v.3.11.2 。示例是在 Unity 5.5 中测试。

渲染结构

NGUI 用 Panel 管理渲染。一个 UIPanel 实例表示一个 Panel 。UIPanel.widgets 表示 Panel 中需要渲染的物体,UIPanel.drawCalls 表示 Panel 中的渲染指令,而 static UIPanel.list 存放所有的 Panel 。默认情况下用于渲染的 GameObject (DrawCall 所在的)是隐藏的,可以定义宏 SHOW_HIDDEN_OBJECTS 来显示 DrawCall GameObject 。于是 NGUI 需要控制的渲染顺序包括以下。

  • 不同的 Panel 的渲染顺序。
  • 同一 Panel 中所有 Widget 的渲染顺序。
  • 同一 DrawCall 中绘制的三角形顺序。

渲染顺序

UI 是 2D 的,不需要在渲染时开启深度测试(depth test)。因此,若渲染位置相同时,后渲染的图形覆盖先渲染的图形。既然 NGUI 通过创建 DrawCall GameObject 来渲染,那么先创建的 DrawCall GameObject 就表示先渲染,后创建的 DrawCall GameObject 在相同位置渲染时就会覆盖之前渲染的图形。

在使用 NGUI 时,都知道可以设置 Panel 和 Widget 的 Depth 属性。Depth 值越低,越容易被覆盖。根据上面的渲染机制分析可以得到:Depth 值低的 Panel 或 Widget 会先被处理。下面进入具体的代码中来验证。

  • 根据 Depth 排序 Panel 。在 UIPanel.cs 文件查找 list.Sort(CompareFunc); ,再看 UIPanel.CompareFunc 函数刚好发现 Depth 越小,在 list 中越靠前。
  • 根据 Depth 排序 Widget 。在 UIPanel.cs 文件查找 widgets.Sort(UIWidget.PanelCompareFunc); 再看比较函数,发现和 Panel 一样,Depth 越小在列表中越靠前。注:NGUI 并不是每次都重新排序所有的 Widget ,新加的 Widget 根据 Depth 插入到指定位置就避免了完全重排,但是排序规则都一样的。
  • 合并 DrawCall 。查看函数 UIPanel.FillAllDrawCalls 可知,若 UIPanel.widgets 相邻的 Widget 满足 Material 、Texture 、Shader 相同,则会把此 Widget 的绘制合并到前一个 Widget 的 DrawCall 中。其实合并或者不合并,后一个 Widget 都是后绘制,因此不影响最终的结果。

我们明白了应用层面 Depth 的设置,但是实际的渲染是在 Shader 中进行的,如何根据应用层的 Depth 控制 Shader 的渲染呢?其实很简单。排序后设置相应的 renderQueue 即可。具体实现搜索 UIDrawCall.cs 中 mDynamicMat.renderQueue = mRenderQueue;

经过上面分析,Depth 小的 Panel 先绘制,同一 Panel 中 Depth 小的 Widget 先绘制。这就是 NGUI 渲染顺序。由于需要控制 Panel 绘制顺序,所以 NGUI 绘制时在单个 LateUpdate 中根据列表处理所有的 Panel ,为了避免所有 Panel 的 LateUpdate 都处理一遍,因此引入了 static UIPanel.mUpdateFrame 控制调用频率。如下代码(为了方便显示,删除了一些代码)。

void LateUpdate ()
{
    if (mUpdateFrame != Time.frameCount)
    {
        mUpdateFrame = Time.frameCount;

        // Update each panel in order
        for (int i = 0, imax = list.Count; i < imax; ++i)
            list[i].UpdateSelf();
    }
}

模拟渲染

既然写了那么多字,下面来模拟一下渲染。先看运行截图。

NGUI 渲染顺序概述

  • 一共绘制左右两组三角形,每组包含 2 个 DrawCall ,每个 DrawCall 绘制了 2 个三角形。

  • 每个 DrawCall 先绘制亮色三角形,再绘制暗色三角形,所以暗色挡住亮色区域。

  • 查看 Hierarchy 视图中以 __dc 开头的 GameObject ,它们就是用于渲染的 DrawCall GameObject 。

  • 看下面的渲染调用。左边绿色三角形的 renderQueue 更大,因此后绘制而挡住了红色三角形。右边则相反。

    DrawCallMgr.CreateDrawCall(Vector3.zero, Color.green, "green", 3001); DrawCallMgr.CreateDrawCall(new Vector3(Vector3.zero.x, Vector3.zero.y - 0.5f, Vector3.zero.z), Color.red, "red", 3000);

    DrawCallMgr.CreateDrawCall(new Vector3(Vector3.zero.x + 3.0f, Vector3.zero.y, Vector3.zero.z), Color.green, "green", 3000); DrawCallMgr.CreateDrawCall(new Vector3(Vector3.zero.x + 3.0f, Vector3.zero.y - 0.5f, Vector3.zero.z), Color.red, "red", 3001);

如何运行示例代码呢。下载 DrawCallTest.csSimpleColor.shader 然后把文件导入 Unity 工程中(我是在 Unity5.5 中测试的,5.x 应该都可以)。导入文件后,新建场景,把 DrawCallTest.cs 拖动到 Main Camera 上,然后运行游戏即可。


我的博客地址 https://my.oschina.net/iirecord/blog

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
thinkphp3.2.3模板渲染支持三元表达式
thinkphp3.2.3模板渲染支持三元表达式{$status?'正常':'错误'}{$info'status'?$info'msg':$info'error'}注意:三元运算符中暂时不支持点语法。如下:           <divclass"modalhidefade"id'myModa
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
Oracle一张表中实现对一个字段不同值和总值的统计(多个count)
需求:统计WAIT\_ORDER表中的工单总数、未处理工单总数、已完成工单总数、未完成工单总数。表结构:为了举例子方便,WAIT\_ORDER表只有两个字段,分别是ID、STATUS,其中STATUS为工单的状态。1表示未处理,2表示已完成,3表示未完成总数。 SQL:  1.SELECT   2
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这