Unity URP 下 UI 特效开发指南 深入探索顶点色、Mask 交互与扭曲特效的实战技巧
前置知识
本文基于 Unity 2022.3 LTS + URP 14.0,假设你已掌握 Shader Graph 基础用法,熟悉 Canvas 渲染模式与 UI 组件层级关系。
在游戏开发中,UI 不仅是信息载体,更是视觉体验的重要组成部分。Unity 的 URP(Universal Render Pipeline)为 UI 特效提供了强大的着色器能力,配合顶点色、Mask 和扭曲 Shader,可以实现诸如高光扫过、流光边框、涟漪效果等丰富表现。本文将从原理到实现,系统讲解这三类特效的开发方法。
一、UI 顶点色的利用
1.1 顶点色的基本原理
每个网格顶点可以携带一个 4 通道颜色值(RGBA),默认全为白色(1,1,1,1)。在 URP 的 Canvas 渲染中,这个颜色会被直接传递给顶点着色器,供片段着色器使用。
相比纹理顶点色更轻量,无需额外的贴图采样开销,即可驱动多种视觉效果。
1.2 通过脚本设置顶点色
using UnityEngine; using UnityEngine.UI; public class VertexColorController : MonoBehaviour { [SerializeField] private Graphic targetGraphic; [SerializeField] private float sweepSpeed = 2f; [ private Material materialInstance; private int propertyID; void Start() { // 创建材质实例,避免污染共享材质 materialInstance = new Material(targetGraphic.material); targetGraphic.material = materialInstance; // 获取 Shader 中声明的属性 ID,提升查找性能 propertyID = Shader.PropertyToID("_SweepProgress"); } void Update() { float progress = (Mathf.Sin(Time.time * sweepSpeed) + 1f) * 0.5f; materialInstance.SetFloat(propertyID, progress); } }1.3 Shader Graph 中读取顶点色
// Vertex Color Sweep Shader — 顶点色驱动扫光特效 // 关键节点:Vertex Color(获取网格顶点颜色) // 配合 SweepProgress 参数实现左右扫光效果 // 输入:Vertex Color (RGBA) // 输入:SweepProgress (Float 0~1) // 输入:SweepColor (Color) // 输出:Base Color + Alpha1.4 顶点色的实用技巧
🎨
颜色分层
利用 RGBA 四个通道分别存储不同信息,如 R 通道控制主色,G 通道控制高光,B 通道控制阴影。
⚡
性能优势
无需额外贴图采样,直接使用网格数据。适合大量 UI 元素需要独立颜色的场景。
🔄
动态更新
配合脚本可在运行时动态修改顶点色,实现渐变、闪烁、生命周期等效果。
⚠️ 注意事项
并非所有 UI 组件都支持自定义 Shader:Image、RawImage、Text 支持较好;Toggle、Slider 等复合组件需要额外处理子对象。
二、Mask 组件与 Shader 的交互
2.1 URP Mask 的工作机制
URP 中的 Mask 通过 Stencil Buffer 实现遮罩剔除。Mask 组件会将 Stencil Reference Value 写入屏幕,当 UI 元素的 Shader 读取Stencil 时,会根据比较操作决定是否渲染。
// Maskable UI Shader — 支持 Stencil Mask 遮罩 Properties { _MainTex ("Sprite Texture", 2D) = "white" {} _Color ("Tint", Color) = (1,1,1,1) } SubShader { Tags { "Queue"="Transparent" "RenderType"="Transparent" } // Stencil 配置 — 关键部分 Stencil { Ref 1 // 参考值,与 Mask 组件的 Stencil 值对应 Comp Equal // 比较操作:等于才渲染 Pass Keep // 通过时保持Stencil不变 Fail Zero // 失败时写入0(不显示) } Blend SrcAlpha OneMinusSrcAlpha // ... Fragment Shader 代码 ... }2.2 Mask 组件属性解析
| 属性 | 说明 | 常用值 |
|---|---|---|
Show Graphic | 是否在遮罩范围内显示 Mask 图形 | true / false |
Mask材质的 Stencil Ref | Mask 写入的 Stencil 参考值 | 通常为 1 |
Mask材质的 Stencil Comp | 遮罩的比较操作 | Always / Equal |
2.3 多层 Mask 嵌套
通过设置不同的 Stencil 值,可以实现多层 Mask 嵌套效果。例如在一个圆形头像框内再套一个方形的信息标签。
创建外层 Mask
在 UI 层级中放置 Mask 组件,设置其 Source Graphic 的 Stencil Ref = 1,确保子元素受此 Mask 约束。
创建内层 Mask
作为外层 Mask 的子对象,再次添加 Mask 组件,设置 Source Graphic 的 Stencil Ref = 2。注意子 Mask 的形状必须完全包含在外层 Mask 范围内。
配置子元素 Shader
子元素的 Shader 需要将 Stencil Comp 设置为 Greater 或 Equal,以匹配对应层级的 Mask。
三、UI 扭曲特效
3.1 扭曲特效的原理
UI 扭曲本质上是对 UV 坐标进行数学变换。最常见的是波形扰动(Wave Distortion)和中心膨胀(Spherize)。通过在 Shader 中对顶点或片段的 UV 进行偏移,可以实现涟漪、缩放、呼吸等视觉效果。
核心公式
finalUV = baseUV + sin/cos(offset) * intensity
通过叠加正弦/余弦波形,并控制振幅(amplitude)、频率(frequency)和相位(phase),可以生成丰富的扭曲形态。
3.2 涟漪效果实现
void RippleUV(float2 uv, float2 center, float time, float speed, float frequency, float amplitude, out float2 distortedUV) { // 计算到中心的距离 float2 delta = uv - center; float dist = length(delta); // 涟漪波:随着时间向外扩散 float wave = sin(dist * frequency - time * speed); // 振幅随距离衰减(远处涟漪更弱) float attenuation = 1.0 / (1.0 + dist * 5.0); // 计算径向扰动方向 float2 direction = normalize(delta); // 应用扰动偏移 distortedUV = uv + direction * wave * amplitude * attenuation; }3.3 Shader Graph 实现扭曲节点
如果使用 Shader Graph,可以通过自定义 Function Node 封装扭曲逻辑,或者使用内置的 Tiling And Offset、Twirl 等节点组合。
3.4 实际应用:技能按钮涟漪反馈
public class SkillButtonRipple : MonoBehaviour, IPointerClickHandler { [SerializeField] private Material rippleMaterial; [SerializeField] private float rippleDuration = 0.6f; private int timeID; private int centerID; private float currentTime = 0f; private bool isRippling = false; Vector2 clickPosition; void Start() { timeID = Shader.PropertyToID("_RippleTime"); centerID = Shader.PropertyToID("_RippleCenter"); } public void OnPointerClick(PointerEventData eventData) { // 将屏幕坐标转换为 UI 局部 UV RectTransformUtility.ScreenPointToLocalPointInRectangle( transform.as RectTransform, eventData.position, eventData.pressEventCamera, out Vector2 localPoint); RectTransform rect = transform as RectTransform; Vector2 normalizedPos = (Vector2)localPoint / rect.sizeDelta + 0.5f; clickPosition = normalizedPos; isRippling = true; currentTime = 0f; } void Update() { if (!isRippling) return; currentTime += Time.deltaTime; float progress = currentTime / rippleDuration; rippleMaterial.SetFloat(timeID, currentTime); rippleMaterial.SetVector(centerID, clickPosition); if (progress >= 1f) isRippling = false; } }3.5 扭曲特效类型一览
🌊
涟漪 Ripple
以点击位置为中心,向外扩散的同心圆波纹。适合按钮点击反馈、水波特效。
🌀
旋转 Twirl
以某点为中心进行漩涡状旋转扭曲。适合魔法阵、传送门、眩晕状态等。
📍
球面化 Spherize
将平面 UV 向中心点投影成球面效果。适合球形头像框、3D 按钮感。
💨
呼吸 Breathe
UV 随时间周期性缩放,产生膨胀收缩感。适合加载动画、待机特效。
性能建议
扭曲特效通常需要在每个片段计算中访问 UV 并进行数学运算。建议将计算量大的扭曲效果限制在小尺寸 UI 元素上,避免全屏使用。如需全屏扭曲,可考虑在 Post-processing 阶段实现。
总结与实践建议
| 特效类型 | 实现难度 | 性能开销 | 适用场景 |
|---|---|---|---|
| 顶点色 | ⭐ 简单 | 极低 | 批量颜色变化、简单状态指示 |
| Mask 遮罩 | ⭐⭐ 中等 | 低 | 头像框、渐变遮罩、层级裁剪 |
| 扭曲特效 | ⭐⭐⭐ 较高 | 中等 | 点击反馈、特效装饰、动画增强 |
在实际项目中,建议将常用的 UI 特效封装为可复用组件:
建立特效 Shader 库
将顶点色、Mask、扭曲等 Shader 整理为 Shader Graph 资产,配合可视化调参面板。
封装 C# 控制器
针对每种特效编写配套脚本,处理事件触发、参数动画、资源生命周期。
Prefab 化与配置化
将特效 UI 预制件化,通过 ScriptableObject 或 Inspector 配置特效参数,便于设计师调整。
掌握以上技术后,你将能够为游戏 UI 赋予更丰富的视觉表现力,让玩家在交互过程中获得更细腻的反馈体验。
