Dissolve shader: VFX's bread and butter
If there is one shader trick anyone should learn about when creating visual effects, it's dissolving, as it is very versatile and allows you to do many effects with just 1 shader.
Dissolve 101
Binary Mask
Let's create an empty scene with a ColorRect node and a ShaderMaterial resource as its material and edit our first iteration of the shader.
For that we only need:
- A mask texture (sampler2D uniform)
- A dissolve value (float uniform) ranged between 0 and 1 to shift the edge of the mask
shader_type canvas_item;
uniform sampler2D noise_sampler : repeat_enable;
uniform float dissolve : hint_range(0.0, 1.0, 0.1) = 0.0;
void fragment() {
float mask = texture(noise_sampler, UV).x;
// set COLOR.a of ALPHA with dissolve "method"
}
This code above is the base setup for the dissolve effect, the important part is the dissolve "method" you use to apply your mask. This one is the simpler mask effect we can do, as we simply pick a mask texture and map its value to 0.0 or 1.0 producing a hard edge between the visible and masked area.
To do that, we could use the step function and give it dissolve as its edge argument and mask as its value argument.
COLOR.a = step(dissolve, mask);
Rounding the value with a ceil, round or floor function is also valid, but this one requires to offset the mask value with the dissolve value.
COLOR.a = ceil(mask - dissolve);
Smooth Mask
Binary mask is great for some art styles, but sometimes you also need to be able to control the smoothness of your mask. Let's use the previous setup and add a smoothnes parameter.
uniform float smoothness : hint_range(0.0, 1.0, 0.01) = 0.1;
The masked method is a bit different, as we need to remap the dissolve value to cover the min and max edge of smoothness. For that I'm using the map method as a quick solution. Feeding all this into the smootstep method produces a mask with configurable smooth edges.
shader_type canvas_item;
uniform sampler2D noise_sampler : repeat_enable;
uniform float dissolve : hint_range(0.0, 1.0, 0.1) = 0.0;
uniform float smoothness : hint_range(0.0, 1.0, 0.01) = 0.1;
// Map Method
void fragment() {
float mask = texture(noise_sampler, UV).x;
float mapped_dissolve = map(dissolve, 0.0, 1.0, - smoothness, 1.0 + smoothness);
COLOR.a *= smoothstep( - smoothness, + smoothness, mask - mapped_dissolve);
}
We now have 2 solutions for dissolving stuff, but you don't have to stop here, the possibility are endless as you can throw other property in the mix.
- Dissolving along UV coordinate
- Mixing Albedo and Emission to create a burning paper effect
- etc...
Spicing up particles
The practical application of this method is wide, I wouldn't be able to list everything in one blog post. Think of it as an extra tool in your VFX toolbox. However, there is one use case that I would like to highlight, which is mixing this method with particles to create a utility shader that you can throw in your particle system to create and iterate quick effects.
Differences
This example shader is similar to the smooth mask we did previously but diverges in some parts.
- It doesn't map the mask from 1.0 to 0.0, as it would display a fully rendered quad when a particle spawns.
- It doesn't have a dissolve parameter but uses the particle system INSTANCE_CUSTOM.y (representing the particle lifetime value as we want the particle to dissolve along its lifetime.)
float noise = clamp(texture(noise_sampler, UV * noise_scale + custom.z).x - custom.y, 0.0, 1.0);
Additions
There are also a few more parameters added
- edge: represents the side of the mask favored during dissolving.
- energy: particle emission power, 1.0 is neutral.
- gradient_sampler: color along the mask, accounting for dissolving.
This shader can create simple effects such as smoke or dust. It is possible to create more “complex” effects, such as the fire above constructed with several particle systems. I would recommend using this technique in combination with other elements for greater variety. Dissolve shader is good for creating or garnishing organic effects like magic spells.
Going further
This blog post only scratches the surface of this topic and provides a few simple basics to take you further. If you found it interesting or inspiring, don't hesitate to share what you have done with it. If you have any feedback or comments, tell me :)!
Socials