Skills/godot-shaders-vfx/SKILL.md

300 lines
7.7 KiB
Markdown

---
name: godot-shaders-vfx
description: "Visual effects patterns for Godot 4.x: Godot Shader Language (GSL) basics, canvas_item/vertex/spatial shaders, particle systems (GPUParticles2D/3D), shader materials, and post-processing. Use when implementing custom rendering effects, particles, or visual polish in Godot."
license: MIT
compatibility:
- godot-4.0
- godot-4.1
- godot-4.2
- godot-4.3
- godot-4.4
- godot-4.5
- godot-4.6
- godot-4.7
metadata:
author: vl4dt
version: 0.1.0
tags:
- godot
- shaders
- vfx
- particles
- post-processing
- gsl
created: 2026-06-21
---
# Shaders and Visual Effects in Godot
Patterns for custom rendering and particle effects.
## Shader Types
Godot supports three shader modes:
| Mode | Node Type | Use Case |
|------|-----------|----------|
| `canvas_item` | Sprite2D, TileMap, Control | 2D sprite effects, UI shaders |
| `spatial` | MeshInstance3D, SurfaceTool | 3D material effects |
| `vertex` | Any mesh | Displacement, morphing (no fragment stage) |
## Canvas Item Shader (2D)
Basic 2D shader structure:
```gdscript
// Pixelate effect for retro aesthetics
shader_type canvas_item;
uniform float pixel_size : hint_range(1.0, 32.0) = 4.0;
uniform float brightness : hint_range(0.0, 2.0) = 1.0;
void fragment() {
// Snap UV to pixel grid
vec2 uv = floor(UV * (1.0 / pixel_size)) * pixel_size;
vec4 color = texture(TEXTURE, uv);
// Apply brightness
color.rgb *= brightness;
COLOR = color;
}
```
### Applying Shaders in Code
```gdscript
# Apply shader material to a sprite
var shader_mat = ShaderMaterial.new()
shader_mat.shader = preload("res://shaders/pixelate.gdshader")
$Sprite2D.material = shader_mat
# Set uniform at runtime
shader_mat.set_shader_parameter("pixel_size", 8.0)
shader_mat.set_shader_parameter("brightness", 1.5)
```
### Color Tint Shader
```gdscript
// Selective color tint — useful for damage flash, selection highlight
shader_type canvas_item;
uniform vec4 tint_color : hint_color = vec4(1.0, 0.0, 0.0, 1.0);
uniform float tint_amount : hint_range(0.0, 1.0) = 0.5;
void fragment() {
vec4 original = texture(TEXTURE, UV);
COLOR = mix(original, original * tint_color, tint_amount);
}
```
## Spatial Shader (3D)
Basic 3D shader with lighting:
```gdscript
// Animated water surface
shader_type spatial;
render_mode unshaded, depth_prepass_alpha_blend;
uniform float wave_speed : hint_range(0.1, 5.0) = 1.0;
uniform float wave_height : hint_range(0.01, 0.5) = 0.1;
uniform vec4 water_color : hint_color = vec4(0.0, 0.5, 0.8, 0.7);
void vertex() {
// Animate vertices with sine waves
float wave = sin(UV.x * 10.0 + TIME * wave_speed) * wave_height;
wave += sin(UV.y * 8.0 + TIME * wave_speed * 1.3) * wave_height * 0.5;
VERTEX.y += wave;
// Recalculate normal for lighting
NORMAL.y += wave * 2.0;
NORMAL = normalize(NORMAL);
}
void fragment() {
// Fresnel-like effect — edges more reflective
float fresnel = pow(1.0 - abs(dot(NORMAL, VIEW_DIR)), 3.0);
vec3 color = mix(water_color.rgb, vec3(1.0), fresnel * 0.5);
ALBEDO = color;
ALPHA = water_color.a;
SPECULAR = 0.8;
ROUGHNESS = 0.2;
}
```
## Particle Systems
### GPUParticles2D
```gdscript
# Code-driven particle setup
var particles = GPUParticles2D.new()
particles.one_shot = true
particles.lifetime = 1.5
particles.amount = 50
particles.emitting = false
# Process mode
particles.process_material = ParticleProcessMaterial.new()
particles.process_material.initial_velocity_min = 50.0
particles.process_material.initial_velocity_max = 150.0
particles.process_material.gravity = Vector3(0, -200, 0)
particles.process_material.spread = 360.0
# Visual
particles.material = ParticleMaterial.new()
particles.material.texture = preload("res://assets/particle.png")
particles.material.h_color = Color(1.0, 0.5, 0.0, 1.0)
particles.material.h_color2 = Color(1.0, 1.0, 0.0, 0.0) # Fade to transparent
add_child(particles)
```
### Triggering Particles
```gdscript
func emit_explosion(position: Vector2) -> void:
var particles = load("res://vfx/explosion.tscn").instantiate() as GPUParticles2D
particles.global_position = position
particles.emitting = true
get_parent().add_child(particles)
# Auto-destroy after particles finish
particles.finished.connect(func(): particles.queue_free())
```
### GPUParticles3D
```gdscript
# 3D particle burst (fire, smoke, magic)
var particles_3d = GPUParticles3D.new()
particles_3d.one_shot = true
particles_3d.lifetime = 2.0
particles_3d.amount = 100
# Directional emission from a cone
particles_3d.direction = Vector3.UP
particles_3d.emitting = false
add_child(particles_3d)
```
## Post-Processing
Apply screen-space effects via WorldEnvironment:
```gdscript
# Access environment settings
var env = get_world_3d().environment
# Glow/bloom
env.glow_enabled = true
env.glow_bloom_intensity = 0.5
env.glow_bloom_soft = true
env.glow_bloom_threshold = 0.8
# Tone mapping
env.tone_mapping = Environment.TONE_MAPPING_ACES
# Fog
env.fog_mode = Environment.FOG_MODE_EXPONENTIATED
env.fog_color = Color(0.6, 0.7, 0.8)
env.fog_density = 0.02
# SSAO (3D ambient occlusion)
env.ssao_enabled = true
env.ssao_max_distance = 5.0
```
### Screen Shader (WorldEnvironment)
For custom full-screen post-processing:
```gdscript
// Vignette + chromatic aberration post-process
shader_type spatial;
render_mode unshaded;
uniform float vignette_amount : hint_range(0.0, 1.0) = 0.5;
uniform float chroma_amount : hint_range(0.0, 0.01) = 0.003;
void fragment() {
// Chromatic aberration — shift RGB channels radially
vec2 center = UV - 0.5;
float dist = length(center);
vec2 dir = normalize(center) * chroma_amount * dist;
float r = texture(SCREEN_TEXTURE, UV + dir).r;
float g = texture(SCREEN_TEXTURE, UV).g;
float b = texture(SCREEN_TEXTURE, UV - dir).b;
// Vignette
float vignette = mix(1.0, 0.0, dist * vignette_amount * 2.0);
vec3 color = vec3(r, g, b) * vignette;
COLOR = vec4(color, 1.0);
}
```
## Shader Performance Tips
### Minimize Fragment Operations
```gdscript
// Bad — expensive per-pixel
void fragment() {
COLOR.rgb = pow(texture(TEXTURE, UV).rgb, 3.0) * sin(TIME * 10.0);
}
// Good — precompute where possible
uniform float precomputed_value;
void fragment() {
COLOR.rgb = texture(TEXTURE, UV).rgb * precomputed_value;
}
```
### Use `hint_range` for Uniforms
Always constrain uniforms to prevent editor/script errors:
```gdscript
uniform float speed : hint_range(0.1, 10.0) = 1.0;
uniform vec4 color : hint_color = vec4(1.0);
uniform sampler2D tex : hint_black; // Default black texture fallback
```
### Shader Material Reuse
```gdscript
# Share one ShaderMaterial across multiple nodes for performance
var shared_material = ShaderMaterial.new()
shared_material.shader = preload("res://shaders/wave.gdshader")
$Sprite2D_A.material = shared_material
$Sprite2D_B.material = shared_material # Same material instance
```
## Godot 4.7 VFX Notes
- **GPUParticles** — Improved burst and emit rate controls
- **Screen shaders** — Enhanced SCREEN_TEXTURE sampling quality
- **render_mode** — New `world_vertex_coords` option for world-space shader effects
## MCP Bridge Tools (Optional — Live Editor Integration)
When the MCP bridge is running (Phase 2), these tools can inspect and modify your project live:
- **`script_read`** / **`script_edit`** — Read and modify shader scripts
- **`scene_tree`** — Inspect particle node hierarchy and parameters
> **Note:** MCP tools require the Godot editor to be running with the MCP plugin enabled. Skills work independently without the bridge.
## References
- [GSL Shader Patterns](references/gsl-patterns.md)
- [Particle System Guide](references/particle-systems.md)
| [Post-Processing Effects](references/post-processing.md)