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)
以下讨论基于NGUI3.8版本,后续版本代码可能进行了修改
NGUI渲染流程的更多相关文章
- NGUI 渲染流程深入研究 (UIDrawCall UIGeometry UIPanel UIWidget)
上图是一个简要的NGUI的图形工作流程,UIGeometry被UIWidget实例化之后,通过UIWidget的子类,也就是UISprit,UILabel等,在OnFill()函数里算出所需的Geom ...
- cocos2d-x渲染流程
Cocos2Dx之渲染流程 发表于8个月前(2014-08-08 22:46) 阅读(3762) | 评论(2) 17人收藏此文章, 我要收藏 赞2 如何快速提高你的薪资?-实力拍“跳槽吧兄弟”梦 ...
- webview渲染流程
文档标记说明 ################# 消息边界 +++++++++++++++++ 区域分隔 $$$$$$$$$$$$$$$$$ 线程边界 ~~~~~~~~~~~~~~~~~ 进程边界 - ...
- D3D渲染流程--转载
http://www.cnblogs.com/ixnehc/articles/1282350.html 先从最基础的写起吧,关于Device的渲染流程. D3D9的Device就是D3D给我们提供的一 ...
- 【Stage3D学习笔记续】山寨Starling(三):Starling核心渲染流程
这篇文章我们剔除Starling的Touch事件体系和动画体系,专门来看看Starling中的渲染流程实现,以及其搭建的显示列表结构. 由于Starling是模仿Flash的原生显示列表,所以我们可以 ...
- Ogre内部渲染流程分析系列
come from:http://blog.csdn.net/weiqubo/article/details/6956005 要理解OGRE引擎,就要理解其中占很重要位置的 Renderable接口, ...
- mapbox.gl源码解析——基本架构与数据渲染流程
加载地图 Mapbox GL JS是一个JavaScript库,使用WebGL渲染交互式矢量瓦片地图和栅格瓦片地图.WebGL渲染意味着高性能,MapboxGL能够渲染大量的地图要素,拥有流畅的交互以 ...
- react16 渲染流程
前言 react升级到16之后,架构发生了比较大的变化,现在不看,以后怕是看不懂了,react源码看起来也很麻烦,也有很多不理解的地方. 大体看了一下渲染过程. react16架构的变化 react ...
- Cocos2dx开发之运行与渲染流程分析
学习Cocos2dx,我们都知道程序是由 AppDelegate 的方法 applicationDidFinishLaunching 开始,在其中做些必要的初始化,并创建运行第一个 CCScene 即 ...
随机推荐
- mongo-c-driver使用VS2013编译
1.下载mongo-c-driver源码文件 使用github来下载. git clone https://github.com/mongodb/mongo-c-driver.git 下载完之后,进入 ...
- ubuntu15.10安装搜狗拼音输入法
sudo vim /etc/apt/sources.list.d/ubuntukylin.list(我的默认显示没有这个文件将自动创建) 添加源deb http://archive.ubuntukyl ...
- iOS 向客户发送xcarchive文件(整理中)
由于安全的原因,客户不会把app的 distribution provisioning profile给开发者的,但是可以给一个development provisioning profile.这种情 ...
- VS2013无法连接到SqlServer的问题解决
在本机安装Vs2013后,安装Sqlserver2012数据库,在VS开发时,数据库一直查询不到 点击刷新后,看不见本机Sql服务器 最初检查防火墙设置,发现添加sqlservr.exe依然不起作用 ...
- QQ付费群规则重大变更!特别是这类群!
很多人都是知道现在QQ有个付费群功能.以往加群需要你填写一些验证信息给管理员,管理员看你顺眼就放你进去,不顺眼你就进不去了.另外还有一些打广告的人难免会被管理员误判给放进去,令管理员苦恼不已,总有审核 ...
- 编译带有PROJ4和GEOS模块的GDAL
1.下载三个软件的源代码(去各自官网下载即可) 2.将PROJ4和GEOS的源码放到GDAL目录下的supportlibs文件夹中. 3.修改GDAL的nmake.opt文件,部分内容如下: # Un ...
- thinkphp 杂项(3.2.3)
G('start');//时间描点start php代码.... G('end');//时间描点end echo G('start','end',4).'-----------'; 时间统计echo ...
- js通过location.search来获取页面传来的参数
这篇文章主要介绍了通过window.location.search来获取页面传来的参数,经测试是OK的 ? 1 2 3 4 5 function GetQueryString(name) { var ...
- 卸载 ibus 使Ubuntu16.04任务栏与启动器消失 问题解决
经查证是unity误卸载了,我使用了命令: sudo apt-get remove --purge ibus 解决方法是: 使用以下命令:重置compiz: dconf reset -f /org/c ...
- 【tomcat ecplise】新下载一个tomcat,无法成功启动,或者启动了无法访问localhost:8080页面/ecplise无法添加新的tomcat/ecplise启动tomcat启动不起来
今天转头使用ecplise,于是新下载一个tomcat7来作为服务器使用 但是问题来了: [问题1:全新的tomcat启动即消耗了不可思议的时间,并且启动了之前其他tomcat中的很多项目] [注意: ...