NGUI中穿插粒子或者mesh渲染層級

  在項目中因爲特效的層級與NGUI UI的層級不太還規範,致使特效的渲染層級較爲雜亂因而就想把特效層級與NGUI的層級管理混合在一塊兒;canvas

  在修改以前首先要了解NGUI的層級管理以及DC的合併:緩存

   在NGUI中層級的管理以及Drawcall的合併都是由UIPanel這個組件來完成的;在NGUI中UIpanel就至關於UGUI中canvas和canvasrender,在UIpanel中會維護兩個隊列分別是UIWidget和UIDrawcall的隊列並按照深度排序;ide

每當該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;ui

以及一個虛函數OnFill;這些是對渲染來講是主要的屬性;this

  而將特效層級插入的思路則是給特效上添加一個widget組件並納入NGUI渲染排序中,這樣就能夠和討巧的去管理整個渲染順序:spa

首先新建一個繼承自UIWidget的UINGUIEffect組件:code

  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代碼就須要稍做需改:orm

//添加委託
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     }

 

這是一個較爲粗糙的作法,還有不少不足,若是有更好的作法還請留言相告謝謝!!!!好了回去擼代碼了

相關文章
相關標籤/搜索