【ShaderLab】利用shader实现中心遮罩缩放效果

写在前面 

       前几天一个以前的同事突然说想叫我帮忙搞个着色器,想要做一个UI的过场效果。大概就像类似动画片里面结束的时候的黑场效果,需要用一个透明UI图在一个全黑的背景中间做放大缩小的操作。当透明UI最大时,显示场景的其他UI,当透明UI最小时,整个场景黑场,效果如下。

       其实这就是同事想要的效果了,实现的原理很简单,没花什么功夫就写出来了。后来想想写都写了,看看能不能优化一下这个显示效果,让这个过场更加好看点,于是便有了这篇文章。

正文

      实现这个效果原理也十分简单,首先获取到这张需要放大小缩小的图片uv,用于修改它的tilling跟offset,因为uv都是从0到1的,如果直接改变的话uv的话,只会以左上角为原点进行缩放,所以这里要将图片的offset偏移到(0.5,0.5)的位置再进行缩放;然后就是获取需要放大缩小的这张图片的alpha通道的信息,与底图进行一个比对,让两张图重合在一起并剔除掉缩放图的alpha为1的部分,也就是常说的掩膜效果。代码如下所示

Shader "custom/masknormal"
{
	Properties
	{
		//底图
		_MainTex("_MainTex",2D) = "white"{}	
		//需要缩放的图
		_MaskTex("_MaskTex", 2D) = "white" {}
		//缩放比例
		_Progress("Progress", Range(-0.5,0.5)) = 0                                       
	}
		SubShader
	{
		Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
		Blend SrcAlpha OneMinusSrcAlpha
		Cull Off ZWrite Off ZTest Always

		Pass
	{
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#pragma target 3.0
		#include "UnityCG.cginc"

	struct appdata
	{
		float4 vertex : POSITION;
		float2 uv : TEXCOORD0;
	};

	struct v2f
	{
		float2 uv : TEXCOORD0;
		float2 uv2:TEXCOORD1;
		float4 vertex : SV_POSITION;
	};
	sampler2D _MainTex;
	sampler2D _MaskTex;
	float _Progress; 
	float4 _MaskTex_ST;

	v2f vert(appdata v)
	{
		v2f o;
		o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
		o.uv = v.uv;
		return o;
	}
	fixed4 frag(v2f i) : SV_Target
	{
		//_Progress = pow(_Progress,2);
		_Progress = log(0.5 - _Progress);

	_MaskTex_ST.xy = _Progress;                                        
	_MaskTex_ST.zw = (_Progress - 1)*-0.5;     
	//缩放图 同时做为掩膜图 
	fixed4 maskcol = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw);   
	//底图取样
	fixed4 outcol = tex2D(_MainTex,i.uv);
	//去除缩放图的alpha值 
	outcol.w = outcol.a*(1 - maskcol.a);
	return outcol;
	}
		ENDCG
	}
	}
}

用了上面的shader,再在c#代码里面对_Progress进行一个插值控制,那么实现的效果就会跟这图一样了。

虽然说这效果给那前同事看了还挺满意的,但是这效果还是十分的简陋,本着写都写了还是弄个更好看的效果吧!

替换底色

黑色的背景换成了可调色,这个实现比较简单,直接将原本的输出颜色跟输入的颜色相乘即可。或者因为是纯色图,可以直接拿输入颜色的rgb与缩放的掩膜图的alpha通道作为颜色输出

模糊效果

透明的缩放图与底图之间的过度效果有点突兀,加上了一个模糊效果,实现的原理也相当简单,直接取当前顶点的左上,左下,右上,右下和本身五个点做平均再输出,得到的平均颜色作为片段的输出即可。

附上个效果图

代码如下

Shader "custom/mask"
{
	Properties
	{
		//底图
		_MainTex("_MainTex",2D) = "white"{}
	//需要缩放的图
	_MaskTex("_MaskTex", 2D) = "white" {}
	//缩放比例
	_Progress("Progress", Range(-0.5,0.5)) = 0    
         //底图颜色                                                             
		_Color("Color",color) = (0,0,0,0)
		//模糊系数
		_blurOffset("Blur",Range(0,0.01)) = 0.0075
	}
		SubShader
	{
		Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
		Blend SrcAlpha OneMinusSrcAlpha
		Cull Off ZWrite Off ZTest Always

		Pass
	{
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#pragma target 3.0
		#include "UnityCG.cginc"

		struct appdata
		{
			float4 vertex : POSITION;
			float2 uv : TEXCOORD0;
		};

		struct v2f
		{
			float2 uv : TEXCOORD0;
			float2 uv2:TEXCOORD1;
			float4 vertex : SV_POSITION;
		};

		sampler2D _MaskTex;
		sampler2D _MainTex;
		float _Progress;
		float4 _MaskTex_ST;
		fixed4 _Color;
		fixed _blurOffset;

	v2f vert(appdata v)
	{
		v2f o;
		o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
		o.uv = v.uv;
		o.uv2 = cos(o.uv*_Time.x);
		return o;
	}
	fixed4 frag(v2f i) : SV_Target
	{
			//_Progress = pow(_Progress,2);
			_Progress = log2(0.5 - _Progress);
			_MaskTex_ST.xy = _Progress;                      
			_MaskTex_ST.zw = (_Progress - 1)*-0.5;                               
			fixed4 maskcol = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw);   
			//高斯模糊
			//leftup
			fixed4 maskcol1 = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw + fixed2(-_blurOffset, _blurOffset));
			//leftdown
			fixed4 maskcol2 = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw + fixed2(-_blurOffset, -_blurOffset));
			//rightup
			fixed4 maskcol3 = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw + fixed2(_blurOffset, _blurOffset));
			//rightdown
			fixed4 maskcol4 = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw + fixed2(_blurOffset, -_blurOffset));
			fixed4 mix = (maskcol + maskcol1 + maskcol2 + maskcol3 + maskcol4)*0.2;
			mix = lerp(maskcol, mix, 0.5);
			fixed4 outcol = tex2D(_MainTex,i.uv);
			outcol.w = outcol.a*(1 - mix.a);
			outcol = fixed4(_Color.x,_Color.y,_Color.z, outcol.w);
			return outcol;
	}
		ENDCG
	}
	}
}

这么一来看起来就顺眼多了。。

但是,不知道是脑抽还是怎么了,我还是想加个缩放图旋转的效果。就是因为这抽一抽上网研究了几天才研究出来,只怪自己以前数学菜又没好好听课,先贴上个uv旋转公式

至于为什么是这个公式,可以看下我的笔记

附上个效果图

最后附上代码

Shader "custom/mask1"
{
	Properties
	{
		_MainTex("_MainTex",2D) = "white"{}	
		_MaskTex("_MaskTex", 2D) = "white" {}  
		_Progress("Progress", Range(0,1)) = 0 
		_Color("Color",color) = (0,0,0,0)		
		_blurOffset("Blur",Range(0,0.03)) = 0.0075
		_RotateSpeed("RotateSpeed",Range(0,10))=5
	}
		SubShader
	{
		Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
		Blend SrcAlpha OneMinusSrcAlpha
		Cull Off ZWrite Off ZTest Always

		Pass
	{
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#pragma target 3.0
		#include "UnityCG.cginc"

		struct appdata
		{
			float4 vertex : POSITION;
			float2 uv : TEXCOORD0;
		};

		struct v2f
		{
			float2 uv : TEXCOORD0;
			float4 vertex : SV_POSITION;
		};

		sampler2D _MaskTex;
		sampler2D _MainTex;
		float _Progress;  //声明缩放量
		float4 _MaskTex_ST;
		fixed4 _Color;
		fixed _blurOffset;
		fixed _RotateSpeed;

		v2f vert(appdata v)
		{
			v2f o;
			o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
			o.uv = v.uv;
			return o;
		}
		fixed4 frag(v2f i) : SV_Target
		{
			fixed4 uv;
			//手动控制缩放
			//_Progress =abs(log(_Progress)));
			_Progress =abs(log(sin(_Time.y)));
			//offset偏移
			 uv.xy = i.uv.xy- fixed2(0.5, 0.5);
			uv.zw *=(1-_Progress) ; 
			//旋转公式 乘上缩放
			uv.xy = fixed2(uv.x * cos(_RotateSpeed * _Time.y) - uv.y * sin(_RotateSpeed * _Time.y),
			uv.x * sin(_RotateSpeed * _Time.y) + uv.y * cos(_RotateSpeed * _Time.y))*_Progress;
			//复原offset
			uv.xy += fixed2(0.5, 0.5);
			fixed4 maskcol = tex2D(_MaskTex, uv); 

			//高斯模糊
			//leftup
			fixed4 maskcol1 = tex2D(_MaskTex, uv+fixed2(-_blurOffset, _blurOffset));
			//leftdown
			fixed4 maskcol2 = tex2D(_MaskTex, uv + fixed2(-_blurOffset, -_blurOffset));
			//rightup
			fixed4 maskcol3 = tex2D(_MaskTex, uv + fixed2(_blurOffset, _blurOffset));
			//rightdown
			fixed4 maskcol4 = tex2D(_MaskTex, uv + fixed2(-_blurOffset, -_blurOffset));
			fixed4 mix = (maskcol + maskcol1 + maskcol2 + maskcol3 + maskcol4)*0.2;
			mix = lerp(maskcol, mix, 0.5);
			fixed4 outcol = tex2D(_MainTex,uv);
			outcol.w = outcol.a*(1 - mix.a);
			outcol = fixed4(_Color.x,_Color.y,_Color.z, outcol.w);
			return outcol;
		}
		ENDCG
	}
	}
}

总结

      因为懒得再写C#代码来控制shader,所以直接用sin函数来控制了缩放所以看起来周期会有点长,同时缩放比例的控制还是有点不太理想,只想到用一个log方法来控制,所以缩放会一下子消失,之后再想想优化的方法吧就写到这里。uv的旋转以前都是直接网上抄别人的公式,这次是自己来推导了一次,还好没忘光。uv的旋转和缩放这两个分开来写比较简单,但运用在一起了就会乱,改了几天代码缩放是没问题的,但旋转却是绕着某一个顶点进行旋转了,都几乎快放弃了不知为何又脑子一抽想出来了,果然做事只要坚持下去还是会有收获的!

猜你喜欢

转载自blog.csdn.net/ssssssilver/article/details/91822748