Unity游戏开发中,模型、界面、特效等,需要规划好layer的概念,涉及到摄像机(Camera)、画布(Canvas)、Shader等相关内容。

在 Unity 中,渲染顺序是由多个因素共同决定的,大致分为三层优先级:Camera depth、Sorting Layer/Order in Layer 和 RenderQueue

一.Camera

一般游戏项目,会创建至少两个摄像机,一个3D摄像机(使用透视视角)和一个UI摄像机(使用正交视角)

在 Unity 中,每个 Camera 都有一个 depth 属性,用于控制该 Camera 在渲染管线中的渲染顺序。Camera depth 值越小,表示该 Camera 越优先被渲染,即它会在其他 depth 值较大的 Camera 之前被渲染。这个规则同样适用于渲染物体,即物体的 Camera depth 值越小,表示该物体越优先被渲染。需要注意的是,Camera depth 值相同的物体渲染顺序是不确定的,因为它们会根据其他因素进行排序。

Unity HybridCLR接入_UI


如图 3D摄像机

Unity HybridCLR接入_ui_02


如图UI摄像机一般UI摄像机的Depth要大于3D摄像机的Depth,这样才能使UI在3D摄像机渲染的物体的前面显示。

摄像机的Clear Flags建议使用Depth only,然后在Culling Mask选择相应的层(Layer)。默认就有UI layer

Unity HybridCLR接入_ui_03


如图,默认的UI层级

摄像机会根据Depth从小到大的顺序,渲染各自Culling Mask的层。

注意,在世界坐标下,物体A挡在物体B前面,但是只要渲染物体A的摄像机的Depth大于渲染物体B的摄像机的Depth,那么在Game视图中看到的效果就是物体B挡在物体A前面,如果物体A和物体B同在一个摄像机中渲染,那么正常情况下就是物体A挡住物体B(这里说正常情况下,是因为还可以通过下文的RenderQueue、SortingLayer、SortingOrder等的设置,让物体B挡在物体A前面)。

Unity HybridCLR接入_UI_04

UGUI中,所有UI元素都必须在画布(Canvas)的子节点中。Canvas的Render Mode一般是使用Screen Space - Camera模式,把UICamera赋值给Canvas的Render Camera

Unity HybridCLR接入_Unity HybridCLR接入_05


canvas默认是屏幕空间的2D对象,在屏幕空间时仅具有sort order属性,当把它设置为世界空间时,sort order属性消失了,变成另外两个属性: sorting layer, order in layer。这个时候,可以看到Canvas的面板中,出现了两个关键的属性:Sorting Layer、Order in Layer

添加Sorting Layer可以点击Inspector窗口的Tag -> AddTag -> SortingLayer当把Canvas设置为worldspace后,Canvas及Canvas上的UI对象在世界空间默认也是靠Z值来决定渲染顺序的,离相机远的先渲染。当设置了sorting layer后,渲染的次序就由sorting layer来确定了。sorting layer是自定义的标识符,哪个sortinglayer在前(在layer & tags中设置的先后顺序)哪个先渲染。

Unity HybridCLR接入_UI_06


为什么要有这个Sorting Layer呢,因为我们可以创建很多个Canvas,默认Sorting Layer是Default,这个时候,渲染顺序是根据Canvas的节点在Hierarchy窗口中的顺序来决定的,上层的先渲染,下层的后渲染。

而有时候,可能需要打破这个顺序,让上层节点的Canvas后渲染,这个时候,就可以设置这个Sorting Layer为高的值,当然,也可以保持相等,通过设置Order in Layer。(层内排序

order in layer是个数值,是在同一个sorting layer内的细分,sorting layer相同时order in layer的数值越小越先渲染。

屏幕空间的东西都处于UI层不需要sorting layer,只提供sort order(其实也就是order in layer)。 默认情况下sort oder都是0,此时UI物件按照在hierachy中出现的顺序决定渲染顺序。如果sort order不同时,值越小越先渲染。

UGUI会自动合并批次,原理是它会把一个Canvas下的所有元素合并在一个Mesh里,如果Canvas下的元素很多,任意一个元素发生位置、大小的改变,就需要重新合并所有元素的Mesh。如果元素非常多的话,就可能会造成卡顿。
一个比较好的做法是每个UI界面都设置成一个Canvas。如果这个界面下的元素比较多,可以考虑嵌套多几个Canvas。尤其是会频繁改变位置大小的元素,这样可以降低它们合并Mesh的开销。但是Canvas嵌套太多也不好,Mesh合并是降低了,但是DrawCall又上去了,因为每个Canvas都会单独占用一个DrawCall。

NGUI中的自动合批通常在Panel上执行。当你将多个相同材质(Material)的UI元素放置在同一个Panel中时,NGUI会尝试将它们合并为一个绘制调用,从而减少绘制操作的次数,提高渲染性能。这种合并绘制操作可以减少CPU和GPU的负载,因为绘制调用通常是较为耗费的操作之一,过多的合批可能会导致内存占用增加(WebGL慎用!!!!),因为合批后的绘制调用可能会生成更大的顶点和三角形数据。因此,在设计UI时,需要综合考虑性能和内存的平衡。

Order in Layer就是Sorting Layer的内部排序,这样配合Sorting Layer就是两级的排序,可以解决大部分情况的渲染顺序需求。
当然,如果创建多个UI摄像机,不同Canvas绑定不同的UI摄像机,再配合摄像机的Depth,就是三级排序,但一般不创建太多的UI摄像机。

比如挂在世界空间画布上的角色名字和用sprite实现的遮罩,通过调整sorting layer及order in layer就可以实现两者之间的先后

另外,ParticleSystem也有Sorting Layer和Order in Layer。所以我们通常会遇到 策划的

我需要把C粒子插在AB两个UI中间。这种情况,我们就需要把AB拆开,调节RenderQueue 不用要用粒子的Order in Layer(原因在总结时会提到)

RenderQueue调节渲染顺序

Unity HybridCLR接入_ui_07


提到RenderQueue就不得不提及unity内置的渲染队列了

Background(1000) 最早被渲染的物体的队列。

Geometry (2000) 不透明物体的渲染队列。大多数物体都应该使用该队列进行渲染,也是Unity Shader中默认的渲染队列。

AlphaTest (2450) 有透明通道,需要进行Alpha Test的物体的队列,比在Geomerty中更有效。

Transparent(3000) 半透物体的渲染队列。一般是不写深度的物体,Alpha Blend等的在该队列渲染。

Overlay (4000) 最后被渲染的物体的队列,一般是覆盖效果,比如镜头光晕,屏幕贴片之类的。
Unity中设置渲染队列也很简单,我们不需要手动创建,也不需要写任何脚本,只需要在shader中增加一个Tag就可以了,当然,如果不加,那么就是默认的渲染队列Geometry。比如我们需要我们的物体在Transparent这个渲染队列中进行渲染的话,就可以这样写:
{ “Queue” = “Transparent”}
我们可以直接在shader的Inspector面板上看到shader的渲染队列,甚至可以在代码里控制它RenderQueue调节渲染顺序

RenderQueue > 2500的物体绝对会在RenderQueue <= 2500的物体前面,即渲染时RenderQueue大的会挡住RenderQueue小的,不论它的Sorting Layer和Order in Layer怎么设置都是不起作用的。

当两个的RenderQueue都在同一队列时,在Sorting Layer高的绝对会在Sorting Layer前面,无视RenderQueue跟Order in Layer,只有在Sorting Layer相同的前提下,Order in Layer高的会在Order in Layer低的前面,无视RenderQueue(解释上文,并且今天犯了这个错误)。当Sorting Layer跟Order in Layer相同时,才看RenderQueue的高低,高的在前面。

后续我会更新SetRenderder的方法,在粒子与NGUI中,还有spine与NGUI的排序方式