• NGUI渲染原理:UIDrawCallUIPanel


    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不在界面显示
    这个类主要做以下几件事:
    1. 生成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;
       }
    1. 把所属的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();
    1. 填充Mesh显示所需要的顶点、颜色、uv、法线等信息,UpdateGeometry方法。
    1. 生成临时的材质球,设置材质球的贴图、shader等信息,CreateMaterial方法。
    1. 设置shader的裁剪属性,已实现先UIPanel的裁剪功能,OnWillRenderObject和SetClipping方法,最终裁剪的实现在shader里。
    1. 到这里,一个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。
  • 相关阅读:
    ubuntu 下常用的命令(仅做记录)
    mysql5.7二进制包安装方法
    mysql5.6编译安装方法
    将yum下载的安装包保存到本地
    bash-completion--好用又强力的bash补全工具
    shell--破解RANDOM随机数
    shell--使用for循环统计一个网段内的在线主机
    shell--写一个脚本,批量创建10个用户用户名为userAdd1-10,并给他们随机密码
    shell--使用for循环批量创建10个随机小写字字母加固定字符的.txt文件,并写另一个脚本批量修改为.html文件
    如何选择开源许可证
  • 原文地址:https://www.cnblogs.com/wang-jin-fu/p/13509076.html
Copyright © 2020-2023  润新知