src/workshop.wgsl
Repository source snapshot · Plain text · GitHub
1// ANCHOR: layout2struct Uniforms { view_projection: mat4x4<f32>, viewport_stage: vec4<f32> };3struct Instance { center_alpha: vec4<f32>, scale_pad: vec4<f32>, color_pad: vec4<f32> };4@group(0) @binding(0) var<uniform> globals: Uniforms;5@group(0) @binding(1) var<storage, read> instances: array<Instance>;6struct Out {7 @builtin(position) clip: vec4<f32>,8 @location(0) local: vec2<f32>,9 @location(1) rgba: vec4<f32>,10};11// END: layout1213fn corner(i: u32) -> vec2<f32> {14 let corners = array<vec2<f32>, 6>(vec2(-1., -1.), vec2(1., -1.), vec2(-1., 1.), vec2(-1., 1.), vec2(1., -1.), vec2(1., 1.));15 return corners[i];16}1718// ANCHOR: jacobian19fn project_axis(center: vec4<f32>, axis: vec3<f32>) -> vec2<f32> {20 let delta = globals.view_projection * vec4(axis, 0.0);21 // Quotient rule for (clip.xy / clip.w), then NDC -> pixels.22 return (delta.xy * center.w - center.xy * delta.w) / (center.w * center.w) * globals.viewport_stage.xy * 0.5;23}24// END: jacobian2526@vertex fn vs_main(@builtin(vertex_index) vertex: u32, @builtin(instance_index) index: u32) -> Out {27 let stage = u32(globals.viewport_stage.z);28 var out: Out;29 // ANCHOR: triangle30 if stage == 1u {31 let positions = array<vec2<f32>, 3>(vec2(-0.7, -0.6), vec2(0.7, -0.6), vec2(0.0, 0.7));32 let colors = array<vec3<f32>, 3>(vec3(1., 0., 0.), vec3(0., 1., 0.), vec3(0., 0., 1.));33 out.clip = vec4(positions[vertex], 0.5, 1.0);34 out.local = vec2(0.0);35 out.rgba = vec4(colors[vertex], 1.0);36 return out;37 }38 // END: triangle39 let splat = instances[index];40 let q = corner(vertex);41 out.local = q * 3.0;42 out.rgba = vec4(splat.color_pad.xyz, splat.center_alpha.w);43 if stage < 5u {44 out.clip = vec4(q * vec2(0.72, 0.62), 0.5, 1.0);45 return out;46 }47 // ANCHOR: covariance48 let center = globals.view_projection * vec4(splat.center_alpha.xyz, 1.0);49 let scale = splat.scale_pad.xyz;50 // Axis-aligned world ellipsoids here; the real pass also rotates these axes.51 let a0 = project_axis(center, vec3(scale.x, 0., 0.));52 let a1 = project_axis(center, vec3(0., scale.y, 0.));53 let a2 = project_axis(center, vec3(0., 0., scale.z));54 let a = a0.x*a0.x + a1.x*a1.x + a2.x*a2.x + 0.3;55 let b = a0.x*a0.y + a1.x*a1.y + a2.x*a2.y;56 let c = a0.y*a0.y + a1.y*a1.y + a2.y*a2.y + 0.3;57 let root = sqrt(max(0.0, (a-c)*(a-c) + 4.0*b*b));58 let large = max(0.1, 0.5*(a+c+root));59 let small = max(0.1, 0.5*(a+c-root));60 var axis = vec2(1.0, 0.0);61 if abs(b) > 0.00001 { axis = normalize(vec2(b, large-a)); }62 else if c > a { axis = vec2(0.0, 1.0); }63 let perpendicular = vec2(-axis.y, axis.x);64 let pixels = 3.0 * (q.x * sqrt(large) * axis + q.y * sqrt(small) * perpendicular);65 let ndc = pixels * 2.0 / globals.viewport_stage.xy;66 out.clip = center + vec4(ndc * center.w, 0.0, 0.0);67 // END: covariance68 return out;69}7071// ANCHOR: fragment72@fragment fn fs_main(input: Out) -> @location(0) vec4<f32> {73 let stage = u32(globals.viewport_stage.z);74 if stage <= 2u { return vec4(input.rgba.rgb, 1.0); }75 let weight = exp(-0.5 * dot(input.local, input.local));76 let alpha = min(0.99, input.rgba.a * weight);77 if alpha < 1.0 / 255.0 { discard; }78 return vec4(input.rgba.rgb * alpha, alpha);79}80// END: fragment81