Search code examples
shadergodotfragment-shadertilegodot4

Godot Tilemap Tile Shader | Map pixel data to tile color


I've been trying to set the color of a tile based on a color in a texture. The texture's size is equivalent to the size of the tilemap, where each pixel represents one tile. I need this info because I want to apply an alpha value over the tiles color based on that texture's data.

I'm relatively new to shaders, so I'm struggling to figure out how to translate the tiles UVs to map to the correct pixel in the texture.

I've tried the following code, and it sort of works:

vec2 global_pos = FRAGCOORD.xy;

vec2 tile_position = floor(global_pos / tile_size);
vec2 uv = tile_position / (tilemap_size / tile_size);
vec4 color_map_color = texture(corruption_base_texture, uv);
vec4 final_color = mix(COLOR, color_map_color, 0.5);

COLOR = final_color;

This allows me to get the correct value from the texture, though the coordinates are totally off. It creates an effect where the TileMap itself is almost like a window that I'm looking at the correct texture through.

My guess is that I need to apply additional offsets about camera position and whatnot, but exactly how, I'm not sure.

Any information would be helpful, including links to shader code that works with some of these offsets that I need. I looked at several other shaders on the GodotShaders website, but most were implementing their entirely own TileMap, rather than using the built in one.


Solution

  • I ended up figuring it out. I needed to get the global position of the tile being drawn from the vertex shader. I used a slightly modified version of what godot docs suggests here. I then had to create a sample point based on that position, flooring it to a range within the sample texture. This is the completed code:

    shader_type canvas_item;
    
    uniform sampler2D corruption_base_texture : filter_nearest;
    uniform vec4 tint_color : source_color;
    
    // TODO: Add noise.
    
    const float tile_size = 16.0;
    varying flat vec2 world_pos;
    
    void vertex() {
        world_pos = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy;
    }
    
    void fragment() {
        vec4 color = texture(TEXTURE, UV);
        
        int sample_x = int(floor((world_pos.x + tile_size / 2.0) / tile_size));
        int sample_y = int(floor((world_pos.y + tile_size / 2.0) / tile_size));
        
        float sample_value = texelFetch(corruption_base_texture, ivec2(sample_x, sample_y), 0).r;
        if (sample_value > 0.0)
        {
            color = mix(color, tint_color, sample_value);
        }
        
        COLOR = color;
    }
    

    I hope this helps anyone who runs into a similar issue.