Dissolve (Godot 4.x)

Shader code
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_disabled, diffuse_lambert, specular_schlick_ggx;

// Varyings
varying vec3 worldPos;
varying vec3 localPos;
varying vec2 uv;

// Group: Base Color 
group_uniforms base_color;
uniform vec4 baseColor : source_color = vec4(1.0);
uniform sampler2D baseColorTexture : source_color; 

// Group: Extra Maps
group_uniforms extra_maps;
uniform sampler2D normalTexture : hint_normal;            
uniform sampler2D aoTexture : hint_default_white;           
uniform sampler2D heightTexture : hint_default_black;      
uniform sampler2D roughnessTexture : hint_default_white;    

// Group: Dissolve
group_uniforms dissolve;
uniform float dissolveSlider : hint_range(-1.0, 1.5) = 0.0; 
uniform float dissolveNoiseScale = 2.0; 

// Group: Edges
group_uniforms edges;
uniform float edgeNoiseScale : hint_range(-10.0, 0.0, 0.01) = 0.0;  
uniform float edgesThickness : hint_range(0, 0.3) = 0.01;
uniform vec4 edgeColor : source_color = vec4(0.0, 0.0, 1.0, 1.0);
uniform float edgeEmissionSlider : hint_range(0.0, 10.0, 0.01) = 1.0;

// Group: Animation
group_uniforms animation;
uniform bool enable_dissolve_animation = false;
uniform float animation_speed : hint_range(0.0, 5.0, 0.1) = 1.0;
uniform float animation_intensity : hint_range(0.0, 1.0, 0.01) = 0.2;

// Perlin Noise Functions
vec3 mod289_vec3(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 mod289_vec4(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) { return mod289_vec4(((x * 34.0) + 1.0) * x); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
vec3 fade(vec3 t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); }

float cnoise(vec3 P) {
    vec3 Pi0 = floor(P);
    vec3 Pi1 = Pi0 + vec3(1.0);
    Pi0 = mod289_vec3(Pi0);
    Pi1 = mod289_vec3(Pi1);
    vec3 Pf0 = fract(P);
    vec3 Pf1 = Pf0 - vec3(1.0);
    vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
    vec4 iy = vec4(Pi0.yy, Pi1.yy);
    vec4 iz0 = vec4(Pi0.z);
    vec4 iz1 = vec4(Pi1.z);
    vec4 ixy = permute(permute(ix) + iy);
    vec4 ixy0 = permute(ixy + iz0);
    vec4 ixy1 = permute(ixy + iz1);
    vec4 gx0 = ixy0 * (1.0 / 7.0);
    vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
    gx0 = fract(gx0);
    vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
    vec4 sz0 = step(gz0, vec4(0.0));
    gx0 -= sz0 * (step(0.0, gx0) - 0.5);
    gy0 -= sz0 * (step(0.0, gy0) - 0.5);
    vec4 gx1 = ixy1 * (1.0 / 7.0);
    vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
    gx1 = fract(gx1);
    vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
    vec4 sz1 = step(gz1, vec4(0.0));
    gx1 -= sz1 * (step(0.0, gx1) - 0.5);
    gy1 -= sz1 * (step(0.0, gy1) - 0.5);
    vec3 g000 = vec3(gx0.x, gy0.x, gz0.x);
    vec3 g100 = vec3(gx0.y, gy0.y, gz0.y);
    vec3 g010 = vec3(gx0.z, gy0.z, gz0.z);
    vec3 g110 = vec3(gx0.w, gy0.w, gz0.w);
    vec3 g001 = vec3(gx1.x, gy1.x, gz1.x);
    vec3 g101 = vec3(gx1.y, gy1.y, gz1.y);
    vec3 g011 = vec3(gx1.z, gy1.z, gz1.z);
    vec3 g111 = vec3(gx1.w, gy1.w, gz1.w);
    vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
    g000 *= norm0.x;
    g010 *= norm0.y;
    g100 *= norm0.z;
    g110 *= norm0.w;
    vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
    g001 *= norm1.x;
    g011 *= norm1.y;
    g101 *= norm1.z;
    g111 *= norm1.w;
    float n000 = dot(g000, Pf0);
    float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
    float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
    float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
    float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
    float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
    float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
    float n111 = dot(g111, Pf1);
    vec3 fade_xyz = fade(Pf0);
    vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
    vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
    float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
    return 2.2 * n_xyz;
}

void vertex() {
    worldPos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
    localPos = VERTEX;
    uv = UV;
}

void fragment() {
    // Compute parallax UV coordinates using height mapping.
    // (If no height texture is assigned, the default black returns 0.)
    vec2 texUV = uv;
    float height = texture(heightTexture, uv).r;
    float heightScale = 0.05; // Adjust for parallax intensity.
    vec3 viewDir = normalize(VIEW);
    vec3 T = normalize(TANGENT);
    vec3 B = normalize(cross(NORMAL, T));
    vec2 parallaxOffset = vec2(dot(viewDir, T), dot(viewDir, B)) * height * heightScale;
    texUV += parallaxOffset;
    
    // Compute base albedo using the base color texture.
    vec4 albedoColor = baseColor * texture(baseColorTexture, texUV);
    
    // Apply normal mapping.
    // (If no normal texture is assigned, the default neutral normal is used.)
    vec3 nMap = texture(normalTexture, texUV).rgb;
    nMap = nMap * 2.0 - 1.0;
    T = normalize(TANGENT);
    B = normalize(cross(NORMAL, T));
    NORMAL = normalize(T * nMap.x + B * nMap.y + NORMAL * nMap.z);
    
    // Set ambient occlusion.
    // (Default white AO texture yields AO = 1.0)
    AO = texture(aoTexture, texUV).r;
    
    // Set roughness.
    ROUGHNESS = texture(roughnessTexture, texUV).r;
    
    // Dissolve and edge effects.
    float dissolveNoise = cnoise(localPos * dissolveNoiseScale) * 0.5 + 0.5;
    float edgeNoise = cnoise(localPos * edgeNoiseScale) * 0.5 + 0.5;
    float animatedDissolve = (1.0 - (dissolveSlider + 0.5)) + dissolveNoise;
    
    if (enable_dissolve_animation) {
        float time_factor = TIME * animation_speed;
        animatedDissolve += animation_intensity *
            (sin(time_factor + localPos.x * 2.0 +
                 localPos.y * 1.5 + localPos.z * 2.5) * 0.5 + 0.5);
    }
    animatedDissolve = clamp(animatedDissolve, 0.0, 1.0);
    float dissolveFactor = step(edgeNoise, animatedDissolve);
    float edgeMask = step(edgeNoise, animatedDissolve) - step(edgeNoise, animatedDissolve - edgesThickness);
    vec3 edgeEmission = edgeMask * edgeColor.rgb * edgeEmissionSlider;
    
    ALBEDO = albedoColor.rgb;
    ALPHA = dissolveFactor;
    EMISSION = edgeEmission;
    ALPHA_SCISSOR_THRESHOLD = 0.5;
}
Tags
animated, dissolve
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from KitsuneKotta

Loading Dots Animation

Chromatic Aberration Sphere (Godot 4.x)

Related shaders

2D Burn/dissolve from point (V 1.0)

Fire dissolve shader

3D Vertical dissolve

Subscribe
Notify of
guest

6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
lorenso
lorenso
1 month ago

It is really great , thanks for sharing but I have one question , how can we reach this dissolveSlider parameter from script . I want to change value from script

lorenso
lorenso
25 days ago
Reply to  KitsuneKotta

thanks a lot

babbdi
21 days ago

hey, its there any way that i can put a texture instead of a fixed color?

babbdi
21 days ago
Reply to  babbdi

just find out:

shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_disabled, diffuse_lambert, specular_schlick_ggx;

// Varyings
varying vec3 worldPos;
varying vec3 localPos;

//Group: Base Color 
uniform sampler2D TEXTURE : source_color; // pra pegar a textura (tipo um @export)
group_uniforms base_color;
uniform vec4 baseColor : source_color = vec4(1.0);

//Group: Dissolve
group_uniforms dissolve;
uniform float dissolveSlider : hint_range(-1.0, 1.5) = 0.0; 
uniform float dissolveNoiseScale = 2.0; 

//Group: Edges
group_uniforms edges;
uniform float edgeNoiseScale : hint_range(-10.0, 0.0, 0.01) = 0.0;  
uniform float edgesThickness : hint_range(0, 0.3) = 0.01;
uniform vec4 edgeColor : source_color = vec4(0.0, 0.0, 1.0, 1.0);
uniform float edgeEmissionSlider : hint_range(0.0, 10.0, 0.01) = 1.0;

//Group: Animation
group_uniforms animation;
uniform bool enable_dissolve_animation = false;
uniform float animation_speed : hint_range(0.0, 5.0, 0.1) = 1.0;
uniform float animation_intensity : hint_range(0.0, 1.0, 0.01) = 0.2;

// Perlin Noise Functions
vec3 mod289_vec3(vec3 x) { return x – floor(x * (1.0 / 289.0)) * 289.0; }
vec4 mod289_vec4(vec4 x) { return x – floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) { return mod289_vec4(((x * 34.0) + 1.0) * x); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 – 0.85373472095314 * r; }
vec3 fade(vec3 t) { return t * t * t * (t * (t * 6.0 – 15.0) + 10.0); }

float cnoise(vec3 P) {
  vec3 Pi0 = floor(P);
  vec3 Pi1 = Pi0 + vec3(1.0);
  Pi0 = mod289_vec3(Pi0);
  Pi1 = mod289_vec3(Pi1);
  vec3 Pf0 = fract(P);
  vec3 Pf1 = Pf0 – vec3(1.0);
  vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
  vec4 iy = vec4(Pi0.yy, Pi1.yy);
  vec4 iz0 = vec4(Pi0.z);
  vec4 iz1 = vec4(Pi1.z);
  vec4 ixy = permute(permute(ix) + iy);
  vec4 ixy0 = permute(ixy + iz0);
  vec4 ixy1 = permute(ixy + iz1);
  vec4 gx0 = ixy0 * (1.0 / 7.0);
  vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) – 0.5;
  gx0 = fract(gx0);
  vec4 gz0 = vec4(0.5) – abs(gx0) – abs(gy0);
  vec4 sz0 = step(gz0, vec4(0.0));
  gx0 -= sz0 * (step(0.0, gx0) – 0.5);
  gy0 -= sz0 * (step(0.0, gy0) – 0.5);
  vec4 gx1 = ixy1 * (1.0 / 7.0);
  vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) – 0.5;
  gx1 = fract(gx1);
  vec4 gz1 = vec4(0.5) – abs(gx1) – abs(gy1);
  vec4 sz1 = step(gz1, vec4(0.0));
  gx1 -= sz1 * (step(0.0, gx1) – 0.5);
  gy1 -= sz1 * (step(0.0, gy1) – 0.5);
  vec3 g000 = vec3(gx0.x, gy0.x, gz0.x);
  vec3 g100 = vec3(gx0.y, gy0.y, gz0.y);
  vec3 g010 = vec3(gx0.z, gy0.z, gz0.z);
  vec3 g110 = vec3(gx0.w, gy0.w, gz0.w);
  vec3 g001 = vec3(gx1.x, gy1.x, gz1.x);
  vec3 g101 = vec3(gx1.y, gy1.y, gz1.y);
  vec3 g011 = vec3(gx1.z, gy1.z, gz1.z);
  vec3 g111 = vec3(gx1.w, gy1.w, gz1.w);
  vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
  g000 *= norm0.x;
  g010 *= norm0.y;
  g100 *= norm0.z;
  g110 *= norm0.w;
  vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
  g001 *= norm1.x;
  g011 *= norm1.y;
  g101 *= norm1.z;
  g111 *= norm1.w;
  float n000 = dot(g000, Pf0);
  float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
  float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
  float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
  float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
  float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
  float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
  float n111 = dot(g111, Pf1);
  vec3 fade_xyz = fade(Pf0);
  vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
  vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
  float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
  return 2.2 * n_xyz;
}

void vertex() {
  worldPos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
  localPos = VERTEX;
}

void fragment() {
vec4 tex_color = texture(TEXTURE, UV);
  vec4 albedoColor = tex_color;
  float dissolveNoise = cnoise(localPos * dissolveNoiseScale) * 0.5 + 0.5;
  float edgeNoise = cnoise(localPos * edgeNoiseScale) * 0.5 + 0.5;
  float animatedDissolve = (1.0 – (dissolveSlider + 0.5)) + dissolveNoise;

  // Animation
  if (enable_dissolve_animation) {
    float time_factor = TIME * animation_speed;
    animatedDissolve += animation_intensity *
      (sin(time_factor + localPos.x * 2.0 +
         localPos.y * 1.5 + localPos.z * 2.5) * 0.5 + 0.5);
  }
  animatedDissolve = clamp(animatedDissolve, 0.0, 1.0);
  float dissolveFactor = step(edgeNoise, animatedDissolve);
  float edgeMask = step(edgeNoise, animatedDissolve) – step(edgeNoise, animatedDissolve – edgesThickness);

  vec3 edgeEmission = edgeMask * edgeColor.rgb * edgeEmissionSlider;

  ALBEDO = albedoColor.rgb;
  ALPHA = dissolveFactor;
  EMISSION = edgeEmission;
  ALPHA_SCISSOR_THRESHOLD = 0.5;
}