Search code examples
shadergodotfragment-shadergodot4

In Godot 4, Shader not shading the correct pixels


I have a TileSet and I'm trying to apply a custom Shader and ShaderMaterial to one of my tiles. However, it's not shading the right area. I'm a newbie to both Godot and game development, but everything I've read seems to indicate my code is correct.

shader_type canvas_item;

uniform vec2 points[3]; // Polygon points
uniform vec2 atlas_id; // atlas position within the TileSet
uniform vec2 tile_size = vec2(32.0, 32.0);
uniform vec4 modulate_color : source_color;

// code generated by Copilot
bool point_in_triangle(vec2 p, vec2 p0, vec2 p1, vec2 p2) {
    vec2 v0 = p2 - p0;
    vec2 v1 = p1 - p0;
    vec2 v2 = p - p0;

    float dot00 = dot(v0, v0);
    float dot01 = dot(v0, v1);
    float dot02 = dot(v0, v2);
    float dot11 = dot(v1, v1);
    float dot12 = dot(v1, v2);

    float invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
    float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
    float v = (dot00 * dot12 - dot01 * dot02) * invDenom;

    return (u >= 0.0) && (v >= 0.0) && (u + v < 1.0);
}

void fragment() {
  vec2 top_left_px = atlas_id * tile_size;
  vec2 pixel_coordinate = UV / TEXTURE_PIXEL_SIZE;
  pixel_coordinate[0] = mod(pixel_coordinate[0], tile_size[0]);
  vec2 relative_coord = pixel_coordinate - top_left_px;

  // Check if the current fragment is inside the triangle
  if (point_in_triangle(relative_coord, points[0], points[1], points[2])) {
    COLOR *= modulate_color;
  }
}

My TileSet's texture is 256x256 with tiles 32x32.

TileSet texture

I'm trying to shade the triangles on the third row with my shader. This is the result of the above code.

Result of shader

For the life of me, I can't figure out what I'm doing wrong. If I had to guess, it's the calculation of vec2 pixel_coordinate = UV / TEXTURE_PIXEL_SIZE;. I just don't understand why, and haven't found a good way to debug a shader without a debugger or printf.

Edit

When I hard-code the texture size, it works perfectly.

  vec2 pixel_coordinate = UV * vec2(256.0, 256.0);

This is hardly ideal. I have made it a uniform, but that still means if I ever change my texture's size, I'll need to update every place that I use this shader. I'd still like a general answer.


Solution

  • Just disable texture padding in the TileSet configuration, shader is being applied to the original texture and is not aware of the padding, hence the weird scaling.

    "Use Texture Padding" option