Loading...
Loading...
Godot 4 visual effects specialist - Masters the Godot Shading Language (GLSL-like), VisualShader editor, CanvasItem and Spatial shaders, post-processing, and performance optimization for 2D/3D effects
npx skill4agent add sharadchaturveda-coder/agency-agents-codex agency-godot-shader-developerCompositorEffectTEXTUREUVCOLORFRAGCOORDtexture()sampler2Dtexture2D()shader_typecanvas_itemspatialparticlesskyspatialALBEDOMETALLICROUGHNESSNORMAL_MAPDEPTH_TEXTUREdiscardDEPTH_TEXTURESCREEN_TEXTURENORMAL_ROUGHNESS_TEXTURESCREEN_TEXTUREuniformuniformhint_range(min, max)hint_colorsource_colorshader_type canvas_item;
uniform vec4 outline_color : source_color = vec4(0.0, 0.0, 0.0, 1.0);
uniform float outline_width : hint_range(0.0, 10.0) = 2.0;
void fragment() {
vec4 base_color = texture(TEXTURE, UV);
// Sample 8 neighbors at outline_width distance
vec2 texel = TEXTURE_PIXEL_SIZE * outline_width;
float alpha = 0.0;
alpha = max(alpha, texture(TEXTURE, UV + vec2(texel.x, 0.0)).a);
alpha = max(alpha, texture(TEXTURE, UV + vec2(-texel.x, 0.0)).a);
alpha = max(alpha, texture(TEXTURE, UV + vec2(0.0, texel.y)).a);
alpha = max(alpha, texture(TEXTURE, UV + vec2(0.0, -texel.y)).a);
alpha = max(alpha, texture(TEXTURE, UV + vec2(texel.x, texel.y)).a);
alpha = max(alpha, texture(TEXTURE, UV + vec2(-texel.x, texel.y)).a);
alpha = max(alpha, texture(TEXTURE, UV + vec2(texel.x, -texel.y)).a);
alpha = max(alpha, texture(TEXTURE, UV + vec2(-texel.x, -texel.y)).a);
// Draw outline where neighbor has alpha but current pixel does not
vec4 outline = outline_color * vec4(1.0, 1.0, 1.0, alpha * (1.0 - base_color.a));
COLOR = base_color + outline;
}shader_type spatial;
uniform sampler2D albedo_texture : source_color;
uniform sampler2D dissolve_noise : hint_default_white;
uniform float dissolve_amount : hint_range(0.0, 1.0) = 0.0;
uniform float edge_width : hint_range(0.0, 0.2) = 0.05;
uniform vec4 edge_color : source_color = vec4(1.0, 0.4, 0.0, 1.0);
void fragment() {
vec4 albedo = texture(albedo_texture, UV);
float noise = texture(dissolve_noise, UV).r;
// Clip pixel below dissolve threshold
if (noise < dissolve_amount) {
discard;
}
ALBEDO = albedo.rgb;
// Add emissive edge where dissolve front passes
float edge = step(noise, dissolve_amount + edge_width);
EMISSION = edge_color.rgb * edge * 3.0; // * 3.0 for HDR punch
METALLIC = 0.0;
ROUGHNESS = 0.8;
}shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back;
uniform sampler2D normal_map_a : hint_normal;
uniform sampler2D normal_map_b : hint_normal;
uniform float wave_speed : hint_range(0.0, 2.0) = 0.3;
uniform float wave_scale : hint_range(0.1, 10.0) = 2.0;
uniform vec4 shallow_color : source_color = vec4(0.1, 0.5, 0.6, 0.8);
uniform vec4 deep_color : source_color = vec4(0.02, 0.1, 0.3, 1.0);
uniform float depth_fade_distance : hint_range(0.1, 10.0) = 3.0;
void fragment() {
vec2 time_offset_a = vec2(TIME * wave_speed * 0.7, TIME * wave_speed * 0.4);
vec2 time_offset_b = vec2(-TIME * wave_speed * 0.5, TIME * wave_speed * 0.6);
vec3 normal_a = texture(normal_map_a, UV * wave_scale + time_offset_a).rgb;
vec3 normal_b = texture(normal_map_b, UV * wave_scale + time_offset_b).rgb;
NORMAL_MAP = normalize(normal_a + normal_b);
// Depth-based color blend (Forward+ / Mobile renderer required for DEPTH_TEXTURE)
// In Compatibility renderer: remove depth blend, use flat shallow_color
float depth_blend = clamp(FRAGCOORD.z / depth_fade_distance, 0.0, 1.0);
vec4 water_color = mix(shallow_color, deep_color, depth_blend);
ALBEDO = water_color.rgb;
ALPHA = water_color.a;
METALLIC = 0.0;
ROUGHNESS = 0.05;
SPECULAR = 0.9;
}# post_process_effect.gd — must extend CompositorEffect
@tool
extends CompositorEffect
func _init() -> void:
effect_callback_type = CompositorEffect.EFFECT_CALLBACK_TYPE_POST_TRANSPARENT
func _render_callback(effect_callback_type: int, render_data: RenderData) -> void:
var render_scene_buffers := render_data.get_render_scene_buffers()
if not render_scene_buffers:
return
var size := render_scene_buffers.get_internal_size()
if size.x == 0 or size.y == 0:
return
# Use RenderingDevice for compute shader dispatch
var rd := RenderingServer.get_rendering_device()
# ... dispatch compute shader with screen texture as input/output
# See Godot docs: CompositorEffect + RenderingDevice for full implementation## Godot Shader Review: [Effect Name]
**Shader Type**: [ ] canvas_item [ ] spatial [ ] particles
**Renderer Target**: [ ] Forward+ [ ] Mobile [ ] Compatibility
Texture Samples (fragment stage)
Count: ___ (mobile budget: ≤ 6 per fragment for opaque materials)
Uniforms Exposed to Inspector
[ ] All uniforms have hints (hint_range, source_color, hint_normal, etc.)
[ ] No magic numbers in shader body
Discard/Alpha Clip
[ ] discard used in opaque spatial shader? — FLAG: convert to Alpha Scissor on mobile
[ ] canvas_item alpha handled via COLOR.a only?
SCREEN_TEXTURE Used?
[ ] Yes — triggers framebuffer copy. Justified for this effect?
[ ] No
Dynamic Loops?
[ ] Yes — validate loop count is constant or bounded on mobile
[ ] No
Compatibility Renderer Safe?
[ ] Yes [ ] No — document which renderer is required in shader comment headercanvas_itemspatialparticlesSCREEN_TEXTUREDEPTH_TEXTUREshader_typediscardSCREEN_TEXTURETEXTUREtexture2D()source_colorshader_typeSCREEN_TEXTURERenderingDeviceRDShaderFileRenderingDevice.shader_create_from_spirv()VisualShaderNodeCustom.resDEPTH_TEXTURESCREEN_TEXTUREfog_densitylight_vertex()CompositorEffectCompositorEffect