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 值相同的物体渲染顺序是不确定的,因为它们会根据其他因素进行排序。
如图 3D摄像机
如图UI摄像机一般UI摄像机的Depth要大于3D摄像机的Depth,这样才能使UI在3D摄像机渲染的物体的前面显示。
摄像机的Clear Flags建议使用Depth only,然后在Culling Mask选择相应的层(Layer)。默认就有UI layer
如图,默认的UI层级
摄像机会根据Depth从小到大的顺序,渲染各自Culling Mask的层。
注意,在世界坐标下,物体A挡在物体B前面,但是只要渲染物体A的摄像机的Depth大于渲染物体B的摄像机的Depth,那么在Game视图中看到的效果就是物体B挡在物体A前面,如果物体A和物体B同在一个摄像机中渲染,那么正常情况下就是物体A挡住物体B(这里说正常情况下,是因为还可以通过下文的RenderQueue、SortingLayer、SortingOrder等的设置,让物体B挡在物体A前面)。
UGUI中,所有UI元素都必须在画布(Canvas)的子节点中。Canvas的Render Mode一般是使用Screen Space - Camera模式,把UICamera赋值给Canvas的Render Camera
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中设置的先后顺序)哪个先渲染。
为什么要有这个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调节渲染顺序
提到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的排序方式