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

Posted smallboat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的渲染层级管理中了

 

优化:在一些共享材质的效果上会引起渲染混乱所以在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     }

 

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

以上是关于NGUI中穿插粒子或者mesh渲染层级的主要内容,如果未能解决你的问题,请参考以下文章

Unity NGUI UIPanel下对粒子或自定义Mesh的剪裁

Unity3d开发(十九)调整SortingOrder解决UGUI中粒子层级问题

Unity5 怎么把Mesh渲染成RenderTexture

realflow缓存模拟可以提高渲染速度吗

Unity NGUI UIPanel下对粒子的剪裁

[Shader]NGUI与粒子系统