- alexlpc2015
[Shader Notes] #02 - Dissolve
Hi and welcome to my Shader Notes Series!
This is a comprehensive project with notes about shaders of nearly every common effect included (implemented in Unity). I will explain the key points and theories behind them for you to understand them thoroughly and then enjoy creating your own shaders with proficiency.
Let's start learning together!
#02 - Dissolve
Difficulty: 3/10
Points covered:
Basic dissolve effect
Dissolve effect with direction
Dissolve effect with color fading
Dissolve effect is very commonly used in various kinds of games. We will explore 3 forms of dissolve effects in this note.
Final effect:

Setup:
Create a new material and attach it to an object.
Prepare the properties and declare them in CGPROGRAM.
//Properties
_Color ("Color Tint", Color) = (1,1,1,1)
_HighlightColor ("Highlight Color", Color) = (0.9,0.8,0.7,1)
_MainTex ("Texture", 2D) = "white" {}
_Noise ("Noise", 2D) = "white" {}
_Direction ("Direction", Vector) = (0,1,0,0)
_Threshold ("Threshold", range(0,1)) = 0.5
_HighlightThreshold ("Highlight Threshold", float) =1
There is not much to do in the vertex shader. Prepare some data for the fragment shader and we are good to go.
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.noiseuv = TRANSFORM_TEX(v.uv, _Noise);
return o;
}
In the fragment shader, sample the main texture and multiply it by the tint color.
fixed4 col = tex2D(_MainTex, i.uv) * _Color;
Point #1: Basic dissolve effect
Whenever there is a seemingly random visual effect, it's probably associated with a noise texture. We'll use this one for this effect.

The theory behind this effect is very intuitive:
- Sample a color on the noise texture according to a fragment's UV.
fixed4 noiseTex = tex2D(_Noise,i.noiseuv);
- Lerp between the original color and a transparent color according to the sampled noise color.
fixed noiseDiff = noiseTex.r- _Threshold;
col = lerp(col, fixed4(col.xyz,0), saturate(1 - noiseDiff));
- Smoothly adjust the threshold for the last step.
Although this simple approach generates a dissolve effect, it is very limited and can hardly be used in actual games.
Point #2: Dissolve effect with direction
The effect in #1 is dissolving as a whole, but what if we want a effect with customizable direction?
We can specify the x and y direction in the inspector and multiply by the corresponding UV component to "increase" or "decrease" the noise value sampled by the actual UV.
_Direction ("Direction", Vector) = (0,1,0,0)
//...
float t =( i.uv.x * _Direction.x - 1+_Threshold*2) +
( i.uv.y * _Direction.y - 1+_Threshold*2);
fixed noiseDiff = noiseTex.r - t - _Threshold;
col = lerp(col, fixed4(col.xyz,0), saturate(1 - noiseDiff));
Point #3: Dissolve effect with color fading
The dissolve effect by far is nice, but it is a bit stiff. We can add color fading around the "holes" on the object to smoothen the process.

To do this, recall that we can use the power function on the noiseDiff variable to create a glowing effect.
fixed noiseDiff = pow(saturate(noiseTex.r- t)/ _HighlightThreshold, 5);
Then, we can clip the unneeded fragments and blend the highlight color into the final output.
clip(noiseTex.r- t);
col = lerp(col, _HighlightColor, saturate(1 - noiseDiff));
And don't forget to set the
Tags { "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Full Shader Code:
Shader "AlexLiu/Dissolve_1"
{
Properties
{
_Color ("Color Tint", Color) = (1,1,1,1)
_HighlightColor ("Highlight Color", Color) = (0.9,0.8,0.7,1)
_MainTex ("Texture", 2D) = "white" {}
_Noise ("Noise", 2D) = "white" {}
_Direction ("Direction", Vector) = (0,1,0,0)
_Threshold ("Threshold", range(0,1)) = 0.5
_HighlightThreshold ("Highlight Threshold", float) =1
}
SubShader
{
Tags { "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float2 noiseuv : TEXCOORD1;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
sampler2D _Noise;
float4 _MainTex_ST;
float4 _Noise_ST;
fixed4 _Color;
fixed4 _HighlightColor;
float _Threshold;
float _HighlightThreshold;
float4 _Direction;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.noiseuv = TRANSFORM_TEX(v.uv, _Noise);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv) * _Color;
fixed4 noiseTex = tex2D(_Noise,i.noiseuv);
float t =( i.uv.x * _Direction.x - 1+_Threshold*2) + ( i.uv.y * _Direction.y - 1+_Threshold*2);
fixed noiseDiff = pow(saturate(noiseTex.r- t)/ _HighlightThreshold, 5);
clip(noiseTex.r- t);
col = lerp(col, _HighlightColor, saturate(1 - noiseDiff));
return col;
}
ENDCG
}
}
}
Thank you for reading!