src/relight.wgsl
Build-time source snapshot · Plain text
1// This lab shades known synthetic surfels. It does not infer materials from RGB.2struct Globals {3 vp: mat4x4<f32>,4 eye: vec4<f32>,5 light: vec4<f32>, // toward light, perpendicular incident irradiance6 material: vec4<f32>, // perceptual roughness, metallic, mode, unused7 viewport: vec4<f32>, // width, height, sRGB attachment flag, unused8};9struct Splat {10 center: vec4<f32>, // xyz, opacity11 u: vec4<f32>, v: vec4<f32>, w: vec4<f32>, // transformed covariance axes12 normal: vec4<f32>, base: vec4<f32>, // linear RGB, NOT observed RGB13};14@group(0) @binding(0) var<uniform> g: Globals;15@group(0) @binding(1) var<storage, read> splats: array<Splat>;16struct Out {17 @builtin(position) clip: vec4<f32>,18 @location(0) local: vec2<f32>,19 @location(1) @interpolate(flat) radiance: vec3<f32>,20 @location(2) @interpolate(flat) opacity: f32,21};2223// ANCHOR: brdf24fn masking(cosine: f32, a2: f32) -> f32 {25 return 2.0 * cosine / max(cosine + sqrt(a2 + (1.0-a2)*cosine*cosine), 1e-6);26}27fn shade(s: Splat) -> vec3<f32> {28 let n = normalize(s.normal.xyz);29 let v = normalize(g.eye.xyz - s.center.xyz);30 let l = g.light.xyz;31 let mode = u32(g.material.z);32 if mode == 3u { return n * 0.5 + 0.5; }33 if mode == 4u { return s.base.xyz; }34 let nl = max(dot(n,l), 0.0);35 let nv = max(dot(n,v), 0.0);36 if nl <= 0.0 || nv <= 0.0 { return vec3(0.0); }37 let h = normalize(l+v);38 let nh = max(dot(n,h), 0.0);39 let vh = max(dot(v,h), 0.0);40 let a = g.material.x * g.material.x;41 let a2 = a*a;42 let denominator = nh*nh*(a2-1.0)+1.0;43 let d = a2 / (3.14159265359 * denominator * denominator);44 let f0 = mix(vec3(0.04), s.base.xyz, g.material.y);45 let fresnel = f0 + (1.0-f0)*pow(1.0-vh, 5.0);46 let geometry = masking(nl,a2)*masking(nv,a2);47 let specular = d*fresnel*geometry / max(4.0*nl*nv, 1e-6);48 let diffuse = (1.0-fresnel)*(1.0-g.material.y)*s.base.xyz/3.14159265359;49 var brdf = diffuse+specular;50 if mode == 1u { brdf = diffuse; }51 if mode == 2u { brdf = specular; }52 // One white distant light, no occlusion, no indirect illumination.53 return brdf * nl * g.light.w;54}55// END: brdf5657fn axis_projection(center: vec4<f32>, axis: vec3<f32>) -> vec2<f32> {58 let delta = g.vp*vec4(axis,0.0);59 return (delta.xy*center.w-center.xy*delta.w)/(center.w*center.w)*g.viewport.xy*0.5;60}61@vertex fn vs_main(@builtin(vertex_index) vi: u32, @builtin(instance_index) si: u32) -> Out {62 let s = splats[si];63 let corners = array<vec2<f32>,6>(vec2(-1.,-1.),vec2(1.,-1.),vec2(-1.,1.),vec2(-1.,1.),vec2(1.,-1.),vec2(1.,1.));64 let q = corners[vi];65 let center = g.vp*vec4(s.center.xyz,1.0);66 let x = axis_projection(center,s.u.xyz);67 let y = axis_projection(center,s.v.xyz);68 let z = axis_projection(center,s.w.xyz);69 let a = x.x*x.x+y.x*y.x+z.x*z.x+0.3;70 let b = x.x*x.y+y.x*y.y+z.x*z.y;71 let c = x.y*x.y+y.y*y.y+z.y*z.y+0.3;72 let delta = sqrt(max((a-c)*(a-c)+4.0*b*b,0.0));73 let major = max((a+c+delta)*0.5,0.1);74 let minor = max((a+c-delta)*0.5,0.1);75 var axis = vec2(1.,0.);76 if abs(b)>1e-5 { axis=normalize(vec2(b,major-a)); }77 else if c>a { axis=vec2(0.,1.); }78 let offset = 3.0*(q.x*sqrt(major)*axis+q.y*sqrt(minor)*vec2(-axis.y,axis.x));79 var out: Out;80 out.clip = center+vec4(offset*2.0/g.viewport.xy*center.w,0.0,0.0);81 out.local = q*3.0;82 out.radiance = shade(s);83 out.opacity = s.center.w;84 if dot(s.normal.xyz,g.eye.xyz-s.center.xyz)<=0.0 { out.opacity=0.0; }85 return out;86}87// ANCHOR: composite88@fragment fn fs_main(input: Out) -> @location(0) vec4<f32> {89 let alpha = min(0.99,input.opacity*exp(-0.5*dot(input.local,input.local)));90 if alpha < 1.0/255.0 { discard; }91 // Premultiplied linear radiance into RGBA16Float, far to near.92 return vec4(input.radiance*alpha,alpha);93}94// END: composite9596@group(1) @binding(0) var hdr: texture_2d<f32>;97@vertex fn vs_display(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {98 let p = array<vec2<f32>,3>(vec2(-1.,-1.),vec2(3.,-1.),vec2(-1.,3.));99 return vec4(p[i],0.,1.);100}101// ANCHOR: display102@fragment fn fs_display(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {103 let sample = textureLoad(hdr,vec2<i32>(p.xy),0);104 var linear = sample.rgb + (1.0-sample.a)*vec3(0.006,0.008,0.014);105 if g.material.z < 3.0 { linear = linear/(1.0+linear); }106 // sRGB attachments encode automatically. UNORM canvases need this once.107 if g.viewport.z < 0.5 {108 linear = select(1.055*pow(linear,vec3(1.0/2.4))-0.055,12.92*linear,linear<=vec3(0.0031308));109 }110 return vec4(linear,1.0);111}112// END: display113