NGUI中穿插粒子或者mesh渲染层级

NGUI中穿插粒子或者mesh渲染层级

大家好,又见面了,我是全栈君。

  在项目中由于特效的层级与NGUI UI的层级不太还规范,导致特效的渲染层级较为杂乱于是就想把特效层级与NGUI的层级管理混合在一起;

  在修改之前首先要了解NGUI的层级管理以及DC的合并:

   在NGUI中层级的管理以及Drawcall的合并都是由UIPanel这个组件来完成的;在NGUI中UIpanel就相当于UGUI中canvas和canvasrender,在UIpanel中会维护两个队列分别是UIWidget和UIDrawcall的队列并按照深度排序;

每当该UIPanel下有UIWidget的信息改变例如可见性或者是大小等改变的时候这个UIpanel就会刷新该panel的所有UIWidget并重新生成UIdrawcall进行合并;而说到合并Drawcall在NGUI中是按照就近合并的原则:当下一个Drawcall(即UIWidget组件的派生组件如UIsprite等的材质,贴图和shader)和上一个完全一样的时候则合并两个Drawcall;在生成并排序合并之后就开始按照既定的顺序开始指定每个Drawcall的渲染层级,而对于同一个Drawcall的不同元素则通过shader进行相对排序显示。基本流程讲完了上代码:

 1 void LateUpdate ()
 2     {
 3 #if UNITY_EDITOR
 4         if (mUpdateFrame != Time.frameCount || !Application.isPlaying)
 5 #else
 6         if (mUpdateFrame != Time.frameCount)
 7 #endif
 8         {
 9             mUpdateFrame = Time.frameCount;
10 
11             // Update each panel in order
12             for (int i = 0, imax = list.Count; i < imax; ++i)
13                 list[i].UpdateSelf();
14 // updateself开始刷新widget并排序
15             int rq = 3000;
16 
17             // Update all draw calls, making them draw in the right order
18             for (int i = 0, imax = list.Count; i < imax; ++i)
19             {
20                 UIPanel p = list[i];
21 
22                 if (p.renderQueue == RenderQueue.Automatic)
23                 {
24 // 循环分配渲染层级
25                     p.startingRenderQueue = rq;
26                     p.UpdateDrawCalls();
27                     rq += p.drawCalls.Count;
28                 }
29                 else if (p.renderQueue == RenderQueue.StartAt)
30                 {
31 // 循环分配渲染层级
32                     p.UpdateDrawCalls();
33                     if (p.drawCalls.Count != 0)
34                         rq = Mathf.Max(rq, p.startingRenderQueue + p.drawCalls.Count);
35                 }
36                 else // Explicit
37                 {
38 // 循环分配渲染层级
39                     p.UpdateDrawCalls();
40                     if (p.drawCalls.Count != 0)
41                         rq = Mathf.Max(rq, p.startingRenderQueue + 1);
42                 }
43             }
44         }
45     }
46 void UpdateSelf ()
47     {
48         mUpdateTime = RealTime.time;
49 
50         UpdateTransformMatrix();
51         UpdateLayers();
52 // 更新widget并排序
53         UpdateWidgets();
54 
55         if (mRebuild)
56         {
57             mRebuild = false;
58 // 重新绘制合并排序DC
59             FillAllDrawCalls();
60         }
61         else
62         {
63 // 移除不可用dc
64             for (int i = 0; i < drawCalls.Count; )
65             {
66                 UIDrawCall dc = drawCalls[i];
67 
68                 if (dc.isDirty && !FillDrawCall(dc))
69                 {
70                     UIDrawCall.Destroy(dc);
71                     drawCalls.RemoveAt(i);
72                     continue;
73                 }
74                 ++i;
75             }
76         }
77 
78         if (mUpdateScroll)
79         {
80             mUpdateScroll = false;
81             UIScrollView sv = GetComponent<UIScrollView>();
82             if (sv != null) sv.UpdateScrollbars();
83         }
84     }

 

 1     void FillAllDrawCalls ()
 2     {
 3         for (int i = 0; i < drawCalls.Count; ++i)
 4             UIDrawCall.Destroy(drawCalls[i]);
 5         drawCalls.Clear();
 6     
 7         Material mat = null;
 8         Texture tex = null;
 9         Shader sdr = null;
10         UIDrawCall dc = null;
11      // widget排序
12         if (mSortWidgets) SortWidgets();
13      // 根据既定顺序开始生成Drawcall并合并
14         for (int i = 0; i < widgets.Count; ++i)
15         {
16             UIWidget w = widgets[i];
17 
18             if (w.isVisible && w.hasVertices)
19             {
20                 Material mt = w.material;
21                 Texture tx = w.mainTexture;
22                 Shader sd = w.shader;
23           
24                 if (mat != mt || tex != tx || sdr != sd)
25                 {
    
    
             // 跟上一个不同重新生成DC
26 if (dc != null && dc.verts.size != 0) 27 { 28 drawCalls.Add(dc); 29 dc.UpdateGeometry(); 30 dc = null; 31 } 32 33 mat = mt; 34 tex = tx; 35 sdr = sd; 36 } 37 38 if (mat != null || sdr != null || tex != null) 39 { 40 if (dc == null) 41 {
                // 生成dc
42 dc = UIDrawCall.Create(this, mat, tex, sdr); 43 dc.depthStart = w.depth; 44 dc.depthEnd = dc.depthStart; 45 dc.panel = this; 46 47 } 48 else 49 {
                // 合并dc
50 int rd = w.depth; 51 if (rd < dc.depthStart) dc.depthStart = rd; 52 if (rd > dc.depthEnd) dc.depthEnd = rd; 53 } 54 55 w.drawCall = dc; 56 57 if (generateNormals) w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, dc.norms, dc.tans); 58 else w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, null, null); 59 } 60 } 61 else w.drawCall = null; 62 } 63 64 if (dc != null && dc.verts.size != 0) 65 { 66 drawCalls.Add(dc);
         // 绘制dc
67 dc.UpdateGeometry(); 68 } 69 }

 

在NGUI中所有的UI组件都是继承自UIwidget这个容器;在UIwidget组件中可以看见他维护了这样几个属性:材质,贴图,以及shader,还有该容器所属的UIpanel和该容器的Drawcall;

  

 1 public UIPanel panel;
 2 public UIDrawCall drawCall;
 3 
 4 public virtual Material material
 5     {
 6         get
 7         {
 8             return null;
 9         }
10         set
11         {
12             throw new System.NotImplementedException(GetType() + " has no material setter");
13         }
14     }
15 
16 public virtual Texture mainTexture
17     {
18         get
19         {
20             Material mat = material;
21             return (mat != null) ? mat.mainTexture : null;
22         }
23         set
24         {
25             throw new System.NotImplementedException(GetType() + " has no mainTexture setter");
26         }
27     }
28 
29 public virtual Shader shader
30     {
31         get
32         {
33             Material mat = material;
34             return (mat != null) ? mat.shader : null;
35         }
36         set
37         {
38             throw new System.NotImplementedException(GetType() + " has no shader setter");
39         }
40     }
41 
42 virtual public void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
43     {
44         // Call this in your derived classes:
45         //if (onPostFill != null)
46         //    onPostFill(this, verts.size, verts, uvs, cols);
47     }

材质,贴图,以及shader,还有该容器所属的UIpanel和该容器的Drawcall;

以及一个虚函数OnFill;这些是对渲染来说是主要的属性;

  而将特效层级插入的思路则是给特效上添加一个widget组件并归入NGUI渲染排序中,这样就可以和讨巧的去管理整个渲染顺序:

首先新建一个继承自UIWidget的UINGUIEffect组件:

  1 using UnityEngine;
  2 using System.Collections;
  3 
  4 // 该组件必须有渲染组件
  5 [RequireComponent(typeof(Renderer))]
  6 public class UINGUIEffect : UIWidget {
  7     // 维护材质 贴图 以及 shader 
  8     private Material mMaterial;
  9     private Texture mMainTexture;
 10     private Shader mShader;
 11     private Renderer mRender;
 12 
 13     // 重写
 14     public override Material material
 15     {
 16         get
 17         {
 18             return mMaterial;
 19         }
 20 
 21         set
 22         {
 23             mMaterial = value;
 24         }
 25     }
 26 
 27     public override Shader shader
 28     {
 29         get
 30         {
 31             return mShader;
 32         }
 33 
 34         set
 35         {
 36             mShader = value;
 37         }
 38     }
 39 
 40     public override Texture mainTexture
 41     {
 42         get
 43         {
 44             return mMainTexture;
 45         }
 46 
 47         set
 48         {
 49             mMainTexture = value;
 50         }
 51     }
 52 
 53     protected override void Awake()
 54     {
 55         if (GetComponent<Renderer>())
 56         {
 57             mRender = GetComponent<Renderer>();
 58             mMaterial = mRender.sharedMaterial;
 59             mMainTexture = mRender.sharedMaterial.mainTexture;
 60             mShader = mRender.sharedMaterial.shader;
 61         }
 62         // 这里缓存设置Drawcall渲染层级时的回调
 63         onRenderQueueChanged = OnRQChanged;
 64         base.Awake();
 65     }
 66 
 67     void OnRQChanged(int rq)
 68     {
 69         // 回调指定该渲染层级
 70         GetComponent<Renderer>().sharedMaterial.renderQueue = rq;
 71      
 72     }
 73 
 74     protected override void OnInit()
 75     {
 76         base.OnInit();
 77     }
 78 
 79     protected override void OnStart()
 80     {
 81         base.OnStart();
 82     }
 83 
 84     protected override void OnEnable()
 85     {
 86         base.OnEnable();
 87     }
 88 
 89     protected override void OnDisable()
 90     {
 91         base.OnDisable();
 92     }
 93 
 94     protected override void OnUpdate()
 95     {
 96         base.OnUpdate();
 97     }
 98 
 99     public override void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
100     {
101        // 创建一个空四边形占据一个dc;如过这里是空的话该组件就不会生成dc所以必须有一个;
102         verts.Add(new Vector3(1, 0, 1));
103         verts.Add(new Vector3(1, 0, -1));
104         verts.Add(new Vector3(-1, 0, 1));
105         verts.Add(new Vector3(-1, 0, -1));
106 
107         uvs.Add(new Vector2(1, 1));
108         uvs.Add(new Vector2(1, 0));
109         uvs.Add(new Vector2(0, 1));
110         uvs.Add(new Vector2(0, 0));
111 
112         cols.Add(new Color32(255,255,255, 255));
113         cols.Add(new Color32(255, 255, 255, 255));
114         cols.Add(new Color32(255, 255, 255, 255));
115         cols.Add(new Color32(255, 255, 255, 255));
116 
117         base.OnFill(verts, uvs, cols);
118         if (onPostFill != null)
119             onPostFill(this, verts.size, verts,uvs,cols);
120     }
121 }

因此NGUi代码就需要稍作需改:

//添加委托
public delegate void OnRenderQueueChanged(int renderQueue);

//在UIWidget 以及UIdrawcall中增加委托

public OnRenderQueueChanged onRenderQueueChanged;

//在UIPanel中FillAllDrawCalls方法中生成Drawcall时将widget维护的委托赋给它的dc

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;
// 赋值委托
                        dc.onRenderQueueChanged = w.onRenderQueueChanged;
                    }

//最后在UIdrawcall设置renderQueque时调用委托设置特效层级

    public int renderQueue
    {
        get
        {
            return mRenderQueue;
        }
        set
        {
            if (mRenderQueue != value)
            {
                mRenderQueue = value;
                // 调用回调设置特效层级
                if (onRenderQueueChanged != null)
                    onRenderQueueChanged(mRenderQueue);

                if (mDynamicMat != null)
                {
                    mDynamicMat.renderQueue = value;
#if UNITY_EDITOR
                    if (mRenderer != null) mRenderer.enabled = isActive;
#endif
                }
            }
        }
    }

这样特效的层级就可以归入NGUI的渲染层级管理中了

NGUI中穿插粒子或者mesh渲染层级

NGUI中穿插粒子或者mesh渲染层级

 

优化:在一些共享材质的效果上会引起渲染混乱所以在Awake的时候复制一个材质避免影响其他界面显示:

 1  protected override void Awake()
 2     {
 3         if (GetComponent<Renderer>())
 4         {
 5             mRender = GetComponent<Renderer>();
 6             mMaterial = new Material(mRender.sharedMaterial) ;
 7             GetComponent<Renderer>().sharedMaterial = mMaterial;
 8             mMainTexture = mMaterial.mainTexture;
 9             mShader = mMaterial.shader;
10         }
11         onRenderQueueChanged = OnRQChanged;
12         base.Awake();
13     }
14 
15     void OnRQChanged(int rq)
16     {
17         if (!gameObject.activeSelf) return;
18         mMaterial.renderQueue = 0;
19         mMaterial.renderQueue = rq;
20     }

 

这是一个较为粗糙的做法,还有很多不足,如果有更好的做法还请留言相告谢谢!!!!好了回去撸代码了

转载于:https://www.cnblogs.com/smallboat/p/5999784.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108807.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • telnet不是内部或外部命令怎么办[通俗易懂]

    telnet不是内部或外部命令怎么办[通俗易懂]telnet不是内部或外部命令怎么办命令提示符下执行telnet,提示:’telnet’不是内部或外部命令,也不是可运行的程序或批处理文件。Win7,Win10系统环境下,Telnet客户端默认是关闭状态。下面我们来看看如何打开Telnet客户端?打开【控制面板】。单击“程序”;单击“打开或关闭Windows功能”;勾选“Telnet客户端”,单击“确定”。打开Telnet客户端;无需重启,再次运行Telnet,成功进入MicrosoftTelnet

    2022年9月23日
    0
  • docker 权限问题 Got permission denied while trying to connect to the Docker daemon socket at 。。。「建议收藏」

    docker 权限问题 Got permission denied while trying to connect to the Docker daemon socket at 。。。「建议收藏」在用户权限下docker命令需要sudo否则出现以下问题通过将用户添加到docker用户组可以将sudo去掉,命令如下sudogroupadddocker#添加docker用户组sudogpasswd-a$USERdocker#将登陆用户加入到docker用户组中newgrpdocker#更新用户组…

    2022年5月13日
    43
  • tkMapper的使用-超详细

    tkMapper的使用-超详细tkMapper已经完成了对单表的通用操作的封装,主要封装在Mapper接口和MysqlMapper接口中,因此我们如果要完成对单表的操作,只需要自定义dao接口继承这两个接口即可。增加方法在准备工作中已经完成,如果想了解此部分内容,可以向上进行查看,此处主要是添加功能的另一种实现————主键回填。注意在进行主键回填的时候,实体类中id必须要用@Id指定一下,要不然映射的时候找不到id;过程如下创建一个users对象,对象的id是需要修改的用户的id,其他信息是需要更改后的信息。…

    2022年10月6日
    0
  • 用python实现祝福弹窗

    用python实现祝福弹窗

    2021年9月17日
    182
  • struts2拦截器和aop拦截器_自定义拦截器

    struts2拦截器和aop拦截器_自定义拦截器拦截器拦截器Filter是Struts2的核心。Struts2的拦截器与Servlet中的过滤器相似。在执行Action的execute()方法之前,Struts2先执行struts.xml中引用的拦截器,在执行完所有引用的拦载器的doIntercept()方法后,会执行Action的execute()方法。在Struts2的拦截器体系中,Struts2的内建拦截器完成了该框架的大部分操作,…

    2022年9月26日
    0
  • 数据类型

    一:什么是数据在接触数据之前我们已经知道了变量,那么什么是变量呢又该如何声明变量呢声明变量name="王妃"在这个变量中共包含三个部分:在这个示例中变量的值“王妃”

    2022年3月29日
    33

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号