Search code examples
shadergodot4godot-shader-language

Determine position of light relative to a tile in a Godot shader


I have a Godot CanvasItem shader that is assigned to the material of a specific tile in a TileMap. In the shader, I need to know the position of a light relative to the bottom of the tile.

I am computing the bottom right corner like this:

varying vec2 bottom_right_corner;

void vertex() {
    vec2 world_vertex_position = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy;
    bottom_right_corner = floor(world_vertex_position / 32.0 + 1.0) * 32.0;
}

void light() {
    // How to compare bottom_right_corner.y with LIGHT_POSITION.y ?
}

I think now I need to apply some transformation to these coordinates so that I can compare them with LIGHT_POSITION, but I'm not sure which.

How can I perform this comparison?


Solution

  • I found the solution. We need to multiply LIGHT_POSITION by the inverse of CANVAS_MATRIX. Here's what I ended up with:

    shader_type canvas_item;
    
    uniform ivec2 tile_size = ivec2(32, 32); // Size of the tiles
    
    varying flat ivec2 bottom_left_corner;
    varying flat ivec2 bottom_right_corner;
    varying mat4 inv_canvas_matrix;
    
    void vertex() {
        inv_canvas_matrix = inverse(CANVAS_MATRIX);
        
        vec2 world_vertex_position = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy;
        bottom_right_corner = (ivec2(round(world_vertex_position / vec2(tile_size))) + 1) * tile_size;
        bottom_left_corner = bottom_right_corner + ivec2(-tile_size.x, 0);
    }
    
    void light() {
        // Default light calculation
        vec4 color = texture(TEXTURE, UV);
        LIGHT = vec4(color.rgb * LIGHT_COLOR.rgb * LIGHT_ENERGY, LIGHT_COLOR.a);
        
        // Compute the light position in world space
        ivec2 world_light_position = ivec2((inv_canvas_matrix * vec4(LIGHT_POSITION, 1.0)).xy);
        
        // Now you can compare world_light_position with bottom_right_corner or bottom_left_corner
        // ...
    }