Dissolve with glowing burn edge
Disintegration effect for sprites: organic, curly holes open across the texture with a glowing rim that cools from white-hot to ember.
How to use: put the shader on a Sprite2D (or any CanvasItem) and animate dissolve from 0 (untouched) to 1 (completely gone), for example with a Tween.
Settings:
- dissolve (0-1): how much has burnt away.
- noise_scale: size of the burnt patches. warp: how curly their edges are.
- sweep (0-1) and sweep_angle: burn from one side toward the other instead of everywhere at once.
- edge_width, edge_color, edge_hot: the glowing rim.
Works with the Compatibility renderer and respects the node modulate.
This shader is part of the free 2D FX Essentials pack (9 sprite shaders with a demo scene): https://dimensionalmios.itch.io/almios-2d-fx-essentials
Shader code
shader_type canvas_item;
// Disintegration with a glowing ember front. Animate `dissolve` from 0 (untouched)
// to 1 (completely gone). The hot rim follows the burn line and cools to ember
// toward the untouched pixels.
group_uniforms Dissolve;
uniform float dissolve : hint_range(0.0, 1.0) = 0.5;
uniform float noise_scale : hint_range(2.0, 64.0) = 22.0; // size of the burnt patches
uniform float warp : hint_range(0.0, 1.0) = 0.6; // how organic, curly the edges are
// 0 = the holes open everywhere at once, 1 = the burn sweeps across the sprite
uniform float sweep : hint_range(0.0, 1.0) = 0.0;
uniform float sweep_angle : hint_range(-3.1416, 3.1416) = 1.5708; // default: from top to bottom
group_uniforms Edge;
uniform float edge_width : hint_range(0.0, 0.25) = 0.10;
uniform vec4 edge_color : source_color = vec4(1.0, 0.5, 0.1, 1.0);
uniform vec4 edge_hot : source_color = vec4(1.0, 0.95, 0.6, 1.0);
float hash(vec2 p) {
p = fract(p * 0.3183099 + vec2(0.1, 0.113));
p *= 17.0;
return fract(p.x * p.y * (p.x + p.y));
}
float vnoise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0); // quintic: no grid creases
return mix(mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x),
mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x), u.y);
}
// rotated octaves hide the square noise grid
float fbm(vec2 p) {
mat2 r = mat2(vec2(0.8, 0.6), vec2(-0.6, 0.8));
float v = 0.0;
float a = 0.5;
for (int i = 0; i < 4; i++) {
v += a * vnoise(p);
p = r * p * 2.03 + 11.7;
a *= 0.5;
}
return v / 0.9375;
}
varying vec4 vertex_tint; // modulate and self_modulate, applied at the end
void vertex() {
vertex_tint = COLOR;
}
void fragment() {
vec4 col = texture(TEXTURE, UV);
vec2 p = UV * noise_scale * 0.35;
// domain warp: the burnt patches get curly, organic outlines
vec2 w = vec2(fbm(p * 0.7 + 3.1), fbm(p * 0.7 + 8.4)) - 0.5;
float n = fbm(p + w * warp * 2.5);
// fbm values bunch up around 0.5: spread them evenly over 0..1 (an approximate
// normal CDF) so the burn progresses steadily from start to end
float z = (n - 0.5) / 0.11;
n = 0.5 + 0.5 * tanh(z * 0.85);
vec2 dir = vec2(cos(sweep_angle), sin(sweep_angle));
float g = dot(UV - 0.5, dir) / (abs(dir.x) + abs(dir.y)) + 0.5;
n = mix(n, g * 0.75 + n * 0.25, sweep);
// the cut: 0 keeps every pixel, 1 removes every pixel, whatever the noise
float thr = mix(-edge_width - 0.02, 1.04, dissolve);
float aa = min(fwidth(n), 0.02) + 0.001;
float gone = smoothstep(thr - aa, thr + aa, n);
// ember hugs the front: hottest at the burn line, cooling outward
float e = clamp((n - thr) / max(edge_width, 0.0001), 0.0, 1.0);
vec3 ember = mix(edge_hot.rgb, edge_color.rgb, e);
col.rgb = mix(col.rgb, ember, (1.0 - e) * gone * step(0.0001, edge_width));
col.a *= gone;
COLOR = col;
COLOR *= vertex_tint;
}


