Constant TRACE_WGSL
Source pub const TRACE_WGSL: &str = "struct Frame {\n size: vec4u,\n counts: vec4u,\n origin: vec4f,\n forward: vec4f,\n right: vec4f,\n up: vec4f,\n sun_dir: vec4f,\n sun_color: vec4f,\n lens: vec4f,\n window: vec4u,\n columns: vec4u,\n}\noverride TOTAL_BOUNCES: u32 = 16u;\noverride DIFFUSE_BOUNCES: u32 = 4u;\noverride GLOSSY_BOUNCES: u32 = 8u;\noverride TRANSMISSION_BOUNCES: u32 = 12u;\noverride BLOCK_STRIDE: u32 = 1u;\noverride TRANSMISSIVE_SHADOWS: bool = false;\noverride CLAMP_INDIRECT: f32 = 0.0;\noverride FILTER_GLOSSY: f32 = 0.0;\noverride SOBOL: bool = false;\noverride SOBOL_LENGTH: u32 = 256u;\noverride STEAM_SPREAD_BASE: f32 = 0.7;\noverride STEAM_SPREAD_GROWTH: f32 = 4.0;\noverride STEAM_DRIFT_X_QUAD: f32 = -0.9;\noverride STEAM_DRIFT_X_LIN: f32 = -0.12;\noverride STEAM_DRIFT_Z_QUAD: f32 = 0.0;\noverride STEAM_DRIFT_Z_LIN: f32 = 0.02;\noverride STEAM_SWAY_X_WAVE: f32 = 17.0;\noverride STEAM_SWAY_X_RATE: f32 = 1.3;\noverride STEAM_SWAY_X_AMP: f32 = 0.012;\noverride STEAM_SWAY_X_BASE: f32 = 0.4;\noverride STEAM_SWAY_X_GROWTH: f32 = 6.0;\noverride STEAM_SWAY_Z_WAVE: f32 = 13.0;\noverride STEAM_SWAY_Z_RATE: f32 = 0.9;\noverride STEAM_SWAY_Z_AMP: f32 = 0.01;\noverride STEAM_SWAY_Z_BASE: f32 = 0.4;\noverride STEAM_SWAY_Z_GROWTH: f32 = 5.0;\noverride STEAM_FADE_IN: f32 = 0.02;\noverride STEAM_FADE_OUT_START: f32 = 0.07;\noverride STEAM_FADE_OUT_END: f32 = 0.2;\noverride STEAM_GRAIN_H: f32 = 70.0;\noverride STEAM_GRAIN_V: f32 = 42.0;\noverride STEAM_LIFT_RATE: f32 = 0.05;\noverride STEAM_WARP_AMP: f32 = 2.2;\noverride STEAM_WARP_RATE: f32 = 0.2;\noverride STEAM_CHURN: f32 = 0.7;\noverride STEAM_THRESHOLD_LOW: f32 = 0.42;\noverride STEAM_THRESHOLD_HIGH: f32 = 0.9;\nstruct Tri { a: vec4f, b: vec4f, c: vec4f, na: vec4f, nb: vec4f, nc: vec4f }\nstruct Node { lo: vec4f, hi: vec4f, first: u32, count: u32, pad: vec2u }\nstruct Shape { a: vec4f, b: vec4f, c: vec4f, d: vec4f }\nstruct Material {\n base_roughness: vec4f,\n metal_spec_coat: vec4f,\n sheen_trans_ior_disp: vec4f,\n thick_sub_film: vec4f,\n tint_absorption: vec4f,\n emission_film: vec4f,\n normal_maps: vec4f,\n maps_content: vec4f,\n age_a: vec4f,\n age_b: vec4f,\n layer0: vec4f,\n layer1: vec4f,\n layer2: vec4f,\n layer3: vec4f,\n tail: vec4f,\n params: array<vec4f, 16>,\n}\nstruct Hit { t: f32, n: vec3f, geometric: vec3f, material: u32, uv: vec2f, tangent: vec3f, bitangent: vec3f, surface: u32, shape: u32 }\nstruct ContentInfo { offset: u32, width: u32, height: u32, kind: u32 }\nstruct InstanceSurface { clip0: vec4f, clip1: vec4f, transform: vec4f, crop: vec4f }\nstruct ContentLayer { layer: vec4f, extra: vec4f }\nstruct LightSample { dir: vec3f, pdf: f32, channel: u32, weight: vec3f, lobe: u32 }\nstruct Accum { color: vec4f, albedo: vec4f, normal: vec4f }\nstruct DetailResult { shade: Shaded, normal: vec3f }\nstruct Candidate { wi: vec3f, scale: vec3f, pdf: f32, roughness: f32, direction: vec3f, limit: f32, shape: u32 }\n@group(0) @binding(0) var<uniform> frame: Frame;\n@group(0) @binding(1) var<storage, read> triangles: array<Tri>;\n@group(0) @binding(2) var<storage, read> nodes: array<Node>;\n@group(0) @binding(3) var<storage, read> shapes: array<Shape>;\n@group(0) @binding(4) var<storage, read> materials: array<Material>;\n@group(0) @binding(5) var<storage, read> triangle_uv_v: array<vec4f>;\n@group(0) @binding(6) var<storage, read> content_texels: array<vec4f>;\n@group(0) @binding(7) var<storage, read_write> accum: array<Accum>;\n@group(0) @binding(8) var output: texture_storage_2d<rgba32float, write>;\n@group(0) @binding(9) var albedo: texture_storage_2d<rgba32float, write>;\n@group(0) @binding(10) var normal: texture_storage_2d<rgba32float, write>;\n@group(0) @binding(11) var<storage, read> content_info: array<ContentInfo>;\n@group(0) @binding(12) var<storage, read> instance_surfaces: array<InstanceSurface>;\n@group(0) @binding(13) var<storage, read> content_layers: array<ContentLayer>;\n\nfn effects() -> Material {\n return materials[arrayLength(&materials)-1u];\n}\n\nfn trace_noise_start() {}\nfn pcg(v: u32) -> u32 {\n let s = v * 747796405u + 2891336453u;\n let w = ((s >> ((s >> 28u) + 4u)) ^ s) * 277803737u;\n return (w >> 22u) ^ w;\n}\nfn hash3_xor(p: vec3i) -> f32 {\n let h = u32(p.x) * 73856093u ^ u32(p.y) * 19349663u ^ u32(p.z) * 83492791u;\n return f32(pcg(h) >> 8u) / 16777216.0;\n}\nfn hash3_nested(p: vec3i) -> f32 {\n let h = pcg(u32(p.x) * 73856093u ^ pcg(u32(p.y) * 19349663u ^ pcg(u32(p.z) * 83492791u)));\n return f32(h >> 8u) / 16777216.0;\n}\nfn value_noise3(p: vec3f, nested: bool) -> f32 {\n let i=vec3i(floor(p));\n let f=fract(p);\n let u=f*f*(3.0-2.0*f);\n var a0: f32;\n var a1: f32;\n var a2: f32;\n var a3: f32;\n var b0: f32;\n var b1: f32;\n var b2: f32;\n var b3: f32;\n if (nested) {\n a0=hash3_nested(i);\n a1=hash3_nested(i+vec3i(1,0,0));\n a2=hash3_nested(i+vec3i(0,1,0));\n a3=hash3_nested(i+vec3i(1,1,0));\n b0=hash3_nested(i+vec3i(0,0,1));\n b1=hash3_nested(i+vec3i(1,0,1));\n b2=hash3_nested(i+vec3i(0,1,1));\n b3=hash3_nested(i+vec3i(1,1,1));\n } else {\n a0=hash3_xor(i);\n a1=hash3_xor(i+vec3i(1,0,0));\n a2=hash3_xor(i+vec3i(0,1,0));\n a3=hash3_xor(i+vec3i(1,1,0));\n b0=hash3_xor(i+vec3i(0,0,1));\n b1=hash3_xor(i+vec3i(1,0,1));\n b2=hash3_xor(i+vec3i(0,1,1));\n b3=hash3_xor(i+vec3i(1,1,1));\n }\n let a=mix(mix(a0,a1,u.x),mix(a2,a3,u.x),u.y);\n let b=mix(mix(b0,b1,u.x),mix(b2,b3,u.x),u.y);\n return mix(a,b,u.z);\n}\nfn trace_noise_end() {}\n\nfn random(state: ptr<function,u32>) -> f32 {\n *state = pcg(*state);\n return f32(*state >> 8u) / 16777216.0;\n}\nvar<private> rng: vec3u;\nfn hash_iqnt2d(x: u32, y: u32) -> u32 {\n let qx=1103515245u*((x>>1u)^y);\n let qy=1103515245u*((y>>1u)^x);\n return 1103515245u*(qx^(qy>>3u));\n}\nfn trace_sobol_start() {}\nfn sobol(dimension: u32) -> vec4f {\n return vec4f(0.0);\n}\nfn trace_sobol_end() {}\nfn rnd(state: ptr<function,u32>, value: f32) -> f32 {\n if (SOBOL) { return value; }\n return random(state);\n}\nfn light_dim(k: u32) -> u32 {\n if (k==0u) { return 1u; }\n if (k<13u) { return 3u+k; }\n return k<<16u;\n}\nfn box_hit(node: Node, o: vec3f, d: vec3f, limit: f32) -> bool {\n var near = 0.0;\n var far = limit;\n for (var axis=0u; axis<3u; axis++) {\n if (abs(d[axis]) < 1e-12) {\n if (o[axis] < node.lo[axis] || o[axis] > node.hi[axis]) { return false; }\n } else {\n let a = (node.lo[axis]-o[axis])/d[axis];\n let b = (node.hi[axis]-o[axis])/d[axis];\n near = max(near,min(a,b));\n far = min(far,max(a,b));\n if (near > far) { return false; }\n }\n }\n return true;\n}\nconst ONE_SIDED: u32 = 8388608u;\nconst LAYER_MASK: u32 = 262143u;\nfn hit_layer(i: u32, material: u32) -> ContentLayer {\n let layer=(u32(triangles[i].b.w)>>5u) & LAYER_MASK;\n if (layer>0u) { return content_layers[layer-1u]; }\n return content_layers[material];\n}\nfn cut_out(i: u32, u: f32, v: f32) -> bool {\n let tri = triangles[i];\n if ((u32(tri.b.w)&4u)==0u) { return false; }\n let slot=i32(round(hit_layer(i,u32(tri.a.w)).layer.x));\n if (slot<0) { return true; }\n let uv_v=triangle_uv_v[i];\n let uv=vec2f(tri.na.w*(1.0-u-v)+tri.nb.w*u+tri.nc.w*v,uv_v.x*(1.0-u-v)+uv_v.y*u+uv_v.z*v);\n let surface=instance_surfaces[i];\n let at=content_uv(uv,surface.transform);\n if (!content_inside(at,surface.crop)) { return true; }\n let cutoff=uv_v.w;\n if (cutoff==0.5) { return content_at(slot,at).a<0.5; }\n return content_at(slot,at).a<cutoff;\n}\nfn tri_hit(i: u32, o: vec3f, d: vec3f, shadow_ray: bool, hit: ptr<function,Hit>) {\n let tri = triangles[i];\n let flags = u32(tri.b.w);\n if ((flags&1u)!=0u && !shadow_ray) { return; }\n if ((flags&2u)!=0u && shadow_ray) { return; }\n let e1 = tri.b.xyz-tri.a.xyz;\n let e2 = tri.c.xyz-tri.a.xyz;\n let p = cross(d,e2);\n let det = dot(e1,p);\n if (abs(det)<1e-9) { return; }\n if (!shadow_ray && (flags & ONE_SIDED) != 0u && det < 0.0) { return; }\n let inv = 1.0/det;\n let s = o-tri.a.xyz;\n let u = dot(s,p)*inv;\n if (u<0.0 || u>1.0) { return; }\n let q = cross(s,e1);\n let v = dot(d,q)*inv;\n if (v<0.0 || u+v>1.0) { return; }\n let t = dot(e2,q)*inv;\n if (t>0.0001 && t<(*hit).t) {\n let surface=instance_surfaces[i];\n let point=o+d*t;\n if ((any(surface.clip0.xyz != vec3f(0.0)) && dot(surface.clip0.xyz,point)>surface.clip0.w)\n || (any(surface.clip1.xyz != vec3f(0.0)) && dot(surface.clip1.xyz,point)>surface.clip1.w)) { return; }\n if (cut_out(i,u,v)) { return; }\n (*hit).t=t;\n (*hit).surface=i;\n let geometric=normalize(cross(e1,e2));\n (*hit).geometric=geometric;\n let shaded=tri.na.xyz*(1.0-u-v)+tri.nb.xyz*u+tri.nc.xyz*v;\n (*hit).n=select(geometric,normalize(shaded),dot(shaded,shaded)>1e-12);\n (*hit).material=u32(tri.a.w);\n let uv_v=triangle_uv_v[i];\n (*hit).uv=vec2f(tri.na.w*(1.0-u-v)+tri.nb.w*u+tri.nc.w*v,\n uv_v.x*(1.0-u-v)+uv_v.y*u+uv_v.z*v);\n let du1=tri.nb.w-tri.na.w;\n let du2=tri.nc.w-tri.na.w;\n let dv1=uv_v.y-uv_v.x;\n let dv2=uv_v.z-uv_v.x;\n let uv_det=du1*dv2-du2*dv1;\n var tangent=normalize(e1);\n if (abs(uv_det)>1e-10) { tangent=normalize((e1*dv2-e2*dv1)/uv_det); }\n (*hit).tangent=tangent;\n (*hit).bitangent=normalize(cross(geometric,tangent))*select(-1.0,1.0,uv_det>=0.0);\n }\n}\nfn ellipsoid(p: vec3f, radius: vec3f) -> f32 {\n let k0 = length(p/radius);\n let k1 = length(p/(radius*radius));\n return k0*(k0-1.0)/max(k1,1e-8);\n}\nfn cone(p: vec3f, a: vec3f, b: vec3f, r1: f32, r2: f32) -> f32 {\n let ba=b-a;\n let length_axis=length(ba);\n if (length_axis<=abs(r1-r2)) {\n return select(length(p-b)-r2,length(p-a)-r1,r1>=r2);\n }\n let l2=dot(ba,ba);\n let rr=r1-r2;\n let a2=l2-rr*rr;\n let pa=p-a;\n let y=dot(pa,ba);\n let z=y-l2;\n let xv=pa*l2-ba*y;\n let x2=dot(xv,xv);\n let y2=y*y*l2;\n let z2=z*z*l2;\n let k=sign(rr)*rr*rr*x2;\n if (sign(z)*a2*z2>k) { return sqrt(x2+z2)/l2-r2; }\n if (sign(y)*a2*y2<k) { return sqrt(x2+y2)/l2-r1; }\n return sqrt(max(x2*a2/l2,0.0))/l2+y*rr/l2-r1;\n}\nfn base_distance(i: u32, p: vec3f) -> f32 {\n let s=shapes[i];\n let kind=u32(s.a.w);\n if (kind==1u) {\n let q=abs(p-s.a.xyz)-(s.b.xyz-vec3f(s.b.w));\n return length(max(q,vec3f(0.0)))+min(max(q.x,max(q.y,q.z)),0.0)-s.b.w;\n }\n if (kind==2u) { return cone(p,s.a.xyz,s.b.xyz,s.c.x,s.c.y); }\n if (kind==3u) { return ellipsoid(p-s.a.xyz,s.b.xyz); }\n if (kind==4u) {\n let ba=s.b.xyz-s.a.xyz;\n let h=clamp(dot(p-s.a.xyz,ba)/max(dot(ba,ba),1e-9),0.0,1.0);\n return length(p-s.a.xyz-ba*h)-s.c.x;\n }\n return 1e30;\n}\nfn shape_distance(i: u32, p: vec3f) -> f32 {\n let s=shapes[i];\n if (u32(s.a.w)==5u) {\n let a=base_distance(u32(s.b.x),p);\n let b=base_distance(u32(s.b.y),p);\n let radius=max(s.b.z,1e-6);\n let h=clamp(0.5+0.5*(b-a)/radius,0.0,1.0);\n return mix(b,a,h)-radius*h*(1.0-h);\n }\n return base_distance(i,p);\n}\nfn roots(a: f32, b: f32, c: f32) -> vec2f {\n if (abs(a)<1e-10) {\n if (abs(b)<1e-10) { return vec2f(1e30); }\n return vec2f(-c/(2.0*b),1e30);\n }\n let disc=b*b-a*c;\n if (disc<0.0) { return vec2f(1e30); }\n let root=sqrt(disc);\n return vec2f((-b-root)/a,(-b+root)/a);\n}\nfn consider(i: u32, o: vec3f, d: vec3f, t: f32, best: ptr<function,f32>) {\n if (t>0.0001 && t<*best && abs(shape_distance(i,o+d*t))<0.002) { *best=t; }\n}\nfn primitive_hit(i: u32, o: vec3f, d: vec3f, limit: f32) -> f32 {\n let shape=shapes[i];\n let kind=u32(shape.a.w);\n var best=limit;\n if (kind==3u) {\n let origin=(o-shape.a.xyz)/shape.b.xyz;\n let direction=d/shape.b.xyz;\n let candidates=roots(dot(direction,direction),dot(origin,direction),dot(origin,origin)-1.0);\n consider(i,o,d,candidates.x,&best);\n consider(i,o,d,candidates.y,&best);\n }\n if (kind==1u) {\n let core=max(shape.b.xyz-vec3f(shape.b.w),vec3f(0.0));\n let radius=shape.b.w;\n for (var axis=0u;axis<3u;axis++) {\n if (abs(d[axis])>1e-9) {\n for (var side=0u;side<2u;side++) {\n let sign_side=select(-1.0,1.0,side==1u);\n consider(i,o,d,(shape.a[axis]+sign_side*(core[axis]+radius)-o[axis])/d[axis],&best);\n }\n }\n let j=(axis+1u)%3u;\n let k=(axis+2u)%3u;\n for (var sj=0u;sj<2u;sj++) {\n for (var sk=0u;sk<2u;sk++) {\n let js=select(-1.0,1.0,sj==1u);\n let ks=select(-1.0,1.0,sk==1u);\n let oj=o[j]-shape.a[j]-js*core[j];\n let ok=o[k]-shape.a[k]-ks*core[k];\n let candidates=roots(d[j]*d[j]+d[k]*d[k],oj*d[j]+ok*d[k],oj*oj+ok*ok-radius*radius);\n for (var root=0u;root<2u;root++) {\n let t=candidates[root];\n if (abs(o[axis]+d[axis]*t-shape.a[axis])<=core[axis]+0.002) { consider(i,o,d,t,&best); }\n }\n }\n }\n }\n for (var sx=0u;sx<2u;sx++) {\n for (var sy=0u;sy<2u;sy++) {\n for (var sz=0u;sz<2u;sz++) {\n let corner=shape.a.xyz+vec3f(select(-1.0,1.0,sx==1u),select(-1.0,1.0,sy==1u),select(-1.0,1.0,sz==1u))*core;\n let v=o-corner;\n let candidates=roots(dot(d,d),dot(v,d),dot(v,v)-radius*radius);\n consider(i,o,d,candidates.x,&best);\n consider(i,o,d,candidates.y,&best);\n }\n }\n }\n }\n if (kind==4u) {\n let ba=shape.b.xyz-shape.a.xyz;\n let oa=o-shape.a.xyz;\n let baba=dot(ba,ba);\n let bard=dot(ba,d);\n let baoa=dot(ba,oa);\n let rdoa=dot(d,oa);\n let oaoa=dot(oa,oa);\n let radius=shape.c.x;\n let candidates=roots(baba*dot(d,d)-bard*bard,baba*rdoa-baoa*bard,baba*oaoa-baoa*baoa-radius*radius*baba);\n for (var root=0u;root<2u;root++) {\n let t=candidates[root];\n let y=baoa+t*bard;\n if (y>=0.0 && y<=baba) { consider(i,o,d,t,&best); }\n }\n for (var end=0u;end<2u;end++) {\n let center=select(shape.a.xyz,shape.b.xyz,end==1u);\n let v=o-center;\n let candidates=roots(dot(d,d),dot(v,d),dot(v,v)-radius*radius);\n consider(i,o,d,candidates.x,&best);\n consider(i,o,d,candidates.y,&best);\n }\n }\n if (kind==2u) {\n let ba=shape.b.xyz-shape.a.xyz;\n let len=length(ba);\n if (len>1e-8) {\n let axis=ba/len;\n let slope=(shape.c.x-shape.c.y)/len;\n if (abs(slope)<1.0) {\n let side=sqrt(1.0-slope*slope);\n let oa=o-shape.a.xyz;\n let y0=dot(oa,axis);\n let yd=dot(d,axis);\n let radial0=oa-axis*y0;\n let radiald=d-axis*yd;\n let e=shape.c.x-slope*y0;\n let candidates=roots(side*side*dot(radiald,radiald)-slope*slope*yd*yd,side*side*dot(radial0,radiald)+e*slope*yd,side*side*dot(radial0,radial0)-e*e);\n for (var root=0u;root<2u;root++) {\n let t=candidates[root];\n let y=y0+t*yd;\n if (y>=slope*shape.c.x-0.002 && y<=len+slope*shape.c.y+0.002) { consider(i,o,d,t,&best); }\n }\n }\n }\n for (var end=0u;end<2u;end++) {\n let center=select(shape.a.xyz,shape.b.xyz,end==1u);\n let radius=select(shape.c.x,shape.c.y,end==1u);\n let v=o-center;\n let candidates=roots(dot(d,d),dot(v,d),dot(v,v)-radius*radius);\n consider(i,o,d,candidates.x,&best);\n consider(i,o,d,candidates.y,&best);\n }\n }\n return best;\n}\nfn union_hit(i: u32, o: vec3f, d: vec3f, limit: f32) -> f32 {\n var t=0.0;\n var last=shape_distance(i,o);\n for (var step=0u;step<384u;step++) {\n let previous=t;\n t+=max(abs(last),0.00005);\n if (t>=limit) { return limit; }\n let distance=shape_distance(i,o+d*t);\n if (abs(distance)<0.0001) { return t; }\n if ((last>0.0 && distance<0.0) || (last<0.0 && distance>0.0)) {\n var lo=previous;\n var hi=t;\n for (var refine=0u;refine<24u;refine++) {\n let mid=(lo+hi)*0.5;\n let value=shape_distance(i,o+d*mid);\n if (sign(value)==sign(last)) { lo=mid; } else { hi=mid; }\n }\n return (lo+hi)*0.5;\n }\n last=distance;\n }\n return limit;\n}\nfn scene(o: vec3f, d: vec3f, limit: f32, shadow_ray: bool) -> Hit {\n var hit=Hit(limit,vec3f(0.0),vec3f(0.0),0u,vec2f(0.0),vec3f(1.0,0.0,0.0),vec3f(0.0,1.0,0.0),0u,0u);\n if (frame.counts.x>0u) {\n var stack: array<u32,64>;\n var top=1u;\n stack[0]=0u;\n while (top>0u) {\n top-=1u;\n let index=stack[top];\n let node=nodes[index];\n if (!box_hit(node,o,d,hit.t)) { continue; }\n if (node.count==0u) {\n if (top+2u<=64u) {\n stack[top]=node.first;\n stack[top+1u]=index+1u;\n top+=2u;\n }\n } else {\n for (var j=0u;j<node.count;j++) { tri_hit(node.first+j,o,d,shadow_ray,&hit); }\n }\n }\n }\n for (var i=0u;i<frame.counts.y;i++) {\n if (shapes[i].d.x > 0.5) { continue; }\n var t=hit.t;\n if (u32(shapes[i].a.w)==5u) { t=union_hit(i,o,d,hit.t); } else { t=primitive_hit(i,o,d,hit.t); }\n if (t<hit.t && t>0.0001) {\n let p=o+d*t;\n let e=0.0002;\n let n=normalize(vec3f(\n shape_distance(i,p+vec3f(e,0.0,0.0))-shape_distance(i,p-vec3f(e,0.0,0.0)),\n shape_distance(i,p+vec3f(0.0,e,0.0))-shape_distance(i,p-vec3f(0.0,e,0.0)),\n shape_distance(i,p+vec3f(0.0,0.0,e))-shape_distance(i,p-vec3f(0.0,0.0,e))));\n hit=Hit(t,n,n,u32(shapes[i].c.w),vec2f(0.0),vec3f(1.0,0.0,0.0),vec3f(0.0,1.0,0.0),0u,i+1u);\n }\n }\n return hit;\n}\nfn tri_blocks(i: u32, o: vec3f, d: vec3f, limit: f32) -> bool {\n let tri = triangles[i];\n if ((u32(tri.b.w)&2u)!=0u) { return false; }\n let e1 = tri.b.xyz-tri.a.xyz;\n let e2 = tri.c.xyz-tri.a.xyz;\n let p = cross(d,e2);\n let det = dot(e1,p);\n if (abs(det)<1e-9) { return false; }\n let inv = 1.0/det;\n let s = o-tri.a.xyz;\n let u = dot(s,p)*inv;\n if (u<0.0 || u>1.0) { return false; }\n let q = cross(s,e1);\n let v = dot(d,q)*inv;\n if (v<0.0 || u+v>1.0) { return false; }\n let t = dot(e2,q)*inv;\n if (t<=0.0001 || t>=limit) { return false; }\n let surface=instance_surfaces[i];\n let point=o+d*t;\n if ((any(surface.clip0.xyz != vec3f(0.0)) && dot(surface.clip0.xyz,point)>surface.clip0.w)\n || (any(surface.clip1.xyz != vec3f(0.0)) && dot(surface.clip1.xyz,point)>surface.clip1.w)) { return false; }\n return !cut_out(i,u,v);\n}\nfn occluded(o: vec3f, d: vec3f, limit: f32) -> bool {\n if (frame.counts.x>0u) {\n var stack: array<u32,64>;\n var top=1u;\n stack[0]=0u;\n while (top>0u) {\n top-=1u;\n let index=stack[top];\n let node=nodes[index];\n if (!box_hit(node,o,d,limit)) { continue; }\n if (node.count==0u) {\n if (top+2u<=64u) {\n stack[top]=node.first;\n stack[top+1u]=index+1u;\n top+=2u;\n }\n } else {\n for (var j=0u;j<node.count;j++) {\n if (tri_blocks(node.first+j,o,d,limit)) { return true; }\n }\n }\n }\n }\n for (var i=0u;i<frame.counts.y;i++) {\n if (shapes[i].d.x > 0.5) { continue; }\n var t=limit;\n if (u32(shapes[i].a.w)==5u) { t=union_hit(i,o,d,limit); } else { t=primitive_hit(i,o,d,limit); }\n if (t<limit && t>0.0001) { return true; }\n }\n return false;\n}\nfn cast_tint(material: u32) -> vec3f {\n let transmission=materials[material].sheen_trans_ior_disp.y;\n if (transmission<=0.0) { return vec3f(0.0); }\n var passed=materials[material].base_roughness.xyz;\n let thickness=materials[material].thick_sub_film.x;\n let absorption=materials[material].tint_absorption.w;\n if (thickness>0.0 && absorption>0.0) { passed=beer(thickness,passed,absorption); }\n return clamp(passed*transmission,vec3f(0.0),vec3f(1.0));\n}\nfn tri_passes(i: u32, o: vec3f, d: vec3f, limit: f32) -> vec3f {\n if (!tri_blocks(i,o,d,limit)) { return vec3f(1.0); }\n return sqrt(cast_tint(u32(triangles[i].a.w)));\n}\nfn shadow_passed(o: vec3f, d: vec3f, limit: f32) -> vec3f {\n var passed=vec3f(1.0);\n if (frame.counts.x>0u) {\n var stack: array<u32,64>;\n var top=1u;\n stack[0]=0u;\n while (top>0u) {\n top-=1u;\n let index=stack[top];\n let node=nodes[index];\n if (!box_hit(node,o,d,limit)) { continue; }\n if (node.count==0u) {\n if (top+2u<=64u) {\n stack[top]=node.first;\n stack[top+1u]=index+1u;\n top+=2u;\n }\n } else {\n for (var j=0u;j<node.count;j++) {\n passed*=tri_passes(node.first+j,o,d,limit);\n if (all(passed<=vec3f(0.0))) { return vec3f(0.0); }\n }\n }\n }\n }\n for (var i=0u;i<frame.counts.y;i++) {\n if (shapes[i].d.x > 0.5) { continue; }\n var t=limit;\n if (u32(shapes[i].a.w)==5u) { t=union_hit(i,o,d,limit); } else { t=primitive_hit(i,o,d,limit); }\n if (t<limit && t>0.0001) { passed*=cast_tint(u32(shapes[i].c.w)); }\n }\n return passed;\n}\nfn offset_axis(position: f32, normal: f32) -> f32 {\n if (abs(position) < 1.0/32.0) { return position + normal/65536.0; }\n let bits=bitcast<i32>(position);\n let shift=i32(normal*256.0);\n return bitcast<f32>(bits + select(shift, -shift, position < 0.0));\n}\nfn offset_ray(p: vec3f, n: vec3f) -> vec3f {\n return vec3f(offset_axis(p.x,n.x), offset_axis(p.y,n.y), offset_axis(p.z,n.z));\n}\nfn spawn_point(p: vec3f, dir: vec3f, origin: vec3f, t: f32) -> vec3f {\n let err=4e-7*(max(abs(origin.x),max(abs(origin.y),abs(origin.z)))+t);\n return offset_ray(p, dir)+dir*err;\n}\nfn basis(n: vec3f) -> mat3x3f {\n let t=normalize(cross(select(vec3f(0.0,1.0,0.0),vec3f(1.0,0.0,0.0),abs(n.y)>0.9),n));\n return mat3x3f(t,cross(n,t),n);\n}\nconst ROUGHNESS_FLOOR: f32 = 0.05;\nfn surface(material: Material) -> Shaded {\n return Shaded(material.base_roughness.xyz,material.base_roughness.w,\n material.metal_spec_coat.x,material.metal_spec_coat.y,material.metal_spec_coat.z,material.metal_spec_coat.w,\n material.sheen_trans_ior_disp.x,material.sheen_trans_ior_disp.y,material.sheen_trans_ior_disp.z,material.sheen_trans_ior_disp.w,\n material.thick_sub_film.x,material.thick_sub_film.y,material.tint_absorption.xyz,material.tint_absorption.w,\n material.thick_sub_film.z,material.thick_sub_film.w,material.emission_film.w,material.emission_film.xyz,\n material.age_b.w,vec3f(0.0,0.0,1.0));\n}\nfn detail_wobble(p: vec3f, scale: f32) -> vec3f {\n let q=p*scale;\n let e=0.35;\n return vec3f(value_noise3(q+vec3f(e,0.0,0.0),false)-value_noise3(q-vec3f(e,0.0,0.0),false),\n value_noise3(q+vec3f(0.0,e,0.0),false)-value_noise3(q-vec3f(0.0,e,0.0),false),\n value_noise3(q+vec3f(0.0,0.0,e),false)-value_noise3(q-vec3f(0.0,0.0,e),false))/(2.0*e);\n}\nfn detail_perturb(n: vec3f, g: vec3f) -> vec3f {\n return normalize(n-(g-n*dot(g,n)));\n}\nfn detail_layer(value: Shaded, normal_in: vec3f, layer: vec4f, pa: vec4f, pb: vec4f, pc: vec4f, pd: vec4f, position: vec3f) -> DetailResult {\n var shade=value;\n var base=value.base;\n var roughness=value.roughness;\n var coat_roughness=value.clearcoat_roughness;\n var n=normal_in;\n let kind=u32(round(layer.x));\n let amp=layer.z;\n let seed=bitcast<u32>(layer.w);\n let p=position+vec3f(f32(seed)*0.001,f32(seed)*0.001,0.0);\n if (kind==1u) {\n let fibre=value_noise3(p*vec3f(pa.x,pa.y,pa.x),false)*0.6+value_noise3(p*pa.z,false)*0.4;\n let mottle=value_noise3(p*pa.w,false);\n base*=1.0+((fibre-0.5)*pb.z+(mottle-0.5)*pb.w)*amp;\n n=detail_perturb(n,detail_wobble(p,pb.x)*(pc.x*amp)+detail_wobble(p,pb.y)*(pc.y*amp));\n } else if (kind==2u) {\n n=detail_perturb(n,detail_wobble(p,pa.x)*(pa.z*amp)+detail_wobble(p,pa.y)*(pa.w*amp));\n } else if (kind==3u && pa.x>0.0) {\n let plank=floor(p.z*pd.x);\n let tone=hash3_xor(vec3i(i32(plank),7,3));\n let across=fract(p.z*pd.x);\n let seam=smoothstep(0.0,0.006,across)+smoothstep(0.0,0.006,1.0-across)-1.0;\n let warp=value_noise3(vec3f(p.x*1.4,plank*3.7,p.z*6.0),false)*0.9+value_noise3(vec3f(p.x*6.0,plank,p.z*20.0),false)*0.25;\n let rings=fract(fma(fma(warp,pa.z,p.z),pa.y,value_noise3(vec3f(p.x*0.8,plank,0.0),false)*pc.y));\n let figure=smoothstep(0.55,0.95,rings)*0.55+value_noise3(vec3f(p.x*pa.w,p.z*pb.x,plank),false)*0.25;\n let factor=(0.78+tone*pb.y)*(1.08-figure*pb.z)*(pb.w+clamp(seam,0.0,1.0)*pd.y);\n base*=mix(1.0,factor,clamp(amp,0.0,1.0));\n roughness=mix(roughness,sqrt(min(roughness*roughness*(0.85+figure*pc.x),1.0)),clamp(amp,0.0,1.0));\n } else if (kind==4u) {\n let broad=value_noise3(p*pa.x,false)*0.6+value_noise3(p*pa.y,false)*0.4;\n let fine=value_noise3(p*pa.z,false)*0.6+value_noise3(p*pa.w,false)*0.4;\n base*=mix(1.0,1.0+(broad-0.5)*pb.z+(fine-0.5)*pb.w,clamp(amp,0.0,1.0));\n n=detail_perturb(n,detail_wobble(p,pb.x)*(pc.x*amp)+detail_wobble(p,pb.y)*(pc.y*amp));\n } else if (kind==5u) {\n let grime=value_noise3(p*pa.x,false)*0.6+value_noise3(p*pa.y,false)*0.4;\n let wear=smoothstep(0.35,0.75,grime);\n base*=mix(vec3f(1.0),mix(vec3f(pa.z,pa.w,pb.x),vec3f(pb.y,pb.z,pb.w),wear),clamp(amp,0.0,1.0));\n roughness=mix(roughness,sqrt(min(roughness*roughness*mix(pc.x,pc.y,wear),1.0)),clamp(amp,0.0,1.0));\n } else if (kind==15u) {\n base*=mix(1.0,1.0+(value_noise3(p*layer.y,false)-0.5)*2.0,clamp(amp,0.0,1.0));\n } else if (kind==17u) {\n let line=1.0-smoothstep(0.0,0.045,abs(value_noise3(vec3f(p.x*pd.x+f32(seed),p.y*pa.x,p.z*pd.x),false)-0.5));\n let smudge=smoothstep(0.52,0.78,value_noise3(p*pa.y,false)*0.65+value_noise3(p*pa.z,false)*0.35);\n let cell=vec3i(floor(p*pa.w));\n let speck=select(0.0,1.0,hash3_xor(vec3i(cell.x,cell.y,cell.z^i32(seed)))>0.9985);\n let breakup=clamp(smudge*0.55+line*0.35+speck,0.0,1.0)*min(amp,1.0);\n if (value.clearcoat>0.0) {\n coat_roughness=sqrt(mix(coat_roughness*coat_roughness,min(coat_roughness*coat_roughness*4.0+0.02,0.35),breakup));\n } else {\n roughness=sqrt(mix(roughness*roughness,min(roughness*roughness*4.0+0.02,0.35),breakup));\n }\n base=mix(base,base*0.9+vec3f(0.06),speck*min(amp,1.0));\n } else if (kind==18u) {\n n=detail_perturb(n,detail_wobble(p,pa.x)*amp+detail_wobble(p,pa.y)*(amp*pa.w)+detail_wobble(p,pa.z)*(amp*pb.x));\n }\n shade.base=base;\n shade.roughness=roughness;\n shade.clearcoat_roughness=coat_roughness;\n return DetailResult(shade,n);\n}\nfn leaf_hash(p: vec2f) -> f32 {\n let q=fract(p*vec2f(123.34,456.21));\n return fract((q.x+45.32)*(q.y+45.32)*34.23+q.x*13.7);\n}\nfn leaf_noise(p: vec2f) -> f32 {\n let i=floor(p);\n let f=fract(p);\n let u=f*f*(3.0-2.0*f);\n return mix(mix(leaf_hash(i),leaf_hash(i+vec2f(1.0,0.0)),u.x),\n mix(leaf_hash(i+vec2f(0.0,1.0)),leaf_hash(i+vec2f(1.0,1.0)),u.x),u.y);\n}\nfn leaf_fbm(p: vec2f) -> f32 {\n return leaf_noise(p)*0.55+leaf_noise(p*2.13+3.1)*0.28+leaf_noise(p*4.37+7.7)*0.17;\n}\nfn canopy(p: vec3f) -> f32 {\n let sun=normalize(frame.sun_dir.xyz);\n let e1=normalize(cross(sun,vec3f(0.0,1.0,0.0)));\n let e2=cross(e1,sun);\n let q=vec2f(dot(p-effects().metal_spec_coat.xyz,e1),dot(p-effects().metal_spec_coat.xyz,e2));\n let t=effects().base_roughness.x;\n let gust=sin(t*0.21)*0.6+sin(t*0.37+1.3)*0.4;\n let sway=vec2f(sin(t*0.9+q.y*9.0)*0.005+gust*0.011,cos(t*0.7+q.x*7.0)*0.004+gust*0.004);\n let shifted=q+sway;\n let branch=smoothstep(-0.55,0.15,shifted.x*0.8+shifted.y*0.6);\n let clusters=smoothstep(0.3,0.62,leaf_fbm(shifted*3.4+vec2f(5.0,2.0)));\n let leaves=smoothstep(0.36,0.56,leaf_fbm(shifted*17.0+vec2f(sin(t*1.3)*0.08,cos(t*1.1)*0.08)));\n return mix(1.0,1.0-leaves*clusters*branch*0.88,effects().base_roughness.y);\n}\nfn steam_fbm(p: vec3f) -> f32 {\n var sum=0.0;\n var amp=0.55;\n var q=p;\n for (var k=0;k<4;k++) {\n sum+=value_noise3(q,false)*amp;\n q=q*2.07+vec3f(1.7,9.2,3.1);\n amp*=0.5;\n }\n return sum;\n}\nfn steam_density(p: vec3f) -> f32 {\n let t=effects().base_roughness.w;\n let source=effects().sheen_trans_ior_disp.xyz;\n let rise=p.y-source.y;\n if (rise<0.0 || rise>STEAM_FADE_OUT_END) { return 0.0; }\n let drift=vec3f(STEAM_DRIFT_X_QUAD*rise*rise+STEAM_DRIFT_X_LIN*rise,0.0,STEAM_DRIFT_Z_QUAD*rise*rise+STEAM_DRIFT_Z_LIN*rise);\n let sway=vec3f(sin(rise*STEAM_SWAY_X_WAVE-t*STEAM_SWAY_X_RATE)*STEAM_SWAY_X_AMP*(STEAM_SWAY_X_BASE+rise*STEAM_SWAY_X_GROWTH),0.0,\n cos(rise*STEAM_SWAY_Z_WAVE-t*STEAM_SWAY_Z_RATE)*STEAM_SWAY_Z_AMP*(STEAM_SWAY_Z_BASE+rise*STEAM_SWAY_Z_GROWTH));\n let axis=source+vec3f(0.0,rise,0.0)+drift+sway;\n let width=effects().sheen_trans_ior_disp.w*(STEAM_SPREAD_BASE+rise*STEAM_SPREAD_GROWTH);\n let r=length((p-axis).xz);\n let core=exp(-r*r/(width*width));\n let fade=smoothstep(0.0,STEAM_FADE_IN,rise)*(1.0-smoothstep(STEAM_FADE_OUT_START,STEAM_FADE_OUT_END,rise));\n if (core*fade<0.002) { return 0.0; }\n let q=vec3f(p.x*STEAM_GRAIN_H,(p.y-t*STEAM_LIFT_RATE)*STEAM_GRAIN_V,p.z*STEAM_GRAIN_H);\n let warp=vec3f(steam_fbm(q*0.5+vec3f(t*STEAM_WARP_RATE,0.0,0.0)),0.0,\n steam_fbm(q*0.5+vec3f(3.1,0.0,t*STEAM_WARP_RATE)))*STEAM_WARP_AMP;\n let turbulence=smoothstep(STEAM_THRESHOLD_LOW,STEAM_THRESHOLD_HIGH,steam_fbm(q+warp+vec3f(0.0,-t*STEAM_CHURN,0.0)));\n return core*turbulence*fade*effects().thick_sub_film.w;\n}\nfn steam_composite(origin: vec3f, direction: vec3f, surface_depth: f32, color: vec3f) -> vec3f {\n if (effects().thick_sub_film.w<=0.0) { return color; }\n let inv=1.0/select(direction,sign(direction)*vec3f(1e-8),abs(direction)<vec3f(1e-8));\n let t0=(effects().thick_sub_film.xyz-origin)*inv;\n let t1=(effects().tint_absorption.xyz-origin)*inv;\n let enter=max(max(min(t0.x,t1.x),min(t0.y,t1.y)),max(min(t0.z,t1.z),0.0));\n let leave=min(min(max(t0.x,t1.x),max(t0.y,t1.y)),min(max(t0.z,t1.z),surface_depth));\n if (enter>=leave) { return color; }\n let dt=(leave-enter)/20.0;\n var transmittance=1.0;\n var scattered=vec3f(0.0);\n let sun=normalize(frame.sun_dir.xyz);\n let g=effects().metal_spec_coat.w;\n let phase=(1.0-g*g)/pow(1.0+g*g-2.0*g*dot(direction,sun),1.5);\n for (var k=0u;k<20u;k++) {\n let p=origin+direction*(enter+(f32(k)+0.5)*dt);\n let density=steam_density(p);\n if (density>1e-4) {\n let extinction=exp(-density*90.0*dt);\n let open=select(1.0,0.0,scene(spawn_point(p,sun,p,0.0),sun,1e20,true).t<1e19);\n let lit=frame.sun_color.xyz*frame.sun_color.w*open*phase;\n scattered+=transmittance*(lit+vec3f(effects().tint_absorption.w))*(1.0-extinction)*0.85;\n transmittance*=extinction;\n }\n }\n return scattered+transmittance*color;\n}\nfn texels_at(offset: u32, width: u32, height: u32, uv: vec2f) -> vec4f {\n let size=vec2f(f32(width),f32(height));\n let p=clamp(uv*size-vec2f(0.5),vec2f(0.0),size-vec2f(1.0));\n let lo=vec2u(floor(p));\n let hi=min(lo+vec2u(1u),vec2u(width-1u,height-1u));\n let f=fract(p);\n let a=content_texels[offset+lo.y*width+lo.x];\n let b=content_texels[offset+lo.y*width+hi.x];\n let c=content_texels[offset+hi.y*width+lo.x];\n let d=content_texels[offset+hi.y*width+hi.x];\n return mix(mix(a,b,f.x),mix(c,d,f.x),f.y);\n}\nfn atlas_texel(offset: u32, width: u32, x: u32, y: u32) -> vec3f {\n let at=y*width+x;\n let packed=u32(content_texels[offset+at/4u][at&3u]);\n return vec3f(f32(packed&255u),f32((packed>>8u)&255u),f32(packed>>16u))/255.0;\n}\nfn atlas_at(offset: u32, width: u32, height: u32, uv: vec2f) -> vec3f {\n let p=uv*vec2f(f32(width),f32(height))-vec2f(0.5);\n let start=floor(p);\n let f=p-start;\n let lo=vec2u(clamp(start,vec2f(0.0),vec2f(f32(width-1u),f32(height-1u))));\n let hi=vec2u(clamp(start+vec2f(1.0),vec2f(0.0),vec2f(f32(width-1u),f32(height-1u))));\n let a=atlas_texel(offset,width,lo.x,lo.y);\n let b=atlas_texel(offset,width,hi.x,lo.y);\n let c=atlas_texel(offset,width,lo.x,hi.y);\n let d=atlas_texel(offset,width,hi.x,hi.y);\n return mix(mix(a,b,f.x),mix(c,d,f.x),f.y);\n}\nfn text_at(offset: u32, uv: vec2f) -> vec4f {\n let grid=content_texels[offset];\n let shape=vec4u(content_texels[offset+1u]);\n let atlas=vec4u(content_texels[offset+2u]);\n let picture=vec4u(content_texels[offset+3u]);\n var color=vec4f(0.0);\n if (picture.x>0u) { color=texels_at(offset+picture.z,picture.x,picture.y,uv); }\n let cell=floor((uv-grid.xy)*grid.zw);\n if (cell.x<0.0 || cell.y<0.0 || cell.x>=f32(shape.x) || cell.y>=f32(shape.y)) { return color; }\n let index=u32(cell.y)*shape.x+u32(cell.x);\n let pair=content_texels[offset+4u+index/2u];\n let entry=vec2u(select(pair.xy,pair.zw,(index&1u)==1u));\n let size=vec2f(f32(atlas.x),f32(atlas.y));\n let half=vec2f(0.5)/size;\n for (var k=0u;k<entry.y;k++) {\n let at=entry.x+k;\n let glyph=offset+shape.z+u32(content_texels[offset+shape.w+at/4u][at&3u])*5u;\n let m=content_texels[glyph];\n let t=content_texels[glyph+1u];\n let clip=content_texels[glyph+2u];\n let box=content_texels[glyph+3u];\n let ink=content_texels[glyph+4u];\n let f=vec2f(m.x*uv.x+m.y*uv.y+t.x,m.z*uv.x+m.w*uv.y+t.y);\n if (any(f<max(clip.xy,vec2f(0.0))) || any(f>min(clip.zw,vec2f(1.0)))) { continue; }\n let low=min(box.xy+half,box.zw);\n let high=max(box.zw-half,low);\n let sampled=atlas_at(offset+atlas.z,atlas.x,atlas.y,clamp(box.xy+(box.zw-box.xy)*f,low,high));\n var coverage=sampled.x;\n if (atlas.w==3u) {\n let median=max(min(sampled.x,sampled.y),min(max(sampled.x,sampled.y),sampled.z));\n coverage=select(0.0,1.0,median>=0.5);\n }\n let opacity=coverage*ink.a;\n color=vec4f(ink.rgb*opacity+color.rgb*(1.0-opacity),opacity+color.a*(1.0-opacity));\n }\n return color;\n}\nfn content_at(slot: i32, uv: vec2f) -> vec4f {\n if (slot < 0) { return vec4f(0.0); }\n let info=content_info[u32(slot)];\n if (info.width==0u || info.height==0u) { return vec4f(0.0); }\n if (info.kind==1u) { return text_at(info.offset,uv); }\n return texels_at(info.offset,info.width,info.height,uv);\n}\nconst CF_WEIGHTS = array<vec3f, 81>(\n vec3f(5.039987e-05, 0.0, 0.00035778867),\n vec3f(6.775547e-05, 0.0, 0.0005768482),\n vec3f(0.00013374595, 0.0, 0.001105031),\n vec3f(0.000244155, -8.0429134e-05, 0.001990094),\n vec3f(0.0004620315, -0.00031065758, 0.0037327078),\n vec3f(0.00075178174, -0.00079914776, 0.0060585286),\n vec3f(0.0013901147, -0.0016784245, 0.01140122),\n vec3f(0.0024506692, -0.0030452313, 0.020409657),\n vec3f(0.0041841697, -0.00489408, 0.035484146),\n vec3f(0.006491386, -0.0070713423, 0.05709876),\n vec3f(0.008225769, -0.009268977, 0.07611217),\n vec3f(0.00893323, -0.0110506825, 0.08910742),\n vec3f(0.008651618, -0.011844616, 0.09585401),\n vec3f(0.0075308583, -0.0111710355, 0.09771433),\n vec3f(0.00574333, -0.009713182, 0.0970225),\n vec3f(0.0034815131, -0.0075853216, 0.09534322),\n vec3f(0.00069844024, -0.0048573813, 0.091060266),\n vec3f(-0.0036886954, -0.0017018986, 0.083120264),\n vec3f(-0.0063273897, 0.0017186198, 0.06969027),\n vec3f(-0.008724144, 0.005763426, 0.05593676),\n vec3f(-0.011347991, 0.009961927, 0.04306675),\n vec3f(-0.014388451, 0.014161423, 0.031928584),\n vec3f(-0.017747117, 0.018677758, 0.023228362),\n vec3f(-0.021200482, 0.023959707, 0.016556388),\n vec3f(-0.024560854, 0.030223139, 0.011426527),\n vec3f(-0.027776832, 0.03802389, 0.00728438),\n vec3f(-0.030845057, 0.046441782, 0.0033540253),\n vec3f(-0.03346058, 0.055137716, 0.0),\n vec3f(-0.03508027, 0.06285677, -0.0025753065),\n vec3f(-0.035013195, 0.068301246, -0.004734569),\n vec3f(-0.032630157, 0.071965545, -0.006312869),\n vec3f(-0.027983842, 0.07395785, -0.00737929),\n vec3f(-0.02177047, 0.07446333, -0.008106243),\n vec3f(-0.0140442625, 0.07356939, -0.008540686),\n vec3f(-0.0049150633, 0.07139566, -0.008724192),\n vec3f(0.0046487506, 0.06808836, -0.008696091),\n vec3f(0.0153741175, 0.063680954, -0.008493348),\n vec3f(0.026958833, 0.058147695, -0.008150433),\n vec3f(0.03911687, 0.051680956, -0.007698217),\n vec3f(0.05145111, 0.04444711, -0.0071432022),\n vec3f(0.063475594, 0.036714625, -0.006503886),\n vec3f(0.07455033, 0.028763412, -0.005807879),\n vec3f(0.08411886, 0.020992132, -0.0050818375),\n vec3f(0.091669224, 0.0137893725, -0.00435086),\n vec3f(0.09618923, 0.007610871, -0.0036377995),\n vec3f(0.097940184, 0.002461353, -0.0029763517),\n vec3f(0.09633994, -0.0015017615, -0.0023928043),\n vec3f(0.0919427, -0.004451467, -0.0018944021),\n vec3f(0.08495084, -0.0062283436, -0.0014817494),\n vec3f(0.07555293, -0.0070042815, -0.0011499332),\n vec3f(0.06515909, -0.006994329, -0.00089005305),\n vec3f(0.05535661, -0.0064279693, -0.00069090264),\n vec3f(0.046014983, -0.005523557, -0.0005405573),\n vec3f(0.037232462, -0.004469, -0.00042767118),\n vec3f(0.029350668, -0.0034102271, -0.0003423706),\n vec3f(0.022698376, -0.0024474342, -0.00027671963),\n vec3f(0.017146055, -0.001637843, -0.00022480598),\n vec3f(0.012616245, -0.0010029421, -0.00018254654),\n vec3f(0.00910757, -0.0005379737, -0.00014732922),\n vec3f(0.0066325837, -0.00022167167, -0.00011759913),\n vec3f(0.004884865, -2.4797864e-05, -9.247229e-05),\n vec3f(0.0034370946, 0.0, -7.142631e-05),\n vec3f(0.0023721277, 0.0, -5.40864e-05),\n vec3f(0.0016515272, 0.0, -4.010171e-05),\n vec3f(0.0011923393, 0.0, -2.9092696e-05),\n vec3f(0.00084797293, 0.0, -2.064557e-05),\n vec3f(0.000605779, 0.0, -1.4331459e-05),\n vec3f(0.0004273203, 0.0, -9.733269e-06),\n vec3f(0.00030592916, 0.0, -6.469539e-06),\n vec3f(0.00021032631, 0.0, -4.210301e-06),\n vec3f(0.00014668911, 0.0, -2.683982e-06),\n vec3f(0.0001021725, 0.0, -1.6768181e-06),\n vec3f(7.628618e-05, 0.0, -1.027186e-06),\n vec3f(5.108625e-05, 0.0, -6.1728224e-07),\n vec3f(3.186762e-05, 0.0, -3.6408065e-07),\n vec3f(1.9218627e-05, 0.0, -2.1085688e-07),\n vec3f(1.9218627e-05, 0.0, -1.1996055e-07),\n vec3f(1.2648995e-05, 0.0, 2.5954927e-07),\n vec3f(1.2648995e-05, 0.0, 2.5954927e-07),\n vec3f(1.2648995e-05, 0.0, 2.5954927e-07),\n vec3f(-5.4965028e-08, 0.0, -1.0592136e-08),\n);\nfn cf_fresnel(cos_i: f32, na: f32, nb: f32) -> vec3f {\n let sin2=(na/nb)*(na/nb)*(1.0-cos_i*cos_i);\n if (sin2>=1.0) { return vec3f(1.0,1.0,0.0); }\n let cos_t=sqrt(1.0-sin2);\n let rs=(na*cos_i-nb*cos_t)/(na*cos_i+nb*cos_t);\n let rp=(nb*cos_i-na*cos_t)/(nb*cos_i+na*cos_t);\n return vec3f(rs,rp,cos_t);\n}\nfn cf_film(cosine: f32, thickness: f32, n1: f32, nf: f32, n3: f32) -> vec3f {\n let first=cf_fresnel(clamp(cosine,0.0,1.0),n1,nf);\n if (first.z<=0.0) { return vec3f(1.0); }\n let second=cf_fresnel(first.z,nf,n3);\n if (second.z<=0.0) { return vec3f(1.0); }\n let r12=first.xy;\n let r23=second.xy;\n let a=r12*r12+r23*r23;\n let b=2.0*r12*r23;\n let e=1.0+r12*r12*r23*r23;\n let path=4.0*PI*nf*thickness*first.z;\n var weights=CF_WEIGHTS;\n var total=vec3f(0.0);\n for (var k=0u;k<81u;k++) {\n let c=cos(path/(380.0+5.0*f32(k)));\n let reflect=0.5*((a.x+b.x*c)/(e.x+b.x*c)+(a.y+b.y*c)/(e.y+b.y*c));\n total+=weights[k]*reflect;\n }\n return clamp(total,vec3f(0.0),vec3f(1.0));\n}\nfn film_active(s: Shaded) -> bool {\n return s.thin_film>0.0 && s.thin_film_amount>0.0;\n}\nfn cf_surface(cosine: f32, thickness: f32, film_ior: f32, ior: f32, entering: bool) -> vec3f {\n let nf=max(film_ior,1.01);\n if (entering) { return cf_film(cosine,thickness,1.0,nf,ior); }\n return cf_film(cosine,thickness,1.0,nf/ior,1.0/ior);\n}\nfn transmissive_fresnel(s: Shaded, cosine: f32, ratio: f32, entering: bool, ior: f32) -> vec3f {\n let plain=vec3f(fresnel_dielectric(cosine,ratio));\n if (!film_active(s)) { return plain; }\n let filmed=cf_surface(cosine,s.thin_film,s.thin_film_ior,ior,entering);\n return mix(plain,filmed,clamp(s.thin_film_amount,0.0,1.0));\n}\nconst IOR_SPLIT: f32 = 1e-4;\nfn channel_mask(channel: u32) -> vec3f {\n return vec3f(select(0.0,1.0,channel==0u),select(0.0,1.0,channel==1u),select(0.0,1.0,channel==2u));\n}\nfn channel_ior(s: Shaded, channel: u32) -> f32 {\n if (channel>2u) { return max(s.ior,1.01); }\n return max(dispersed_ior(s.ior,s.dispersion)[channel],1.01);\n}\nfn glossy_share(s: Shaded) -> f32 {\n return 1.0-clamp(s.transmission,0.0,1.0)*(1.0-clamp(s.metalness,0.0,1.0));\n}\nfn sample_bsdf(s: Shaded, wo: vec3f, entering: bool, carried: u32, state: ptr<function,u32>, u: vec4f) -> LightSample {\n let transmission=clamp(s.transmission,0.0,1.0)*(1.0-clamp(s.metalness,0.0,1.0));\n if (rnd(state,u.w)<transmission) {\n let iors=dispersed_ior(s.ior,s.dispersion);\n var channel=carried;\n var weight=vec3f(1.0);\n if (channel>2u && max(abs(iors.x-iors.y),abs(iors.z-iors.y))>IOR_SPLIT) {\n channel=min(u32(rnd(state,u.z)*3.0),2u);\n weight=channel_mask(channel)*3.0;\n }\n let ior=channel_ior(s,channel);\n let ratio=select(1.0/ior,ior,entering);\n let live=select(channel_mask(channel),vec3f(1.0),channel>2u);\n let fresnel=transmissive_fresnel(s,max(wo.z,0.0),ratio,entering,ior)*live;\n let chance=dot(fresnel,vec3f(1.0))/dot(live,vec3f(1.0));\n let incident=-wo;\n let mirrored=reflect(incident,vec3f(0.0,0.0,1.0));\n if (rnd(state,u.x)<chance) {\n return LightSample(mirrored,-1.0,channel,weight*fresnel/chance,1u);\n }\n let refracted=refract_dir(incident,vec3f(0.0,0.0,1.0),1.0/ratio);\n if (refracted.w>0.0) { return LightSample(normalize(refracted.xyz),-2.0,channel,weight*(live-fresnel)/(1.0-chance),2u); }\n return LightSample(mirrored,-1.0,channel,weight*live,1u);\n }\n let coat_weight=clamp(s.clearcoat*(0.12+0.88*(0.04+0.96*pow(1.0-max(wo.z,0.0),5.0))),0.0,0.9);\n let spec_weight=(1.0-coat_weight)*clamp(s.metalness+s.specular,0.0,0.9);\n let pick=rnd(state,u.z);\n if (pick<coat_weight+spec_weight) {\n let coat=pick<coat_weight;\n let alpha=alpha_rough(select(s.roughness,s.clearcoat_roughness,coat));\n let u1=rnd(state,u.x);\n let u2=rnd(state,u.y);\n let phi=2.0*PI*u1;\n let cos_theta=sqrt((1.0-u2)/(1.0+(alpha*alpha-1.0)*u2));\n let h=vec3f(cos(phi)*sqrt(max(0.0,1.0-cos_theta*cos_theta)),sin(phi)*sqrt(max(0.0,1.0-cos_theta*cos_theta)),cos_theta);\n let wi=reflect(-wo,h);\n return LightSample(wi,bsdf_pdf(s,wo,wi),3u,vec3f(1.0),1u);\n }\n let u1=rnd(state,u.x);\n let u2=rnd(state,u.y);\n let phi=2.0*PI*u1;\n let r=sqrt(u2);\n let wi=vec3f(cos(phi)*r,sin(phi)*r,sqrt(1.0-u2));\n return LightSample(wi,bsdf_pdf(s,wo,wi),3u,vec3f(1.0),0u);\n}\nfn bsdf_pdf(s: Shaded, wo: vec3f, wi: vec3f) -> f32 {\n if (wi.z<=0.0) { return 0.0; }\n let trans=clamp(s.transmission,0.0,1.0)*(1.0-clamp(s.metalness,0.0,1.0));\n let coat=clamp(s.clearcoat*(0.12+0.88*(0.04+0.96*pow(1.0-max(wo.z,0.0),5.0))),0.0,0.9);\n let spec=(1.0-coat)*clamp(s.metalness+s.specular,0.0,0.9);\n let h=normalize(wo+wi);\n let a=alpha_rough(s.roughness);\n let ca=alpha_rough(s.clearcoat_roughness);\n let denom=max(4.0*abs(dot(wi,h)),1e-8);\n let base_pdf=ggx_d(max(h.z,0.0),a*a)*max(h.z,0.0)/denom;\n let coat_pdf=ggx_d(max(h.z,0.0),ca*ca)*max(h.z,0.0)/denom;\n return (1.0-trans)*(coat*coat_pdf+spec*base_pdf+(1.0-coat-spec)*wi.z/PI);\n}\nfn trace_lights_start() {}\nfn emitter_weight(hit: Hit, p: vec3f, previous_point: vec3f, previous_pdf: f32) -> f32 {\n return 1.0;\n}\nfn light_hits(o: vec3f, d: vec3f, limit: f32, previous_point: vec3f, previous_pdf: f32) -> vec3f {\n return vec3f(0.0);\n}\nfn light_slots() -> u32 {\n return 0u;\n}\nfn sampled_emitter(hit: Hit) -> bool {\n return false;\n}\nfn direct_light(slot: u32, transform: mat3x3f, p: vec3f, origin: vec3f, geometric: vec3f, state: ptr<function,u32>, u: vec4f) -> Candidate {\n return Candidate(vec3f(0.0,0.0,1.0),vec3f(0.0),0.0,0.0,vec3f(0.0,0.0,1.0),-1.0,0u);\n}\nfn trace_lights_end() {}\nfn catcher_visible(origin: vec3f, direction: vec3f, limit: f32) -> f32 {\n if (TRANSMISSIVE_SHADOWS) { return dot(shadow_passed(origin,direction,limit),vec3f(0.2126,0.7152,0.0722)); }\n return select(1.0,0.0,occluded(origin,direction,limit));\n}\nfn catcher_shadow(p: vec3f, n: vec3f, o: vec3f, t: f32, state: ptr<function,u32>) -> f32 {\n let transform=basis(n);\n let origin=spawn_point(p,n,o,t);\n var open=0.0;\n var seen=0.0;\n for (var k=0u;k<2u+light_slots();k++) {\n let u=sobol(light_dim(k));\n var direction=vec3f(0.0,0.0,1.0);\n var strength=0.0;\n var limit=1e19;\n var shape=0u;\n if (k==0u) {\n if (frame.sun_color.w<=0.0) { continue; }\n direction=normalize(frame.sun_dir.xyz);\n if (frame.sun_dir.w<1.0) {\n let height=1.0-rnd(state,u.x)*(1.0-frame.sun_dir.w);\n let radius=sqrt(max(0.0,1.0-height*height));\n let phi=2.0*PI*rnd(state,u.y);\n direction=normalize(basis(direction)*vec3f(radius*cos(phi),radius*sin(phi),height));\n }\n strength=dot(frame.sun_color.xyz,vec3f(0.2126,0.7152,0.0722))*frame.sun_color.w*max(dot(direction,n),0.0);\n } else if (k==1u) {\n if (sky_cdf.width==0u) { continue; }\n for (var j=0u;j<4u;j++) {\n let light=sky_sample(vec2f(random(state),random(state)));\n if (!(light.pdf>0.0)) { continue; }\n let part=dot(sky_surface_radiance(light.direction),vec3f(0.2126,0.7152,0.0722))*max(dot(light.direction,n),0.0)/light.pdf*0.25;\n if (!(part>0.0)) { continue; }\n open+=part;\n seen+=part*catcher_visible(origin,light.direction,1e19);\n }\n continue;\n } else {\n let picked=direct_light(k-2u,transform,p,origin,n,state,u);\n if (picked.limit<0.0) { continue; }\n direction=picked.direction;\n limit=picked.limit;\n shape=picked.shape;\n strength=dot(picked.scale,vec3f(0.2126,0.7152,0.0722))*max(picked.wi.z,0.0)/select(1.0,picked.pdf,picked.pdf>0.0);\n }\n if (!(strength>0.0)) { continue; }\n if (shape>0u) {\n let i=shape-1u;\n var hit_t=1e20;\n if (u32(shapes[i].a.w)==5u) { hit_t=union_hit(i,origin,direction,1e20); } else { hit_t=primitive_hit(i,origin,direction,1e20); }\n if (hit_t<=0.0001 || hit_t>=1e19) { continue; }\n limit=hit_t*0.9999-0.0002;\n }\n open+=strength;\n if (limit>0.0) { seen+=strength*catcher_visible(origin,direction,limit); } else { seen+=strength; }\n }\n if (open<=0.0) { return 0.0; }\n return clamp(1.0-seen/open,0.0,1.0);\n}\nconst INDIRECT_LUMA: vec3f = vec3f(0.2126,0.7152,0.0722);\nfn indirect_capped(value: vec3f, deep: bool) -> vec3f {\n if (!(CLAMP_INDIRECT>0.0) || !deep) { return value; }\n let luma=dot(value,INDIRECT_LUMA);\n if (!(luma>CLAMP_INDIRECT)) { return value; }\n return value*(CLAMP_INDIRECT/luma);\n}\nfn radiance(o0: vec3f, d0: vec3f, state: ptr<function,u32>, first_albedo: ptr<function,vec4f>, first_normal: ptr<function,vec4f>) -> vec3f {\n var o=o0;\n var d=d0;\n var throughput=vec3f(1.0);\n var result=vec3f(0.0);\n var previous_pdf=0.0;\n var carried=3u;\n var lobes=vec3u(0u);\n var last=false;\n var lit_vertex=false;\n var through=false;\n var diffused=false;\n for (var bounce=0u;bounce<=TOTAL_BOUNCES+1u;bounce++) {\n if (SOBOL) { rng.z=16u*(bounce+1u); }\n let hit=scene(o,d,1e20,false);\n let seen=TRANSMISSIVE_SHADOWS && through;\n if (!seen) { result+=indirect_capped(throughput*light_hits(o,d,hit.t,spawn_point(o,-d,o,0.0),previous_pdf),bounce>1u); }\n if (hit.t>=1e19) {\n if (bounce==0u && frame.forward.w>0.0) { break; }\n if (seen && sky_cdf.width>0u) { break; }\n let pdf=sky_pdf(d);\n let weight=select(1.0,previous_pdf/(previous_pdf+pdf),bounce>0u && previous_pdf>0.0);\n let environment=select(sky_radiance(d),sky_surface_radiance(d),bounce>0u);\n result+=indirect_capped(throughput*environment*weight,bounce>1u);\n break;\n }\n let p=o+d*hit.t;\n var n=select(hit.n,-hit.n,dot(hit.n,d)>0.0);\n if (bounce==0u && frame.forward.w>1.5 && hit.material==u32(frame.forward.w)-2u) {\n *first_albedo=vec4f(0.0,0.0,0.0,catcher_shadow(p,n,o,hit.t,state));\n *first_normal=vec4f(n,1.0);\n break;\n }\n let m=materials[hit.material];\n var s=surface(m);\n let layers=array<vec4f,4>(m.layer0,m.layer1,m.layer2,m.layer3);\n for (var i=0u;i<4u;i++) {\n if (layers[i].x>0.0 && layers[i].z!=0.0) {\n let detailed=detail_layer(s,n,layers[i],m.params[i*4u],m.params[i*4u+1u],m.params[i*4u+2u],m.params[i*4u+3u],p);\n s=detailed.shade;\n n=detailed.normal;\n }\n }\n let geometric=select(hit.geometric,-hit.geometric,dot(hit.geometric,d)>0.0);\n if (dot(n,geometric)<0.05) { n=normalize(n+geometric*0.1); }\n var layer=content_layers[hit.material];\n var shown=true;\n if (hit.shape==0u) {\n layer=hit_layer(hit.surface,hit.material);\n let face=(u32(triangles[hit.surface].b.w)>>3u)&3u;\n let front=dot(hit.geometric,d)<0.0;\n shown=face==0u || (face==1u && front) || (face==2u && !front);\n }\n let slot=i32(round(layer.layer.x));\n if (slot>=0 && shown) {\n let surface_data=instance_surfaces[hit.surface];\n let at=content_uv(hit.uv,surface_data.transform);\n if (content_inside(at,surface_data.crop)) {\n let texel=content_at(slot,at);\n let blend=u32(round(layer.layer.y));\n s=content_composite(s,blend,texel,layer.layer.z,layer.extra.x);\n if (layer.layer.w!=0.0) {\n let info=content_info[u32(slot)];\n if (info.width>0u && info.height>0u) {\n let tangent=normalize(hit.tangent-n*dot(hit.tangent,n));\n let bitangent=normalize(hit.bitangent-n*dot(hit.bitangent,n));\n let step=vec2f(1.5/f32(info.width),1.5/f32(info.height));\n let gx=content_at(slot,at+vec2f(step.x,0.0)).a-content_at(slot,at-vec2f(step.x,0.0)).a;\n let gy=content_at(slot,at+vec2f(0.0,step.y)).a-content_at(slot,at-vec2f(0.0,step.y)).a;\n n=content_emboss(n,tangent,bitangent,vec2f(gx,gy),layer.layer.w);\n let geometric=select(hit.geometric,-hit.geometric,dot(hit.geometric,d)>0.0);\n if (dot(n,geometric)<0.05) { n=normalize(n+geometric*0.1); }\n }\n }\n }\n }\n s.roughness=max(s.roughness,ROUGHNESS_FLOOR);\n s.clearcoat_roughness=max(s.clearcoat_roughness,ROUGHNESS_FLOOR);\n if (FILTER_GLOSSY>0.0 && diffused) {\n s.roughness=max(s.roughness,FILTER_GLOSSY);\n s.clearcoat_roughness=max(s.clearcoat_roughness,FILTER_GLOSSY);\n }\n if (bounce==0u) {\n *first_albedo=vec4f(s.base,1.0);\n *first_normal=vec4f(n,1.0);\n }\n s.emission*=emitter_weight(hit,p,spawn_point(o,-d,o,0.0),previous_pdf);\n if (seen && sampled_emitter(hit)) { s.emission=vec3f(0.0); }\n result+=indirect_capped(throughput*s.emission,bounce>1u);\n if (last) { break; }\n let transform=basis(n);\n let wo=transpose(transform)*(-d);\n let origin=spawn_point(p,n,o,hit.t);\n let entering=dot(hit.n,d)<0.0;\n for (var k=0u;k<2u+light_slots();k++) {\n let u=sobol(light_dim(k));\n var local=vec3f(0.0,0.0,1.0);\n var direction=vec3f(0.0,0.0,1.0);\n var strength=vec3f(0.0);\n var limit=1e19;\n var shape=0u;\n var pdf=0.0;\n var lit=s;\n if (k==0u) {\n if (frame.sun_color.w<=0.0) { continue; }\n direction=normalize(frame.sun_dir.xyz);\n if (frame.sun_dir.w<1.0) {\n let height=1.0-rnd(state,u.x)*(1.0-frame.sun_dir.w);\n let radius=sqrt(max(0.0,1.0-height*height));\n let phi=2.0*PI*rnd(state,u.y);\n let orient=basis(direction);\n direction=normalize(orient*vec3f(radius*cos(phi),radius*sin(phi),height));\n }\n local=transpose(transform)*direction;\n if (local.z<=0.0) { continue; }\n var shade=1.0;\n if (bounce==0u && effects().base_roughness.y>0.0) { shade=canopy(p); }\n strength=frame.sun_color.xyz*(frame.sun_color.w*shade);\n } else if (k==1u) {\n if (sky_cdf.width==0u) { continue; }\n let light=sky_sample(vec2f(rnd(state,u.x),rnd(state,u.y)));\n direction=light.direction;\n local=transpose(transform)*direction;\n if (local.z<=0.0) { continue; }\n strength=sky_surface_radiance(direction);\n pdf=light.pdf;\n if (!(pdf>0.0)) { continue; }\n } else {\n let picked=direct_light(k-2u,transform,p,origin,geometric,state,u);\n if (picked.limit<0.0) { continue; }\n local=picked.wi;\n direction=picked.direction;\n strength=picked.scale;\n pdf=picked.pdf;\n limit=picked.limit;\n shape=picked.shape;\n lit.roughness=max(s.roughness,picked.roughness);\n }\n if (pdf>0.0) {\n let bsdf=bsdf_pdf(s,wo,local);\n if (k==1u) {\n strength/=pdf+bsdf;\n } else {\n let ratio=bsdf/pdf;\n strength/=pdf*(1.0+ratio*ratio);\n }\n }\n if (shape>0u) {\n let i=shape-1u;\n var t=1e20;\n if (u32(shapes[i].a.w)==5u) { t=union_hit(i,origin,direction,1e20); } else { t=primitive_hit(i,origin,direction,1e20); }\n if (t<=0.0001 || t>=1e19) { continue; }\n limit=t*0.9999-0.0002;\n }\n if (limit>0.0) {\n if (TRANSMISSIVE_SHADOWS) {\n var unshadowed=throughput*eval(lit,wo,local)*select(1.0,glossy_share(s),pdf>0.0)*local.z*strength;\n let reach=throughput*strength;\n let keep=clamp(16.0*PI*max(unshadowed.r,max(unshadowed.g,unshadowed.b))/max(max(reach.r,max(reach.g,reach.b)),1e-30),0.0,1.0);\n if (keep<1.0) {\n if (rnd(state,u.w)>=keep) { continue; }\n unshadowed/=keep;\n }\n result+=indirect_capped(unshadowed*shadow_passed(origin,direction,limit),bounce>0u);\n continue;\n } else if (occluded(origin,direction,limit)) { continue; }\n }\n result+=indirect_capped(throughput*eval(lit,wo,local)*select(1.0,glossy_share(s),pdf>0.0)*local.z*strength,bounce>0u);\n }\n let sample=sample_bsdf(s,wo,entering,carried,state,sobol(3u));\n let wi=sample.dir;\n diffused=diffused || sample.lobe==0u;\n lobes+=vec3u(select(0u,1u,sample.lobe==0u),select(0u,1u,sample.lobe==1u),select(0u,1u,sample.lobe==2u));\n last=bounce>=TOTAL_BOUNCES || any(lobes>vec3u(DIFFUSE_BOUNCES,GLOSSY_BOUNCES,TRANSMISSION_BOUNCES));\n through=sample.pdf< -1.5 && lit_vertex;\n lit_vertex=sample.pdf>0.0 || through;\n if (sample.pdf<0.0) {\n throughput*=sample.weight;\n carried=sample.channel;\n if (sample.pdf< -1.5) {\n throughput*=sqrt(clamp(s.base,vec3f(0.0),vec3f(1.0)))*beer(s.thickness,s.subsurface_tint,s.absorption);\n }\n d=normalize(transform*wi);\n previous_pdf=0.0;\n } else {\n if (wi.z<=0.0 || sample.pdf<=0.0) { break; }\n throughput*=eval(s,wo,wi)*glossy_share(s)*wi.z/sample.pdf;\n d=normalize(transform*wi);\n previous_pdf=sample.pdf;\n }\n o=spawn_point(p,d,o,hit.t);\n if (bounce>=3u) {\n let keep=clamp(max(throughput.r,max(throughput.g,throughput.b)),0.05,0.95);\n if (rnd(state,sobol(0u).x)>keep) { break; }\n throughput/=keep;\n }\n }\n return result;\n}\n@compute @workgroup_size(8,8)\nfn main(@builtin(workgroup_id) group: vec3u, @builtin(local_invocation_id) local: vec3u) {\n let lane=local.y*8u+local.x;\n let first_lane=(frame.counts.w>>16u)&0x7fu;\n let end_lane=frame.counts.w>>23u;\n let blocks=(frame.size.y+7u)/8u;\n let block=((frame.counts.w&0xffffu)+group.y)*BLOCK_STRIDE%blocks;\n let gid=vec3u((group.x+frame.columns.x)*8u+local.x,block*8u+local.y,0u);\n if (lane<first_lane || lane>=end_lane || gid.x>=frame.size.x || gid.y>=frame.size.y) { return; }\n let index=gid.y*frame.size.x+gid.x;\n let pixel=gid.xy+frame.window.zw;\n let stream=pixel.y*frame.window.x+pixel.x;\n var sum=Accum(vec4f(0.0),vec4f(0.0),vec4f(0.0));\n if (frame.size.z>0u) { sum=accum[index]; }\n if (sum.color.w<0.0) { return; }\n for (var sample=0u;sample<frame.size.w;sample++) {\n var first_albedo=vec4f(0.0);\n var first_normal=vec4f(0.0);\n var state=pcg(stream*1973u+pcg(frame.counts.z*9277u+(frame.size.z+sample)*7919u+26699u));\n if (SOBOL) { rng=vec3u(hash_iqnt2d(pixel.x,pixel.y)^frame.counts.z,frame.size.z+sample,0u); }\n let pixel_filter=sobol(0u);\n let jitter=vec2f(rnd(&state,pixel_filter.x),rnd(&state,pixel_filter.y));\n let ndc=(vec2f(pixel)+jitter)/vec2f(frame.window.xy)*2.0-1.0;\n let kind=u32(round(frame.origin.w));\n var direction=normalize(frame.forward.xyz+frame.right.xyz*(ndc.x+frame.lens.x)+frame.up.xyz*(-ndc.y+frame.lens.y));\n var origin=frame.origin.xyz;\n if (kind==1u) {\n direction=normalize(frame.forward.xyz);\n origin+=normalize(frame.right.xyz)*((ndc.x+frame.lens.x)*frame.right.w)+normalize(frame.up.xyz)*((-ndc.y+frame.lens.y)*frame.up.w);\n } else if (kind==2u) {\n let phi=(ndc.x*0.5)*2.0*PI;\n let theta=(ndc.y*0.5+0.5)*PI;\n let local=vec3f(sin(theta)*sin(phi),cos(theta),-sin(theta)*cos(phi));\n direction=normalize(normalize(frame.right.xyz)*local.x+normalize(frame.up.xyz)*local.y-normalize(frame.forward.xyz)*local.z);\n } else if (frame.lens.z>0.0) {\n let focus=origin+direction*(frame.lens.w/dot(direction,normalize(frame.forward.xyz)));\n let aperture=sobol(1u);\n let radius=sqrt(rnd(&state,aperture.x))*frame.lens.z;\n let phi=2.0*PI*rnd(&state,aperture.y);\n origin+=normalize(frame.right.xyz)*(radius*cos(phi))+normalize(frame.up.xyz)*(radius*sin(phi));\n direction=normalize(focus-origin);\n }\n let traced=radiance(origin,direction,&state,&first_albedo,&first_normal);\n var value=traced;\n if (effects().thick_sub_film.w>0.0) {\n let depth=scene(origin,direction,1e20,false).t;\n value=steam_composite(origin,direction,depth,traced);\n }\n sum.color=vec4f(sum.color.xyz+value,sum.color.w);\n sum.albedo+=first_albedo;\n sum.normal+=first_normal;\n }\n sum.color.w=f32(frame.size.z+frame.size.w);\n accum[index]=sum;\n let count=max(sum.color.w,1.0);\n let n=sum.normal.xyz/count;\n textureStore(output,vec2i(gid.xy),vec4f(sum.color.xyz/count,select(1.0,sum.albedo.w/count,frame.forward.w>0.0)));\n textureStore(albedo,vec2i(gid.xy),sum.albedo/count);\n textureStore(normal,vec2i(gid.xy),vec4f(select(vec3f(0.0),normalize(n),length(n)>1e-6),sum.normal.w/count));\n}\n";