# Rendering workflow (GI + post-process volumes)

Step-by-step guide for game teams managing look development without Rust changes.

## Mental model

1. **Project defaults** — `assets/project.ron` → `rendering` section (GI mode, lighting, post FX).
2. **Post-process volumes** — scene actors with local overrides (rooms, biomes, boss arenas).
3. **Requested profile** — the active viewport camera always uses `ActiveCameraRenderProfile` (project + blended volumes), whether it is the editor fly camera or the possessed player camera.
4. **Effective stack** — `EffectiveRenderStack` records requested GI, effective GI, and fallback reason for renderer sync and diagnostics.
5. **Atmosphere ownership** — Bevy 0.19 uses one stable project `Atmosphere` entity; active cameras only carry `AtmosphereSettings` when the profile enables sky rendering.
6. **Antialiasing ownership** — all 3D cameras spawn with MSAA disabled. The project rendering profile owns temporal antialiasing, and Bevy's deferred path does not support MSAA.

See [ADR 0013](../adr/0013-rendering-tiers-and-post-process-volumes.md) for tier policy and crate boundaries, and [ADR 0016](../adr/0016-unified-rendering-contract.md) for the requested/effective stack contract.

## Project defaults

1. **Edit → Project Settings**
2. Expand **Global Illumination**, **Lighting**, and **Post Process** sections.
3. Click **Apply** (updates runtime; **Save to disk** writes `assets/project.ron`).

| Section | Key controls |
|---------|----------------|
| GI | `GiMode`: Auto / Forward / Solari; status line shows RT fallback |
| Lighting | Ambient, sun lux, shadow cascades |
| Post Process | HDR, exposure mode (Manual EV100 / Auto), auto exposure speeds, bloom, TAA, SSAO, atmosphere, fog |

## Post-process volumes

### Create

- **Scene → Create → Post Process Volume**
- Asset Browser → Built-ins → **Post Process Volume** (drag into the viewport)
- Command palette: `rendering.create_volume`

### Inspector (inherit / override)

Each FX field uses **Override** checkbox:

- **Unchecked** — inherits project value (shown greyed as *Inherited: …*).
- **Checked** — local value; **Reset to project** per field.

Volume fields: label (HUD), priority, blend distance, half extents (local-space AABB).

### Rendering panel

**Window → Rendering** tabs:

| Tab | Purpose |
|-----|---------|
| Active Camera | Resolved GI path, exposure, fog, FX flags, contributing volumes |
| Project | Read-only `project.ron` summary + link to Project Settings |
| Volumes | All volumes in level (priority sort, select/focus) |

Viewport overlays: **GI badge** (Forward / Solari), **volume HUD** when inside a volume.

## GI modes

| Mode | Behavior |
|------|----------|
| Auto | Startup default: Solari when RT GPU support is available; Forward otherwise |
| Forward | Bevy Forward PBR with HDR/TAA/SSAO/atmosphere/shadows |
| Solari | Request Solari; the camera attaches Bevy Solari when RT support is available |

Solari in Bevy 0.19 samples directional lights and emissive meshes. Point and spot lights require the normal PBR light pass, so `LightDesc` point/spot components are runtime-disabled while Solari is active. The component remains in the scene, the inspector shows an unsupported message, and hydration strips/skips the Bevy `PointLight` / `SpotLight` until authors switch the light to Directional or switch GI to Forward. The viewport resolves Hybrid Auto through **`EffectiveRenderStack`**: requested Solari keeps project meshes eligible for raytracing and attaches Bevy Solari when RT support is available. Effective GI falls back to **Forward** only when RT is unavailable. Effective Solari forces an HDR camera target even if project HDR is off, because Bevy Solari writes to the main texture through a storage binding and sRGB textures are invalid for that use. Primitive hydration generates tangents so built-in boxes/spheres can enter Solari BLAS/TLAS; imported/custom meshes still need TriangleList geometry, POSITION/NORMAL/UV_0/TANGENT attributes, and U32 indices. The Rendering panel reports the exact fallback reason, Solari-compatible asset counts, bind-group state, and compatible light counters. Set `BEVY_FPS_FORCE_SOLARI=1` to force RT feature probing in dev (see README troubleshooting).

## Emissive lighting

Material authoring includes emissive color, emissive intensity (nits), and an optional emissive texture. Emissive materials contribute to bloom in Forward PBR and count as Solari-compatible light sources when the material is on eligible project geometry. Use emissive surfaces for area-style indirect light in Solari scenes; use point/spot lights when local direct lighting and shadows are more important.

## Asset folders

| Path | Purpose |
|------|---------|
| `assets/rendering_profiles/*.ron` | Named override bundles (`RenderingProfileAsset`); Apply / Revert in volume inspector |
| `assets/post_fx/*.ron` + `.wgsl` | Fullscreen effects (`PostProcessEffectAsset`); picker in volume inspector |
| `assets/materials/*.ron` | Material presets, including emissive fields for Solari-compatible surfaces |

Shipped examples: `post_fx/vignette.ron`, `post_fx/chromatic_aberration.ron`, `rendering_profiles/cave_dark.ron`, `rendering_profiles/outdoor_haze.ron`, `materials/emissive_panel.ron`.

## Game team checklist

- Keep global look in `assets/project.ron`.
- Use volumes for rooms/biomes; set `label` for HUD clarity.
- Reuse looks via `rendering_profiles/` presets.
- Add custom shaders under `assets/post_fx/` — do not fork `camera_fx.rs` for one-off levels.
- Prefer `GiMode::Auto` for the default project path. Use `GiMode::Forward` when authoring point/spot lighting or debugging classic shadow maps.
- Use emissive material presets for Solari indirect-light contributors; keep helper meshes tagged `RaytracingExcluded`.

## Example level

Open **File > Open Sample > Rendering Lab** for the committed rendering fixture. Its red sphere,
cyan cube, amber block, and emissive horizon make the adjacent vignette, fog, and dark-exposure zones
visually comparable while the volume actors exercise stable ownership, profile, and fullscreen-effect
references. See the [sample regression pack](sample-regression-pack.md) for the native QA protocol.

## Troubleshooting

| Symptom | Check |
|---------|-------|
| Point/spot weak at max lumens | Outdoor exposure (~12–15 EV100 in Project Settings) needs much higher lumens than indoor (~7 EV100). Bevy’s reference scale is ~1M lumens (`VERY_LARGE_CINEMA_LIGHT`); inspector allows up to 2M. Narrow spot cones for brighter hotspots. For local lights, GI path must be **Forward** (Solari uses directional + emissive only). |
| Point/spot controls disabled under GiMode Auto | Auto selected Solari on this GPU. Point/spot `LightDesc` is disabled while Solari is active; switch the light to Directional, use emissive materials, or set GI to Forward. |
| Scene sun shadow range wrong vs Project Settings | Scene directionals use `cascade_config_from_rendering`; Apply shadow cascade settings and confirm `sync_scene_directional_shadow_cascades` |
| Auto exposure not active | **Rendering → Active Camera**: needs **Exposure: Auto**, HDR on, and GPU compute. Volume **Exposure EV100** override forces manual for that view. |
| Viewport black in Lit | Rendering panel → **Runtime lights**, **Stack**, and **Solari raytracing scene**; Collider mode hides meshes |
| Solari warming up | Solari raytracing scene readiness: tagged meshes, Solari-compatible mesh assets, render instances, bind group, compatible lights |
| Lighting changes do nothing in Solari | Solari samples directional lights and emissive meshes; point/spot `LightDesc` is disabled while Solari is active |
| GI badge shows Forward unexpectedly | Solari RT wgpu features unavailable; Rendering → Active Camera tab |
| Volume has no effect | Camera inside AABB? Priority vs other volumes? Overrides enabled? |
| Custom FX missing | RON path in volume matches `assets/post_fx/`; shader path in RON |
| Atmosphere flickers or artifacts | Confirm only the project `Atmosphere` entity exists and active cameras have `AtmosphereSettings`, not camera-side `Atmosphere` components. |

Command palette: `rendering.select_volumes_at_camera`, `rendering.focus_active_volumes`.
