# 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).