NGUI渲染原理:UIDrawCall\UIPanel
目录:NGUI源码学习
UIDrawCall:所有的NGUI渲染都是通过UIDrawCall这个类实现的。
UIDrawCall的实现原理前面已经讲过了:NGUI Panel裁剪、层级实现原理
核心方法及属性:
UIDrawCall:矩形渲染,本身是new出来的GameObject,动态挂载了UIDraw、Mesh、Render等渲染组件。一个DC可以包含多个Widget,但一个Widget只能有一个DC。 property: verts/triangles/norms/tans/uvs/cols:Mesh渲染相关参数,顶点、三角形面、法线、切线、uv、颜色 isDirty:标记dc是否改变,是否需要更新 mRebuildMat:标记材质是否发生变化,true的话会调用RebuildMaterial重新生成材质球 depthStart/depthEnd:渲染层级 renderQueue/sortingOrder:get;set;材质球渲染层级 baseMaterial/dynamicMaterial/mainTexture/shader:渲染相关参数 mMesh/mFilter/mRenderer:渲染组件 function: CreateMaterial:创建mDynamicMat,会根据panel.clipping和mClipCount加载对应的shader并赋值给mDynamicMat RebuildMaterial/UpdateMaterials:重新生成/更新材质球,在UpdateGeometry填充完Mesh后调用 UpdateGeometry:核心方法,在UIPanel的FillAllDrawCalls、FillDrawCall调用,更新geometry,生成和设置mFilter、mMesh、mRenderer,设置mesh的顶点、uv、颜色、三角形面、法线、切线等 OnWillRenderObject:调用SetClipping更新mDynamicMat shader的_ClipRange、_ClipArgs属性,该属性用于裁剪 SetClipping:设置裁剪信息 Create:创建一个UIDrawCall,这边NGUI有做缓存池(mInactiveList缓存暂时不用的)。HideFlags.HideAndDontSave不在界面显示
这个类主要做以下几件事:
- 生成dc对象,需要先生成GameObject用于绑定。
static UIDrawCall Create (string name) { while (mInactiveList.size > 0) { UIDrawCall dc = mInactiveList.Pop(); if (dc != null) { mActiveList.Add(dc); if (name != null) dc.name = name; NGUITools.SetActive(dc.gameObject, true); return dc; } } GameObject go = new GameObject(name); DontDestroyOnLoad(go); UIDrawCall newDC = go.AddComponent<UIDrawCall>(); // Create the draw call mActiveList.Add(newDC); return newDC; }
- 把所属的widget的顶点、uv等信息填充到自己的verts/triangles/norms/tans/uvs/cols,在UIPanel的更新DC方法里调用,widgetsInDrawCall是当前DC的widget列表缓存。
foreach (var widget in widgetsInDrawCall) { if (generateNormals) { widget.WriteToBuffers(dc.verts, dc.uvs,dc.cols, dc.norms, dc.tans, generateUV2? dc.uv2:null); } else { widget.WriteToBuffers(dc.verts, dc.uvs,dc.cols, null, null, generateUV2? dc.uv2:null); } } widgetsInDrawCall.Clear();
- 填充Mesh显示所需要的顶点、颜色、uv、法线等信息,UpdateGeometry方法。
- 生成临时的材质球,设置材质球的贴图、shader等信息,CreateMaterial方法。
- 设置shader的裁剪属性,已实现先UIPanel的裁剪功能,OnWillRenderObject和SetClipping方法,最终裁剪的实现在shader里。
- 到这里,一个DC就可以在unity里显示出来了。
UIPanel:在lateUpdate轮询所属widget变化,更新DC的Mesh、层级、裁剪裁剪、坐标等信息。
核心属性/方法:
UIPanel:UIRect。 property: renderQueue/startingRenderQueue:渲染队列 widgets:panel管理的渲染对象 drawCalls:panel生成的渲染dc worldToLocal:世界坐标转成相对panel的本地坐标矩阵 drawCallClipRange:特殊裁剪区域,DC使用。new Vector4(finalClipRegion.x, finalClipRegion.y, finalClipRegion.z/2, finalClipRegion.w/2) mAlpha:透明度,会影响所有子对象 mDepth/mSortingOrder:渲染层级相关 mRebuild:是否需要重绘界面。需要重新设置所有的DC mResized:是否需要更新widget的可见性 nextUnusedDepth:获取当前已使用的最大depth+1,可以用于界面的depth动态管理 alpha:透明度,会直接影响子节点的透明度 mClipRange:显示范围,范围以外的widgit不显示。vector4(x,y,z,w),其中x/y是坐标,z/w是范围大小 clipCount:总裁剪次数,一个panel裁剪次数加1(父panel和自己本身) clipOffset:裁剪区域偏移,通过值偏移实现Scroll views,避免了移动造成的界面重建 finalClipRegion:Vector4(mClipRange.x + mClipOffset.x, mClipRange.y + mClipOffset.y, mClipRange.z, mClipRange.w),x,y是矩形中心点坐标 clipSoftness:裁剪时的渐隐特效范围大小(例如y=4代表从顶部往下、底部往上4个像素渐变) worldCorners/localCorners:裁剪区域(矩形)四个顶点的坐标 function: CalculateFinalAlpha:获取panel的最终透明度,该透明根据当前panel的alpha*所有父panel的alpha(panel的多层嵌套) SetRect:设置裁剪区域mClipRange IsVisible:判断某个widget或position是否在裁剪范围内 RebuildAllDrawCalls:设置mRebuild = true,重绘整个panel GetViewSize:获取界面显示范围,即裁剪区域 OnDisable:隐藏界面会删除UIPanel所属的所有的UIDrawCall,在下次LateUpdate时需要重新生成,LateUpdate在对象Active时每帧调用 LateUpdate:更新界面,调用顺序UIPanel.UpdateSelf-UIPanel.UpdateDrawCalls,先更新dc的渲染信息,在更新DC坐标 UpdateTransformMatrix:设置当前更新帧,设置Panel的显示范围mMin/mMax UpdateWidgets:调用child widget的UpdateTransform/UpdateVisibility/UpdateGeometry进行更新 UpdateSelf:更新所有widget和DrawCall,更新UIScrollView.调用UpdateWidgets/FillAllDrawCalls. FillAllDrawCalls:删除当前所有的DC对象重新生成dc,性能很差。 核心方法 1.根据depth排序widget。2.若widget可见,加入dc。3.如果当前widget的material、mainTexture、shader和上一个相等,共用一个dc。4.把widget的geometry内的顶点、纹理、颜色等信息写入dc 5.调用dc.UpdateGeometry更新DC渲染 FillDrawCall:更新单个dc,只要dc.verts.Count != 0,也就是说当前dc有顶点需要渲染,调用dc.UpdateGeometry)更新Mesh信息 FindDrawCall:将某个widget加入到已存在的material、mainTexture、shader相等的dc内 AddWidget:添加指定widget到当前panel中 RemoveWidget:移除指定widget,如果widget的(depth == w.drawCall.depthStart || depth == w.drawCall.depthEnd),会触发整个panel重建 UpdateDrawCalls:更新drawCallClipRange(UIDrawcall裁剪区域)、所有drawCalls的坐标、旋转、缩放、renderQueue、sortingOrder等属性
lateUpdate更新流程图:
核心方法就是FillAllDrawCalls,负责DC的生成(合批也在这里处理)。
FillDrawCall更新单个的DC。
一直想把之前工作、学习时记录的文档整理到博客上,一方面温故而知新,一方面和大家一起学习 -程序小白