Instantly share code, notes, and snippets.
Last active
April 14, 2020 04:59
-
Save daniellanner/dab6e1b0a01c44bfafd39cec11276e96 to your computer and use it in GitHub Desktop.
Expanding Button - Unity shader done for a dribbble shot.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
(c) 2020 Daniel Lanner | |
This code is licensed under MIT license, do with it as you will | |
This shader creates an expanding button effect I used for a loading animation to a screen wipe. | |
It uses the uv.y coordinates to "expand" the button beyond it's boundaries. This means the geometry width | |
set within the inspector is not the final output as _IdleContraction reduces the de facto height of the button. | |
*/ | |
Shader "InDiversity/Unlit Frag/Expanding Button" | |
{ | |
Properties | |
{ | |
_PrevTex("Prev Tex", 2D) = "white" {} | |
_NextTex("Next Tex", 2D) = "white" {} | |
// expanding wave shape | |
_IdleContraction("Idle Contraction", Range(0.0, 0.5)) = 0.1 // uv.y width the button defaults to | |
_HighContraction("High Contraction", Range(0.0, 0.5)) = 0.0 // uv.y value the button expands to for the initial wave | |
_LowContraction("Low Contraction", Range(0.0, 0.5)) = 0.2 // uv.y value the button contracts to after the initial wave | |
_HighContractionWidth("Contraction Width", Range(0.0, 1.0)) = 0.1 // uv.x value of the width of the expanding initial wave | |
// contracted sin shape | |
_LowContractionFrequency("Low Contraction Frequency", float) = 4 | |
_LowContractionOffset("Low Contraction Offset", float) = 4 | |
_LowContractionSinStrength("Low Contraction Sin Strength", Range(0,1)) = 1 | |
// driving value | |
_T("Amount to Interpolate", Range(0.0, 1.0)) = 0.0 | |
} | |
SubShader | |
{ | |
Tags { "RenderType"="Opaque" } | |
LOD 100 | |
Pass | |
{ | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
#define PI 3.14159265 | |
#define HALF_PI 1.57079632 | |
#define TWO_PI 6.28318530 | |
#include "UnityCG.cginc" | |
struct appdata | |
{ | |
float4 vertex : POSITION; | |
float2 uv : TEXCOORD0; | |
}; | |
struct v2f | |
{ | |
float2 uv : TEXCOORD0; | |
float4 vertex : SV_POSITION; | |
float2 screenpos : TEXCOORD1; | |
}; | |
sampler2D _PrevTex; | |
float4 _PrevTex_ST; | |
sampler2D _NextTex; | |
float4 _NextTex_ST; | |
float _IdleContraction; | |
float _HighContraction; | |
float _LowContraction; | |
float _HighContractionWidth; | |
float _LowContractionFrequency; | |
float _LowContractionOffset; | |
float _LowContractionSinStrength; | |
float _T; | |
// utility functions | |
float GreaterThan(float a, float b) | |
{ | |
return (sign(a - b) + 1.0) / 2.0; | |
} | |
float LessThan(float a, float b) | |
{ | |
return 1.0 - GreaterThan(a, b); | |
} | |
float Ease(float x, float a) | |
{ | |
return pow(x, a) / (pow(x, a) + pow(1.0 - x, a)); | |
} | |
float SinBellEase(float x) | |
{ | |
return (sin(-HALF_PI + x * TWO_PI) + 1) * .5; | |
} | |
float SinBellEaseWithOffset(float x, float freq, float off) | |
{ | |
return (sin(-HALF_PI + x * freq * TWO_PI + off) + 1) * .5; | |
} | |
float NormalizedInRange(float value, float range_min, float range_max) | |
{ | |
return saturate((value - range_min) / (range_max - range_min)); | |
} | |
v2f vert (appdata v) | |
{ | |
v2f o; | |
o.vertex = UnityObjectToClipPos(v.vertex); | |
o.uv = TRANSFORM_TEX(v.uv, _PrevTex); | |
o.screenpos = ComputeScreenPos(o.vertex); | |
return o; | |
} | |
fixed4 frag (v2f i) : SV_Target | |
{ | |
// find state of pixel in uv.x axis | |
float halfConWidth = _HighContractionWidth * 0.5; | |
float delta = lerp(_T - halfConWidth, _T + halfConWidth, _T); | |
float highContraction = _IdleContraction; | |
highContraction += (_HighContraction - _IdleContraction) * SinBellEase(i.uv.x); | |
float lowContraction = _LowContraction * SinBellEaseWithOffset(i.uv.x, _LowContractionFrequency, _LowContractionOffset); | |
lowContraction = lerp(_LowContraction, lowContraction, _LowContractionSinStrength); | |
float isContracted = LessThan(i.uv.x, delta - halfConWidth); | |
float isIdle = GreaterThan(i.uv.x, delta + halfConWidth); | |
float isExpanded = 1.0 - (isContracted + isIdle); | |
// determine shape based on contraction state | |
float leftLerp = NormalizedInRange(i.uv.x, delta - halfConWidth, delta); | |
leftLerp = Ease(leftLerp, 2.0); | |
float rightLerp = NormalizedInRange(i.uv.x, delta, delta + halfConWidth); | |
rightLerp = Ease(rightLerp, 2.0); | |
float isLeftLerp = LessThan(i.uv.x, delta); | |
float expandedValue = | |
isLeftLerp * lerp(lowContraction, highContraction, leftLerp) + | |
(1.0 - isLeftLerp) * lerp(highContraction, _IdleContraction, rightLerp); | |
// color the pixel based on calculated values | |
// NOTE: this example is just setting the "non-button" part as the background color | |
float yRefPoint = abs(i.uv.y - 0.5); | |
fixed4 bgColor = tex2D(_PrevTex, i.screenpos.xy); | |
fixed4 frontColor = tex2D(_NextTex, i.screenpos.xy); | |
fixed4 leftCol = lerp(frontColor, bgColor, GreaterThan(yRefPoint, lowContraction)); | |
fixed4 centerCol = lerp(frontColor, bgColor, GreaterThan(yRefPoint, expandedValue)); | |
fixed4 rightCol = lerp(frontColor, bgColor, GreaterThan(yRefPoint, _IdleContraction)); | |
fixed4 col = | |
leftCol * isContracted + | |
rightCol * isIdle + | |
centerCol * isExpanded; | |
return col; | |
} | |
ENDCG | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment