按钮UI动效——水波纹涟漪点击效果
- 效果图:
- 按钮Mask贴图:
可以自己画
- 源代码:
- Shader(不使用SDF):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127Shader "Unlit/BtnClickWaveShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ColorLeft ("ColorLeft", Color) = (0,1,0,1)
_ColorRight ("ColorRight", Color) = (0,0,1,1)
_Radius ("Radius", Float) = 1
_RadiusThickness ("RadiusThickness", Float) = 1
_WaveAlpha ("WaveAlpha", Range(0, 1)) = 0.5
[HDR] _WaveColor ("WaveColor", Color) = (1,1,1,1)
[hideInInspector] _CircleScaleOffset ("CircleScaleOffset", Float) = 1 // Width / Height
[hideInInspector] _MouseUVPos ("MouseUVPos", Vector) = (0,0,0,0)
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _ColorLeft;
float4 _ColorRight;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 shape = tex2D(_MainTex, i.uv);
fixed4 col = fixed4(lerp(_ColorLeft, _ColorRight, i.uv.x).rgb, shape.a);
return col;
}
ENDCG
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _ColorLeft;
float4 _ColorRight;
float _Radius;
float _RadiusThickness;
float _CircleScaleOffset;
float _WaveAlpha;
float4 _WaveColor;
float4 _MouseUVPos;
fixed4 PixelColor(float2 uv)
{
// 将uv原点移到中心
uv -= _MouseUVPos.xy;
uv = uv * 2 - 1;
// 离uv坐标原点的距离,排除屏幕长宽比、图片长宽比的影响
float2 dv = float2(uv.x, uv.y)* float2(_ScreenParams.x / _ScreenParams.y * _CircleScaleOffset, 1);
float d = sqrt(pow(dv.x, 2) + pow(dv.y, 2));
// 用小圆减大圆 得到圆环
fixed3 c = smoothstep(_Radius-_RadiusThickness, _Radius, d) - smoothstep(_Radius, _Radius+_RadiusThickness, d);
return fixed4(c * _WaveColor.rgb, saturate(c.g)); // -0.5
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 shape = tex2D(_MainTex, i.uv);
fixed4 wave = PixelColor(i.uv);
wave.rgb *= _WaveColor.rgb;
wave.a *= shape.a * _WaveAlpha;
return wave;
}
ENDCG
}
}
} - Shader(使用SDF):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132Shader "Unlit/BtnClickWaveSDFShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ColorLeft ("ColorLeft", Color) = (0,1,0,1)
_ColorRight ("ColorRight", Color) = (0,0,1,1)
[hideInInspector] _CircleScaleOffset ("CircleScaleOffset", Float) = 1 // Width / Height
[hideInInspector] _CirclePosOffset ("CirclePosOffset", Vector) = (0,0,0,0)
_Radius ("Radius", Float) = 1.5
_RadiusThickness ("RadiusThickness", Float) = 0.2
[HDR] _WaveColor ("WaveColor", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _ColorLeft;
float4 _ColorRight;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 shape = tex2D(_MainTex, i.uv);
fixed4 col = fixed4(lerp(_ColorLeft, _ColorRight, i.uv.x).rgb, shape.a);
return col;
}
ENDCG
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _CircleScaleOffset;
float _Radius;
float4 _CirclePosOffset;
float _RadiusThickness;
float4 _WaveColor;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
float sdCircle(float2 p, float r)
{
return length(p) - r;
}
float opAnnular(float sdf, float r)
{
return abs(sdf) - r;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 shape = tex2D(_MainTex, i.uv);
// 全象限
i.uv -= _CirclePosOffset.xy;
i.uv.y = 1.0 - i.uv.y;
i.uv = i.uv * 2 - 1;
// 消除屏幕拉伸影响
float w = _ScreenParams.x;
float h = _ScreenParams.y;
half co = w/h * _CircleScaleOffset;
i.uv = float2(i.uv.x * co, i.uv.y);
float step = 1.0 / w;
fixed4 circle = smoothstep(step, -step, opAnnular(sdCircle(i.uv, _Radius) , _RadiusThickness));
fixed4 col = fixed4(circle.rgb, shape.a);
col.rgb = col.rgb * _WaveColor.rgb;
col.a *= col.r;
col.a = _WaveColor.a * col.a;
return col;
}
ENDCG
}
}
} - C#(两种shader共用):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
public class BtnClickWaveControl : MonoBehaviour
{
private Material material;
private Button btn;
private Sequence sequence;
[SerializeField] private float waveDuration = 2f;
private Vector2 mouseUVPos;
// SDF
[SerializeField] private bool useSDF = false;
private Vector4 tmp_color;
private Vector4 targetColor;
void Start()
{
material = GetComponent<Image>().material = new Material(GetComponent<Image>().material);
btn = GetComponent<Button>();
RectTransform rect = GetComponent<RectTransform>();
material.SetFloat("_CircleScaleOffset", rect.sizeDelta.x / rect.sizeDelta.y);
material.SetFloat("_Radius", -material.GetFloat("_RadiusThickness"));
if (useSDF)
{
tmp_color = material.GetColor("_WaveColor");
targetColor = new Vector4(tmp_color.x, tmp_color.y, tmp_color.z, 0);
}
btn.onClick.AddListener(() =>
{
Vector2 uipos = Vector3.one;
RectTransformUtility.ScreenPointToLocalPointInRectangle(transform.parent as RectTransform, Input.mousePosition, Camera.main, out uipos);
mouseUVPos = (uipos - (Vector2)rect.localPosition) / rect.sizeDelta;
if (!useSDF)
{
material.SetVector("_MouseUVPos", mouseUVPos);
sequence?.Kill();
sequence = DOTween.Sequence().SetUpdate(true).SetId(transform)
.AppendCallback(() =>
{
material.SetFloat("_Radius", -material.GetFloat("_RadiusThickness"));
})
.Append(material.DOFloat(10f, "_Radius", waveDuration));
}
else
{
material.SetVector("_CirclePosOffset", mouseUVPos);
sequence?.Kill();
sequence = DOTween.Sequence().SetUpdate(true).SetId(transform)
.AppendCallback(() =>
{
material.SetFloat("_Radius", -material.GetFloat("_RadiusThickness"));
material.SetColor("_WaveColor", tmp_color);
})
.Append(material.DOFloat(6f, "_Radius", waveDuration))
.Join(material.DOColor(targetColor, "_WaveColor", waveDuration));
}
});
}
private void OnDestroy()
{
DOTween.Kill(transform);
}
}