struct VertexShaderInput { // Per Vertex @builtin(vertex_index) vertex_index: u32, @location(0) pos: vec3, @location(1) uv: vec2, // Per Instance @builtin(instance_index) instance_index: u32, @location(2) tile: u32, }; struct VertexShaderOutput { @builtin(position) pos: vec4, @location(0) uv: vec2, @location(1) tile: u32, }; struct FragmentShaderOutput { @location(0) color: vec4, }; struct Per_Frame_Data { drag_start: vec2, mouse: vec2, grid_offset: vec2, grid_width: u32, map_width: u32, }; @group(0) @binding(0) var view_projection_matrix: mat4x4; @group(0) @binding(1) var per_frame: Per_Frame_Data; @vertex fn main_vertex(input: VertexShaderInput) -> VertexShaderOutput { var output: VertexShaderOutput; let tile_pos = vec2(f32(input.instance_index % per_frame.map_width), f32(input.instance_index / per_frame.map_width)) - vec2(0.5, 0.5); output.tile = input.tile; output.pos = vec4(tile_pos + input.pos.xy, 0, 1) * view_projection_matrix; output.uv = input.uv; return output; } @group(1) @binding(0) var texture1: texture_2d; @group(1) @binding(1) var sampler1: sampler; @group(1) @binding(2) var tint: vec3; @group(1) @binding(3) var tile_uvs: array>; @fragment fn main_fragment(input: VertexShaderOutput) -> FragmentShaderOutput { var output: FragmentShaderOutput; output.color = pixel_art_sample(texture1, sampler1, input.uv, input.tile); output.color = vec4(output.color.rgb * tint.rgb, output.color.a); return output; } fn pixel_art_sample(input_texture: texture_2d, input_sampler: sampler, input_uv: vec2, tile: u32) -> vec4 { let dimensions = vec2(textureDimensions(input_texture)); let tile_uv = tile_uvs[tile]; let texture_uv = mix(tile_uv.xy, tile_uv.zw, input_uv); let sample_uv = (floor(texture_uv) + saturate(fract(texture_uv) / fwidth(texture_uv)) - 0.5) / dimensions; let uv = clamp(sample_uv, (tile_uv.xy + 0.5) / dimensions, (tile_uv.zw - 0.5) / dimensions); return textureSample(input_texture, input_sampler, uv); }