A shader that will a add a bit of liveliness to your UI is always a good thing, especially when you are making a mobile game where grabbing and holding user attention is top priority.

This effect is known to hypnotize users into spending big bucks in your game (sarcasm). Anyway this shader is based on the optimized UI shader provided by Unity.
Masking, Clipping and some other fancy stuff is not available when using this shader on UI components.
Let's get to making out jiggly wiggly UI shader.
We will look at the properties being declared :
Properties
{
[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
_Color("Tint", Color) = (1,1,1,1)
_WobblesPerSecond("Wobbles per Second ", Float) = 2
_WobblePeriod("Wobble Period (pixels)", Float) = 45
_WobbleAmplitude("Wobble Amplitude (pixels)", Float) = 3.2
}
Here are the properties defined in the CG PROGRAM
along with #define
s:
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
/*1*/float _WobblesPerSecond;
/*2*/float _WobblePeriod;
/*3*/float _WobbleAmplitude;
/*4*/#define TWO_PI 6.28318
}
The things added on top of the optimized UI shader are the three floats - (1), (2) & (3) and we also define the value of 2x PI (4) as we will be using it in the vertex shader.
Now take a look the vertex shader as it does all the work:
1v2f vert(appdata_t IN)
2{
3 v2f OUT;
4 /*1*/float4 worldPos = IN.vertex;
5 /*2*/float offsetInput = (_Time.y + frac(worldPos.x)) * _WobblesPerSecond * TWO_PI + worldPos.x / _WobblePeriod;
6 /*3*/worldPos.y += sin(offsetInput) * _WobbleAmplitude;
7 /*4*/worldPos.x += cos(offsetInput) * _WobbleAmplitude;
8 /*5*/OUT.vertex = UnityObjectToClipPos(worldPos);
9 OUT.texcoord = IN.texcoord;
10 #ifdef UNITY_HALF_TEXEL_OFFSET
11 OUT.vertex.xy += (_ScreenParams.zw - 1.0)*float2(-1,1);
12 #endif
13 OUT.color = IN.color * _Color;
14 return OUT;
15}
Let's breakdown the steps:
- We are storing world space position of vertex in
worldPos
. - Calculate the offset that will be applied on the vertex position.
frac(worldPos.x)
acts as random seed value so that each objects wiggles differently and addition with_Time.y
will keep changing the offset value every frame.- Then the
_WobblesPerSecond
multiplied withTWO_PI
increases the frequency of the wiggle effect.+ worldPos.x / _WobblePeriod
adds additional variance to the wiggle movement, A largerWobblePeroid
means a larger distance between a crest and a trough and vise-versa. - Changing the y position of vertex with
sin(offsetInput)
which gives -1 to 1 result and then multiplying with_WobbleAmplitude
to get results in-_WobbleAmplitude
to+_WobbleAmplitude
. - Same thing as (3) except it's for changing x position of vertex and cosine is used.
- Converting the vertices from world space to clip space which gives the output for the vertex shader.
The fragment shader does not undergo any changes. The default values present in the UI shader is enough.
Here is the entire shader:
1Shader "BitshiftProductions/Fast-UI-Jiggle"
2{
3 Properties
4 {
5 [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
6 _Color("Tint", Color) = (1,1,1,1)
7 _WobblesPerSecond("Wobbles per Second ", Float) = 1
8 _WobblePeriod("Wobble Period (pixels)", Float) = 1
9 _WobbleAmplitude("Wobble Amplitude (pixels)", Float) = 0.03
10 }
11
12 SubShader
13 {
14 Tags
15 {
16 "Queue" = "Transparent"
17 "IgnoreProjector" = "True"
18 "RenderType" = "Transparent"
19 "PreviewType" = "Plane"
20 "CanUseSpriteAtlas" = "True"
21 }
22
23 Cull Off
24 Lighting Off
25 ZWrite Off
26 ZTest[unity_GUIZTestMode]
27 Blend SrcAlpha OneMinusSrcAlpha
28
29 Pass
30 {
31 CGPROGRAM
32 #pragma vertex vert
33 #pragma fragment frag
34
35 #include "UnityCG.cginc"
36 #include "UnityUI.cginc"
37
38 struct appdata_t
39 {
40 float4 vertex : POSITION;
41 float4 color : COLOR;
42 float2 texcoord : TEXCOORD0;
43 };
44
45 struct v2f
46 {
47 float4 vertex : SV_POSITION;
48 fixed4 color : COLOR;
49 half2 texcoord : TEXCOORD0;
50 };
51
52 fixed4 _Color;
53 fixed4 _TextureSampleAdd;
54
55 float _WobblesPerSecond;
56 float _WobblePeriod;
57 float _WobbleAmplitude;
58
59 #define PI 3.14159
60 #define TWO_PI 6.28318
61 v2f vert(appdata_t IN)
62 {
63 v2f OUT;
64 float4 worldPos = IN.vertex;
65 worldPos.y += sin((_Time.y + frac(worldPos.x)) * _WobblesPerSecond * TWO_PI + worldPos.x / _WobblePeriod) * _WobbleAmplitude;
66 worldPos.x += cos((_Time.y + frac(worldPos.x)) * _WobblesPerSecond * TWO_PI + worldPos.x / _WobblePeriod) * _WobbleAmplitude;
67 OUT.vertex = UnityObjectToClipPos(worldPos);
68 OUT.texcoord = IN.texcoord;
69
70 #ifdef UNITY_HALF_TEXEL_OFFSET
71 OUT.vertex.xy += (_ScreenParams.zw - 1.0)*float2(-1,1);
72 #endif
73 OUT.color = IN.color * _Color;
74 return OUT;
75 }
76
77 sampler2D _MainTex;
78 fixed4 frag(v2f IN) : SV_Target
79 {
80 return (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
81 }
82 ENDCG
83 }
84 }
85}