Unity Shader之Mask与Rect Mask 2D
引言
Unity中遮罩是非常常用的组件。而其子对象也常常需要自定义shader来显示不同的效果,比如:圆角矩形、涂色等等。这种情况下,需要在子对象的shader中额外添加几行代码来保证遮罩生效。因为默认Shader(UI/Default)实现了遮罩功能,而自定义的Shader往往只实现了效果部分,忽略了遮罩部分。
内容
本文会分别讲述使Mask和Rect Mask 2D生效的Shader写法,由于它们的Shader代码写法直接和底层原理相关,所以会大致讲述它们的底层原理,更详细的原理可以看下面两篇文章。
Unity3D Shader系列之模板测试
[UGUI源码分析] Unity遮罩之RectMask2D详细解读
Mask遮罩
Mask遮罩是基于模板测试的原理——挂载Mask组件的UI对象对应的模板缓存区中的模板值会被设置成1(1是默认的,也可以自己设置),模板缓存区其他区域被设置成0。给Mask的子对象设置一个模板参考值(即Mask区域模板缓存区中的模板值),Mask子对象与模板参考值进行比较,如果相等保留像素,则可以只保留Mask子对象在Mask区域的像素。 Shader代码如下
Stencil
{
Ref 1 // 模板参考值设置为1
Comp Equal // 模板缓冲区值等于参考值的像素通过,即只通过Mask区域的像素
Pass Keep // 不更新模板
}
也可以将参考值、测试条件和测试通过操作参数化,Shader代码如下。需要注意:运行时Shader的参数值并不一定等于代码里面设定的参数值,需要在Inspector里面修改Shader参数值。
Properties
{
_StencilComp("Stencil Comparison", Float) = 3
_Stencil("Stencil ID", Float) = 1
_StencilOp("Stencil Operation", Float) = 0
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
}
Rect Mask 2D遮罩
Rect Mask 2D遮罩是基于UI裁剪的方式实现的,计算父物体中所有RectMask2D覆盖区域的交集——得到的交集是矩形裁剪区域。将矩形裁剪区域传给UI子对象的shader(UI系统自动传给Shader中的_ClipRect),Shader中的片元着色器会丢弃不在裁剪区域的片元。Shader代码如下
Shader "Unlit/OriginalFill"
{
Properties
{
_MainTex ("Mask", 2D) = "white" {}
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
LOD 100
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
#include "UnityUI.cginc" //使用UnityGet2DClipping()需要加上的头文件
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 worldPosition : TEXCOORD1; // 世界坐标(用于裁剪计算)
};
sampler2D _MainTex;
float4 _ClipRect; // 裁剪区域(自动由 UI 系统填充)
v2f vert (appdata v)
{
v2f o;
o.worldPosition = v.vertex; // 记录世界坐标
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// 1. 裁剪测试:判断像素是否在 Mask 定义的裁剪区域内
float clip = UnityGet2DClipping(i.worldPosition.xy, _ClipRect);
if (clip <= 0)
{
discard;
}
return tex2D(_MainTex, i.uv);
}
ENDCG
}
}
}
分析区别
(1)Mask和Rect Mask 2D遮罩的底层原理完全不同;
(2)Rect Mask 2D遮罩只能用于矩形遮罩,但性能开销更低。建议优先使用Rect Mask 2D遮罩来实现功能,如Rect Mask 2D遮罩无法满足需求,再考虑使用Mask遮罩;
小结
想要编写使遮罩生效的Shader代码,需要清楚Mask和Rect Mask 2D遮罩的底层原理。此外,Mask和Rect Mask 2D是两种截然不同的遮罩方式,无论是它们的实现原理还是它们适用的场合。

浙公网安备 33010602011771号