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中明确描述渲染队列,如:

  1. 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深度从小到到的顺序进行填充。也即是说深度越小的组件先绘制,会被后面深度大的组件遮挡住。

  1. // Widget 排序策略,深度相同情况下排序规则就会不明确
  2. static public int PanelCompareFunc (UIWidget left, UIWidget right)
  3. {
  4. if (left.mDepth < right.mDepth) return -1;
  5. if (left.mDepth > right.mDepth) return 1;
  6. Material leftMat = left.material;
  7. Material rightMat = right.material;
  8. if (leftMat == rightMat) return 0;
  9. if (leftMat == null) return 1;
  10. if (rightMat == null) return -1;
  11. return (leftMat.GetInstanceID() < rightMat.GetInstanceID()) ? -1 : 1;
  12. }
  13. // 更新Drawcall的绘制顺序
  14. void UpdateDrawCalls ()
  15. {
  16. for (int i = 0; i < drawCalls.Count; ++i)
  17. {
  18. UIDrawCall dc = drawCalls[i];
  19. dc.renderQueue = (renderQueue == RenderQueue.Explicit) ? startingRenderQueue : startingRenderQueue + i;
  20. dc.alwaysOnScreen = alwaysOnScreen &&
  21. (mClipping == UIDrawCall.Clipping.None || mClipping == UIDrawCall.Clipping.ConstrainButDontClip);
  22. dc.sortingOrder = mSortingOrder;
  23. dc.sortingLayerName = mSortingLayerName;
  24. dc.clipTexture = mClipTexture;
  25. }
  26. }

Panel绘制顺序

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

  1. /// <summary>
  2. /// Function that can be used to depth-sort panels.
  3. /// </summary>
  4. static public int CompareFunc (UIPanel a, UIPanel b)
  5. {
  6. if (a != b && a != null && b != null)
  7. {
  8. if (a.mDepth < b.mDepth) return -1;
  9. if (a.mDepth > b.mDepth) return 1;
  10. return (a.GetInstanceID() < b.GetInstanceID()) ? -1 : 1;
  11. }
  12. return 0;
  13. }
  14. void LateUpdate ()
  15. {
  16. if (mUpdateFrame != Time.frameCount)
  17. {
  18. mUpdateFrame = Time.frameCount;
  19. // Update each panel in order
  20. for (int i = 0, imax = list.Count; i < imax; ++i)
  21. list[i].UpdateSelf();
  22. int rq = 3000;
  23. // 更新Panel的渲染顺序
  24. for (int i = 0, imax = list.Count; i < imax; ++i)
  25. {
  26. UIPanel p = list[i];
  27. if (p.renderQueue == RenderQueue.Automatic)
  28. {
  29. p.startingRenderQueue = rq;
  30. p.UpdateDrawCalls();
  31. rq += p.drawCalls.Count;
  32. }
  33. else if (p.renderQueue == RenderQueue.StartAt)
  34. {
  35. p.UpdateDrawCalls();
  36. if (p.drawCalls.Count != 0)
  37. rq = Mathf.Max(rq, p.startingRenderQueue + p.drawCalls.Count);
  38. }
  39. else // Explicit
  40. {
  41. p.UpdateDrawCalls();
  42. if (p.drawCalls.Count != 0)
  43. rq = Mathf.Max(rq, p.startingRenderQueue + 1);
  44. }
  45. }
  46. }
  47. }

结论

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. 1.从Node.js链接到MongoDB

    MongoDB采用了MongoDB Node.js驱动程序作为标准. 1.安装MongoDB驱动 npm install mongoDB npm install mongoose require('m ...

  2. java开发JDBC连接数据库详解

    JDBC连接数据库 好文一定要让大家看见 •创建一个以JDBC连接数据库的程序,包含7个步骤: 1.加载JDBC驱动程序: 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机) ...

  3. js颠倒数组元素顺序reverse()

    颠倒数组元素顺序reverse() reverse() 方法用于颠倒数组中元素的顺序. 语法: arrayObject.reverse() 注意:该方法会改变原来的数组,而不会创建新的数组. 定义数组 ...

  4. AngularJS多模块开发

    angularJS中的多模块开发是指多个module模块开发,步骤为: 1. 确定主模块    var app=angular.module('myApp',[]); 2. 其他的子模块添加到主模块后 ...

  5. [Android Pro] ScrollView嵌套RecyclerView时滑动出现的卡顿

    reference to : http://zhanglu0574.blog.163.com/blog/static/113651073201641853532259/ ScrollView嵌套Rec ...

  6. PHP基础之POST与GET

    post 与 get区别 *.Post传输数据时,不需要在URL中显示出来,而Get方法要在URL中显示.*.Post传输的数据量大,可以达到2M,而Get方法由于受到URL长度的限制,只能传递大约1 ...

  7. C语言中的强符号与弱符号

    转自:http://blog.csdn.net/astrotycoon/article/details/8008629 一.概述 在C语言中,函数和初始化的全局变量(包括显示初始化为0)是强符号,未初 ...

  8. Oracle在存储过程中如何返回结果集

    Oracle和Sqlserver不一样的地方有很多. 个人最深的体会是存储过程返回结果集,在Sqlserver中直接select查询就行,Oracle就不行了. 这里,就用最简单的例子说明存储过程返回 ...

  9. Python学习笔记(一)——环境搭建

    一.安装包下载: 国内镜像:32位:http://pan.baidu.com/s/1jI4q4lS        64位:http://pan.baidu.com/s/1eRPhpRW 版本更迭速度很 ...

  10. 转债---Pregel: A System for Large-Scale Graph Processing(译)

    转载:http://duanple.blog.163.com/blog/static/70971767201281610126277/   作者:Grzegorz Malewicz, Matthew ...