调UI遮挡关系有三种思路:
- 调Sorting Layer,层级越后渲染到越前面
- 调Order in Layer,数字越大渲染到越前面
- 修改UI材质调RenderQueue,数字越大越后渲染
对前两种比较陌生的同学可以看一下我以前写的这篇,不看也没事,后面会解释Unity Canvas的sorting Layer作用_unity sortinglayer-CSDN博客
第三种方法的话,就是通过给UI材质的方式,把材质的Shader改成渲染顺序在后面,并且关闭深度测试(被别的遮挡),以此来实现始终渲染在最前方。但是这个方法更多的适用于你只需要保证一个UI显示在最前方,而不在意其他UI会不会被它影响的极端情况。
另外如果使用第三种方法的UI(暂时称为A)上面有文本B的话,文本B也需要调整材质为Overlay,使其渲染顺序大于A
同理,如果需要一个UI永远渲染在最后方,可以添加关闭深度写入(遮挡别的)且Hierarchy靠前的材质。
方法看起来很简单,但是项目大了为了满足各种需求难免会出现方法混用的情况,这时候这三种顺序控制的方式很容易搅在一起形成非常古怪的效果 ,因此本文在拷打GPT后深度解析了unity的UGUI渲染顺序。
Unity 在渲染 UGUI 时:
- 收集所有 Canvas → 按 sortingLayer & sortingOrder 排序
- 对每个 Canvas 内的元素进行 Batching(合批)
- 再生成绘制命令 → 提交到 GPU
因为 Canvas 本身是作为渲染批次的边界,所以先后顺序受 Canvas 排序控制。另外在overlay以外的情况下UGUI(Canvas Renderer)都参与深度测试(被别的遮挡)写入深度缓冲(ZWrite Off)(遮挡别的)
CPU层管理渲染顺序:
(1)Canvas 自身的顺序
-
每个 Canvas 都有:
-
sortingLayer
(图层) -
sortingOrder
(层内顺序)
-
-
排序先按
sortingLayer
(后画的图层覆盖前画的) -
再按
sortingOrder
(数字大的后画)
如果两个 UI 元素在不同 Canvas上,那么:
-
sortingLayer 顺序决定谁先画
-
如果 sortingLayer 相同,sortingOrder 数值大的后画 → 后画就会盖住前画
(2)同一个 Canvas 内部:
-
按 Hierarchy 层级顺序(从上到下,越下越后渲染)
-
添加SortingGroup排序如果它们被不同的 SortingGroup 包起来,且每个 SortingGroup 设置了不同的 sortingOrder: Unity 会把每个 SortingGroup 当成单独的渲染批次(draw call);不同组之间无法合批(在
World Space
或Screen Space - Camera
+Override Sorting
时,SortingGroup 才真正控制排序)
(3)Render Queue 在 UI 下的角色
UGUI 的渲染系统会给它生成的材质(批次)加上默认的 Render Queue:
-
通常在
Overlay
(4000)队列(测试发现WorldSpace下是3000) -
这个 Render Queue 是系统根据 Canvas 类型决定的
-
如果你手动改了 Shader 的 Queue,只能在 Canvas 内部影响先后顺序,但无法跨 Canvas 打破 sortingOrder 约束(因为需要合批)
也就是说:
-
不同 Canvas:优先用 sortingLayer/sortingOrder 控制
-
同一 Canvas 内的材质:sortingGroup管理内部合批,Render Queue 和材质顺序也会影响
GPU层像素级别决定是否渲染:
因为光是谁前谁后渲染在三维空间下是无法判断出像下面这样重叠态时怎么渲染的,是白色全渲染还是红色全渲染?这时候就需要深度测试
图形渲染流水线大体是按CPU给的顺序分批次逐像素处理
顶点着色器 → 裁剪 → 光栅化 → 片元着色器 → 深度/模板测试 → 合成到帧缓冲
具体:
- 顶点着色器:计算模型顶点的坐标
- 裁剪:裁掉视锥外的部分
- 光栅化:把三角形拆成像素(片元)
- 片元着色器:对每个片元计算最终颜色,此时得到了一个片元:位置(Z)、颜色、透明度等信息
- 深度测试 & 模板测试(Depth & Stencil Test):对比当前片元的 Z 值和深度缓冲区已有值,根据 ZTest 设置(比如 Less)决定要不要丢弃
- 通过测试的片元:才写进帧缓冲(Color Buffer)和/或写进深度缓冲(Depth Buffer,取决于 ZWrite)
关键点
-
深度测试在片元着色器之后
→ 是像素级别的决定:这个像素画不画 -
Render Queue、sortingOrder 决定什么时候发出 draw call(绘制指令)
-
深度测试决定:这个 draw call 产生的片元,最后要不要显示
步骤 | 做什么 | 谁决定 |
---|---|---|
收集所有要画的 Canvas | 根据 sortingLayer & sortingOrder 排序 | Unity CPU 层逻辑 |
按顺序提交 draw call(材质+网格) | 内部按 render queue 排序 | Unity CPU 层逻辑 |
GPU 渲染片元时 | 每个像素都要经过深度测试 | GPU 硬件 |
总结
sortingOrder 和 render queue 让CPU决定谁先谁后提交 draw call;
深度测试在 GPU 上决定像素要不要画,深度测试是在「片元着色器之后」到「写入帧缓冲之前」进行的。
最后,在不考虑性能的情况下(drawcall很费性能,而一个canvas/sortingGroup会形成一个batch,batch会产生drawcall),协调各个UI顺序的邪门快捷方法是每个面板都加个canvas。
或者针对非Overlay的canvas的内部元素添加sortingGroup,调节其order in layer手动进行排序,遮挡关系一下就捋顺了