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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 全面认识MOS管,一篇文章就够了

    全面认识MOS管,一篇文章就够了一篇文章,彻底掌握mos管!

  • 配置JAVA的环境变量「建议收藏」

    配置JAVA的环境变量「建议收藏」配置JAVA的环境变量为什么配置path?希望在命令行使用javac.exe等工具时,任意目录下都可以找到这个工具所在的目录。例如:我们在C:\Users\Irene目录下使用java命令,结果如下:我们在JDK的安装目录的bin目录下使用java命令,结果如下:我们不可能每次使用java.exe,javac.exe等工具的时候都进入到JDK的安装目录下,太麻烦了。我们希望在任意目录下都可以使用JDK的bin目录的开发工具,因此我们需要告诉操作系统去哪里找这些开发工具,这就需要配置path环境

  • 树莓派3B 系统安装及初始化配置教程[通俗易懂]

    树莓派3B 系统安装及初始化配置教程[通俗易懂]本文仅供学习交流使用,如侵立删!企鹅:1033383881相关软件下载链接SD卡格式化工具、系统烧录工具、Raspbian系统镜像https://pan.baidu.com/s/1o5j_uD31hxLsPP–GRZ4Bw提取码:9nhv1.烧录系统1.1SD卡格式化安装SD卡格式化工具,格式化SD卡1.2写入系统镜像至SD卡点击写入后会有个确认覆盖弹窗提示,YES即…

  • listbox里面添加WrapPanel ,支持自适应换行[通俗易懂]

    listbox里面添加WrapPanel ,支持自适应换行[通俗易懂]listbox大家都会用,如果要让它支持换行操作还必须加上ListBox.ItemsPanelItemsPanelTemplatetoolkit:WrapPanel//ItemsPanelTemplate/ListBox.ItemsPanel但是也有问题了,必须设置WrapPanel的宽度,也就是不能自适应宽度去调整每一行的宽度,这样的后果可能会出现要么全部推在一起,要么要有横向的滚动…

  • java的栈内存和堆内存_Java本地方法栈

    java的栈内存和堆内存_Java本地方法栈Java把内存分成两种,一种叫做栈内存,一种叫做堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。

  • 利用Python+阿里云实现DDNS(动态域名解析)

    利用Python+阿里云实现DDNS(动态域名解析)引子我想大家应该都很熟悉DNS了,这回在DNS前面加了一个D又变成了什么呢?这个D就是Dynamic(动态),也就是说,按照传统,一个域名所对应的IP地址应该是定死的,而使用了DDNS后,域名所对应的IP是可以动态变化的。那这个有什么用呢?比如,在家里的路由器上连着一个raspberrypi(树莓派),上面跑着几个网站,我应该如和在外网环境下访问网站、登陆树莓派的SSH呢?还有,家里…

发表回复

您的电子邮箱地址不会被公开。

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