Claude-skill-registry godot-convert-shaders
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/godot-convert-shaders" ~/.claude/skills/majiayu000-claude-skill-registry-godot-convert-shaders && rm -rf "$T"
skills/data/godot-convert-shaders/SKILL.mdGodot 3.x to 4.x Shader Converter
Converts Godot 3.x shader code to Godot 4.x shader syntax.
Core Conversions
This skill performs five key shader modernizations:
| Godot 3.x | Godot 4.x | Type |
|---|---|---|
| | Screen sampling |
| | Depth sampling |
| | Texture sampling |
→ | → | Built-in varyings |
signature | with new params | Light functions |
UPON INVOCATION - START HERE
When this skill is invoked, IMMEDIATELY execute:
1. Verify Godot Project (5 seconds)
ls project.godot 2>/dev/null && echo "✓ Godot project detected" || echo "✗ Not a Godot project"
If NOT a Godot project:
- Inform user this skill only works on Godot projects
- STOP here
If IS a Godot project:
- Proceed to step 2
2. Detect Shader Files (10 seconds)
# Find all shader files echo "=== Detecting Shader Files ===" echo ".gdshader files (Godot 4.x format):" find . -name "*.gdshader" -type f | wc -l echo ".shader files (Godot 3.x format):" find . -name "*.shader" -type f | wc -l
3. Detect Godot 3.x Patterns (15 seconds)
echo "=== Detecting Godot 3.x Shader Patterns ===" echo "SCREEN_TEXTURE references:" grep -rn "SCREEN_TEXTURE" --include="*.shader" --include="*.gdshader" . 2>/dev/null | wc -l echo "DEPTH_TEXTURE references:" grep -rn "DEPTH_TEXTURE" --include="*.shader" --include="*.gdshader" . 2>/dev/null | wc -l echo "texture() with hint_screen_texture:" grep -rn "hint_screen_texture" --include="*.shader" --include="*.gdshader" . 2>/dev/null | wc -l echo "Built-in varying uppercase (VERTEX, UV, COLOR):" grep -rn "^varying.*VERTEX\|^varying.*UV\|^varying.*COLOR" --include="*.shader" --include="*.gdshader" . 2>/dev/null | wc -l echo "Light functions (light():" grep -rn "^void light()" --include="*.shader" --include="*.gdshader" . 2>/dev/null | wc -l echo "Light functions with old signature:" grep -rn "light.*DIFFUSE\|light.*SPECULAR" --include="*.shader" --include="*.gdshader" . 2>/dev/null | wc -l
4. Present Findings
Show the user:
=== Godot 4.x Shader Conversion Analysis === Project: [project name] Shaders to convert: - .shader files (Godot 3.x): X - .gdshader files: X Patterns requiring conversion: - SCREEN_TEXTURE usage: X - DEPTH_TEXTURE usage: X - hint_screen_texture uniforms: X - Built-in varyings (uppercase): X - Light function signatures: X Total shaders to update: X Conversion includes: ✓ SCREEN_TEXTURE → screen_texture uniform ✓ DEPTH_TEXTURE → depth_texture uniform ✓ texture() → textureLod() for screen/depth ✓ Varying built-ins → lowercase ✓ Light function parameters → new signature ✓ File extension .shader → .gdshader ✓ Git commit per file ✓ Backup before changes Would you like me to: 1. Convert all shaders (recommended) 2. Show detailed breakdown first 3. Select specific conversions 4. Cancel
5. Wait for User Choice
- If 1 (Proceed): Start Phase 2 immediately
- If 2 (Details): Show file-by-file breakdown, then offer to proceed
- If 3 (Selective): Ask which conversions to apply
- If 4 (Cancel): Exit skill
Phase 1: Analysis & Inventory
1.1 Create Shader Inventory
# Find all shader files (both .shader and .gdshader) find . -name "*.shader" -o -name "*.gdshader" | sort > /tmp/shader_files.txt wc -l /tmp/shader_files.txt echo "shader files found"
1.2 Analyze Each Shader
For each shader file, detect patterns:
# Analyze patterns per file for file in $(cat /tmp/shader_files.txt); do echo "=== $file ===" grep -c "SCREEN_TEXTURE" "$file" 2>/dev/null || echo 0 grep -c "DEPTH_TEXTURE" "$file" 2>/dev/null || echo 0 grep -c "hint_screen_texture" "$file" 2>/dev/null || echo 0 grep -c "^void light()" "$file" 2>/dev/null || echo 0 # Check if .shader extension (needs rename) [[ "$file" == *.shader ]] && echo "RENAME_NEEDED" || echo "EXTENSION_OK" done
1.3 Create Conversion Plan
Shader Conversion Plan: ======================= 1. SCREEN_TEXTURE → screen_texture (X files) 2. DEPTH_TEXTURE → depth_texture (X files) 3. texture() → textureLod() (X files) 4. Built-in varyings lowercase (X files) 5. Light function signatures (X files) 6. File extension .shader → .gdshader (X files) Total files to modify: X Estimated time: Auto (user doesn't wait) Backup created: YES (git tag) Rollback available: YES
Phase 2: Conversion Operations
Conversion A: SCREEN_TEXTURE → screen_texture uniform
Detection:
grep -rn "SCREEN_TEXTURE" --include="*.shader" --include="*.gdshader" .
Transformation Process:
-
Add uniform declaration at top of shader:
// Add near top of file, after shader_type uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap; -
Replace SCREEN_TEXTURE references:
// Before (Godot 3.x) vec4 screen_color = texture(SCREEN_TEXTURE, SCREEN_UV); // After (Godot 4.x) vec4 screen_color = textureLod(screen_texture, SCREEN_UV, 0.0);
Mapping Table:
| Godot 3.x | Godot 4.x |
|---|---|
| (uniform) |
| |
| |
Important: Use
textureLod() instead of texture() for screen and depth textures in Godot 4.x to ensure correct mipmap behavior.
Conversion B: DEPTH_TEXTURE → depth_texture uniform
Detection:
grep -rn "DEPTH_TEXTURE" --include="*.shader" --include="*.gdshader" .
Transformation Process:
-
Add uniform declaration at top of shader:
// Add near top of file, after shader_type uniform sampler2D depth_texture : hint_depth_texture, filter_linear_mipmap; -
Replace DEPTH_TEXTURE references:
// Before (Godot 3.x) float depth = texture(DEPTH_TEXTURE, SCREEN_UV).r; // After (Godot 4.x) float depth = textureLod(depth_texture, SCREEN_UV, 0.0).r;
Mapping Table:
| Godot 3.x | Godot 4.x |
|---|---|
| (uniform) |
| |
| |
Conversion C: texture() Function Changes
Detection:
grep -rn "texture(SCREEN_TEXTURE\|texture(DEPTH_TEXTURE" --include="*.shader" --include="*.gdshader" .
Core Changes:
Godot 4.x requires
textureLod() for screen and depth textures to ensure explicit mipmap level control:
// Godot 3.x - implicit LOD vec4 color = texture(SCREEN_TEXTURE, uv); // Godot 4.x - explicit LOD uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap; vec4 color = textureLod(screen_texture, uv, 0.0);
Conversion Pattern:
| Pattern | Replacement |
|---|---|
| |
| |
| |
| |
Conversion D: Built-in Varyings (Uppercase → Lowercase)
Detection:
grep -rn "^varying.*VERTEX\|^varying.*UV\|^varying.*COLOR\|^varying.*NORMAL" --include="*.shader" --include="*.gdshader" .
Built-in Varying Changes:
In Godot 3.x, built-in varyings like
VERTEX, UV, COLOR were accessed as-is.
In Godot 4.x, when you declare your own varying, the built-ins become lowercase in the fragment shader.
Mapping Table:
| Declared Varying | Godot 3.x Fragment | Godot 4.x Fragment |
|---|---|---|
| (unchanged) | (unchanged) |
Built-in | | |
Built-in | | |
Built-in | | |
Built-in | | |
Important: Only built-in varyings change to lowercase. User-declared varyings keep their original case.
Example:
// Before (Godot 3.x) shader_type spatial; varying vec2 custom_uv; void vertex() { custom_uv = UV; // UV is uppercase built-in VERTEX.y += 1.0; // VERTEX is uppercase } void fragment() { ALBEDO = texture(TEXTURE, custom_uv).rgb; if (VERTEX.y > 0.0) { // VERTEX still uppercase in vertex(), but... // In Godot 4.x, would be 'vertex' in fragment() } } // After (Godot 4.x) shader_type spatial; varying vec2 custom_uv; void vertex() { custom_uv = uv; // uv is lowercase built-in vertex.y += 1.0; // vertex is lowercase } void fragment() { ALBEDO = texture(TEXTURE, custom_uv).rgb; // custom_uv unchanged if (vertex.y > 0.0) { // vertex is lowercase in fragment() } }
Conversion E: Light Function Signature Changes
Detection:
grep -rn "^void light()" --include="*.shader" --include="*.gdshader" . grep -rn "light.*DIFFUSE\|light.*SPECULAR\|light.*ATTENUATION" --include="*.shader" --include="*.gdshader" .
Light Function Changes:
Godot 4.x changes how light calculations are accessed within the
light() function.
Mapping Table:
| Godot 3.x | Godot 4.x |
|---|---|
| |
| |
| |
| |
Complete Example:
// Before (Godot 3.x) shader_type spatial; void light() { DIFFUSE = ALBEDO * ATTENUATION; SPECULAR = vec3(0.5) * pow(max(dot(NORMAL, LIGHT), 0.0), 32.0) * ATTENUATION; } // After (Godot 4.x) shader_type spatial; void light() { diffuse_light = ALBEDO * attenuation; specular_light = vec3(0.5) * pow(max(dot(normal, LIGHT), 0.0), 32.0) * attenuation; }
Light Function Parameter Changes:
// Godot 3.x - no parameters void light() { // Access global light properties } // Godot 4.x - accepts light index (for multi-light) void light(int light_index) { // Access light properties via LIGHT, attenuation, etc. }
Conversion F: File Extension (.shader → .gdshader)
Detection:
find . -name "*.shader" -type f | grep -v ".gdshader"
Transformation: Rename files from
.shader to .gdshader:
# Rename all .shader files to .gdshader for file in $(find . -name "*.shader" -type f); do newname="${file%.shader}.gdshader" mv "$file" "$newname" echo "Renamed: $file → $newname" done
Note: Godot 4.x uses
.gdshader extension exclusively. .shader files from Godot 3.x should be renamed.
Phase 3: Execution & Safety
3.1 Create Git Baseline
# Create backup tag git tag shader-baseline-$(date +%Y%m%d-%H%M%S) # Stage any current changes git add . git commit -m "Baseline: Pre-shader conversion" || echo "No changes to commit"
3.2 Process Files Sequentially
For each shader file:
# Read and process shader content python3 << 'EOF' import re import sys def convert_shader(content, filename): original = content conversions_applied = [] # Conversion 1: Add screen_texture uniform if SCREEN_TEXTURE is used if 'SCREEN_TEXTURE' in content and 'uniform sampler2D screen_texture' not in content: # Find shader_type line and add uniform after it content = re.sub( r'(shader_type\s+\w+;\n?)', r'\1\nuniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap;\n', content ) conversions_applied.append("Added screen_texture uniform") # Conversion 2: Add depth_texture uniform if DEPTH_TEXTURE is used if 'DEPTH_TEXTURE' in content and 'uniform sampler2D depth_texture' not in content: content = re.sub( r'(shader_type\s+\w+;\n?)', r'\1\nuniform sampler2D depth_texture : hint_depth_texture, filter_linear_mipmap;\n', content ) conversions_applied.append("Added depth_texture uniform") # Conversion 3: SCREEN_TEXTURE → screen_texture in texture() calls if 'texture(SCREEN_TEXTURE' in content or 'textureLod(screen_texture' in content: # Replace texture(SCREEN_TEXTURE, ...) with textureLod(screen_texture, ..., 0.0) content = re.sub( r'texture\s*\(\s*SCREEN_TEXTURE\s*,\s*([^,\)]+)\s*\)', r'textureLod(screen_texture, \1, 0.0)', content ) # Replace texture(SCREEN_TEXTURE, ..., lod) with textureLod(screen_texture, ..., lod) content = re.sub( r'texture\s*\(\s*SCREEN_TEXTURE\s*,\s*([^,\)]+)\s*,\s*([^\)]+)\s*\)', r'textureLod(screen_texture, \1, \2)', content ) # Replace remaining SCREEN_TEXTURE references content = content.replace('SCREEN_TEXTURE', 'screen_texture') conversions_applied.append("Converted SCREEN_TEXTURE to screen_texture") # Conversion 4: DEPTH_TEXTURE → depth_texture if 'texture(DEPTH_TEXTURE' in content or 'textureLod(depth_texture' in content: content = re.sub( r'texture\s*\(\s*DEPTH_TEXTURE\s*,\s*([^,\)]+)\s*\)', r'textureLod(depth_texture, \1, 0.0)', content ) content = re.sub( r'texture\s*\(\s*DEPTH_TEXTURE\s*,\s*([^,\)]+)\s*,\s*([^\)]+)\s*\)', r'textureLod(depth_texture, \1, \2)', content ) content = content.replace('DEPTH_TEXTURE', 'depth_texture') conversions_applied.append("Converted DEPTH_TEXTURE to depth_texture") # Conversion 5: Light function variables (only in light() function) if 'void light()' in content: # Replace light variables content = content.replace('DIFFUSE', 'diffuse_light') content = content.replace('SPECULAR', 'specular_light') content = content.replace('ATTENUATION', 'attenuation') conversions_applied.append("Converted light function variables") # Conversion 6: Built-in varyings in fragment() function (lowercase) # This is complex - only affect fragment() scope if 'void fragment()' in content: # Find fragment function and convert built-in varyings there fragment_start = content.find('void fragment()') if fragment_start != -1: # Find the end of fragment function (next void or end of file) fragment_end = len(content) next_func = re.search(r'\nvoid\s+\w+\s*\(\)', content[fragment_start+1:]) if next_func: fragment_end = fragment_start + 1 + next_func.start() fragment_section = content[fragment_start:fragment_end] # Convert built-ins in fragment only fragment_section = re.sub(r'\bVERTEX\b', 'vertex', fragment_section) fragment_section = re.sub(r'\bUV\b', 'uv', fragment_section) fragment_section = re.sub(r'\bCOLOR\b', 'color', fragment_section) fragment_section = re.sub(r'\bNORMAL\b', 'normal', fragment_section) content = content[:fragment_start] + fragment_section + content[fragment_end:] conversions_applied.append("Converted built-in varyings to lowercase in fragment()") return content, conversions_applied # Process file filename = sys.argv[1] if len(sys.argv) > 1 else "shader.gdshader" with open(filename, 'r') as f: content = f.read() new_content, conversions = convert_shader(content, filename) with open(filename, 'w') as f: f.write(new_content) print(f"Applied {len(conversions)} conversions:") for c in conversions: print(f" - {c}") EOF
3.3 Rename File Extensions
# Rename .shader to .gdshader after conversion for file in $(find . -name "*.shader" -type f); do if [[ "$file" != *.gdshader ]]; then newname="${file%.shader}.gdshader" mv "$file" "$newname" git add "$newname" git rm "$file" 2>/dev/null || echo "Old file: $file (deleted)" fi done
3.4 Commit Per File
# After each file modification git add "$file" git commit -m "Convert shader: $file to Godot 4.x - Updated texture sampling (SCREEN_TEXTURE/DEPTH_TEXTURE) - Added uniform declarations with hints - Converted texture() to textureLod() - Updated built-in varyings to lowercase - Fixed light function variables - Renamed extension to .gdshader"
Phase 4: Verification & Testing
4.1 Syntax Validation
# Check all modified shaders for syntax errors echo "=== Validating Shader Syntax ===" for file in $(git diff --name-only HEAD~10..HEAD | grep "\.gdshader$"); do echo "Checking $file..." # Godot can validate shaders by loading them godot --headless --quit-after 2 project.godot 2>&1 | grep -i "$file" && echo "Warning: Possible issue in $file" done
4.2 Pattern Verification
echo "=== Verifying Godot 3.x Patterns Removed ===" echo "SCREEN_TEXTURE references remaining:" grep -rn "SCREEN_TEXTURE" --include="*.gdshader" . 2>/dev/null | wc -l echo "DEPTH_TEXTURE references remaining:" grep -rn "DEPTH_TEXTURE" --include="*.gdshader" . 2>/dev/null | wc -l echo "texture(SCREEN_TEXTURE remaining:" grep -rn "texture\s*(\s*SCREEN_TEXTURE" --include="*.gdshader" . 2>/dev/null | wc -l echo "Godot 3.x light variables (DIFFUSE/SPECULAR) remaining:" grep -rn "\bDIFFUSE\b\|\bSPECULAR\b" --include="*.gdshader" . 2>/dev/null | wc -l echo ".shader files (should be 0):" find . -name "*.shader" -type f | grep -v ".gdshader" | wc -l
4.3 Godot Project Test
# Open project in Godot to verify shaders compile echo "=== Testing in Godot ===" godot --editor project.godot & sleep 5 # Check Output panel for shader errors # (User should visually verify no shader-related errors)
Examples
Example 1: Screen Distortion Shader
Before (Godot 3.x):
shader_type canvas_item; uniform float distortion_strength : hint_range(0.0, 1.0) = 0.1; void fragment() { vec2 distorted_uv = SCREEN_UV + (UV - 0.5) * distortion_strength; vec4 screen_color = texture(SCREEN_TEXTURE, distorted_uv); COLOR = screen_color; }
After (Godot 4.x):
shader_type canvas_item; uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap; uniform float distortion_strength : hint_range(0.0, 1.0) = 0.1; void fragment() { vec2 distorted_uv = SCREEN_UV + (uv - 0.5) * distortion_strength; vec4 screen_color = textureLod(screen_texture, distorted_uv, 0.0); COLOR = screen_color; }
Example 2: Depth-Based Fog Shader
Before (Godot 3.x):
shader_type spatial; render_mode depth_draw_opaque, cull_disabled; uniform vec4 fog_color : source_color = vec4(0.5, 0.6, 0.7, 1.0); uniform float fog_density : hint_range(0.0, 1.0) = 0.1; void fragment() { float depth = texture(DEPTH_TEXTURE, SCREEN_UV).r; float fog_factor = exp(-fog_density * depth); ALBEDO = fog_color.rgb; ALPHA = 1.0 - fog_factor; }
After (Godot 4.x):
shader_type spatial; render_mode depth_draw_opaque, cull_disabled; uniform sampler2D depth_texture : hint_depth_texture, filter_linear_mipmap; uniform vec4 fog_color : source_color = vec4(0.5, 0.6, 0.7, 1.0); uniform float fog_density : hint_range(0.0, 1.0) = 0.1; void fragment() { float depth = textureLod(depth_texture, SCREEN_UV, 0.0).r; float fog_factor = exp(-fog_density * depth); ALBEDO = fog_color.rgb; ALPHA = 1.0 - fog_factor; }
Example 3: Custom Light Shader
Before (Godot 3.x):
shader_type spatial; uniform vec4 highlight_color : source_color = vec4(1.0, 0.8, 0.0, 1.0); void fragment() { ALBEDO = vec3(0.5); } void light() { float dot_product = max(dot(NORMAL, LIGHT), 0.0); if (dot_product > 0.9) { DIFFUSE = highlight_color.rgb * ATTENUATION; } else { DIFFUSE = ALBEDO * dot_product * ATTENUATION; } SPECULAR = vec3(0.0); }
After (Godot 4.x):
shader_type spatial; uniform vec4 highlight_color : source_color = vec4(1.0, 0.8, 0.0, 1.0); void fragment() { ALBEDO = vec3(0.5); } void light() { float dot_product = max(dot(normal, LIGHT), 0.0); if (dot_product > 0.9) { diffuse_light = highlight_color.rgb * attenuation; } else { diffuse_light = ALBEDO * dot_product * attenuation; } specular_light = vec3(0.0); }
Example 4: Vertex Displacement Shader
Before (Godot 3.x):
shader_type spatial; uniform float wave_height : hint_range(0.0, 2.0) = 0.5; uniform float wave_speed : hint_range(0.0, 10.0) = 2.0; void vertex() { float wave = sin(VERTEX.x * 2.0 + TIME * wave_speed) * wave_height; VERTEX.y += wave; } void fragment() { vec2 uv_offset = UV * 2.0; ALBEDO = vec3(uv_offset, 0.5); }
After (Godot 4.x):
shader_type spatial; uniform float wave_height : hint_range(0.0, 2.0) = 0.5; uniform float wave_speed : hint_range(0.0, 10.0) = 2.0; void vertex() { float wave = sin(vertex.x * 2.0 + TIME * wave_speed) * wave_height; vertex.y += wave; } void fragment() { vec2 uv_offset = uv * 2.0; ALBEDO = vec3(uv_offset, 0.5); }
Complete Conversion Mapping Reference
Texture Uniforms
| Godot 3.x | Godot 4.x Declaration | Godot 4.x Usage |
|---|---|---|
| | |
| | |
Texture Sampling Functions
| Godot 3.x | Godot 4.x |
|---|---|
| |
| |
| |
| (unchanged) |
| (unchanged) |
Built-in Varyings (fragment() scope only)
| Godot 3.x | Godot 4.x |
|---|---|
| |
| |
| |
| |
| |
| |
Light Function Variables
| Godot 3.x | Godot 4.x |
|---|---|
| |
| |
| |
| |
Success Criteria
Conversion complete when:
- ✓ Zero
references remain (converted to uniform)SCREEN_TEXTURE - ✓ Zero
references remain (converted to uniform)DEPTH_TEXTURE - ✓ All
calls for screen/depth usetexture()textureLod() - ✓ Built-in varyings in
are lowercasefragment() - ✓ Light function uses new variable names (
,diffuse_light
, etc.)specular_light - ✓ All
files renamed to.shader.gdshader - ✓ Uniform declarations include proper hints (
,hint_screen_texture
)hint_depth_texture - ✓ All shaders compile without errors in Godot 4.x
- ✓ Visual output matches Godot 3.x behavior
- ✓ Git history shows clear conversion commits
Common Issues & Solutions
Issue 1: texture() vs textureLod()
Problem:
// Won't work in Godot 4.x vec4 color = texture(screen_texture, UV);
Solution:
// Correct Godot 4.x syntax vec4 color = textureLod(screen_texture, UV, 0.0);
Explanation: Godot 4.x requires explicit LOD for screen and depth textures. Always use
textureLod() with an explicit LOD value (0.0 for no mipmapping).
Issue 2: Missing Uniform Declarations
Problem:
shader_type canvas_item; void fragment() { COLOR = textureLod(screen_texture, UV, 0.0); // ERROR: screen_texture not declared }
Solution:
shader_type canvas_item; uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap; void fragment() { COLOR = textureLod(screen_texture, uv, 0.0); }
Explanation: Godot 3.x provided SCREEN_TEXTURE implicitly. Godot 4.x requires explicit uniform declarations with hints.
Issue 3: Wrong Case in Built-in Varyings
Problem:
void fragment() { ALBEDO = texture(TEXTURE, UV).rgb; // ERROR in Godot 4.x }
Solution:
void fragment() { ALBEDO = texture(TEXTURE, uv).rgb; // Lowercase in fragment() }
Note: Built-in varyings remain uppercase in
vertex() but become lowercase in fragment() in Godot 4.x.
Issue 4: Light Function Variable Names
Problem:
void light() { DIFFUSE = ALBEDO; // ERROR: DIFFUSE doesn't exist }
Solution:
void light() { diffuse_light = ALBEDO; // Correct variable name }
Issue 5: Multiple hint declarations
Problem:
uniform sampler2D screen_texture : hint_screen_texture; uniform sampler2D depth_texture : hint_depth_texture; // Later in code, trying to use both
Issue: If shader already has uniforms declared, don't duplicate them.
Solution: Check for existing declarations before adding.
Rollback Procedure
If conversion causes issues:
# Find baseline tag git tag | grep "shader-baseline" # Reset to pre-conversion state git reset --hard <baseline-tag> # Or revert specific commits git revert <conversion-commit-hash> # Rename files back if needed for file in $(find . -name "*.gdshader" -type f); do if [ ! -f "${file%.gdshader}.shader" ]; then mv "$file" "${file%.gdshader}.shader" fi done
Integration with Other Skills
Use before:
- Update shaders first, then scriptsgodot-modernize-gdscript
- Visual effects often tied to tilemapsgodot-migrate-tilemap
Use after:
- Project conversion from Godot 3.x to 4.x
- Organize shader files firstgodot-organize-assets
This skill automates the tedious parts of Godot 3.x to 4.x shader migration while preserving exact visual output.