xfy ed6639738f render: blur pyramid (bloom stages [3]/[4]) — down then up
Adds BlurMaterial + blur.wgsl with a 13-tap weighted Gaussian kernel
(var<private> arrays — naga rejects dynamic indexing of const arrays),
run in downsample then upsample passes. For the 3-level pyramid:
bloom_0 (half, from brightpass) → bloom_1 (quarter) → bloom_2 (eighth),
then upsample to bloom_final (half). Camera orders -18/-17/-16/-15.

The bloom output is not yet composited (Task 6); this commit only wires
the passes and confirms they run in sequence without crashing.
2026-07-15 00:07:09 +08:00

53 lines
1.9 KiB
WebGPU Shading Language

#import bevy_sprite::mesh2d_vertex_output::VertexOutput
struct BlurUniform {
mode: u32, // 0 = downsample, 1 = upsample
texel_size: vec2<f32>,
blend: f32, // upsample blend factor (ignored for down)
_pad0: f32,
};
@group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> u: BlurUniform;
@group(#{MATERIAL_BIND_GROUP}) @binding(1) var tex: texture_2d<f32>;
@group(#{MATERIAL_BIND_GROUP}) @binding(2) var samp: sampler;
// 13-tap weighted kernel (Gaussian approximation for HDR bloom).
// NOTE: naga rejects dynamic indexing of const arrays ("may only be indexed
// by a constant"), so we use var<private> here — it allows runtime indexing.
var<private> KERNEL_OFFSETS: array<vec2<f32>, 13> = array<vec2<f32>, 13>(
vec2<f32>( 0.0, 0.0),
vec2<f32>( 1.0, 0.0), vec2<f32>(-1.0, 0.0),
vec2<f32>( 0.0, 1.0), vec2<f32>( 0.0, -1.0),
vec2<f32>( 1.0, 1.0), vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0), vec2<f32>(-1.0, -1.0),
vec2<f32>( 2.0, 0.0), vec2<f32>(-2.0, 0.0),
vec2<f32>( 0.0, 2.0), vec2<f32>( 0.0, -2.0),
);
var<private> KERNEL_WEIGHTS: array<f32, 13> = array<f32, 13>(
0.5,
0.25, 0.25, 0.25, 0.25,
0.125, 0.125, 0.125, 0.125,
0.0625, 0.0625, 0.0625, 0.0625,
);
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
let uv = in.uv;
// Downsample and upsample share the same 13-tap weighted kernel.
// mode 0 = down (plain weighted average), mode 1 = up (scaled by blend).
var sum = vec3<f32>(0.0);
var wsum = 0.0;
for (var i: i32 = 0; i < 13; i = i + 1) {
let off = KERNEL_OFFSETS[i] * u.texel_size;
let c = textureSample(tex, samp, uv + off).rgb;
sum = sum + c * KERNEL_WEIGHTS[i];
wsum = wsum + KERNEL_WEIGHTS[i];
}
let blurred = sum / wsum;
if (u.mode == 0u) {
return vec4<f32>(blurred, 1.0);
} else {
return vec4<f32>(blurred * u.blend, 1.0);
}
}