Files
wsg/docs/user/effects/bloom.md
T
Jérôme Bousquié d4c2d93fc5 eng doc
2026-09-25 20:06:10 +02:00

100 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```rust
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
```rust
// 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()` without `with_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").
## Links
- [User README](../README.md) · [HDR & tone mapping](hdr.md) · [Examples](../examples.md)
- [Root README](../../../README.md)