115 lines
3.5 KiB
Markdown
115 lines
3.5 KiB
Markdown
# Emissive + Exposure
|
||
|
||
## Principle
|
||
|
||
Two complementary features (Step 22):
|
||
|
||
| Feature | Effect | Cost |
|
||
|---------|--------|------|
|
||
| **Exposure** (6.1) | Multiplies luminance before the tone-mapping curve | Zero when HDR is inactive |
|
||
| **Emissive** (6.2) | Adds an emitted color (independent of the lights) | Zero when `emissive = [0,0,0,0]` |
|
||
|
||
## Exposure
|
||
|
||
### API
|
||
|
||
```rust
|
||
// Initialization (optional, default = 1.0)
|
||
let app = AppBuilder::new()
|
||
.with_hdr(ToneMapper::Aces)
|
||
.with_exposure(1.5) // start brighter
|
||
.build().await?;
|
||
|
||
// Runtime (in update())
|
||
app.set_exposure(app.exposure() * 1.1); // +1 "stop"
|
||
app.set_exposure(1.0); // reset
|
||
```
|
||
|
||
### Behavior
|
||
|
||
- Exposure is a **multiplier** applied to the HDR texture before the tone-mapping curve.
|
||
- `exposure = 2.0` → the image is 2× brighter (like opening a camera's aperture).
|
||
- `exposure = 0.5` → the image is 2× darker.
|
||
- Clamped to `[0.01, 10.0]` to avoid degenerate values.
|
||
- **Only has an effect when HDR is active** (`with_hdr(...)`). In LDR the value is ignored.
|
||
|
||
### Keyboard (demo)
|
||
|
||
| Key | Effect |
|
||
|-----|--------|
|
||
| `+` | ×1.1 (brighter) |
|
||
| `-` | ÷1.1 (darker) |
|
||
| `0` | Reset to 1.0 |
|
||
|
||
## Emissive
|
||
|
||
### API
|
||
|
||
```rust
|
||
use wsg_lib::resources::Material;
|
||
|
||
// Create a material with emissivity
|
||
let mut mat = /* ... */;
|
||
mat.emissive = [1.0, 0.3, 0.1, 1.5]; // orange, intensity 1.5 (> 1.0 = HDR glow)
|
||
```
|
||
|
||
### Format
|
||
|
||
`emissive = [r, g, b, intensity]`:
|
||
|
||
- **rgb**: the emission color (same space as the vertex base color)
|
||
- **a (intensity)**: the multiplier. `1.0` = normal color, `> 1.0` = highlight (only visible in HDR)
|
||
|
||
### Shader formula
|
||
|
||
```
|
||
final_color = lit + base_color * emissive.rgb * emissive.a
|
||
```
|
||
|
||
- The emission is **additive**: visible even in total darkness (no light needed).
|
||
- It is **independent of shadows**: an emissive object casts no shadow and is not shadowed.
|
||
- `emissive = [0,0,0,0]` (default) → no change (non-regression guaranteed).
|
||
|
||
### Use cases
|
||
|
||
| Use | Value |
|
||
|-----|-------|
|
||
| LED / indicator | `[0, 1, 0, 1.0]` (green, normal intensity) |
|
||
| Flame / sun | `[1, 0.8, 0.2, 3.0]` (orange, HDR glow) |
|
||
| Neon | `[0, 0.5, 1, 2.5]` (cyan, glow) |
|
||
| Inactive | `[0, 0, 0, 0]` (default) |
|
||
|
||
### Keyboard (demo)
|
||
|
||
| Key | Effect |
|
||
|-----|--------|
|
||
| `E` | Toggle orange glow on the sphere/cylinder |
|
||
|
||
## Interactions
|
||
|
||
| Combination | Result |
|
||
|-------------|--------|
|
||
| Emissive + HDR + ACES | Soft glow, highlights roll off (the nicest) |
|
||
| Emissive + LDR | Clamped to 1.0 (no glow, but the color is visible in the dark) |
|
||
| Emissive + shadows | The emissive object is NOT shadowed (emission bypasses the shadow term) |
|
||
| Exposure + Emissive | Exposure also amplifies the emission (consistent: everything is in the HDR texture) |
|
||
|
||
## Non-regression
|
||
|
||
- **Emissive**: `[0,0,0,0]` by default → the shader adds `base * 0 * 0 = 0` → no change.
|
||
- **Exposure**: `1.0` by default → `pow(color, 1/1) = color` → no change.
|
||
- Both are **opt-in**: without `with_hdr(...)` and `emissive != 0`, the pipeline is identical
|
||
to the previous state.
|
||
|
||
## Limitations (MVP)
|
||
|
||
- Emissive is **per material**, not per vertex (no emission gradient within a mesh).
|
||
- Emissive is **static** at material creation (changing `mat.emissive` requires re-registering
|
||
the material via `add_material`).
|
||
- No **bloom** (Step 23): the HDR glow is visible but not "blurred" / spread.
|
||
|
||
## Links
|
||
|
||
- [User README](../README.md) · [HDR & tone mapping](../effects/hdr.md) · [Examples](../examples.md)
|
||
- [Root README](../../../README.md)
|