前言
Unity中UI Shader遮罩RectMask2D
一、需要定义一个变体UNITY_UI_CLIP_RECT
UNITY_UI_CLIP_RECT
当父级物体有Rect Mask 2D组件时激活.
需要先手动定义此变体#pragma multi_compile _ UNITY_UI_CLIP_RECT
同时需要声明:_ClipRect(一个四维向量,四个分量分别表示RectMask2D的左下角点的xy坐标与右上角点的xy坐标.)
UnityGet2DClipping (float2 position, float4 clipRect)即可实现遮罩.
//声明一个变体,用于RectMask使用
#pragma multi_compile _ UNITY_UI_CLIP_RECT
注意: 上面的 _ 前后均有空格
在片元着色器使用以下代码测试看看基本功能
#if UNITY_UI_CLIP_RECT
return 1;
#else
return 0.5;
#endif
测试代码:
Shader"MyShader/P1_1_8"
{
Properties
{
//命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
//比如说,在更改 Image 的源图片时,同时更改这个
[PerRendererData]_MainTex("MainTex",2D) = "white"{}
_StencilComp ("Stencil Comparison", Float) = 8.000000
_Stencil ("Stencil ID", Float) = 0.000000
_StencilOp ("Stencil Operation", Float) = 0.000000
_StencilWriteMask ("Stencil Write Mask", Float) = 255.000000
_StencilReadMask ("Stencil Read Mask", Float) = 255.000000
_ColorMask ("Color Mask", Float) = 15.000000
}
SubShader
{
//更改渲染队列(UI的渲染队列一般是半透明层的)
Tags {"Queue" = "TransParent"}
//混合模式
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Stencil
{
Ref [_Stencil]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
Comp [_StencilComp]
Pass [_StencilOp]
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//声明一个变体,用于RectMask使用
#pragma multi_compile _ UNITY_UI_CLIP_RECT
#include "UnityCG.cginc"
//存储 应用程序输入到顶点着色器的信息
struct appdata
{
//顶点信息
float4 vertex:POSITION;
float2 uv : TEXCOORD;
//这里定义一个语义为Color的4维向量,用于传入顶点颜色,设置语义为COLOR后,这个变量就会与顶点颜色对应
fixed4 color:COLOR;
};
//存储 顶点着色器输入到片元着色器的信息
struct v2f
{
//裁剪空间下的位置信息(SV_POSITION是必须的)
float4 pos:SV_POSITION;
float2 uv : TEXCOORD;
//这里的语义主要代表精度不同,TEXCOORD 在这里只是代表高精度
fixed4 color : TEXCOORD1;
};
sampler2D _MainTex;
fixed4 _Color;
v2f vert(appdata v)
{
v2f o;
//把顶点信息转化到裁剪坐标下
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.color = v.color;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
#if UNITY_UI_CLIP_RECT
return 1;
#else
return 0.5;
#endif
fixed4 mainTex = tex2D(_MainTex,i.uv);
return mainTex * i.color;
}
ENDCG
}
}
}
效果:
二、需要申明一个_ClipRect,这是使用上面这个变体需要使用的,这个属性并没有在Properties声明
同时需要声明:_ClipRect(一个四维向量,四个分量分别表示RectMask2D的左下角点的xy坐标与右上角点的xy坐标.)
这个坐标是用来存储RectMask所占的位置信息
因为UI是一个矩形,所以记录了 左下角 和 右上角 顶点信息 后就可以知道 RectMask所占的位置
注意:在UGUI中模型顶点的本地坐标,经过顶点着色器传入片段着色器会转化为屏幕坐标(即看见的坐标值很大不是UI模型的本地坐标,而是UI模型的屏幕坐标)
1、现在我们用简单的代码测试一下 _ClipRect 的使用
主要逻辑:
1、在应用程序阶段传入顶点着色器的结构体中 加入 顶点信息
2、在顶点着色着色器传入片元着色器的结构体中 加入 顶点信息
3、在 片元着色器中,使用 _ClipRect 的坐标信息用于判断,符合条件的返回 1(半透明白) ,不符合返回 0.5(半透明灰)
Shader"MyShader/P1_1_8"
{
Properties
{
//命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
//比如说,在更改 Image 的源图片时,同时更改这个
[PerRendererData]_MainTex("MainTex",2D) = "white"{}
_StencilComp ("Stencil Comparison", Float) = 8.000000
_Stencil ("Stencil ID", Float) = 0.000000
_StencilOp ("Stencil Operation", Float) = 0.000000
_StencilWriteMask ("Stencil Write Mask", Float) = 255.000000
_StencilReadMask ("Stencil Read Mask", Float) = 255.000000
_ColorMask ("Color Mask", Float) = 15.000000
}
SubShader
{
//更改渲染队列(UI的渲染队列一般是半透明层的)
Tags {"Queue" = "TransParent"}
//混合模式
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Stencil
{
Ref [_Stencil]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
Comp [_StencilComp]
Pass [_StencilOp]
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//声明一个变体,用于RectMask使用
#pragma multi_compile _ UNITY_UI_CLIP_RECT
#include "UnityCG.cginc"
//存储 应用程序输入到顶点着色器的信息
struct appdata
{
//顶点信息
float4 vertex:POSITION;
float2 uv : TEXCOORD;
//这里定义一个语义为Color的4维向量,用于传入顶点颜色,设置语义为COLOR后,这个变量就会与顶点颜色对应
fixed4 color:COLOR;
};
//存储 顶点着色器输入到片元着色器的信息
struct v2f
{
//裁剪空间下的位置信息(SV_POSITION是必须的)
float4 pos:SV_POSITION;
float2 uv : TEXCOORD;
//这里的语义主要代表精度不同,TEXCOORD 在这里只是代表高精度
fixed4 color : COLOR;
//定义一个四维变量存储顶点信息
float4 vertex : TEXCOORD1;
};
sampler2D _MainTex;
fixed4 _Color;
//在使用 RectMask 需要使用的变体时,需要声明一个四维变量 _ClipRect
float4 _ClipRect;
v2f vert(appdata v)
{
v2f o;
//把顶点信息转化到裁剪坐标下
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.color = v.color;
o.vertex = v.vertex;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
#if UNITY_UI_CLIP_RECT
if(_ClipRect.x < i.vertex.x)
{
return 1;
}
else
{
return 0.5;
}
#else
return 0.5;
#endif
fixed4 mainTex = tex2D(_MainTex,i.uv);
return mainTex * i.color;
}
ENDCG
}
}
}
效果:
然后我们基于以上的基础,让 内层UI只在 _ClipRect 范围内渲染
测试代码:
Shader"MyShader/P1_1_8"
{
Properties
{
//命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
//比如说,在更改 Image 的源图片时,同时更改这个
[PerRendererData]_MainTex("MainTex",2D) = "white"{}
_StencilComp ("Stencil Comparison", Float) = 8.000000
_Stencil ("Stencil ID", Float) = 0.000000
_StencilOp ("Stencil Operation", Float) = 0.000000
_StencilWriteMask ("Stencil Write Mask", Float) = 255.000000
_StencilReadMask ("Stencil Read Mask", Float) = 255.000000
_ColorMask ("Color Mask", Float) = 15.000000
}
SubShader
{
//更改渲染队列(UI的渲染队列一般是半透明层的)
Tags {"Queue" = "TransParent"}
//混合模式
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Stencil
{
Ref [_Stencil]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
Comp [_StencilComp]
Pass [_StencilOp]
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//声明一个变体,用于RectMask使用
#pragma multi_compile _ UNITY_UI_CLIP_RECT
#include "UnityCG.cginc"
//存储 应用程序输入到顶点着色器的信息
struct appdata
{
//顶点信息
float4 vertex:POSITION;
float2 uv : TEXCOORD;
//这里定义一个语义为Color的4维向量,用于传入顶点颜色,设置语义为COLOR后,这个变量就会与顶点颜色对应
fixed4 color:COLOR;
};
//存储 顶点着色器输入到片元着色器的信息
struct v2f
{
//裁剪空间下的位置信息(SV_POSITION是必须的)
float4 pos:SV_POSITION;
float2 uv : TEXCOORD;
//这里的语义主要代表精度不同,TEXCOORD 在这里只是代表高精度
fixed4 color : COLOR;
//定义一个四维变量存储顶点信息
float4 vertex : TEXCOORD1;
};
sampler2D _MainTex;
fixed4 _Color;
//在使用 RectMask 需要使用的变体时,需要声明一个四维变量 _ClipRect
float4 _ClipRect;
v2f vert(appdata v)
{
v2f o;
//把顶点信息转化到裁剪坐标下
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.color = v.color;
o.vertex = v.vertex;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
#if UNITY_UI_CLIP_RECT
if(_ClipRect.x < i.vertex.x && _ClipRect.z > i.vertex.x && _ClipRect.y < i.vertex.y && _ClipRect.w > i.vertex.y)
{
fixed4 mainTex = tex2D(_MainTex,i.uv);
return mainTex * i.color;
}
else
{
return 0;
}
#else
return 0.5;
#endif
}
ENDCG
}
}
}
效果:
2、因为 if 语句在 Shader 中十分消耗性能,所以要避免使用 if 语句 ,if只适合用于理解原理(三目运算符也是同理)
我们使用Math中的 step(a,b) 函数来解决这个问题
如果a<=b返回1,否则返回0.
如果使用 这个 Math 方法,则可以按这样的思路设计,使用宏判断后,在宏判断中记录 step 后的值,然后最后与需要输出的颜色混合输出即可。(因为0乘任何数等于0)
所以这里把之前的条件语句转化为了如下语句
value = step(_ClipRect.x,i.vertex.x) * step(i.vertex.x,_ClipRect.z) * step(_ClipRect.y,i.vertex.y) * step(i.vertex.y,_ClipRect.w);
3、以上代码还可以再进一步优化,因为 step 不只可以用与点之间的比较,可以用于向量之间的比较,所以在以上代码的基础上,减少step的使用
fixed2 rect = step(_ClipRect.xy,i.vertex.xy) * step(i.vertex.xy,_ClipRect.zw);
value = rect.x * rect.y ;
因为我们使用的混合模式为
Blend SrcAlpha OneMinusSrcAlpha
所以使用 纹理采样后的透明值 与输出结果相乘,即可让透明部分透明
return mainTex * i.color * value * mainTex.a;
4、使用 UnityGet2DClipping (float2 position, float4 clipRect)
需要导入库:#include <UnityUI.cginc>文章来源:https://www.toymoban.com/news/detail-731136.html
value = UnityGet2DClipping(i.vertex,_ClipRect);
//函数实现 和 法3一样文章来源地址https://www.toymoban.com/news/detail-731136.html
5、最终测试代码
Shader"MyShader/P1_1_9"
{
Properties
{
//命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
//比如说,在更改 Image 的源图片时,同时更改这个
[PerRendererData]_MainTex("MainTex",2D) = "white"{}
_StencilComp ("Stencil Comparison", Float) = 8.000000
_Stencil ("Stencil ID", Float) = 0.000000
_StencilOp ("Stencil Operation", Float) = 0.000000
_StencilWriteMask ("Stencil Write Mask", Float) = 255.000000
_StencilReadMask ("Stencil Read Mask", Float) = 255.000000
_ColorMask ("Color Mask", Float) = 15.000000
}
SubShader
{
//更改渲染队列(UI的渲染队列一般是半透明层的)
Tags {"Queue" = "TransParent"}
//混合模式
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Stencil
{
Ref [_Stencil]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
Comp [_StencilComp]
Pass [_StencilOp]
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//声明一个变体,用于RectMask使用
#pragma multi_compile _ UNITY_UI_CLIP_RECT
#include <UnityUI.cginc>
#include "UnityCG.cginc"
//存储 应用程序输入到顶点着色器的信息
struct appdata
{
//顶点信息
float4 vertex:POSITION;
float2 uv : TEXCOORD;
//这里定义一个语义为Color的4维向量,用于传入顶点颜色,设置语义为COLOR后,这个变量就会与顶点颜色对应
fixed4 color:COLOR;
};
//存储 顶点着色器输入到片元着色器的信息
struct v2f
{
//裁剪空间下的位置信息(SV_POSITION是必须的)
float4 pos:SV_POSITION;
float2 uv : TEXCOORD;
//这里的语义主要代表精度不同,TEXCOORD 在这里只是代表高精度
fixed4 color : COLOR;
//定义一个四维变量存储顶点信息
float4 vertex : TEXCOORD1;
};
sampler2D _MainTex;
fixed4 _Color;
//在使用 RectMask 需要使用的变体时,需要声明一个四维变量 _ClipRect
float4 _ClipRect;
v2f vert(appdata v)
{
v2f o;
//把顶点信息转化到裁剪坐标下
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.color = v.color;
o.vertex = v.vertex;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
float value = 0;
#if UNITY_UI_CLIP_RECT
//法1、使用if有助于理解
/*if(_ClipRect.x < i.vertex.x && _ClipRect.z > i.vertex.x && _ClipRect.y < i.vertex.y && _ClipRect.w > i.vertex.y)
{
return 1;
}
else
{
return 0;
}*/
//法2、利用step来优化if
//value = step(_ClipRect.x,i.vertex.x) * step(i.vertex.x,_ClipRect.z) * step(_ClipRect.y,i.vertex.y) * step(i.vertex.y,_ClipRect.w);
//法3、使用step进行向量比较,减少step的使用数量
/*fixed2 rect = step(_ClipRect.xy,i.vertex.xy) * step(i.vertex.xy,_ClipRect.zw);
value = rect.x * rect.y;*/
//法4、利用Unity自带函数实现
value = UnityGet2DClipping(i.vertex,_ClipRect);
#else
return 0.5;
#endif
fixed4 mainTex = tex2D(_MainTex,i.uv);
return mainTex * i.color * value * mainTex.a;
}
ENDCG
}
}
}
到了这里,关于Unity中UI Shader遮罩RectMask2D的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!