1. 程式人生 > >【Unity Shader】基於UGUI的水波倒影按鈕

【Unity Shader】基於UGUI的水波倒影按鈕

先上效果:


首先說一下原理,這個shader實現的原理很簡單,通過使用兩個Pass,第一個Pass先正常渲染一遍UI,第二個Pass將UV座標y值上下翻轉,並將頂點座標偏移一定距離,再做扭曲操作。實現起來並不是很難,主要需要注意的就是UGUIshader要注意的幾個格式,比如UGUI shader都要有模板測試和ColorMask的部分(如果你希望你的shader能支援UGUI mask的話)。

首先是shader的模板測試部分,(模板測試參考官網:http://docs.unity3d.com/Manual/SL-Stencil.html)


在屬性塊中定義這些屬性,如果希望能支援UIMask,則這是必須的,注意前5段定義了模板快取的比較函式,模板值,測試成功後的操作函式,以及模板讀寫的掩碼,shader中的使用方式如下:


我們知道正常使用模板快取,需要指定模板值,比較函式等,並且通常都是指定一個具體的列舉值,比如Greater,Less,Equal,LEqual等,而UGUIshader則直接將具體的數值傳入,當然這一過程是有UGUIMask指令碼完成的,那麼這些具體的數值是如何和比較函式一一對應的,我們可以看下UnityEngine.Rendering名稱空間,


其中包括一些shader中常用列舉,包括blend的混合因子,當然還有深度測試和模板測試的比較方式,cullMode等,也就是說如果希望嘗試編寫一個shader,其光柵化階段的模板測試,深度測試,以及混合等是可以通過指令碼控制的,則可以使用這種方式。

另外UGUIshader中還是用了ColorMask,注意UGUI原始碼中Mask類是如何使用ColorMask的,可以看到當UGUIMask關閉ShowMaskGraphic時colormask會被設為0,也就是UGUImask通過使用colormask來完成”是否顯示mask本身“的功能。

以上部分是UGUIshader中為了讓你寫的UIshader支援Mask功能所做的事。

(支援Mask的UGUI水波:)


另外,UGUI有一點需要注意,UGUI Image類的顏色資訊和Button類的滑鼠滑過或點選的反饋顏色都是通過直接修改UGUI的網格的頂點色實現的,換句話說如果希望自己寫的UGUIshader能獲取到Button中滑鼠滑過或點選的顏色,則必須在shader的頂點輸入結構中包含頂點顏色資訊。

接下來說一下水波的原理,主要就是使用一張噪波貼圖,其R通道與G通道如下:


shader中通過讀入該噪波貼圖的RG通道,並將其顏色值轉換為-1到1的範圍,再乘上一個偏移強度,就得到了uv的偏移量,當使用_Time將噪波貼圖運動,就能達到水波的效果了。另外我還在shader中加入了扭曲強度和alpha的淡入淡出效果,直接貼原始碼:

Shader "MyUI/UI Wave"
{
	Properties
	{
		[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
		_NoiseTex ("Wave Noise", 2D) = "white" {}//噪波貼圖
		_Offset ("Vertex Offset", vector) = (0, 0, 0, 0)//表示頂點的偏移量
		_Indentity ("Indentity", float) = 0.1//表示水波的扭曲強度
		_SpeedX ("WaveSpeedX", float) = 0.08//噪波貼圖延X方向的移動速度
		_SpeedY ("WaveSpeedY", float) = 0.04//噪波貼圖延Y方向的移動速度
		_AlphaFadeIn ("AlphaFadeIn", float) = 0.0//水波的淡入位置
		_AlphaFadeOut ("AlphaFadeOut", float) = 1.0//水波的淡出位置
		_TwistFadeIn ("TwistFadeIn", float) = 1.0//扭曲的淡入位置
		_TwistFadeOut ("TwistFadeOut", float) = 1.01//扭曲的淡出位置
		_TwistFadeInIndentity ("TwistFadeInIndentity", float) = 1.0//扭曲的淡入強度
		_TwistFadeOutIndentity ("TwistFadeOutIndentity", float) = 1.0//扭曲的淡出強度
		_Color ("Tint", Color) = (1,1,1,1)

		_StencilComp ("Stencil Comparison", Float) = 8
		_Stencil ("Stencil ID", Float) = 0
		_StencilOp ("Stencil Operation", Float) = 0
		_StencilWriteMask ("Stencil Write Mask", Float) = 255
		_StencilReadMask ("Stencil Read Mask", Float) = 255

		_ColorMask ("Color Mask", Float) = 15
	}

	CGINCLUDE
	//定義頂點的輸入結構
		struct appdata_ui
		{
			float4 vertex   : POSITION;
			float4 color    : COLOR;
			float2 texcoord : TEXCOORD0;
		};

		//定義頂點到片段的結構
		struct v2f_ui
		{
			float4 pos   : SV_POSITION;
			fixed4 color    : COLOR;
			half2 uv  : TEXCOORD0;
		};

		fixed4 _Color;

		//兩個Pass通用的頂點函式
		void vert_ui(inout appdata_ui Input, out v2f_ui Output){

			Output.pos = mul(UNITY_MATRIX_MVP, Input.vertex);
				Output.uv = Input.texcoord;
			#ifdef UNITY_HALF_TEXEL_OFFSET
				Output.uv.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
			#endif
				Output.color = Input.color * _Color;
		}
	ENDCG

	SubShader
	{
		Tags
		{
			"Queue"="Transparent"
			"IgnoreProjector"="True"
			"RenderType"="Transparent"
			"PreviewType"="Plane"
			"CanUseSpriteAtlas"="True"
		}

		Stencil
		{
			Ref [_Stencil]
			Comp [_StencilComp]
			Pass [_StencilOp]
			ReadMask [_StencilReadMask]
			WriteMask [_StencilWriteMask]
		}

		Cull Off
		Lighting Off
		ZWrite Off
		ZTest [unity_GUIZTestMode]
		Fog { Mode Off }
		Blend SrcAlpha OneMinusSrcAlpha
		ColorMask [_ColorMask]

		Pass
		{
			//第一個Pass,正常渲染
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"


			v2f_ui vert(appdata_ui v)
			{
				v2f_ui o;
				vert_ui(v, o);
				return o;
			}

			sampler2D _MainTex;

			fixed4 frag(v2f_ui i) : SV_Target
			{
				half4 color = tex2D(_MainTex, i.uv) * i.color;
				clip (color.a - 0.01);
				return color;
			}
		ENDCG
		}

		Pass
		{
			//第二個Pass,渲染水波
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			sampler2D _NoiseTex;

			float4 _Offset;
			half _SpeedX;
			half _SpeedY;
			half _Indentity;
			float _AlphaFadeIn;
			float _AlphaFadeOut;
			half _TwistFadeIn;
			half _TwistFadeOut;
			fixed _TwistFadeInIndentity;
			fixed _TwistFadeOutIndentity;

			v2f_ui vert(appdata_ui v)
			{
				v2f_ui o;
				v.vertex = v.vertex - float4 (_Offset.xyz, 0);//偏移頂點座標
				vert_ui(v, o);
				return o;
			}

			sampler2D _MainTex;

			fixed4 frag(v2f_ui i) : SV_Target
			{
				//對淡入強度和淡出強度的插值
				fixed fadeT = saturate((_TwistFadeOut - i.uv.y) / (_TwistFadeOut - _TwistFadeIn));
				float2 tuv = (i.uv - float2(0.5, 0)) * fixed2(lerp(_TwistFadeOutIndentity, _TwistFadeInIndentity, fadeT), 1) + float2(0.5, 0);

				//計算噪波貼圖的RG值,得到扭曲UV,
				float2 waveOffset = (tex2D(_NoiseTex, tuv + float2(0, _Time.y * _SpeedY)).rg + tex2D(_NoiseTex, tuv + float2(_Time.y * _SpeedX, 0)).rg) - 1;
				float2 ruv = float2(i.uv.x, 1 - i.uv.y) + waveOffset * _Indentity;

				//使用扭曲UV對紋理取樣
				float4 c = tex2D (_MainTex, ruv);

				//對淡入Alpha和淡出Alpha的插值
				fixed fadeA = saturate((_AlphaFadeOut - ruv.y) / (_AlphaFadeOut - _AlphaFadeIn));
				c = c * _Color * i.color * fadeA;
				clip (c.a - 0.01);
				return c;
			}
		ENDCG
		}
	}
}