Constant BLOOM_WGSL
Source pub const BLOOM_WGSL: &str = "// T2 bloom post-pass \u{2014} Kawase dual-filter (low bandwidth, few dispatches).\n// Unified bind group: sampler + tex_a + tex_b + bloom_params + composite_params.\n\nstruct BloomParams {\n threshold: f32,\n intensity: f32,\n offset: f32,\n _pad: f32,\n};\n\nstruct CompositeParams {\n exposure: f32,\n bloom_strength: f32,\n _pad0: f32,\n _pad1: f32,\n};\n\n@group(0) @binding(0) var samp: sampler;\n@group(0) @binding(1) var tex_a: texture_2d<f32>;\n@group(0) @binding(2) var tex_b: texture_2d<f32>;\n@group(0) @binding(3) var<uniform> bloom_params: BloomParams;\n@group(0) @binding(4) var<uniform> composite_params: CompositeParams;\n\nfn fullscreen_pos(vi: u32) -> vec4<f32> {\n let pos = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -1.0),\n vec2<f32>(3.0, -1.0),\n vec2<f32>(-1.0, 3.0)\n );\n return vec4<f32>(pos[vi], 0.0, 1.0);\n}\n\nfn uv_from_pos(pos: vec4<f32>, dims: vec2<u32>) -> vec2<f32> {\n return pos.xy / vec2<f32>(dims);\n}\n\nfn luminance(c: vec3<f32>) -> f32 {\n return dot(c, vec3<f32>(0.2126, 0.7152, 0.0722));\n}\n\nfn reinhard(c: vec3<f32>) -> vec3<f32> {\n return c / (vec3<f32>(1.0) + c);\n}\n\n@vertex\nfn extract_vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {\n return fullscreen_pos(vi);\n}\n\n@fragment\nfn extract_fs(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {\n let full = vec2<f32>(textureDimensions(tex_a));\n // Render target is half-res \u{2014} scale UV to sample full HDR scene.\n let uv = pos.xy / (full * 0.5);\n let c = textureSample(tex_a, samp, uv);\n let bright = max(c.rgb - vec3<f32>(bloom_params.threshold), vec3<f32>(0.0));\n let w = luminance(bright);\n let bloom = bright * smoothstep(bloom_params.threshold, bloom_params.threshold + 0.25, w);\n return vec4<f32>(bloom * bloom_params.intensity, 1.0);\n}\n\n@vertex\nfn kawase_vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {\n return fullscreen_pos(vi);\n}\n\n@fragment\nfn kawase_fs(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {\n let dims = textureDimensions(tex_a);\n let uv = uv_from_pos(pos, dims);\n let texel = 1.0 / vec2<f32>(dims);\n let o = bloom_params.offset * texel;\n var sum = textureSample(tex_a, samp, uv) * 4.0;\n sum += textureSample(tex_a, samp, uv + vec2<f32>(o.x, o.y));\n sum += textureSample(tex_a, samp, uv + vec2<f32>(-o.x, o.y));\n sum += textureSample(tex_a, samp, uv + vec2<f32>(o.x, -o.y));\n sum += textureSample(tex_a, samp, uv + vec2<f32>(-o.x, -o.y));\n return sum / 8.0;\n}\n\n@vertex\nfn composite_vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {\n return fullscreen_pos(vi);\n}\n\n@fragment\nfn composite_fs(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {\n let dims = textureDimensions(tex_a);\n let uv = uv_from_pos(pos, dims);\n let hdr = textureSample(tex_a, samp, uv).rgb;\n let bloom = textureSample(tex_b, samp, uv).rgb;\n let combined = (hdr + bloom * composite_params.bloom_strength) * composite_params.exposure;\n let mapped = reinhard(combined);\n return vec4<f32>(mapped, 1.0);\n}";