UGUI优化

发布于:2025-04-05 ⋅ 阅读:(8) ⋅ 点赞:(0)

1.前言

UGUI优化核心在于减少渲染状态的开销。
那么怎么减少渲染开销呢? 减少渲染过程中的Setpasscall次数(从一个材质的渲染状态切换到另一个材质的不同渲染状态,或者启用 / 禁用某些渲染特性)。明明大头不在DrawCall为什么还要关心合批来减少drawcall? 因为如果更换批次大概率会增加setpasscall,unity不能完全的帮我们处理好渲染的批次分配,我们需要通过一些人为的干预从而减少setpasscall。
详细看我之前的这篇文章
链接跳转

2.批次处理(batching)

当UI颜色,材质等产生变化的时候会调用Rebuild,重构这个批次内的所有顶点信息,颜色,文本等。然而每个批次不应该因为单个物体的频繁改变而导致整体的Rebuild。所以引出第一点:

动静分离

  • 频繁改变的UI应该自己处于一个Canvas中,不频繁改变的UI放在同一个Canvas中,类似Unity自带的静态合批也是在做这个事情。

为了尽可能减少材质改变,可以通过合并图集信息,让某些物体的材质相同,UV映射信息不同。所以引出第二点:

合并图集

  • 尽可能充分利用图集空间,最好是能大图+小图把整个图集拼满,这样没有一个UV坐标信息是浪费的。
  • 需要注意的是:1.图集大小不要超过4096 * 4096否则对内存是很大的开销。2.按照频率来拼图,使用频率高的拼在一起,低的拼在一起。

然而上述只是理想状态能做的优化,放在同一个Canvas里只会帮助Unity进行合批,但是实际的渲染顺序和Drawcall数量还要看你的UI到底是如何摆放的。
所以引出第三点:

避免夹层(不线性的UI摆放)

Unity UI是优先渲染Hierarchy中靠下的元素和普通物体渲染时优先渲染Z轴靠前的情况不一样,即使你调整了UI在场景中的Z位置,渲染仍然按照Hierarchy中靠下的顺序。

  • 尽量避免某个UI打断某些材质相同本能处在同一个批次的UI,从而分批造成多次Rebuild。
  • 详细看某乎这篇文章:kong66

3.Rebuild

  • 由于使用Disable() 和 SetActive()给一个Canvas下的子物体,会被标记为dirty引起Rebuild,所以需要频繁隐藏的物体要另外放在一个Canvas里面,以免标记为dirty。
  • 引用上述文章的一段话“canvas不是划分的越多越好,不同canvas的UI元素是不可能batching 的, 所以canvas多,draw calls就会多; Canvas 太少,每个canvas上的元素太多,任何元素变化频繁, 都将导致canvas频繁dirty, 频繁rebuild, 而且rebuild计算量大. 总之,要在最低的rebuild成本 和 最少的draw calls之间做出权衡.”
  • 使用Canvas Group来管理某个父物体下的子UI对象,作用范围是所有子UI,在可调整选项中调整UI属性可以避免标记为dirty,可调整选项有:
    • Alpha 此组中的 UI 元素的不透明度。该值介于 0 和 1 之间,其中 0 表示完全透明,1 表示完全不透明。请注意,UI 元素也会保留自己的透明度,因此画布组的 Alpha 值将与各个元素的 Alpha 值彼此相乘。
  • Interactable 确定此组件是否接受输入。当设置为 false 时,禁用交互。
  • Block Raycasts 此组件是否作为射线投射的碰撞体?需要在连接到画布的图形射线投射器上调用 RayCast 函数。这_不_适用于 Physics.Raycast。
  • Ignore Parent Groups 此组还会受到游戏对象层级视图中更上层的画布组 (Canvas Group) 组件中的设置所影响,还是会忽略并因此覆盖这些设置?
  • 使用CanvasRenderer.Cull = true 可以手动设置在某个不想渲染的UI,也就是人为的剔除某个UI元素。

4.OverDraw

引用kong66文章的话“因为Canvas的所有元素都会从前往后做透明度融合的渲染,所以重叠区域的所有UI元素都会被采样, 这将造成GPU的填充率(fill-rate)过高,也就是overdraw, 这会进一步导致帧率下降.

  • 所以为了避免这种情况,如果后渲染的不透明页面完全覆盖了先渲染的页面,那么可以把对GamePlay不可视的页面给Disable掉。
  • 如果美术资源允许的情况下,可以对叠加的花纹材质进行处理,以减少叠加区域。然而这会使得素材复用性降低。

5.RayCast

引用kong66文章的话"射线碰撞检测所有的canvas, canvas上所有的Graphic, 最后找到有效点击的UI元素"流程如下图所示。
图片来自kong66

  • 减少不必要的RaycastTraget,确定某些UI元素是不需要和用户交互的,那么就取消勾选RaycastTraget。
  • 减少元素重叠,可点击的重叠元素会在步骤d, f, g增加排序,遍历的次数。
  • 避免2D/3D遮挡检测. 尽量避免使用2D/3D遮挡检测,因为在步骤e中,这些检测函数本身消耗较大,且会产生GC.

6.Mask/RectMask2D

  • 原理
    • Mask:依据父物体图像组件的 alpha 通道来确定子元素的可见区域。当父物体图像的 alpha 值非零时,子元素在对应位置可见;若父物体图像完全透明或没有图像,Mask 可能无法按预期工作。
    • RectMask2D:利用父物体的矩形边界来决定子元素的可见区域,不依赖父物体的图像组件及其 alpha 值。
  • 应用场景
    • Mask:适用于需要非矩形裁剪的场景,如圆形、不规则形状裁剪。例如,制作圆形头像框,可将头像设为 Mask 子物体,通过设置 Mask 形状为圆形实现裁剪。
    • RectMask2D:用于矩形裁剪场景,如滚动列表、窗口等。实现滚动列表时,使用 RectMask2D 可确保列表项只在列表框矩形范围内显示,超出部分被裁剪。
  • 性能消耗
    • Mask:使用模板缓冲区,每次渲染涉及模板缓冲区的写入和比较操作,性能开销大,在大量 Mask 或复杂图形场景中,对性能影响更明显。
    • RectMask2D:基于矩形裁剪,不使用模板缓冲区,渲染开销小,性能较好。