先上效果:
首先說一下原理,這個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
}
}
}
https://github.com/AsehesL/UGUIEffect