关于Unity在2D的UGUI和3D模型交错时渲染顺序的一点探讨

0.前言

写下此文,主要是因为近来学习了一点UGUI的知识,便想着做个小玩意儿作为“毕设”,想做的内容很简单,就是实现在按钮上插入一个3D模型(最近很火的手游《王者荣耀》上就有类似的设计),没想到在渲染顺序的问题上栽了跟斗,一番搜索过后,发现居然打开了新世界的大门。文末附有本文实验用的工程。

1.Unity如何控制渲染顺序

综合文章[1]和[2],笔者认为影响渲染顺序的因素有一下四个:Camera Depth,Sorting Layer,Order In Layer,RenderQueue。关于这四个因素的排序规则,文章[1]给出了以下结论:

1、Camera Depth永远最高。Camera Depth小的一定先进渲染管线。 2、当Sorting Layer和Order In
Layer相同时,RenderQueue小的先进渲染管线。 3、当Sorting Layer和Order In Layer不相同时:
3.1 当两个材质使用了不同的RenderQueue,且这两个RenderQueue都在[0~2500]或[2501~5000]时,SortingLayer和OrderInLayer的排序生效。
3.2 当两个材质使用了不同的RenderQueue,且这两个RenderQueue分别在[0~2500]和[2501~5000]时,则一定会按照RenderQueue绘制,无视SortingLayer、OrderInLayer的排序。
4、MehRenderer的sortingLayer与Canvas的sortingLayer同级;MehRenderer的sortingOrder与Canvas的OrderInLayer同级

以上结论,笔者经过实验(工程中/Assets/TestOrder/TestOrder1场景),于文[1]是一致的,这里文[1]的解释是2500是非透明物体与透明物体渲染队列的分界点(关于RenderQueue可以参考[3]),所以当渲染的sprite同为非透明物体或者同为透明物体时,是根据其SortingLayer、OrderInLayer来排序。
然而,此结论对于UGUI而言,又有所不同。对于UGUI,属于同一个Canvas的控件,其SortingLayer、OrderInLayer都是相同的。根据上面的结论,RenderQueue应该是可以控制控件的渲染顺序,但笔者实验发现(工程中/Assets/TestOrder/TestOrder2场景),通过给控件挂上自定义的material并改变其RenderQueue并不能调整控件的层级关系,真正决定控件渲染顺序的只有控件与其兄弟节点在parent中的相对顺序如图1所示。

关于Unity在2D的UGUI和3D模型交错时渲染顺序的一点探讨_第1张图片
关于Unity在2D的UGUI和3D模型交错时渲染顺序的一点探讨_第2张图片
Front
关于Unity在2D的UGUI和3D模型交错时渲染顺序的一点探讨_第3张图片
关于Unity在2D的UGUI和3D模型交错时渲染顺序的一点探讨_第4张图片
BG
图1 UGUI同一个Canvas下改变控件的RenderQueue
Front的RenderQueue是2504,BG的RenderQueue是2603,然而Front还是在前面。结论:对于UGUI上的控件(如image),其Material的RenderQueue并没有什么用,排序规则仍然是与其它使用UGUI默认material的控件按照节点顺序排序

2.如何在一个UGUI的控件中插入一个3D模型

在2D的UI里面插入一个3D模型的做法有很多种,笔者见过的一个做法是,先将控件的Rect转换到Screen Space,然后将其在Screen Space中所占的比例(如图2)传给3D模型的PS,在PS中利用四个边界做culling。
鉴于此种做法需要一些让人头疼的计算,笔者决定偷懒,借助Stencil Buffer来实现,大致的思路是先在Stencil Buffer上把3D模型要展示的区域标记出来,然后再画3D模型,没有标记的区域就不绘制,这样就可以保证挂在某个控件上的模型不会穿到其它控件上面去。
最终的效果如图3所示,图中用一个Cube加一个Sphere作为模型的实例,按钮是蓝色区域,可以看到蓝色区域外是不会显示模型的。具体的实现包含在工程中(工程中/Assets/ ModelOnButton / ModelOnButton场景)。
关于Unity在2D的UGUI和3D模型交错时渲染顺序的一点探讨_第5张图片
图3模型负载UGUI的按钮上

此处有坑点,上面说到,要保证模型顺利绘制,我们需要先在待绘制区域用Stencil Buffer标记,那么要保证先标记,就需要保证可以先在该区域先渲染一些东西,可以是mesh,但最好是一个UGUI的控件(如image),因为这样可以利用UGUI上一些布局控制,做到与Button的区域完全匹配,而不用在3D控件中把一个mesh对齐到一个2D控件(可以规避一系列烦人的数学运算)。
笔者的第一版是简单的修改了在Stencil Buffer上坐标的shader的RenderQueue,使其小于模型shader的RenderQueue,这在默认情况下(Canvas的SortingOrder=0)是可以正常运作的,但是当Canvas的SortingOrder>0时,模型就看不到了。几番探索,最终通过在3D模型上挂一个Script,用来将模型下所有Renderer的SortingOrder都改为比Canvas大1解决了此问题。此处与文[1]中结论的冲突在于,UGUI的默认shader应该是Transparent(3000),而此处笔者用于绘制3D模型的shader是Geometry(2000),根据文[1],3D模型应该先绘制,而实际上是UI先绘制了(不然模型不可能显示),而文[3]中也有说明Geometry会先于Transparent。关于这个问题,笔者至今不解,unity关于这方面的资料不是很充分,查起来十分费劲。好在此次的“毕设”算是完成了,可以先告一段落,后续若有新的发现再作补充,另外,也希望知道此问题答案的大神不吝赐教,感激不尽。

参考文献:
[1] http://blog.csdn.net/u011748727/article/details/68947207
[2] http://www.cnblogs.com/LeonLazuli/p/3961283.html
[3] http://blog.csdn.net/candycat1992/article/details/37345251

你可能感兴趣的:(Unity联网小游戏)