NGUI 渲染流程深入研究 (UIDrawCall UIGeometry UIPanel UIWidget)

上图是一个简要的NGUI的图形工作流程,UIGeometry被UIWidget实例化之后,通过UIWidget的子类,也就是UISprit,UILabel等,算出所需的Geometry缓存(顶点数,UV,Color,法线,切线)。

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

每次有新的UIDrawCall产生,上一个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 会受到这个属性的影响,值越大越靠前,和空间无关

C.Render Queue 一个material和shader都有的属性,渲染队列,一般从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.空间上的前后关系 这个就不用多说了

以上这些影响视觉先后效果的优先度是A>B>C>D>E的

关于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修改新传入的缓存重绘才能更新效果。

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

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;
                }
            }
        }
    }
}
时间: 2024-08-24 04:41:45

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

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

NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 之前项目中用的NGUI的版本是3.0.7 f3,开始的时候感觉没有什么问题,直达最近项目UI的完成度比较高时,就突然出现掉帧很严重的现象,即使只有一个UI打开(其他都是active = false的情况下),打开profier,发现UIPanel LateUpdate 竟然占了CPU使用率的50%左右

COCOS学习笔记--Cocos引擎渲染流程

近期在研究Cocos引擎的渲染流程.在这里将其整个渲染流程进行一下梳理: 梳理之前我们要知道一些东西,就是我们的Cocos引擎是通过使用OpenGL的一些API来进行渲染绘制的,所以假设我们要彻底理解Cocos引擎的渲染流程并想改动引擎底层渲染的相关内容,熟悉OpenGL是非常有必要的. 这里先简单说一下大概流程,Cocos3.x版本号的渲染是将全部须要渲染的node先通过各种RenderCommand封装起来,你先不用管RenderCommand是什么,仅仅须要记住它把我们要渲染的node封装

Ogre 监听类与渲染流程

Ogre中有许多监听类,我们可以简单理解成C#中的事件,这些类作用都不小,说大点可能改变流程,说小点修改参数等,下面列举一些常用的监听类. FrameListener:由Ogre中的Root负责维护,主要针对所有RenderTarget监听 frameStarted:在一桢开始的时候,所有RenderTarget更新之前. frameRenderingQueued:所有RenderTarget更新之后,但是还没交换缓冲区.(意思屏幕上显示没变) frameEnded:所有RenderTarget

流程管理理论研究与中国实践(一)

流程管理理论研究与中国实践(一) 作者:张国祥 2014年6月16日 编者注:本文为张国祥老师两年前应联创世纪教育训练集团董事长朱栩博士之约撰写的学术论文,旨在向国外管理界介绍中国本土流程管理理论创新与实践总结.今天编者经张老师本人同意,分三期发表,以飨读者. 关键词:流程管理 流程优化 理论创新 方法创新 管理体系 中国实践 摘要:本文概括介绍了流程管理在全球兴起的背景.失败的原因,流程管理在中国的实践与发展,重点介绍了笔者在为中国中小企业进行流程优化与流程管理导入的咨询服务中的经验总结与理论

Ogre2.1 Hlms与渲染流程

在我前面三篇说明Ogre2.x的文章里,第一篇大致说了下Hlms,第二篇说了下和OpenGL结合比较紧的渲染,本文用来说下Hlms如何影响渲染流程中,因为有些概念已经在前面二文里说过了,本文就不再提,最后这篇也算是对Ogre2.1一个渲染流程的详细讲解. PassScene简单流程 首先是所有场景更新,场景更新包含的内容如下. 场景动画 _applySceneAnimations 所有节点数据 UPDATE_ALL_TRANSFORMS 骨骼动画 UPDATE_ALL_ANIMATIONS 所有

转:Ogre内部渲染流程

以下是 Ogre 的代码中的详细说明: Renderable是OGRE中所有可渲染对象的抽象接口 这个接口抽象出了在渲染管线中的被分组的离散的可渲染对象基本的方法. 此接口的实现类必须是基于单一的材质.单一的世界矩阵(或者是一组通过权重混合的世界矩阵),以及单一的渲染操作. 通过这个说明,应该能明确的是,Renderable 封装了3D世界中被渲染对象的基本属性和数据,这包括:渲染操作,材质属性,光照信息.变换矩阵(四元组).LOD信息.渲染方式等信息.这些信息在渲染循环中被取出,并应用在图形渲

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

这篇文章我们剔除Starling的Touch事件体系和动画体系,专门来看看Starling中的渲染流程实现,以及其搭建的显示列表结构. 由于Starling是模仿Flash的原生显示列表,所以我们可以参照原有的知识体系来阅读Straling的代码. Starling类: Straling类是整个Starling框架的核心,该类会管理Straling的显示列表.Touch事件.动画处理等等多个模块的功能: 同时Starling类会实现框架内部的帧循环: 实例化Starling类后会Starling

Cocos2Dx之渲染流程

渲染时一个游戏引擎最重要的部分.渲染的效率决定了游戏的流畅度清晰度,跟前面的介绍的内容相比,渲染是最具技术含量的事情,也是一个需要很多专业知识的事情.这里我们有这个机会,来学习下一个游戏引擎的渲染是怎么做的.Cocos2Dx是一个2D框架,可以简单地看做z轴在一个平面上,Cocos2Dx采用的OpenGL技术决定了往3D渲染上面走也不是不行的.最新3.2版本已经支持3D骨骼动画的CCSprite.3.2版本还有很多其他的变化,可谓是一次伤筋动骨的大版本,现在还不跳跃,继续基于2.2.3进行分析.

Cocos2d-x3.x与OpenGL渲染总结 Cocos2d-x3.x的渲染流程

最近几天,我都在学习如何在Cocos2d-x3.2中使用OpenGL来实现对图形的渲染.在网上也看到了很多好的文章,我在它们的基础上做了这次的我个人认为比较完整的总结.当你了解了Cocos2d-x3.2中对图形渲染的流程,你就会觉得要学会写自己的shader才是最重要的. 第一,渲染流程从2.x到3.x的变化. 在2.x中,渲染过程是通过递归渲染树(Rendering tree)这种图关系来渲染关系图.递归调用visit()函数,并且在visit()函数中调用该节点的draw函数渲染各个节点,此