Files
wsg/docs/user/bloom.md
T
Jérôme Bousquié 35aeb769a8 refactor examples
2026-09-25 10:19:24 +02:00

94 lines
3.3 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 (Étape 23)
Le **bloom** est un post-process qui crée un effet de "glow" autour des zones brillantes de
l'image. Les pixels dont la luminance dépasse un seuil sont extraits, floutés, puis ajoutés
à l'image originale.
> **Prérequis** : le bloom nécessite l'HDR (`AppBuilder::with_hdr`). Sans HDR, les valeurs
> sont déjà clampées à [0,1] et il n'y a rien de "brillant" à extraire.
## Activation
```rust
use wsg_lib::prelude::*;
let app = AppBuilder::new()
.with_hdr(ToneMapper::Aces) // requis
.with_bloom(BloomConfig {
threshold: 1.0, // seuil de luminance HDR
knee: 0.5, // largeur du soft-knee
intensity: 0.8, // intensité du glow
radius: 4.0, // rayon du blur (pixels, demi-rés)
..Default::default()
})
.build()
.await?;
```
## `BloomConfig`
| Champ | Type | Défaut | Description |
|-------|------|--------|-------------|
| `threshold` | `f32` | `1.0` | Seuil de luminance (unités HDR linéaires). Seuls les pixels > seuil contribuent au bloom. |
| `knee` | `f32` | `0.5` | Largeur du soft-knee. Plus grand = transition plus douce. |
| `intensity` | `f32` | `0.8` | Multiplicateur appliqué au résultat flouté avant addition à l'HDR. |
| `radius` | `f32` | `4.0` | Rayon du blur en pixels (à la demi-résolution). Plus grand = glow plus étendu. |
## Mise à jour runtime
```rust
// Dans le handler (fn update):
if app.bloom_enabled() {
app.set_bloom_config(BloomConfig {
intensity: new_intensity,
..app.bloom_config()
});
}
```
Les changements prennent effet au frame suivant (les uniforms sont ré-écrits chaque frame).
## Pipeline (4 passes GPU)
```
Scene ──→ HDR (full res, Rgba16Float)
│
├──→ [1] Threshold (full → half res)
│ Soft-knee: smoothstep(knee, knee+1, lum)
│
├──→ [2] Blur H (half res)
│ 9-tap Gaussian séparable, direction = (1/w, 0)
│
├──→ [3] Blur V (half res)
│ 9-tap Gaussian séparable, direction = (0, 1/h)
│ (ping-pong: écrit dans la texture bright)
│
└──→ [4] Composite (full res)
output = HDR + bloom × intensity
(écrit dans une 3e texture full-res)
│
▼
Tone Mapping (lit le composite)
│
▼
Surface (sRGB)
```
## Coût
- **Sans bloom** (défaut) : zéro overhead. Le TM lit directement la texture HDR.
- **Avec bloom** : 4 passes supplémentaires (1 full-res + 3 half-res) + 3 textures
intermédiaires. Le coût est modéré car le blur est en demi-résolution.
## Non-régression
- `with_bloom()` sans `with_hdr()` → warning + no-op (le bloom est ignoré).
- Sans `with_bloom()` → le TM lit la texture HDR directement (comportement Étape 20 inchangé).
## Limitations (MVP)
- Un seul niveau de mip (pas de multi-mip "soft" bloom à la Unreal).
- Pas de directional bloom.
- Le blur est un Gaussian 9-taps (qualité suffisante pour un glow "soft").
- Pas de bloom séparé par couche (pas de "bloom mask" par matériau).