先上效果:
首先说一下原理,这个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