NGUI元素的遮挡情况是不依赖空间关系,所以在NGUI上添加特效有时候特别蛋疼,特别是美术同学还要依赖空间关系来控制特效效果,那先看看看NGUI的层级是怎么处理的,不过下面的描述都是针对单个相机下的Panel,如果存在多个相机当然还要考虑相机的前后关系。在写之前,还是记录下这篇随笔参考的资源:《NGUI 渲染流程深入研究》) , 一篇不错的介绍,对理解整个流程很有帮助,对层级关系也做了很多描述;作为补充,《NGUI的渲染流程》 对 理解UIPanel、UIWidget、UIDrawCall的关系稍稍有点帮助

Render Queue

也就是渲染队列,默认情况下,Unity会基于对象距离摄像机的远近来排序对象。对象离摄像机越近就会优先绘制在其他更远的对象上面。对于大多数情况这是有效并合适的,但是在一些特殊情况下,你可能想要自己控制对象的绘制顺序。Unity提供给我们一些默认的渲染队列,每一个对应一个唯一的值,来指导Unity绘制对象到屏幕上的顺序。这些内置的渲染队列被称为Background, Geometry, AlphaTest, Transparent, Qverlay。具体描述如下,也就是说数值越小越先绘制。

渲染队列 渲染队列描述 渲染队列值
Background 通常被最先渲染 1000
Geometry 默认的渲染队列,它被用于绝大多数对象。不透明几何体使用该队。 2000
AlphaTest 通道检查的几何体使用该队列。它和Geometry队列不同,对于在所有立体物体绘制后渲染的通道检查的对象,它更有效。 2450
Transparent 该渲染队列在Geometry和AlphaTest队列后被渲染。任何通道混合的(也就是说,那些不写入深度缓存的Shaders)对象使用该队列,例如玻璃和粒子效果 3000
Overlay 该渲染队列是为覆盖物效果服务的。任何最后被渲染的对象使用该队列,例如镜头光晕。 4000
UI RenderQueue

对UI而言,一般是浮动在场景的上层,而且可能使用透明UI,所以RenderQueue一般从3000开始,通常情况下,Render Queue会在Shader的SubShader的Tag中明确描述渲染队列,如:

Tags { "Queue"="Transparent" }

如果查看NGUI的shader,应该可以看到这句话。

动态材质

上一篇文章介绍过,NGUI中所有元素最后都会在DrawCall中生成Mesh、MeshRender、Material,然后被绘制出来。NGUI使用Atlas来管理图片数据,也就是说不同层级的组件也会使用相同的Atlas,这就需要每个DrawCall在运行时动态修改材质的RenderQueue,NGUI通过材质参数来处理这个问题。具体可以可以参考Unity Material.renderQueue的定义, 理解这句话:

By default materials use render queue of the shader it uses. You can override the render queue used using this variable. Note that once render queue is set on the material, it stays at that value, even if shader is later changed to be different.

UIDrawCall实际会创建一个叫做mDynamicMat的Material用作后续的材质渲染顺序、贴图、Shader参数设置。

Widget绘制顺序

先看下DrallCall的生成,在UIPanel.FillllDawcall函数中,先对Widget进行排序,然后在对WIdget进行遍历过程中,如果相邻的Widget使用的材质、贴图或者Shader不相同则创建一个新的Drawcall, 也就说DrawCall列表中的顺序和Widget的顺序是一致的(对于存在DrawCall合并的情况,Drawcall中会记录widget上深度的起始和终止数值)。

在同一个Panel中,Widget会按照深度进行排序,而DrawCall的RenderQueue则根据从Panel的RenderQueue起始数值加上在DrawCall列表中的位置,而对单个drawCll而言,生成的顶点则也会根据Widget深度从小到到的顺序进行填充。也即是说深度越小的组件先绘制,会被后面深度大的组件遮挡住。

    // Widget 排序策略,深度相同情况下排序规则就会不明确
static public int PanelCompareFunc (UIWidget left, UIWidget right)
{
if (left.mDepth < right.mDepth) return -1;
if (left.mDepth > right.mDepth) return 1; Material leftMat = left.material;
Material rightMat = right.material; if (leftMat == rightMat) return 0;
if (leftMat == null) return 1;
if (rightMat == null) return -1; return (leftMat.GetInstanceID() < rightMat.GetInstanceID()) ? -1 : 1;
} // 更新Drawcall的绘制顺序
void UpdateDrawCalls ()
{ for (int i = 0; i < drawCalls.Count; ++i)
{
UIDrawCall dc = drawCalls[i]; dc.renderQueue = (renderQueue == RenderQueue.Explicit) ? startingRenderQueue : startingRenderQueue + i;
dc.alwaysOnScreen = alwaysOnScreen &&
(mClipping == UIDrawCall.Clipping.None || mClipping == UIDrawCall.Clipping.ConstrainButDontClip);
dc.sortingOrder = mSortingOrder;
dc.sortingLayerName = mSortingLayerName;
dc.clipTexture = mClipTexture;
}
}

Panel绘制顺序

对于不同的Panel而言,NGUI会根据Panel的深度值进行排序,然后依次计算其起始RenderQueue数值。这样的话 深度高的Panel,其内部组件的RenderQueue的数值也会相对较高

	/// <summary>
/// Function that can be used to depth-sort panels.
/// </summary> static public int CompareFunc (UIPanel a, UIPanel b)
{
if (a != b && a != null && b != null)
{
if (a.mDepth < b.mDepth) return -1;
if (a.mDepth > b.mDepth) return 1;
return (a.GetInstanceID() < b.GetInstanceID()) ? -1 : 1;
}
return 0;
} 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(); int rq = 3000; // 更新Panel的渲染顺序
for (int i = 0, imax = list.Count; i < imax; ++i)
{
UIPanel p = list[i]; if (p.renderQueue == RenderQueue.Automatic)
{
p.startingRenderQueue = rq;
p.UpdateDrawCalls();
rq += p.drawCalls.Count;
}
else if (p.renderQueue == RenderQueue.StartAt)
{
p.UpdateDrawCalls();
if (p.drawCalls.Count != 0)
rq = Mathf.Max(rq, p.startingRenderQueue + p.drawCalls.Count);
}
else // Explicit
{
p.UpdateDrawCalls();
if (p.drawCalls.Count != 0)
rq = Mathf.Max(rq, p.startingRenderQueue + 1);
}
}
}
}

结论

A 一般情况下,UIPanel\Widget的层级使用Depth来控制其前后关系就可以满足需求,但是对于特效和U> I前后遮挡这种情况就比较难处理,不过可以通过三种方式解决:

  1. 相机深度
  2. SortingOrder(一直没弄明白这是什么鬼)
  3. RenderQueue

B. DrawCall的数量和组件的深度的也有关系,同样材质的组件使用连续的深度值就会合并为一个组件,OK ,实际上使用过程中,不合理使用似乎更多点,下图就是一种比较恶劣的使用情况,两张图片,但是深度设置不合理,却有10个DrawCall

NGUI 层级关系控制的更多相关文章

  1. Unity NGUI和UGUI与模型、特效的层级关系

    目录 1.介绍两大UI插件NGUI和UGUI 2.unity渲染顺序控制方式 3.NGUI的控制 4.UGUI的控制 5.模型深度的控制 6.粒子特效深度控制 7.NGUI与模型和粒子特效穿插层级管理 ...

  2. NGUI与特效的层级关系

    通过调整特效的 render queue 来解决特效与NGUI界面之间的层级关系问题,用以下脚本解决: using System.Collections.Generic; using UnityEng ...

  3. 树状结构Java模型、层级关系Java模型、上下级关系Java模型与html页面展示

    树状结构Java模型.层级关系Java模型.上下级关系Java模型与html页面展示 一.业务原型:公司的组织结构.传销关系网 二.数据库模型 很简单,创建 id 与 pid 关系即可.(pid:pa ...

  4. 【吐血分享】SQL Server With As 递归获取层级关系数据

    纯洁的一周又开始了,今天看到一则新闻,笑尿了,和袁友们一起娱乐下 最近两月在做基于Saas模式的人力资源管理产品,平常数据库设计我经常会遇到如下需求场景: 以前商城类网站在设计类型表的时候,设计成单表 ...

  5. CTE计算层级关系

    推广渠道表有ParentID字段,代表上下层级关系.现要统计每个推广员,推广了多少人? --创建表结构,插入测试数据 USE DBA_Monitor GO CREATE TABLE [dbo].[TG ...

  6. MFC窗口的父子关系和层级关系

    一直对窗口之间的关系有些混乱,遇到需要指定父窗口的函数时常常要考虑很久,究竟父窗口是哪个窗口,遂上网查资料,略有所悟,简记如下: 对话框中的所有控件(比如Button等)都是其子窗口.        ...

  7. vue层级关系的数据管理

    项目背景:为一些有层级关系的数据管理做一套后台管理系统,例如一个小区,里面是有许多楼,楼里有许多层,每一层有许多不同的房······,现在就是要实现对这些数据进行增删改查操作. 1.Tree(树形组件 ...

  8. php解析出带层级关系的mpp文件

    本来要使用DHX gantt插件自带的API做导入,可是做完后,又发现不稳定,不能访问了 可能是屏蔽掉了 所以又想起可以使用javaBridge,借用java的MPXJ php解析mpp的 上一篇介绍 ...

  9. css - Position定位属性与层级关系

    今天同事发现一个有意思的问题,关于position的层级关系的,他要不说我也没注意过 测试后果然有趣,有待深入研究: <!DOCTYPE html> <html> <he ...

随机推荐

  1. javascript闭包和作用域链

    最近在学习前端知识,看到javascript闭包这里总是云里雾里.于是翻阅了好多资料记录下来本人对闭包的理解. 首先,什么是闭包?看了各位大牛的定义和描述各式各样,我个人认为最容易一种说法: 外部函数 ...

  2. php上传图片文件常用的几个方法

    1. 前台 <form class="add-form" method="post" action="/person/save" en ...

  3. 严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderLis

    一个让我崩溃的问题 感谢:http://blog.csdn.net/itlionwoo/article/details/17523371

  4. JavaSE基础01

    JavaSE基础篇01 ------从今天开始,我就学习正式java了,O(∩_∩)O哈哈~,请大家多指教哦 一.Windows常见的dos命令 操作dos命令: win7 --->开始 --- ...

  5. Centos7设置IP为固定值

    1.进入到系统的IP地址保存文件所在目录 [root@localhost ~]# cd /etc/sysconfig/network-scripts 2.修改保存IP信息的文件 [root@local ...

  6. Android笔记:多线程

    定义线程的两个方法: 1. class MyThread extends Thread { public void run() {// 处理具体的逻辑 } } new MyThread().start ...

  7. jmap之使用说明与JVM配置

    详情可参见:http://blog.csdn.net/fenglibing/article/details/6411953. 1 2. 3.vi 打开查看,具体介绍请看上述链接. 4.查看tomcat ...

  8. Socket通信原理探讨(C++为例)

    一.网络中进程之间如何通信? 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 1.消息传递(管道.FIFO.消息队列) 2.同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 3 ...

  9. Memcached vs Redis

    Memcached和Redis哪一个能有更好的表现? Redis可以看作是Memcached的超集,这让Redis不仅仅可以用来当缓存,也可以作为实际的数据存储. 强大的数据结构以及操作命令. 默认持 ...

  10. 【leetcode】Spiral Matrix

    题目概要: Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spi ...