在做功能时美术有个效果是UGUI的渐变透明,实现上可以用遮罩,但是Unity提供的遮罩RectMask2D是硬裁剪,即超出遮罩范围就alpha直接设为0,没有过渡,所以我就要给这个图片设一个自定义shader让它能支持alpha渐变。
其实就是修改下Unity的UI Default shader,但注意Unity在下载时每个版本都提供了内置着色器,所以你还要选择对应的Unity版本的内置着色器代码。
将UI Default shader的UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);替换为自定义函数,UI Default代码不放了,要从对应版本的Unity(官网)下载,我一开始从网上找了个UI Default shader可能因为接口对不上,连遮罩都不成功。
从Unity的安装目录下找到CGIncludes/UnityUI.cginc
inline float UnityGet2DClipping (in float2 position, in float4 clipRect)
{
float2 inside = step(clipRect.xy, position.xy) * step(position.xy, clipRect.zw);
return inside.x * inside.y;
}
clipReect传入的是float4,clipRect.xy是左上遮罩的裁剪位置,clipRect.zw是右下遮罩的裁剪位置.
step()用法:https://developer.download.nvidia.cn/cg/step.html
step - implement a step function returning either zero or one
step(clipRect.xy, position.xy) * step(position.xy, clipRect.zw)
即position.xy >= clipRect.xy返回1,同样position.xy < clipRect.zw返回1,虽然参数是float3,但传fix/fix2/fix3/fix4, half~half4, float~float4都可以cg内部帮我们做了转换。
step的操作减少了gpu做比较运算的消耗,gpu不适合做<这类运算,那是cpu擅长的。
通过这一步,可以判断这个世界坐标在clipRect内否,不满足在内部,则直接设置alpha=0,刚好step() * step()计算后在clipRect外的返回值为0.
SoftUnityGet2DClipping软裁剪
使用的是雨松momo的裁剪,https://www.xuanyusong.com/archives/4650,momo还用UGUI的方式裁剪了模型,我做的功能倒不需要裁剪模型。
float _ClipSoftX;
float _ClipSoftY;
inline float SoftUnityGet2DClipping(in float2 position, in float4 clipRect)
{
float2 xy = (position.xy - clipRect.xy) / float2(_ClipSoftX, _ClipSoftY)*step(clipRect.xy, position.xy);
float2 zw = (clipRect.zw - position.xy) / float2(_ClipSoftX, _ClipSoftY)*step(position.xy, clipRect.zw);
float2 factor = clamp(0, zw, xy);
return saturate(min(factor.x, factor.y));
}
做裁剪区域判定时,添加了类似下面的代码,将裁剪内的坐标alpha添加一个除法,除以我需要软化的边缘像素值,比如
(position.xy - clipRect.xy) / float2(_ClipSoftX, _ClipSoftY)
_ClipSoftX=15,position距离clipRect 15坐标单位的点,其alpha为距离/15。
clamp - returns smallest integer not less than a scalar or each vector component.
factor是在距离左上和右下边距里,取个离边距最近的alpha值。
saturate - returns smallest integer not less than a scalar or each vector component.
Returns x saturated to the range [0,1] as follows:
https://developer.download.nvidia.cn/cg/saturate.html
最后将输出值规范化为0~1区间内的值。
效果:
TMP直接提供了渐变参数,在Debug Settings-Softness X,放在RectMask2D里会软化左右X方向的裁剪。
------------------------------------------2020.5.12 更新 特效遮罩
最近遇到了特效在UI上列表滑动是需要遮罩显示,想起雨松大神的[UGUI软裁剪](https://www.xuanyusong.com/archives/4650),文章里提到用UGUI的方式处理3D模型,按道理特效也可以使用这种用方式,其中遇到一个‘坑’:完全照搬代码不看Unity shader版本你看着代码很完美单就是显示不对,一定要用当前Unity版本的shader!!!
新建ParticleMask.cs继承UIBehavIour, IClippable, ICanvasElement,UGUI在处理响应RectMask2D区域时会调用IClippable相应接口,主要还是SetClipRect()
ParticleMask没有挂RectMask2D,GiftScrollView上有RectMask2D,RectMask2D挂在ScrollView上比较符合我们项目的开发方式,ParticleMask的位置和大小都是0,由于ScrollView的运行方式,ParticleMask不好设置Scale(100, 100, 100)。
特效遮罩需要的ClipRect参数是通过运行时的方式设给特效对应Material,注意要设置aharedMaterial,否则Unity会创建一个Material Instance(多一个Material实例对象)。
public void SetClipRect(Rect value, bool validRect)
{
//在这里就可以将正确的Rect2DMask传入shader了
if (m_RendererList != null && m_RendererList.Count > 0)
{
Vector4 clipRect = new Vector4(value.xMin, value.yMin, value.xMax, value.yMax);
for (int i = 0; i < m_RendererList.Count; i++)
{
//缩放是屏幕高度的一半
float scale = m_Canvas.sizeDelta.y / 2f;
//将RectMask2D的区域传入
m_RendererList[i].sharedMaterial.SetVector("_ClipRect", clipRect);
//将Scale传入
m_RendererList[i].sharedMaterial.SetFloat("_Scale", scale);
}
}
}
在shader内的设置和UI-Default.shader的设置差不多,inline UnityGet2DClipping取自己本地Unity安装目录内的CGINCLUDE文件夹内的UnityUI.cginc函数,注意worldPosition的传值,Particle默认是没有worldPosition的。
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
};
float _Scale;
v2f vert(appdata_t v)
{
v2f o;
... // 这里的_Scale / 10主要是因为我的ParticleMask并没有设置缩放,所以具体场景具体分析~
o.worldPosition = mul(unity_ObjectToWorld, v.vertex * _Scale / 10);
return o;
}
不知道是哪个Unity版本用的clamp(0,zw,xy)裁剪,看来以后要养成习惯一开始就从使用的Unity版本中拷贝inline cg函数或者官方unity shader实现,少绕少纠结,一开始clamp(0, zw, xy)看着代码很正确但结果就是错的,我用的2017.4.11在UnityGet2DClipping最后判定在裁剪区域内用的inside.x * inside.y。
------------------------------------------2020.12.28 更新 示例代码
链接:https://pan.baidu.com/s/1Jrc5wGX0HNznJUfosVpHYQ 提取码:khc2