Unity Shader 系列(一):URP 中的 2D SDF — 圆角 UI、血条与技能遮罩
为什么在 Unity UI 中使用 SDF?
传统 Unity UI 使用图片切片(9-Slice Sprite)来绘制圆角矩形、进度条等元素。这种方案有明显局限:需要美术提供多种尺寸的图片资源,放大后边缘模糊,圆角半径固定无法运行时调整。
2D SDF(有向距离场)Shader 彻底改变了这一局面:
- 任意分辨率下边缘始终锐利(GPU 数学计算,与分辨率无关)
- 圆角半径、边框宽度、颜色全部通过 Inspector 实时调节
- 单张 Shader 就能实现圆角矩形、圆形进度条、技能 CD 扇形遮罩
实际游戏应用场景:
- 《原神》风格 UI:角色血条、护盾条,圆角矩形背景板
- MOBA 类游戏技能图标:技能 CD 的扇形遮罩倒计时
- 卡牌游戏:卡牌边框高亮描边,鼠标悬停时圆角发光效果
URP 2D Renderer 与 UI Canvas 集成
在 Unity 中使用自定义 SDF Shader 有两种主要方式:
方式一:UI 材质(Canvas/CanvasRenderer)
- 在
Image组件上赋予自定义材质 - Shader Tags 需要设置为
"Queue"="Transparent"和"RenderType"="Transparent" - 在 URP 2D Renderer 的
Renderer Feature中正常工作
方式二:Sprite Renderer
- 适用于世界空间中的 2D 元素
- 配合 URP 的
2D Renderer使用Sprites/Lit或自定义 Unlit Pass
本文以 UI 材质方案为主,这是游戏 HUD 的最常见需求。
核心 SDF 数学:HLSL 实现
HLSL 与 GLSL 的 SDF 数学本质相同,主要差异在 API 命名:
mix()→lerp()fract()→frac()vec2/vec3/vec4→float2/float3/float4mat2→float2x2
圆角矩形 SDF
1 | |
圆形 SDF
1 | |
扇形 SDF(技能 CD 遮罩)
1 | |
完整 URP UI SDF Shader
这是一个可以直接挂在 Unity UI Image 组件上的完整 Shader:
1 | |
ShaderGraph 等价实现
如果你的团队使用 ShaderGraph,同样效果的节点连接思路如下:
- UV 准备:
UV节点 →Subtract(0.5)→ 得到以中心为原点的坐标 - SDF 计算:使用
Rounded Rectangle节点(内置,位于Procedural/Shape分类)- 输入:
UV、Width、Height、Radius - 输出:边缘遮罩(0/1)
- 输入:
- 进度裁剪:
Split取 X 通道 →Comparison(Less)与Fill Amount比较 - 边框:用两个
Rounded Rectangle(大、小)相减得到边框遮罩 - 颜色混合:
Lerp节点,用边框遮罩混合填充色和边框色 - 输出:连接到
Unlit Master(UI 不需要光照)的Color和Alpha
注意:ShaderGraph 的 Rounded Rectangle 输出值与 SDF 不完全一致,如果需要精确的 SDF 距离值(用于动态效果),建议用 Custom Function 节点包裹上面的 HLSL 代码。
性能考量
| 平台 | 建议 |
|---|---|
| 移动端(OpenGL ES 3.0) | 去掉边框层(减少一次 SDF 计算),使用 half 精度 |
| PC(DX11) | 完整功能,可增加外发光(Outer Glow:SDF 负值区域的渐变) |
| 主机 | 同 PC,可增加 MSAA 配合 SDF 的超分辨率抗锯齿 |
移动端优化要点:
- 将
float替换为half:half4 fillColor、half d等 - 减少
smoothstep调用数量(每个 SDF 层一次即可) - 使用
#pragma shader_feature关闭不需要的功能变体(边框、进度)
与 URP 渲染流程集成
这个 UI Shader 工作在 URP 的 Transparent Pass 中:
- Canvas 使用
Screen Space - Overlay模式时,UI 在所有 3D 场景之后渲染 - Canvas 使用
World Space模式时,按深度排序与 3D 物体混合 - 不需要 URP Light 系统(UI 通常是 Unlit 的)
如果需要 UI 元素与 3D 光照交互(如世界空间血条跟随角色),改用 World Space Canvas + 在 Shader 中添加 GetMainLight() 光照计算。
常见踩坑
SRP Batcher 报错:Shader 中的 Property 必须全部放入
CBUFFER_START(UnityPerMaterial)...CBUFFER_END,否则会报 “not compatible with SRP Batcher” 并严重影响 UI 批次合并**HLSL 中没有
fract()**:用frac()代替;没有mix():用lerp()代替UI Image 的 UV 方向:Unity UI 的 UV 原点在左下角,这与 URP 的 NDC 坐标(Z 轴 [0,1])不同,但与 OpenGL UV 约定相同,不需要翻转
_MainTex_ST必须声明:即使不使用 Tiling/Offset,也必须在 CBUFFER 中声明float4 _MainTex_ST,并在顶点着色器中调用TRANSFORM_TEX,否则材质球预览会出错Canvas Scaler 影响 SDF 比例:当 Canvas 的
Reference Resolution与实际分辨率不同时,_AAWidth参数需要相应调整,建议通过脚本动态传入1.0 / Screen.height的派生值
扩展:外发光效果
在上面 Shader 基础上,利用 SDF 的负值区域可以轻松实现外发光:
1 | |
掌握了 SDF UI Shader 的基础之后,下一篇文章将介绍程序化噪声在 Unity URP 特效中的应用——FBM 驱动的火焰、烟雾和程序化材质。