上图是一个简要的NGUI的图形工作流程,UIGeometry被UIWidget实例化之后,通过UIWidget的子类,也就是UISprit,UILabel等,在OnFill()函数里算出所需的Geometry缓存(顶点数,UV,Color,法线,切线)。PS:之所以要生成这些数据,是为了之后生成mesh来渲染

而UIPanel,通过遍历自己子类下所有的UIWidget组件(已经按深度排序),先创建一个UIDrawCall,然后把该Widget的material,texture,shader对象以及Geometry的缓存传给UIDrawCall,如此反复循环搜索该UIPanel下的每一个Widget,只要是material,texture,shader都和上一个Widget一样的Widget,他们的缓存都传给同一个UIDrawCall,直到循环结束或者碰到一个材质球,贴图,shader对象任一不相同的Widget。当遇到这种Widget,循环会再创建一个新的UIDrawCall,然后传递material,texture,shader,缓存,如此这般,直到循环完全结束。

每次有新的UIDrawCall产生,UIPanel就会调用上一个UIDrawCall的UpdateGeometry()函数,来创建渲染所需的对象。这些对象分别是MeshFilter,MeshRender,和最重要的Mesh(Mesh的顶点,UV,Color,法线,切线,还有三角面)。这些对象都会像我们正常在游戏中新建Cube一样,依附在创建UIDrawCall时生成的GameObject上以便可以渲染。我们在Editor中是看不到这个GameObject的,是因为创建的时候设置了HideFlags.HideAndDontSave。

所以,NGUI的实际渲染流程,就是一个把Widget上的视觉组件生成的缓存,做成UIDrawCall之后,生成mesh来渲染的过程,很简单。

如果您仅仅只是对NGUI的渲染过程感兴趣,那么看到这里就可以了,下面是一些技术性的问题。

关于渲染顺序还有实际游戏中的NGUI造成的DrawCall

在讨论渲染顺序之前,我们先大概了解一下在NGUI中,什么对渲染的层级有决定性的影响。

A.Camera.Depth 不同相机之前的深度属性,在渲染顺序的优先度里面是最高的,Depth越大,渲染的图像越靠前,和空间无关。

B.render.sortingOrder 一个render上的int属性,正常材质球调节这个属性没有什么反应,但是NGUI的材质球 Transparent Color 会受到这个属性的影响,值越大越靠前,和空间无关,可直接在UIPanel上设置。

C.Render Queue 一个materialshader都有的属性,一个int值,意思是渲染队列,一般从3000开始,如果直接修改material的render queue,就会完全覆盖shader上的该属性。在之前的Widget遍历中,每次新生成UIDrawCall,就会把这个UIDrawCall对应的material的render queue加上1,所以不同UIDrawCall之间的排序靠的就是这个,越晚生成的UIDrawCall的render queue越大,也就越靠前,这个前置效果也和空间无关(注:每个UIDrawCall所调用的material仅仅只是一个副本,所以可以单独修改其render queue)

D.顶点缓存序列的先后 这里说的是UIGeometry里传递的顶点(vertex)序列,这是一组根据Widget上的视觉组件生成的vertex(例如,一般的UISprit在simple模式下,会生成四个vertex,位置和你所看到那个编辑模式下scene视图里的可拖动锚点四个角一样,最后我会把UIDrawCall生成的mesh现实出来,一看就明白)这些vertex传入UIDrawCall之后,会计算出三角面,生成mesh。根据生成的三角面的顺序,也就是这些vertex传入的先后,NGUI的材质球会自绘制一种先后关系。后生成的面视觉上总是能在先生成的面前面,这种先后关系,在之前Widget遍历的时候就已经决定了,Widget深度越小,就会先被传递缓存,那么他提供的vertex就会排在生成列表的前面。这种效果仅能用于NGUI的材质球 Transparent Color.

E.空间上的前后关系 这个就不用多说了,不过NGUI已经抛弃了这个方法。改成直接用深度来控制.除非你是相同的render queue,但是又不是同一个DrawCall(不是同一个MESH)。不过如果出现这种情况,证明你的NGUI使用的不规范。

以上这些影响视觉先后效果的优先度是A>B>C>D>E的。但是这种顺序除了A以外,只适用于特殊的material(目前试过,NGUI的material,还有粒子material,这两个material都相互按照这个规则影响),也许这和shader里面的参数有关。正常物体,和NGUI材质球的关系,空间位置仅仅只有A>E,BCD似乎都没有影响。

关于C我再说一点,就是遍历Widget的时候,就算往后碰到的Widget和之前拥有一样的material texture shader,它们依然会生成新的UIDrawCall(比如,1,2号Widget和3号不一样,那么接下来的4号如果和1号2号相同,它也只能生成新的DrawCall)。这是为了确保层次关系的完全正确性。

Widget更新对渲染的影响,以及Panel的Clip

UIDrawCall.UpdateGeometry()这个函数仅有在Panel.FillDrawCall()和Panel.FillAllDrawCalls ()被调用。UpdateGeometry之前说过,就是将送进来的缓存处理成mesh的一个函数。因为每个DrawCall之对应一个Mesh,如果该DrawCall所属的Widget有改动,那么这个DrawCall就要通过UpdateGeometry修改新传入的缓存重绘才能更新效果。

UIPanel.FillAllDrawCalls()调用的话基本是整个Panel重绘了,还好调用条件比较苛刻,除了第一次LateUpdate,之后若有新的Widget加入进来,并且深度不在之前DrallCall的范围内,或者用了新的matiral shader texture那么就会影响之前已经布好的UI秩序,就会被重绘。之前提到的遍历Panel下的所有Widget就是这个函数,调用的时候性能会损失很大。一般来讲,我们做UI如果都做成prefab,然后前后关系仅仅靠panel的sort order 去控制,那么很少有机会会在运行中调用到这个函数。说简单点,就是当有可能需要生成新的UIDrawCall或者剔除UIDrawCall的时候,就会触发这个函数,这个机制,和之前遍历Widget来生成DrawCall的原理以及目的都是一样的。

UIPanel.FillDrawCall(UIDrawCall dc) 填充单独的DrawCall.一般只有少量的widget更新的时候 没必要更新所有的DrawCall(比如Label上的text有变化),只更新对应widget的DrawCall就好了.FillDrawCall()唯一的执行条件就是该DrawCall的isDirty为true,isDirty被切换为true的条件有三大类:1.widget上的视觉组件被更新,调用widget.MarkAsChanged();2.widget的忽然被添加删除和移动;3.Panel的ALPHA被改动;

Panel对视野外物体的剪切的流程是在UIDrawCall.OnWillRenderObject ()里完成的,使用的是NGUI shader的功能。

小结

A.NGUI的PANEL下Widget的排序,如果没有设定好层次,很容易多出来莫名其妙的DrawCall,如果真的不是必须,那么相同material,texture,shader的Widget的深度应该是连续的。

B.尽可能避免运行时触发FillAllDrawCalls(),制作的时候就把所有UI部件的Prefab做好,不要凭空乱生成UI元素。

C.即使是不可避免的运行FillDrawCall(UIDrawCall dc)(这里说不可避免是肯定的),那么我们要避免过多面数的mesh的DrawCall被更新,经常动的部件,可以考虑单独做成DrawCall,牺牲1~2个DrawCall换来可观的性能,还是值得的.

附上NGUI的Mesh开启代码

using UnityEngine;
using System.Collections;
using UnityEditor; public static class NGUIMESH { [MenuItem("NGUI/NguiMeshView")] static public void NguiMeshView()
{
foreach (var panel in UIPanel.list)
{
foreach(var dc in panel.drawCalls)
{
if (dc.gameObject.hideFlags != HideFlags.DontSave)
{
dc.gameObject.hideFlags = HideFlags.DontSave;
}
else
{
dc.gameObject.hideFlags = HideFlags.HideAndDontSave;
}
}
}
}
}

显示结果: 

NGUI 渲染流程深入研究 (UIDrawCall UIGeometry UIPanel UIWidget)的更多相关文章

  1. NGUI渲染流程

    1 渲染流程 NGUI的渲染流程其实就是把Widget组件生成Mesh所需要的缓存数据,然后生成对应的DrallCall组合对应数据,生成渲染需要的Mesh数据,提交渲染. Widget(数据) UI ...

  2. NGUI的原理机制:深入剖析UIPanel,UIWidget,UIDrawCall底层原理

    这是我去搜狐畅游面试时,面试官问的一个问题.问NGUI的机制原理是什么?就是这个插件是根据什么写出来的.当时没答上来,下面是我从转载过来的,可以研究研究. 之前项目中用的NGUI的版本是3.0.7 f ...

  3. NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理

    NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 之 ...

  4. D3D渲染流程--转载

    http://www.cnblogs.com/ixnehc/articles/1282350.html 先从最基础的写起吧,关于Device的渲染流程. D3D9的Device就是D3D给我们提供的一 ...

  5. cocos2d-x渲染流程

    Cocos2Dx之渲染流程 发表于8个月前(2014-08-08 22:46)   阅读(3762) | 评论(2) 17人收藏此文章, 我要收藏 赞2 如何快速提高你的薪资?-实力拍“跳槽吧兄弟”梦 ...

  6. webview渲染流程

    文档标记说明 ################# 消息边界 +++++++++++++++++ 区域分隔 $$$$$$$$$$$$$$$$$ 线程边界 ~~~~~~~~~~~~~~~~~ 进程边界 - ...

  7. 【Stage3D学习笔记续】山寨Starling(三):Starling核心渲染流程

    这篇文章我们剔除Starling的Touch事件体系和动画体系,专门来看看Starling中的渲染流程实现,以及其搭建的显示列表结构. 由于Starling是模仿Flash的原生显示列表,所以我们可以 ...

  8. Ogre内部渲染流程分析系列

    come from:http://blog.csdn.net/weiqubo/article/details/6956005 要理解OGRE引擎,就要理解其中占很重要位置的 Renderable接口, ...

  9. mapbox.gl源码解析——基本架构与数据渲染流程

    加载地图 Mapbox GL JS是一个JavaScript库,使用WebGL渲染交互式矢量瓦片地图和栅格瓦片地图.WebGL渲染意味着高性能,MapboxGL能够渲染大量的地图要素,拥有流畅的交互以 ...

随机推荐

  1. 全排列 UVA 11525 Permutation

    题目传送门 题意:训练指南P248 分析:逆向考虑,比如一个全排列:7345261,它也可以表示成题目中的形式,第一个数字7是由6 * (7 - 1)得到的,第二个数字3有2 * (7 - 2)得到, ...

  2. iOS NSDate获取当前时间并格式化

    NSDateFormatter *formatter = [[NSDateFormatter alloc]init]; [formatter setDateFormat:@"yyyy-MM- ...

  3. ural 1144. The Emperor's Riddle

    1144. The Emperor's Riddle Time limit: 1.0 secondMemory limit: 4 MB Background In the olden times th ...

  4. zookeeper 3.4.6启动流程粗略梳理

    zookeeper 3.4.6 启动脚本里面 nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" " ...

  5. 【原】iOS学习之Swift之语法1(精简版)

    1. Swift简介 2010年的夏天,苹果公司的开发人员Chris Lattne接到了一个特别的任务,为OS X 和iOS平台开发下一代的编程语言,也就是Swift. 苹果公司于2014年WWDC( ...

  6. [转]OpenVPN 安装与配置

    一.服务器端安装及配置 服务器环境:干净的CentOS6.3 64位系统 内网IP:10.143.80.116 外网IP:203.195.xxx.xxx OpenVPN版本:OpenVPN 2.3.2 ...

  7. 使用javax.persistence注解配置PO对象

    JPA注解持久化类很方便,需要jar包:ejb3-persistence.jar下载 import java.io.Serializable; import javax.persistence.Col ...

  8. 争夺 & KM思想

    题意: 给一张二分图,每个点与两个特定点又一条边相连,边权非负,让你给这个二分图每个点一个顶标,让每一条边两端顶标和大于等于这条边.求出最小顶标和. 这当然是翻译过的题目... 原题: 小Y和小P无聊 ...

  9. Python for Informatics 第11章 正则表达式二(译)

    注:文章原文为Dr. Charles Severance 的 <Python for Informatics>.文中代码用3.4版改写,并在本机测试通过. 11.1 正则表达式的字符匹配 ...

  10. CODEVS 1817 灾后重建 Label:Floyd || 最短瓶颈路

    描述 灾后重建(rebuild)  B地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响.但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车.换句话说,只有连接着两 ...