diff --git a/src/sprite_shader.wgsl b/src/sprite_shader.wgsl new file mode 100644 index 00000000..a1ab6a39 --- /dev/null +++ b/src/sprite_shader.wgsl @@ -0,0 +1,77 @@ +// Vertex shader + +struct ScreenUniform { + resolution : vec2f, +}; +@group(1) @binding(0) // 1. + var screen : ScreenUniform; + +struct VertexInput { + @location(0) position : vec3, @location(1) tex_coords : vec2, +}; + +struct VertexOutput { + @builtin(position) clip_position : vec4, + @location(0) tex_coords : vec2, +}; + +const RES = vec2f(320.0, 240.0); // The logical resolution of the screen. + +@vertex fn vs_main(model : VertexInput)->VertexOutput { + var out : VertexOutput; + out.tex_coords = model.tex_coords; + + let RES_AR = RES.x / RES.y; // The aspect ratio of the logical screen. + + // the actual resolution of the screen. + let screen_ar = screen.resolution.x / screen.resolution.y; + + // Compute the difference in resolution ... correctly? + // + // nudge is the amount to add to the logical resolution so that the pixels + // stay the same size but we respect the aspect ratio of the screen. (So + // there's more of them in either the x or y direction.) + var nudge = vec2f(0.0); + if (screen_ar > RES_AR) { + nudge.x = (RES.y * screen_ar) - RES.x; + } else { + nudge.y = (RES.x / screen_ar) - RES.y; + } + var new_logical_resolution = RES + nudge; + + // Now we can convert the incoming position to clip space, in the new screen. + let in_pos = vec2f(model.position.x, model.position.y); + let centered = in_pos + (nudge / 2.0); + let position = (2.0 * centered / new_logical_resolution) - 1.0; + + out.clip_position = vec4f(position, model.position.z, 1.0); + return out; +} + +// Fragment shader.... + +@group(0) @binding(0) var t_diffuse : texture_2d; +@group(0) @binding(1) var s_diffuse : sampler; + +@fragment fn fs_main(in : VertexOutput)->@location(0) vec4 { + // The "screen" is centered in the window, so anything outside of the + // screen borders should be black. But *where are they*? + let RES_AR = RES.x / RES.y; // The aspect ratio of the logical screen. + let screen_ar = screen.resolution.x / screen.resolution.y; + var black_mod = 1.0; + if (screen_ar > RES_AR) { + // Wider than tall, bars are on the left and right. + let active_width = screen.resolution.y * RES_AR; + let half_delta = (screen.resolution.x - active_width) / 2.0; + if (in.clip_position.x < half_delta || + in.clip_position.x > half_delta + active_width) { + black_mod = 0.0; + } + } else { + // Taller than wide, bars are on top and bottom. + } + + let dims = vec2f(textureDimensions(t_diffuse)); + + return black_mod * textureSample(t_diffuse, s_diffuse, in.tex_coords / dims); +}