BlaXuan 发表于 2022-6-2 09:56

对unity UI层级的一些思考

做客户端开发,经常会遇到UI需要和模型特效进行排序的问题,网上的解决方案也有很多,比较零散,但没看到有更原理性的分析,前段时间看到有人问NGUI层级渲染的原理是什么,于是对层级进行了一些思考,希望能对层级问题给出一个更深层一点的解释,而不只是调几个参数。
<hr/>首先层级是什么以及调整层级的原理

        要明确的是UI的层级是个逻辑的概念,从显卡层面来说,以像素为单位,是没有层级这一说的。我们想要的层级,就是把我们理解的一个个对象看做整体,在这些整体之间排序。
        实现我们想要的层级的效果,本质上说是通过控制drawcall顺序实现的。
        为什么这么说,先看看显卡是怎么确定哪些像素显示在屏幕上的,从显卡层面来说,分两种情况,开启深度测试和不开启深度测试。

[*]开启深度测试的情况下,深度值决定哪些像素最终被显示,drawcall的顺序实际并不影响渲染结果,但是影响渲染效率,主要是overdraw,所以不透明物体,引擎层面为了减少overdraw,会按近到远的顺序渲染。
[*]关闭深度测试,最终渲染的效果,是按画家算法实现的,就是多次覆盖上一次渲染的结果,overdraw比较严重,所以一般游戏都会限制半透物体数量。这并不是唯一的处理半透物体的方式,只是unity默认是这样处理的。
        ui一般关闭深度测试和并写入深度值,所以drawcall提交顺序决定了层级,后渲染的挡住先渲染的。
unity drawcall的提交顺序

        实际这只是个策略,在URP中可以自定义。
        按默认管线来说,camera的depth为最高,depth值越大,越在前面。
        然后是RenderQueue,在逻辑上将渲染的物体分成几个大类。内部对不同类型有不同策略,透明从后往前,不透明从前往后。
        最终Renderer基类的layer和sortingOrder决定每个render的提交顺序。
        对于UI来说,都是透明渲染,所以sortingOrder就是最关键的参数了。
<hr/>原理基本就是这样,下面来看看具体实现,先来看通用方法

        最简单的解决方案,把特效和模型变成RT,就和图片一样处理,直接降维打击。但是这种适合处理模型,特效消耗比较大。
        另一个方法是用多个相机,但当层级多的时候,那真的是很多个相机才行,并且相机多了可能有性能问题。
模型的处理,和特效还不一样,这里单独说一下。

        首先要知道UI和模型之间的遮挡关系是怎么处理的。
                因为模型一般是不透明渲染,不能用调整drawcall的方式让模型后渲染,当然也可以改shader,让UI上的模型特殊处理,但是这样会增加复杂度,不是很实用。
                默认情况下,UI和模型的遮挡关系是按深度处理的,UI整体都是一个深度,也就是说模型只能完全挡住UI或是完全被UI挡住,不能实现在两个UI之间的效果。另外要注意模型是3D的,要在UI之上,z要足够大才行,否则会出现部分像素穿插。
        如果要模型在UI之间,那就只能是RenderTexture或是多个相机了。
        将模型转换为RenderTexture,这样的好处是比较灵活,可以实现多层次,而不增加多少drawcall,但是比直接放模型消耗大些,但是直接放模型不能夹在UI之间。
<hr/>NGUI特效层级

        NGUI的drawcall,实际是创建一个MeshRenderer,交给显卡去渲染。最终决定渲染层级的,是Renderer基类的sortingOrder。
        渲染组件上的depth,只是提供的一个显示,方便查看,会影响层级但不是决定层级,看UIDrawCall的代码可以看到是很多因素叠加的结果。
        原理清楚了,那设置UI和特效之间层级就很容易了。既然都是Renderer,设置好sortingOrder就可以了。
UGUI特效层级

        首先Canvas选择RenderCamera才能渲染特效。
        方案一:特效和Canvas排序
                一个Canvas下不管是几个drawcall,对于和特效排序来说都是一个,特效不会穿插在Canvas下的节点之中。
                UI分多个Canvas,调整orderinlayer,特效值在中间。
                或是order相同,调整特效的z值,但是这样特效只会被Canvas完全挡住或是完全挡住Canvas,不能实现夹在中间的效果,相当于是特效和UI整体做了个排序。
        方案二:将特效转换成CanvasRenderer渲染,这样的好处是不增加额外drawcall。这个开源库提供了两种实现方法。mob-sakai/ParticleEffectForUGUI。用到可以参考一下,简单来说就是设置material。
<hr/>实现层级渲染的方法有很多,要根据实际界面情况选择最合适的。

mypro334 发表于 2022-6-2 10:00

把particle搞成mesh的方法是不是也可以用在局内特效[思考]

JamesB 发表于 2022-6-2 10:00

局内特效指的啥[思考]

DomDomm 发表于 2022-6-2 10:01

github链接看来是做在UI上的特效·在想可不可以用在三维空间里
[疑惑]

ainatipen 发表于 2022-6-2 10:08

理论上可以的,就是把特效变成3DUI的形式,但是想想好像没啥必要,添加特效的时候有点麻烦
[吃瓜]
页: [1]
查看完整版本: 对unity UI层级的一些思考