Skip to content
godot-convert-shaders logo

Godot 3.x to 4.x Shader Converter

godot-convert-shaders

Converts Godot 3.x shader code to Godot 4.x shader syntax. Updates SCREEN_TEXTURE to screen_texture uniform, DEPTH_TEXTURE to depth_texture uniform, texture() function changes, built-in varyings updates, and light function signature changes. Essential for rendering and visual effect migrations.

Asreonn/godot-superpowers0installs4stars

SKILL.md

Full skill instructions

Godot 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.xGodot 4.xType
SCREEN_TEXTUREuniform sampler2D screen_textureScreen sampling
DEPTH_TEXTUREuniform sampler2D depth_textureDepth sampling
texture(SCREEN_TEXTURE, ...)textureLod(screen_texture, ...)Texture sampling
varying → VARIABLEvarying → variableBuilt-in varyings
light() signaturelight() with new paramsLight 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:

  1. 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;
    
  2. 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.xGodot 4.x
SCREEN_TEXTUREscreen_texture (uniform)
texture(SCREEN_TEXTURE, uv)textureLod(screen_texture, uv, 0.0)
texture(SCREEN_TEXTURE, uv, lod)textureLod(screen_texture, uv, lod)

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:

  1. 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;
    
  2. 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.xGodot 4.x
DEPTH_TEXTUREdepth_texture (uniform)
texture(DEPTH_TEXTURE, uv)textureLod(depth_texture, uv, 0.0)
texture(DEPTH_TEXTURE, uv, lod)textureLod(depth_texture, uv, lod)

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:

PatternReplacement
texture(SCREEN_TEXTURE, UV)textureLod(screen_texture, UV, 0.0)
texture(SCREEN_TEXTURE, UV, 2.0)textureLod(screen_texture, UV, 2.0)
texture(DEPTH_TEXTURE, UV)textureLod(depth_texture, UV, 0.0)
texture(DEPTH_TEXTURE, UV, lod)textureLod(depth_texture, UV, lod)

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 VaryingGodot 3.x FragmentGodot 4.x Fragment
varying vec2 my_uvmy_uv (unchanged)my_uv (unchanged)
Built-in VERTEXVERTEXvertex
Built-in UVUVuv
Built-in COLORCOLORcolor
Built-in NORMALNORMALnormal

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.xGodot 4.x
DIFFUSEdiffuse_light
SPECULARspecular_light
ATTENUATIONattenuation
SHADOW_ATTENUATIONshadow_attenuation

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.xGodot 4.x DeclarationGodot 4.x Usage
SCREEN_TEXTUREuniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap;textureLod(screen_texture, uv, lod)
DEPTH_TEXTUREuniform sampler2D depth_texture : hint_depth_texture, filter_linear_mipmap;textureLod(depth_texture, uv, lod)

Texture Sampling Functions

Godot 3.xGodot 4.x
texture(SCREEN_TEXTURE, uv)textureLod(screen_texture, uv, 0.0)
texture(SCREEN_TEXTURE, uv, lod)textureLod(screen_texture, uv, lod)
texture(DEPTH_TEXTURE, uv)textureLod(depth_texture, uv, 0.0)
texture(TEXTURE, uv)texture(TEXTURE, uv) (unchanged)
texture(NORMAL_TEXTURE, uv)texture(NORMAL_TEXTURE, uv) (unchanged)

Built-in Varyings (fragment() scope only)

Godot 3.xGodot 4.x
VERTEXvertex
UVuv
COLORcolor
NORMALnormal
TANGENTtangent
BINORMALbinormal

Light Function Variables

Godot 3.xGodot 4.x
DIFFUSEdiffuse_light
SPECULARspecular_light
ATTENUATIONattenuation
SHADOW_ATTENUATIONshadow_attenuation

Success Criteria

Conversion complete when:

  • ✓ Zero SCREEN_TEXTURE references remain (converted to uniform)
  • ✓ Zero DEPTH_TEXTURE references remain (converted to uniform)
  • ✓ All texture() calls for screen/​depth use textureLod()
  • ✓ Built-in varyings in fragment() are lowercase
  • ✓ Light function uses new variable names (diffuse_light, specular_light, etc.)
  • ✓ All .shader files renamed to .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:

  • godot-modernize-gdscript - Update shaders first, then scripts
  • godot-migrate-tilemap - Visual effects often tied to tilemaps

Use after:

  • Project conversion from Godot 3.x to 4.x
  • godot-organize-assets - Organize shader files first

This skill automates the tedious parts of Godot 3.x to 4.x shader migration while preserving exact visual output.

More skills from Asreonn

godot-sync-camera-positions logo
Asreonn/godot-superpowers

godot-sync-camera-positions

Use when Godot background layers or elements follow the camera at runtime but show incorrect position in editor. Detects camera-following patterns and syncs editor position to match typical runtime state. Makes editor preview accurately show what players will see during gameplay.

4 0
View
godot-refactor logo
Asreonn/godot-superpowers

godot-refactor

Use when Godot project has code-created objects, tight coupling via direct references, monolithic scripts over 150 lines, or needs scene-first architecture with signals and composition. Orchestrates all 5 code quality mini-skills: extract-to-scenes, split-scripts, add-signals, extract-resources, ...

4 0
View
godot-split-scripts logo
Asreonn/godot-superpowers

godot-split-scripts

Use when Godot scripts exceed 150 lines or have multiple responsibilities. Detects scripts doing too many things (player movement + combat + inventory + UI). Automatically splits into focused scripts following Single Responsibility Principle, preserves behavior exactly, and creates git commits pe...

4 0
View
godot-migrate-tilemap logo
Asreonn/godot-superpowers

godot-migrate-tilemap

Migrates legacy TileMap (Godot 4.0-4.2) to new TileMapLayer system (Godot 4.3+). Handles TileMap node conversion, API migration (set_cell to set_cells_terrain_connect), TileSet resource updates, physics layer mapping, navigation layer conversion, and custom data preservation. Essential for upgrad...

4 0
View
godot-setup-animationtree logo
Asreonn/godot-superpowers

godot-setup-animationtree

Use when setting up AnimationTree nodes for state machine-driven animation, creating AnimationNodeStateMachine graphs, configuring BlendSpace2D/3D for locomotion blending, implementing state transition conditions, or creating animation blend trees for complex animation mixing. Supports player cha...

4 0
View
godot-extract-resources logo
Asreonn/godot-superpowers

godot-extract-resources

Use when Godot code has hardcoded game data in const arrays, dictionaries, or embedded values. Detects inline data like enemy stats, item definitions, level configurations. Automatically extracts to .tres resource files, making data visible in editor, easily modifiable, and supports data-driven d...

4 0
View
godot-modernize-input logo
Asreonn/godot-superpowers

godot-modernize-input

Use when handling input in Godot 4.x projects and need to modernize patterns. Generates Input Map configurations from hardcoded key checks, sets up joypad rumble (Godot 4.x feature), implements gyroscope/accelerometer input on mobile, creates context-sensitive input handling for different game st...

4 0
View
godot-sync-parallax logo
Asreonn/godot-superpowers

godot-sync-parallax

Use when Godot ParallaxBackground/ParallaxLayer nodes show incorrect positions in editor due to parallax multipliers. Calculates correct editor positions based on camera start position and parallax motion scale. Makes parallax layers preview realistically in editor, matching gameplay appearance.

4 0
View
godot-setup-export logo
Asreonn/godot-superpowers

godot-setup-export

Use when setting up multi-platform exports for Godot projects, configuring export presets, icons, feature tags, build scripts, or CI/CD pipelines

4 0
View
godot-generate-tests logo
Asreonn/godot-superpowers

godot-generate-tests

Use when developing Godot games and need comprehensive test coverage for GDScript classes, signals, scene initialization, and integration flows. Generates GUT framework unit tests, integration tests, mock/stub helpers, and CI/CD test runner configuration.

4 0
View
godot-fix-positions logo
Asreonn/godot-superpowers

godot-fix-positions

Use when Godot project has position conflicts between editor (.tscn) and code (.gd), camera-following backgrounds, or runtime positions don't match editor preview. Orchestrates all 3 position sync mini-skills: sync-static-positions, sync-camera-positions, and sync-parallax. Each operation targets...

4 0
View
godot-organize-assets logo
Asreonn/godot-superpowers

godot-organize-assets

Use when Godot project has disorganized assets (sprites, audio, fonts, materials). Detects scattered asset files and organizes them into clear categories with consistent naming. Creates sprite atlases where beneficial, organizes audio by type, and groups related assets together.

4 0
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View