在使用Unity时,会发现Mask组件,使用像素比较小的图作为Mask会出现锯齿,看到网上有使用Shader去处理。
这里的原理是:从遮罩纹理中采样透明度来修改片元的透明度, 遮罩的圆形之外的部分透明度为0, 该过度的地方也有不同透明度, 这样就能正确混合也能正确起到遮罩的效果.
使用Mask组件的效果
使用Shader效果:
放在一起对比:
具体Shader代码
Shader "Custom/CustomMask"
{
Properties{
[PerRendererData] _Maintex("Sprite Texture",2D) = "white" {}
_MaskTex("Mask Texture",2D) = "white"{}
_Color("Tint",Color) = (1,1,1,1)
_ColorMask("Color Mask",Range(0,15)) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip",float) = 0
}
SubShader{
Tags{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"//PreviewType 指示材质检视面板预览应如何显示材质。默认情况下,材质为球体,Plane 显示2D Skybox 显示天空盒
"CanUseSpriteAtlas" = "True"
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]//unity_GUIZTestMode Unity的测试模式 适应不同的设备 保证流畅度
Blend SrcAlpha oneMinusSrcAlpha
ColorMask[_ColorMask]
Pass{
Name "CostomMask"
CGPROGRAM //使用CG语言编写着色器程序
#pragma vertex vert //顶点着色器
#pragma fragment frag //片段着色器
#pragma target 2.0 //支持渲染目标
#include "UnityCG.cginc" //包含Unity内置的CG函数和变量
#include "UnityUI.cginc" //包含Unity UI相关的CG函数和变量
#pragma multi_compile __ UNITY_UI_ALPHACLIP // 根据是否用Alpha Clipping 编译多个版本的着色器代码
//定义输入数据结构
struct appdata_t
{
float4 vertex : POSITION; //顶点坐标
float4 color :COLOR; //顶点颜色
float2 texcoord:TEXCOORD0; //纹理坐标
UNITY_VERTEX_INPUT_INSTANCE_ID //实例ID
};
//定义输出数据结构
struct v2f
{
float4 vertex : SV_POSITION; //裁剪空间坐标
float4 color : COLOR; //片段颜色
float2 texcoord : TEXCOORD0; //纹理坐标
float4 worldPosition : TEXCOORD1; //世界坐标
UNITY_VERTEX_OUTPUT_STEREO //立体视图信息
};
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
//顶点着色器
v2f vert(appdata_t IN)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(IN); //设置实例ID
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); //初始化立体视图信息
OUT.worldPosition = IN.vertex; //记录世界坐标
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); //转换为裁剪空间坐标
OUT.texcoord = IN.texcoord; //传递纹理坐标
OUT.color = IN.color * _Color; //传递顶点颜色并应用颜色调整
return OUT;
}
sampler2D _Maintex; //Sprite纹理
sampler2D _MaskTex; //遮罩纹理
float _OneMinusSaturability; //饱和度
// 片段着色器函数
fixed4 frag(v2f IN) : SV_TARGET
{
half4 color = (tex2D(_Maintex,IN.texcoord)+_TextureSampleAdd); // 采样Sprite的纹理并加上采样增量
if(IN.color.r<0.0001 && IN.color.g<0.0001&&IN.color.b>0.0004){ // 判断颜色是否为蓝色
float gray = dot(color.rgb,float3(0.2125,0.7154,0.0721)); // 将颜色转换为灰度值
color.rgb = float3(gray,gray,gray); // 将颜色设置为灰度值
}
else
{
color *= IN.color;
if(_OneMinusSaturability>0.05) // 判断饱和度是否大于0.05
{
float gray = dot(color.rgb,float3(0.2125,0.7154,0.0721)); // 将颜色转换为灰度值
color.rgb = lerp(float3(gray, gray, gray), color.rgb, 1 - _OneMinusSaturability); // 插值计算颜色
}
}
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); // 计算Alpha值
color.a *= tex2D(_MaskTex,IN.texcoord).a;// 乘以遮罩的Alpha值
#ifdef UNITY_UI_ALPHACLIP // 如果定义了UNITY_UI_ALPHACLIP
clip(color.a-0.001); // 进行Alpha剪裁
#endif
return color; // 返回颜色
}
ENDCG
}
}
}
使用shader的好处:
在使用系统自带的Mask,会增加DrallCall。
从图片上看只有两个图片,为啥会有三个DrallCall,这个是因为:绘制完Mask所有节点之后 需要把Stencil复原,增加了一次DrawCall。
使用Shader可以不会增加DrallCall: