zoukankan      html  css  js  c++  java
  • NGUI渲染流程

    1 渲染流程

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

    Widget(数据)

    UIGeometry被UIWidget实例化之后,通过UIWidget的子类,也就是UISprit,UILabel等,在OnFill()函数里算出所需的Geometry缓存(顶点数,UV,Color,法线,切线), 参考Sprite的Fill函数为例,根据Widget的绘制区域、颜色、UV等信息生产顶点、uv、颜色信息,必要的时候其实还会生成法线信息。其他组件类似,但是计算方式差异比较大。

    void SimpleFill (BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color> cols)
        {
        	Vector4 v = drawingDimensions;
        	Vector4 u = drawingUVs;
        	Color gc = drawingColor;
        	Color lc = gc.GammaToLinearSpace();
        
        	verts.Add(new Vector3(v.x, v.y));
        	verts.Add(new Vector3(v.x, v.w));
        	verts.Add(new Vector3(v.z, v.w));
        	verts.Add(new Vector3(v.z, v.y));
        
        	uvs.Add(new Vector2(u.x, u.y));
        	uvs.Add(new Vector2(u.x, u.w));
        	uvs.Add(new Vector2(u.z, u.w));
        	uvs.Add(new Vector2(u.z, u.y));
        
        	if (!mApplyGradient)
        	{
        		cols.Add(lc);
        		cols.Add(lc);
        		cols.Add(lc);
        		cols.Add(lc);
        	}
        	else
        	{
        		AddVertexColours(cols, ref gc, 1, 1);
        		AddVertexColours(cols, ref gc, 1, 2);
        		AddVertexColours(cols, ref gc, 2, 2);
        		AddVertexColours(cols, ref gc, 2, 1);
        	}
        }
    

    UIPanel(管理)

    Panel相当于一个容器管理单元,负责下面Widget组件列表和DrallCall组件列表的维护和协调工作,其主要工作,监视Widget的修改、如果修改则修改DrawCall的数据,必要的时候会重建全部DrallCall。对于每个DrallCall而言,则根据widget填充顶点、uv、颜色等数据到自己缓冲中

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

    void FillAllDrawCalls ()
    	{
    		for (int i = 0; i < drawCalls.Count; ++i)
    			UIDrawCall.Destroy(drawCalls[i]);
    		drawCalls.Clear();
    
    		Material mat = null;
    		Texture tex = null;
    		Shader sdr = null;
    		UIDrawCall dc = null;
    		int count = 0;
    
    		if (mSortWidgets) SortWidgets();
    
    		for (int i = 0; i < widgets.Count; ++i)
    		{
    			UIWidget w = widgets[i];
    
    			if (w.isVisible && w.hasVertices)
    			{
    				Material mt = w.material;
    				Texture tx = w.mainTexture;
    				Shader sd = w.shader;
    
    				if (mat != mt || tex != tx || sdr != sd)
    				{
    					if (dc != null && dc.verts.size != 0)
    					{
    						drawCalls.Add(dc);
    						dc.UpdateGeometry(count);
    						dc.onRender = mOnRender;
    						mOnRender = null;
    						count = 0;
    						dc = null;
    					}
    
    					mat = mt;
    					tex = tx;
    					sdr = sd;
    				}
    
    				if (mat != null || sdr != null || tex != null)
    				{
    					if (dc == null)
    					{
    						dc = UIDrawCall.Create(this, mat, tex, sdr);
    						dc.depthStart = w.depth;
    						dc.depthEnd = dc.depthStart;
    						dc.panel = this;
    					}
    					else
    					{
    						int rd = w.depth;
    						if (rd < dc.depthStart) dc.depthStart = rd;
    						if (rd > dc.depthEnd) dc.depthEnd = rd;
    					}
    
    					w.drawCall = dc;
    
    					++count;
    					if (generateNormals) w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, dc.norms, dc.tans);
    					else w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, null, null);
    
    					if (w.mOnRender != null)
    					{
    						if (mOnRender == null) mOnRender = w.mOnRender;
    						else mOnRender += w.mOnRender;
    					}
    				}
    			}
    			else w.drawCall = null;
    		}
    
    		if (dc != null && dc.verts.size != 0)
    		{
    			drawCalls.Add(dc);
    			dc.UpdateGeometry(count);
    			dc.onRender = mOnRender;
    			mOnRender = null;
    		}
    	}
    

    UIDrallCall

    创建

    对于每一个
    

    更新

    DrawCall其实就是在必要的时候根据组件数据生成对应的Mesh以及渲染相关的组件,其核心在UpdataGeometry()函数, 在该函数中做了一些几个事情:

    • 创建Mesh
    if (mMesh == null)
        {
        	mMesh = new Mesh();
        	mMesh.hideFlags = HideFlags.DontSave;
        	mMesh.name = (mMaterial != null) ? "[NGUI] " +            mMaterial.name : "[NGUI] Mesh";
        	mMesh.MarkDynamic();
        	setIndices = true;
        }
    
    • 填充顶点数据
    mMesh.vertices = verts.buffer;
    mMesh.uv = uvs.buffer;
    mMesh.colors32 = cols.buffer;
    
    if (norms != null) mMesh.normals = norms.buffer;
    if (tans != null) mMesh.tangents = tans.buffer;
    
    if (setIndices)
    {
    	mIndices = GenerateCachedIndexBuffer(count, indexCount);
    	mMesh.triangles = mIndices;
    }
    
    • 更新材质
    UpdateMaterials();
    

    DrawCall在什么时候更新呢?

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

    UIPanel.FillAllDrawCalls()调用的话基本是整个Panel重绘了,还好调用条件比较苛刻,除了第一次LateUpdate,之后若有新的Widget加入进来,并且深度不在之前DrallCall的范围内,或者用了新的matiral shader texture那么就会影响之前已经布好的UI秩序,就会被重绘,调用的时候性能会损失很大。说简单点,就是当有可能需要生成新的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被改动;

    引用

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

    NGUI源码

    以下讨论基于NGUI3.8版本,后续版本代码可能进行了修改

  • 相关阅读:
    Mybatis(二) Mybatis通用的写法
    Mybatis(一)Mybatis相关概念
    NodeJS添加Jquery依赖
    安卓、IOS端AEC密钥加密 Java端密钥解密通用实现(16进制表现形式)
    关于博客园首页及详情页美化的代码
    MD5用户密码加密工具类 MD5Util
    .Net Core跨平台应用研究-CustomSerialPort(增强型跨平台串口类库)
    FtpServer穿透内网访问配置踩坑笔记
    .Net Core之编辑json配置文件
    玩转MQTT-阿里云之MQTT使用(下)
  • 原文地址:https://www.cnblogs.com/zsb517/p/6119264.html
Copyright © 2011-2022 走看看