Skip to content

Commit

Permalink
Fix Tile Bleeding When Using Atlas Feature
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Jun 24, 2022
1 parent 9e431db commit 6a95d15
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/render/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl FromWorld for TilemapPipeline {
let uniform_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
Expand Down
105 changes: 62 additions & 43 deletions src/render/shaders/tilemap-atlas.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var<uniform> tilemap_data: TilemapData;

struct VertexOutput {
[[builtin(position)]] position: vec4<f32>;
[[location(0)]] uv: vec2<f32>;
[[location(0)]] uv: vec4<f32>;
[[location(1)]] color: vec4<f32>;
};

Expand Down Expand Up @@ -62,61 +62,61 @@ fn vertex(
var start_v: f32 = sprite_sheet_y / tilemap_data.texture_size.y;
var end_v: f32 = (sprite_sheet_y + tilemap_data.tile_size.y) / tilemap_data.texture_size.y;

var atlas_uvs: array<vec2<f32>, 4>;

var x1: array<vec2<f32>, 8> = array<vec2<f32>, 8>(
vec2<f32>(start_u, end_v), // no flip/rotation
vec2<f32>(end_u, end_v), // flip x
vec2<f32>(start_u, start_v), // flip y
vec2<f32>(end_u, start_v), // flip x y
vec2<f32>(end_u, start_v), // flip d
vec2<f32>(end_u, end_v), // flip x d
vec2<f32>(start_u, start_v), // flip y d
vec2<f32>(start_u, end_v)
var uvs: array<vec4<f32>, 4>;

var x1: array<vec4<f32>, 8> = array<vec4<f32>, 8>(
// The x and y are the texture UV, and the z and w and the local tile UV
vec4<f32>(start_u, end_v, 0.0, 1.0), // no flip/rotation
vec4<f32>(end_u, end_v, 1.0, 1.0), // flip x
vec4<f32>(start_u, start_v, 0.0, 0.0), // flip y
vec4<f32>(end_u, start_v, 1.0, 0.0), // flip x y
vec4<f32>(end_u, start_v, 1.0, 0.0), // flip d
vec4<f32>(end_u, end_v, 1.0, 1.0), // flip x d
vec4<f32>(start_u, start_v, 0.0, 0.0), // flip y d
vec4<f32>(start_u, end_v, 0.0, 1.0)
);

var x2: array<vec2<f32>, 8> = array<vec2<f32>, 8>(
vec2<f32>(start_u, start_v),
vec2<f32>(end_u, start_v),
vec2<f32>(start_u, end_v),
vec2<f32>(end_u, end_v),
vec2<f32>(start_u, start_v),
vec2<f32>(start_u, end_v),
vec2<f32>(end_u, start_v),
vec2<f32>(end_u, end_v)
var x2: array<vec4<f32>, 8> = array<vec4<f32>, 8>(
vec4<f32>(start_u, start_v, 0.0, 0.0),
vec4<f32>(end_u, start_v, 1.0, 0.0),
vec4<f32>(start_u, end_v, 0.0, 1.0),
vec4<f32>(end_u, end_v, 1.0, 1.0),
vec4<f32>(start_u, start_v, 0.0, 0.0),
vec4<f32>(start_u, end_v, 0.0, 1.0),
vec4<f32>(end_u, start_v, 1.0, 0.0),
vec4<f32>(end_u, end_v, 1.0, 1.0)
);

var x3: array<vec2<f32>, 8> = array<vec2<f32>, 8>(
vec2<f32>(end_u, start_v),
vec2<f32>(start_u, start_v),
vec2<f32>(end_u, end_v),
vec2<f32>(start_u, end_v),
vec2<f32>(start_u, end_v),
vec2<f32>(start_u, start_v),
vec2<f32>(end_u, end_v),
vec2<f32>(end_u, start_v)
var x3: array<vec4<f32>, 8> = array<vec4<f32>, 8>(
vec4<f32>(end_u, start_v, 1.0, 0.0),
vec4<f32>(start_u, start_v, 0.0, 0.0),
vec4<f32>(end_u, end_v, 1.0, 1.0),
vec4<f32>(start_u, end_v, 0.0, 1.0),
vec4<f32>(start_u, end_v, 0.0, 1.0),
vec4<f32>(start_u, start_v, 0.0, 0.0),
vec4<f32>(end_u, end_v, 1.0, 1.0),
vec4<f32>(end_u, start_v, 1.0, 0.0)
);

var x4: array<vec2<f32>, 8> = array<vec2<f32>, 8>(
vec2<f32>(end_u, end_v),
vec2<f32>(start_u, end_v),
vec2<f32>(end_u, start_v),
vec2<f32>(start_u, start_v),
vec2<f32>(end_u, end_v),
vec2<f32>(end_u, start_v),
vec2<f32>(start_u, end_v),
vec2<f32>(start_u, start_v),
var x4: array<vec4<f32>, 8> = array<vec4<f32>, 8>(
vec4<f32>(end_u, end_v, 1.0, 1.0),
vec4<f32>(start_u, end_v, 0.0, 1.0),
vec4<f32>(end_u, start_v, 1.0, 0.0),
vec4<f32>(start_u, start_v, 0.0, 0.0),
vec4<f32>(end_u, end_v, 1.0, 1.0),
vec4<f32>(end_u, start_v, 1.0, 0.0),
vec4<f32>(start_u, end_v, 0.0, 1.0),
vec4<f32>(start_u, start_v, 0.0, 0.0),
);

atlas_uvs = array<vec2<f32>, 4>(
uvs = array<vec4<f32>, 4>(
x1[vertex_uv.y],
x2[vertex_uv.y],
x3[vertex_uv.y],
x4[vertex_uv.y]
);

out.uv = atlas_uvs[v_index % 4u];
out.uv = out.uv + 1e-5;
out.uv = uvs[v_index % 4u];
out.position = view.view_proj * mesh_data.world_position;
out.color = color;
return out;
Expand All @@ -129,7 +129,26 @@ var sprite_sampler: sampler;

[[stage(fragment)]]
fn fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
var color = textureSample(sprite_texture, sprite_sampler, in.uv.xy) * in.color;
var half_texture_pixel_size_u = 0.5 / tilemap_data.texture_size.x;
var half_texture_pixel_size_v = 0.5 / tilemap_data.texture_size.y;
let half_tile_pixel_size_u = 0.5 / tilemap_data.tile_size.x;
let half_tile_pixel_size_v = 0.5 / tilemap_data.tile_size.y;

// Offset the UV 1/2 pixel from the sides of the tile, so that the sampler doesn't bleed onto
// adjacent tiles at the edges.
var uv_offset: vec2<f32> = vec2<f32>(0.0, 0.0);

// To avoid branching with if statements, we use some funky rounding. The meaning is:
// if (in.uv.z < half_tile_pixel_size_u) { uv_offset.x = half_texture_pixel_size_u }
uv_offset.x = min(floor(half_tile_pixel_size_u / in.uv.z), 1.0) * half_texture_pixel_size_u;
// if (in.uv.z > 1.0 - half_tile_pixel_size_u) { uv_offset.x = - half_texture_pixel_size_u }
uv_offset.x = uv_offset.x.x + min(floor(in.uv.z / (1.0 - half_tile_pixel_size_u)), 1.0) * half_texture_pixel_size_u * - 1.0;

// And the same for the y offset
uv_offset.y = min(floor(half_tile_pixel_size_v / in.uv.w), 1.0) * half_texture_pixel_size_v;
uv_offset.y = uv_offset.y + min(floor(in.uv.w / (1.0 - half_tile_pixel_size_v)), 1.0) * half_texture_pixel_size_v * - 1.0;

var color = textureSample(sprite_texture, sprite_sampler, in.uv.xy + uv_offset) * in.color;
if (color.a < 0.001) {
discard;
}
Expand Down

0 comments on commit 6a95d15

Please sign in to comment.