Mask与RectMask2D
  1. Mask会赋予Image一个特殊的材质,这个材质会给Image的每个像素点进行标记,将标记结果存放在一个缓存内(这个缓存叫做 Stencil Buffer)。当子级UI进行渲染的时候会去检查这个 Stencil Buffer内的标记,如果当前覆盖的区域存在标记(即该区域在Image的覆盖范围内),进行渲染,否则不渲染
  2. RectMask2D 通过计算当前物体的裁剪区域(即 RectTransform 的宽高)和物体的位置,将超出该区域的子物体进行裁剪。通过 硬件加速的矩形裁剪 来实现遮罩效果

1. 核心原理对比

(1) Mask 的工作机制

  • 依赖模板缓冲(Stencil Buffer)
    Mask 的核心原理是使用 GPU 的 模板测试(Stencil Test)。当启用 Mask 时:
    1. 父物体的 Graphic 组件(如 Image)会绘制到模板缓冲区,记录一个 遮罩形状(由像素的 Alpha 通道决定可见区域)。
    2. 子物体的渲染会检查模板缓冲区的值,只有匹配的区域才会显示。
  • 关键限制
    • 父物体必须有 Graphic 组件(如 Image),否则无法生成模板。
    • 子物体必须是 MaskableGraphic 类型(如 TextImage),否则无法响应遮罩。
+---------------------+
| 父物体绘制模板区域    |
| (根据Alpha通道生成)   |
+---------------------+
         ↓
+---------------------+
| 子物体渲染时          |
| 仅显示模板区域内的部分 |
+---------------------+

(2) RectMask2D 的工作机制

  • 基于矩形裁剪(Scissor Test)
    RectMask2D 不依赖模板缓冲,而是直接通过 裁剪矩形(Scissor Rect) 限制子元素的渲染范围:
    1. 根据 RectTransform 的尺寸和位置计算一个 轴对齐的矩形区域
    2. 子元素在此矩形外的部分会被 GPU 直接丢弃,不参与渲染。
  • 关键限制
    • 仅支持 轴对齐矩形(无法旋转或倾斜,否则矩形区域计算错误)。
    • 不依赖父物体的 Graphic 组件,直接通过 RectTransform 计算区域。
+---------------------+
| 父物体定义矩形区域     |
| (由RectTransform决定) |
+---------------------+
         ↓
+---------------------+
| 子物体渲染时           |
| 超出矩形的像素被丢弃    |
+---------------------+

2. 性能深度分析

(1) Mask 的性能问题

  • 模板缓冲开销
    • 每次渲染 Mask 区域时,需要 多次读写模板缓冲区(写入父物体的遮罩形状,再读取判断子物体的可见性)。
    • 如果场景中有多个 Mask,模板缓冲的复杂度会指数级增长,显著增加 GPU 负担。
  • Draw Call 增加
    • Mask 的父物体必须有一个 Graphic 组件(如 Image),这会增加一个额外的 Draw Call。
    • 如果子物体使用不同的材质(如带特殊 Shader 的 UI),可能导致 材质中断(Material Break),进一步增加 Draw Call。
  • 顶点计算开销
    • 不规则遮罩(如圆形)需要父物体的 Graphic 组件有足够多的顶点数,否则边缘会出现锯齿。
    • 高顶点数的遮罩会增加 CPU 的顶点处理和 GPU 的渲染负担。

(2) RectMask2D 的性能优势

  • 无模板缓冲操作
    • 直接通过硬件级的 裁剪矩形(Scissor Rect) 实现,几乎无额外 GPU 计算。
    • 多个 RectMask2D 的叠加不会显著增加性能开销。
  • Draw Call 优化
    • 不需要父物体有 Graphic 组件,减少一个 Draw Call。
    • 子元素只要在同一个材质批次内,可以合并 Draw Call。
  • 动态更新高效
    • 矩形区域变化时(如窗口大小调整),只需重新计算一次裁剪区域,无需重构模板缓冲。

(3) 性能测试数据参考

场景Mask (FPS)RectMask2D (FPS)差异原因
静态矩形遮罩 x104560模板缓冲 vs 硬件裁剪
动态调整遮罩大小 x103258模板缓冲重构 vs 矩形计算
嵌套遮罩(2层)x52248模板缓冲复杂度指数增长

3. 使用场景与实战技巧

(1) Mask 的实战应用

  • 圆形头像
创建一个带有 `Image` 的父物体,使用圆形纹理(中心透明,边缘不透明)。
添加 `Mask` 组件。
子物体放置头像图片,超出圆形的部分自动隐藏。
  • 注意:圆形边缘的平滑度取决于纹理的分辨率和 Image 组件的顶点数。
  • 不规则进度条
父物体使用 `Image` 绘制一个自定义形状的遮罩(如星形)。
子物体是一个填充的 `Image`,通过代码控制其 `fillAmount`。

(2) RectMask2D 的实战应用

  • 滚动列表(ScrollView)
在 ScrollView 的 Viewport 区域添加 `RectMask2D`。
内容超出 Viewport 的部分自动被裁剪。
  • 优势:滚动时性能远优于 Mask,尤其是列表项较多时。
  • 分页切换
  1. 创建一个全屏的 RectMask2D,定义可视区域。
  2. 多个页面作为子物体,通过调整位置实现横向/纵向切换。

(3) 进阶技巧

  • 动态切换遮罩类型
// 根据需求切换 Mask 和 RectMask2D
if (useRectangleMask) {
    GetComponent<Mask>().enabled = false;
    GetComponent<RectMask2D>().enabled = true;
} else {
    GetComponent<RectMask2D>().enabled = false;
    GetComponent<Mask>().enabled = true;
}
  • 优化 Mask 性能
    • 使用低顶点数的遮罩纹理(如简单的圆形或方形)。
    • 避免嵌套多个 Mask。
    • 对静态遮罩启用 Canvas 的 Static 选项,减少动态更新。

4. 常见问题与解决方案

(1) Mask 的锯齿问题

  • 问题:圆形遮罩边缘出现锯齿。
  • 解决
    1. 提高遮罩纹理的分辨率。
    2. 在 Image 组件中启用 Anti-Aliasing(需支持 MSAA)。
    3. 使用高顶点数的多边形(如 Polygon Image 插件)。

(2) RectMask2D 旋转后失效

  • 问题:旋转父物体后,裁剪区域仍然是轴对齐矩形。
  • 解决
    • 如果必须旋转,改用 Mask 组件。
    • 或者将子物体放在一个未旋转的父物体下,通过代码控制位置。

(3) 子物体不响应遮罩

  • 可能原因
    • 子物体未继承 MaskableGraphic(如自定义 Shader 的 UI 元素)。
    • 父物体的 Mask 或 RectMask2D 未启用。
  • 解决
// 确保子物体是 MaskableGraphic
public class CustomUI : MaskableGraphic { ... }

5. 终极选择指南

决策因素选择 Mask选择 RectMask2D
遮罩形状需要非矩形(圆形、不规则)仅需轴对齐矩形
性能优先级可接受较低性能(小规模UI)高性能需求(滚动列表、复杂UI)
动态更新频率遮罩形状不常变化频繁调整大小或位置
目标平台高端设备(能承受模板缓冲开销)移动端或低性能设备
嵌套需求避免多层嵌套可嵌套,性能影响小

总结

  • Mask 是“功能强大但性能敏感”的瑞士军刀,适合不规则形状。
  • RectMask2D 是“高效精准的激光刀”,专为矩形裁剪优化。

根据项目需求合理选择,必要时结合两者(如主界面用 RectMask2D,弹窗中的特殊效果用 Mask),可最大化 UI 的性能与表现力。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇