struct Camera {
center: vec2<f32>,
zoom: f32,
fade: f32,
viewport: vec2<f32>,
_pad1: vec2<f32>,
};
@group(0) @binding(0) var<uniform> cam: Camera;
// unit quad in (t, s): t along the edge 0..1, s across -1..1
var<private> CORNERS: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
vec2<f32>(0.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0),
vec2<f32>(0.0, -1.0), vec2<f32>(1.0, 1.0), vec2<f32>(0.0, 1.0),
);
struct VsOut { @builtin(position) clip: vec4<f32>, @location(0) color: vec4<f32>, @location(1) opacity: f32 };
@vertex
fn vs_main(
@builtin(vertex_index) vi: u32,
@location(0) a: vec2<f32>,
@location(1) b: vec2<f32>,
@location(2) color: vec4<f32>,
@location(3) width: f32,
@location(4) opacity: f32,
) -> VsOut {
let c = CORNERS[vi];
let d = b - a;
let len = length(d);
let dir = select(vec2<f32>(1.0, 0.0), d / len, len > 1e-5);
let normal = vec2<f32>(-dir.y, dir.x);
let world = mix(a, b, c.x) + normal * (width * 0.5) * c.y;
let rel = (world - cam.center) * cam.zoom;
let clip = vec2<f32>(rel.x / (cam.viewport.x * 0.5), rel.y / (cam.viewport.y * 0.5));
var out: VsOut;
out.clip = vec4<f32>(clip, 0.0, 1.0);
out.color = color;
out.opacity = opacity;
return out;
}
@fragment
fn fs_main(in: VsOut) -> @location(0) vec4<f32> {
return vec4<f32>(in.color.rgb, in.color.a * in.opacity * cam.fade);
}