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的渲染层级管理中了

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

时间: 2024-08-10 21:28:36

NGUI中穿插粒子或者mesh渲染层级的相关文章

Unity NGUI 中特效(粒子)的显示

NGUI添加粒子特效时,被UI遮挡 因为粒子系统的渲染顺序列默认为3000,而NGUI的渲染顺序默认也是从3000开始,当有嵌套的panel时或者Depth更高的panel时,GUI的渲染顺序会高于3000, 解决办法是: 1.修改Ngui中的UIPanel脚本中的默认的RenderQueue, 调整到3000以下,这样就不会遮挡住粒子特效了,当有的窗口需要显示在特效上面时,在检视面板中把该窗口的Renderer Q选项调整为Start At,值为3000以上,就可以解决 2.使用另外一个摄像机

Unity中的粒子特效的 RendererQ 排序

这里接https://www.cnblogs.com/luguoshuai/p/10021660.html 这里介绍两套粒子排序的方法. 首先声明,这两套排序方法均不是本人所写,是在项目开发的过程当中,看到同事的设计,然后我就记录了下来了,作为后续的学习与使用. 方法一: 1 using System.Collections.Generic; 2 using UnityEngine; 3 4 public class UIRenderQueueSorter : MonoBehaviour 5 {

Unity3D在NGUI中使用mask

过程是这样的:最近一直想做一个头像的mask效果,后来发现原来unity的mask需要用shader来写,网上找了不少资料,也能实现,不过大多数都是用render texture作为相机投影的texture.然后把这个相机的图像作为一个material,然后在ngui中创建一个texture,把这个material赋给texture.怎么说呢,这种方式虽然也能实现,但是必须要创建一个相机,对于一个大型项目来说有点不切实际.一个头像加个mask多简单的一件事,肯定有更好的办法. 最终还是找到了一种

UGUI中显示粒子特效

今天在UGUI上显示粒子特效的时候遇到的一些问题,Mark一下.原理:修改特效中每一个ParticleSystem的Layer为UI,并且把ParticleSystemRenderer.sortingOrder值设置为大于Canvas中的sortingOrder值.其实就是控制两个组件 的Render Order. 1. 在UI控件上(eg: Image)显示一个粒子特效 1).首先创建一个Canvas,RenderMode = Screen Space-Camera ,然后创建一个UICame

解决vs解决方案中的类文件没有正常层级显示的问题

VS2013在使用svn进行文件的下载时,因为某些原因,更新完后,功能节点类文件在解决方案列表中并没有正常的按层级显示,而是显示在了同一层次上.造成这样的原因,无可追溯,但是解决这个问题的办法则简单可循. 在VS2013中建立项目时,VS会自动产生 .csproj 文件,这是C#的工程文件,其中记录了与工程有关的相关信息,例如包含的文件,程序的版本,所生成的文件的类型和位置的信息等. 打开工程文件,发现没有正常显示的类文件信息有缺失,于是按照正常显示的类的文件信息,比照补充后,保存.重新加载项目

将Unity中的世界坐标转换成NGUI中的坐标

将Unity中的世界坐标转换成NGUI中的坐标,比如可用于自制血条等.代码如下: 1 using UnityEngine; 2 using System.Collections; 3 public class Healthbar : MonoBehaviour { 4 public GameObject TargetObject; //目标物体.这里是指Cube 5 public Camera worldcamera; //世界相机. 6 public Camera guiCamera; //U

NGUI中Button与原生2D精灵的混合使用

一些废话 每一篇的首段都是这个“一些废话”,原因是我太能逼逼了,不逼逼一些废话我就觉得难受.这是我第四篇关于Unity的博文,前两篇还是去年写的,“从一点儿不会开始”系列,类似教程和学习笔记的博文,这个系列还会继续的,我没有太监...如果真的有朋友觉得能因此得到一点点的帮助,那就太太开心了. 最近在(shan)做(zhai)一个小游戏,UI刚开始用的是原生2D,用着用着发现NGUI似乎更省事儿(“为什么刚开始你不用NGUI?”“我擦我刚开始那会儿还不会好么...”),然后之前原生2D写的又不想完

CSharpGL(26)在opengl中实现控件布局/渲染文字

CSharpGL(26)在opengl中实现控件布局/渲染文字 效果图 如图所示,可以将文字.坐标轴固定在窗口的一角. 下载 CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL) UI控件布局关键点 ILayout 类似Winform控件那样,控件的位置.大小由其Anchor等属性决定.窗口大小改变时,控件的位置.大小会随之改变. 所以模仿Control类,直接使用Anchor作为UIRendere

NGUI中TweenScale回调函数设为null时导致的BUG

今天使用NGUI的TweenScale时遇到一个问题 使用时TweenScale是用两次,playForward()和playReverse(),要求playForward()后啥都不做,playReverse()后执行函数 由于动画播放需要时间,不可能执行完playReverse()代码就执行指定函数 NGUI提供了对应的委托处理,所以思路上是在playForward()时将onFinished置为null,playReverse()时添加指定方法 对应代码 EventDelegate.Cal