From summer
Overrides a mesh's material with a noise-threshold shader to create a dissolving effect with a glowing edge. Use for enemy death, item pickup, or dramatic object removal.
How this skill is triggered — by the user, by Claude, or both
Slash command
/summer:dissolve**/*.tscn**/*.gd**/*.gdshaderaddons/vfx/**This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
The dissolve shader samples 3D noise per fragment and clips pixels whose noise value is below a `threshold` uniform. Pixels just above the threshold are tinted with an emissive `edge_color` to fake the burning rim. Animate `threshold` from 0 to 1 over `duration` and the mesh appears to dissolve from "nothing missing" to "completely gone." Used for enemy death, item pickups, transitions, summon ...
The dissolve shader samples 3D noise per fragment and clips pixels whose noise value is below a threshold uniform. Pixels just above the threshold are tinted with an emissive edge_color to fake the burning rim. Animate threshold from 0 to 1 over duration and the mesh appears to dissolve from "nothing missing" to "completely gone." Used for enemy death, item pickups, transitions, summon FX. NOT a particle system — this is a material override on the target mesh itself.
queue_free() instantly.fire, not dissolve. Dissolve makes the mesh disappear; fire just sits on top.smoke (recolor to ash gray) spawned at the dissolving mesh's bounds.transparency = TRANSPARENCY_ALPHA and tween albedo_color.a to 0. Dissolve is better but more expensive.addons/vfx/dissolve/dissolve.gdshader
addons/vfx/dissolve/dissolve_controller.gd
No .tscn needed — this overrides materials on existing meshes.
addons/vfx/dissolve/dissolve.gdshader:
shader_type spatial;
render_mode cull_back, depth_draw_opaque;
#include "res://addons/vfx/_building-blocks/noise-3d-fbm.gdshaderinc"
// Original material inputs (so the mesh still looks like itself until it dissolves).
uniform sampler2D albedo_texture : source_color, hint_default_white;
uniform vec4 base_color : source_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform float base_metallic : hint_range(0.0, 1.0) = 0.0;
uniform float base_roughness : hint_range(0.0, 1.0) = 0.7;
// Dissolve params.
uniform float threshold : hint_range(0.0, 1.0) = 0.0; // 0 = whole mesh, 1 = gone
uniform float edge_width : hint_range(0.001, 0.30) = 0.06;
uniform vec4 edge_color : source_color = vec4(1.0, 0.55, 0.10, 1.0);
uniform float edge_emission : hint_range(0.0, 16.0) = 6.0;
uniform float noise_scale : hint_range(0.5, 12.0) = 3.0;
uniform vec3 noise_offset = vec3(0.0);
uniform bool use_object_space = true;
void fragment() {
// Sample noise in object space so the dissolve pattern stays attached to the mesh.
vec3 npos = (use_object_space ? (inverse(MODEL_MATRIX) * vec4(VERTEX, 1.0)).xyz : VERTEX) * noise_scale + noise_offset;
float n = fbm3(npos);
// Discard everything below the threshold.
if (n < threshold) {
discard;
}
// Edge: pixels within `edge_width` of the threshold get the burning glow.
float edge_t = smoothstep(threshold + edge_width, threshold, n);
vec4 tex = texture(albedo_texture, UV) * base_color;
ALBEDO = mix(tex.rgb, edge_color.rgb, edge_t);
METALLIC = base_metallic * (1.0 - edge_t);
ROUGHNESS = mix(base_roughness, 0.4, edge_t);
EMISSION = edge_color.rgb * edge_emission * edge_t;
}
addons/vfx/dissolve/dissolve_controller.gd:
@tool
class_name DissolveController
extends RefCounted
## One-shot dissolve helper. Static-style API. Call from anywhere.
## Example: DissolveController.dissolve_object(enemy, 1.5, Color(1,0.55,0.1)).
const SHADER_PATH := "res://addons/vfx/dissolve/dissolve.gdshader"
## Dissolve a Node3D (and all MeshInstance3Ds under it) over `duration` seconds, then queue_free.
## target: the Node3D to dissolve
## duration: seconds (typical 0.6–2.5)
## edge_color: glow color of the burning edge
## edge_emission: bloom strength of the edge
## free_when_done: queue_free target when threshold reaches 1.0
static func dissolve_object(
target: Node3D,
duration: float = 1.2,
edge_color: Color = Color(1.0, 0.55, 0.10),
edge_emission: float = 6.0,
free_when_done: bool = true
) -> Tween:
var meshes := _collect_meshes(target)
if meshes.is_empty():
push_warning("DissolveController: no MeshInstance3D under %s" % target.name)
if free_when_done: target.queue_free()
return null
var mats: Array[ShaderMaterial] = []
for mi in meshes:
var sm := _override_material(mi, edge_color, edge_emission)
if sm: mats.append(sm)
var tween := target.create_tween()
tween.tween_method(func(v: float) -> void:
for m in mats:
m.set_shader_parameter("threshold", v),
0.0, 1.0, duration).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
if free_when_done:
tween.tween_callback(target.queue_free)
return tween
## Re-form: dissolve from gone (1.0) back to whole (0.0). For summons, teleports-in.
static func materialize_object(
target: Node3D,
duration: float = 1.0,
edge_color: Color = Color(0.55, 0.85, 1.0),
edge_emission: float = 6.0
) -> Tween:
var meshes := _collect_meshes(target)
if meshes.is_empty(): return null
var mats: Array[ShaderMaterial] = []
for mi in meshes:
var sm := _override_material(mi, edge_color, edge_emission)
if sm:
sm.set_shader_parameter("threshold", 1.0)
mats.append(sm)
var tween := target.create_tween()
tween.tween_method(func(v: float) -> void:
for m in mats:
m.set_shader_parameter("threshold", v),
1.0, 0.0, duration).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
tween.tween_callback(func() -> void:
# Restore original materials by clearing overrides.
for mi in meshes:
mi.material_override = null)
return tween
static func _collect_meshes(root: Node) -> Array[MeshInstance3D]:
var out: Array[MeshInstance3D] = []
if root is MeshInstance3D:
out.append(root)
for child in root.get_children():
out.append_array(_collect_meshes(child))
return out
static func _override_material(mi: MeshInstance3D, edge_color: Color, edge_emission: float) -> ShaderMaterial:
var sm := ShaderMaterial.new()
sm.shader = load(SHADER_PATH)
sm.set_shader_parameter("threshold", 0.0)
sm.set_shader_parameter("edge_color", edge_color)
sm.set_shader_parameter("edge_emission", edge_emission)
sm.set_shader_parameter("noise_offset", Vector3(randf(), randf(), randf()) * 10.0)
# Try to inherit base color/texture from the original BaseMaterial3D.
var orig_mat := mi.get_active_material(0)
if orig_mat is BaseMaterial3D:
var bm := orig_mat as BaseMaterial3D
sm.set_shader_parameter("base_color", bm.albedo_color)
sm.set_shader_parameter("base_metallic", bm.metallic)
sm.set_shader_parameter("base_roughness", bm.roughness)
if bm.albedo_texture:
sm.set_shader_parameter("albedo_texture", bm.albedo_texture)
mi.material_override = sm
return sm
No new nodes added. The recipe overrides the material_override on every MeshInstance3D under the target.
<target Node3D> (e.g., the enemy)
├── MeshInstance3D ← gets material_override = ShaderMaterial(dissolve.gdshader)
├── MeshInstance3D ← also overridden
└── ... (Skeleton3D etc. unchanged)
This recipe is mostly script-driven, no scene mutation needed. Just create the files (Write tool) and call from gameplay code.
# When the enemy dies:
DissolveController.dissolve_object($Enemy, 1.5)
# When summoning a creature:
var minion: Node3D = preload("res://scenes/skeleton.tscn").instantiate()
add_child(minion)
minion.global_position = $SummoningCircle.global_position
DissolveController.materialize_object(minion, 1.0, Color(0.45, 0.85, 1.0))
If the target needs the dissolve to be cosmetic-only (no queue_free):
DissolveController.dissolve_object(target, 1.2, Color(1, 0.55, 0.1), 6.0, false)
# ...then re-materialize later
DissolveController.materialize_object(target, 0.8)
| Parameter | Range | Effect |
|---|---|---|
duration | 0.3–4.0 s | how slow the dissolve plays (0.6 = fast, 1.5 = dramatic, 3.0 = ritual) |
edge_width | 0.001–0.30 | thickness of the glowing rim; thicker = more "burning", thinner = "vanishing" |
edge_color | Color | rim glow tint; orange = fire, blue = magic, green = poison, white = holy |
edge_emission | 0.0–16.0 | rim bloom strength; needs Bloom in WorldEnvironment |
noise_scale | 0.5–12.0 | small = big patches dissolving; large = fine pixel-grain dust |
use_object_space | bool | true = pattern attached to mesh; false = pattern stays in world (cool for moving objects) |
Orange edge, 1.2s, fire-y character.
duration = 1.2
edge_width = 0.06
edge_color = Color(1.0, 0.55, 0.10)
edge_emission = 6.0
noise_scale = 3.0
Fast, fine grain, blue edge for the cosmic feel. Pair with smoke in pale gray for ash particles rising.
duration = 0.8
edge_width = 0.04
edge_color = Color(0.45, 0.65, 1.0)
edge_emission = 8.0
noise_scale = 8.0
Slow, white edge, big halo. Add magic-glow underneath for a beam of light.
duration = 2.5
edge_width = 0.15
edge_color = Color(1.0, 1.0, 0.85)
edge_emission = 10.0
noise_scale = 1.5
Slow, sickly green, watery look.
duration = 3.0
edge_width = 0.10
edge_color = Color(0.45, 1.0, 0.30)
edge_emission = 4.0
noise_scale = 5.0
Inverse of enemy-burn-death.
duration = 0.9
edge_color = Color(0.55, 0.85, 1.0)
edge_emission = 7.0
noise_scale = 4.0
dissolve_object and then queue_free immediately. The tween needs the node alive. Pass free_when_done = true (default) so the tween's last call queues the free.transparency = ALPHA). The dissolve shader's discard works in opaque pass; translucent meshes look weird. Convert to opaque or use a custom dissolve-with-alpha shader.spatial, not particles. Stop the particles, don't try to dissolve them.noise_offset per instance. Two enemies dissolving at the same frame use identical patterns → they look like clones. The included controller randomizes per-instance.use_object_space = true (default).base_color defaults to white and no texture is set. The controller pulls from the original BaseMaterial3D; if your mesh uses a custom shader, manually wire the textures.Skeleton3D directly. It's not a mesh — recurse into MeshInstance3D children. The controller's _collect_meshes does this.discard defeats early-Z. For a screen full of dissolving enemies, consider cull_back (already enabled) and avoid stacking 20+ dissolves at once.noise_scale to a value the FBM can compute in 1–2 octaves, or replace the fbm3 include with a single-octave noise._override_material to loop over all surfaces with mi.set_surface_override_material(i, ...).use_object_space = false) shears with deformation.emission_texture uniform and add it to the EMISSION line.Tween and call kill() before starting the next.VFX is code, no MCP required:
addons/vfx/dissolve/ and write the two files above.DissolveController.dissolve_object(target_node).After firing this recipe, suggest:
summer:visual-effects/recipes/smoke — recolor pale gray, spawn at the target's bounds for ash particles. Especially good for thanos-snap.summer:visual-effects/recipes/magic-glow — for holy-banish and summon-arrival variants, add a vertical beam of light at the target.summer:visual-effects/recipes/fire — pair enemy-burn-death with a brief flame burst at the start of the dissolve.summer:visual-effects/game-feel — add a slow-mo on enemy death (Engine.time_scale = 0.4 for 0.3 s) to emphasize the dissolve.summer:audio/sound-effect — generate magical disintegration whoosh, fading shimmer, 1.5s and play in sync._building-blocks/noise-3d-fbm.gdshaderinc — the FBM noise this shader includessummer:visual-effects/recipes/smoke — pair for the ash cloudsummer:visual-effects/recipes/magic-glow — for arrival/departure beamssummer:visual-effects/recipes/fire — for the burn-up variant pairingsummer:gdscript-patterns — for the static-API class patternnpx claudepluginhub summerengine/summer-engine-agent --plugin summerWrites Godot 4.x shaders (.gdshader files) for 2D canvas_item effects, 3D spatial materials, particles, sky, fog, and post-processing. Covers uniforms, hints, structure, and built-in variables.
Creates a magical glow effect for 3D objects using a pulsing OmniLight3D, drifting additive motes, and optional emission shader. Useful for enchanted items, soul gems, runes, summoning circles, wisps, and quest objects.
Advanced shader programming, visual effects, custom materials, and rendering optimization for stunning game graphics.