3.4 KiB
3.4 KiB
Bloom (Step 23)
Bloom is a post-process that creates a "glow" effect around the bright areas of the image. Pixels whose luminance exceeds a threshold are extracted, blurred, then added back to the original image.
Prerequisite: bloom requires HDR (
AppBuilder::with_hdr). Without HDR, values are already clamped to [0,1] and there is nothing "bright" to extract.
Activation
use wsg_lib::prelude::*;
let app = AppBuilder::new()
.with_hdr(ToneMapper::Aces) // required
.with_bloom(BloomConfig {
threshold: 1.0, // HDR luminance threshold
knee: 0.5, // soft-knee width
intensity: 0.8, // glow intensity
radius: 4.0, // blur radius (pixels, half-res)
..Default::default()
})
.build()
.await?;
BloomConfig
| Field | Type | Default | Description |
|---|---|---|---|
threshold |
f32 |
1.0 |
Luminance threshold (linear HDR units). Only pixels above the threshold contribute to the bloom. |
knee |
f32 |
0.5 |
Soft-knee width. Larger = smoother transition. |
intensity |
f32 |
0.8 |
Multiplier applied to the blurred result before adding it to the HDR. |
radius |
f32 |
4.0 |
Blur radius in pixels (at half resolution). Larger = wider glow. |
Runtime update
// In the handler (fn update):
if app.bloom_enabled() {
app.set_bloom_config(BloomConfig {
intensity: new_intensity,
..app.bloom_config()
});
}
Changes take effect on the next frame (the uniforms are re-written every frame).
Pipeline (4 GPU passes)
Scene ──→ HDR (full res, Rgba16Float)
│
├──→ [1] Threshold (full → half res)
│ Soft-knee: smoothstep(knee, knee+1, lum)
│
├──→ [2] Blur H (half res)
│ 9-tap separable Gaussian, direction = (1/w, 0)
│
├──→ [3] Blur V (half res)
│ 9-tap separable Gaussian, direction = (0, 1/h)
│ (ping-pong: writes into the bright texture)
│
└──→ [4] Composite (full res)
output = HDR + bloom × intensity
(writes into a 3rd full-res texture)
│
▼
Tone Mapping (reads the composite)
│
▼
Surface (sRGB)
Cost
- Without bloom (default): zero overhead. The TM reads the HDR texture directly.
- With bloom: 4 extra passes (1 full-res + 3 half-res) + 3 intermediate textures. The cost is moderate because the blur runs at half resolution.
Non-regression
with_bloom()withoutwith_hdr()→ warning + no-op (the bloom is ignored).- Without
with_bloom()→ the TM reads the HDR texture directly (the Step 20 behavior is unchanged).
Limitations (MVP)
- A single mip level (no multi-mip "soft" bloom à la Unreal).
- No directional bloom.
- The blur is a 9-tap Gaussian (good enough for a "soft" glow).
- No per-layer bloom (no per-material "bloom mask").