MSAA
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# MSAA (Anti-aliasing)
|
||||
|
||||
Multi-Sample Anti-Aliasing (MSAA) smooths jagged edges by rendering the scene
|
||||
at a higher sample count (e.g. 4 samples per pixel), then averaging the samples
|
||||
into the final image.
|
||||
|
||||
## Activation
|
||||
|
||||
MSAA is opt-in via the builder. When disabled (default), the renderer uses
|
||||
single-sample rendering with zero overhead:
|
||||
|
||||
```rust
|
||||
let app = AppBuilder::new()
|
||||
.title("My App")
|
||||
.with_msaa(4) // 4× MSAA (also: 2 or 8)
|
||||
.build()
|
||||
.await?;
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
MSAA is a **rasterizer feature** — no new shader is needed. The pipeline:
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Without MSAA (default) │
|
||||
│ │
|
||||
│ Main pass ──→ HDR texture (1 sample) ──→ [Bloom] ──→ TM ──→ Surface │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ With MSAA 4× (and HDR) │
|
||||
│ │
|
||||
│ Main pass ──→ MSAA texture (4 samples, Rgba16Float) │
|
||||
│ ↓ resolve (hardware average) │
|
||||
│ HDR texture (1 sample) │
|
||||
│ ↓ │
|
||||
│ [Bloom] ──→ TM ──→ Surface │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ With MSAA 4× (no HDR) │
|
||||
│ │
|
||||
│ Main pass ──→ MSAA texture (4 samples, surface format) │
|
||||
│ ↓ resolve │
|
||||
│ Swapchain (surface) │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Key points:
|
||||
- The **main scene pass** renders into the MSAA texture (N samples/pixel).
|
||||
- The **resolve** (hardware average) produces the single-sample output.
|
||||
- **Post-processes** (bloom, tone mapping) operate on the **resolved**
|
||||
single-sample texture — they are completely unaffected by MSAA.
|
||||
- The **shadow map** is always single-sample (depth-only, not visible directly).
|
||||
|
||||
## Sample count
|
||||
|
||||
| Count | Quality | Cost (approx.) | Use case |
|
||||
|-------|---------|----------------|----------|
|
||||
| 2 | Basic | ~1.1× | Low-end / battery |
|
||||
| **4** | **Good** | **~1.3–1.5×** | **Default, most games** |
|
||||
| 8 | Excellent | ~1.6–2.0× | High-end / static scenes |
|
||||
|
||||
The cost is in the rasterizer/fill-rate (each pixel is shaded N times at
|
||||
triangles' edges). Interior pixels (covered by a single triangle) are
|
||||
shaded only once — MSAA only multiplies the **edge** cost.
|
||||
|
||||
## Runtime query
|
||||
|
||||
```rust
|
||||
// In AppHandler::setup or update:
|
||||
let active = app.renderer().msaa_enabled(); // true if sample_count > 1
|
||||
let count = app.renderer().msaa_sample_count(); // 1, 2, 4, or 8
|
||||
```
|
||||
|
||||
MSAA is a **build-time** setting: the multi-sample textures are allocated
|
||||
at startup. Changing the sample count requires recreating the textures
|
||||
(window resize does this automatically).
|
||||
|
||||
## Configuration struct
|
||||
|
||||
```rust
|
||||
use wsg_lib::MsaaConfig;
|
||||
|
||||
let config = MsaaConfig { sample_count: 4 };
|
||||
// Or use the builder shortcut (validated):
|
||||
let app = AppBuilder::new().with_msaa(4).build().await?;
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
| Feature | Compatible? | Notes |
|
||||
|---------|-------------|-------|
|
||||
| HDR + Tone Mapping | ✅ | MSAA texture is `Rgba16Float`, resolves into HDR |
|
||||
| Bloom | ✅ | Bloom reads the resolved (single-sample) HDR texture |
|
||||
| Shadows | ✅ | Shadow map is always single-sample |
|
||||
| Frustum Culling | ✅ | Independent (compute pass) |
|
||||
| LOD | ✅ | Independent (draw args) |
|
||||
| Emissive | ✅ | Per-entity, in the main pass |
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Does not smooth UV-dependent aliasing** (texture shimmer). For that,
|
||||
use mipmaps + anisotropic filtering (future: texture module).
|
||||
- **Cost scales with overdraw**: fully transparent or heavily overlapping
|
||||
geometry pays the full N× cost.
|
||||
- **GPU support**: most modern GPUs support 4× for all formats. 8× may be
|
||||
limited for float formats (check `Device::limits().max_color_attachment_samples`).
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
use wsg_lib::app::AppBuilder;
|
||||
use wsg_lib::core::ToneMapper;
|
||||
|
||||
let app = AppBuilder::new()
|
||||
.title("MSAA Demo")
|
||||
.size(1280, 720)
|
||||
.with_msaa(4)
|
||||
.with_hdr(ToneMapper::Aces)
|
||||
.build()
|
||||
.await?;
|
||||
```
|
||||
|
||||
See `examples/msaa.rs` for a full interactive demo with cube, sphere,
|
||||
and ground plane where aliasing is clearly visible without MSAA.
|
||||
Reference in New Issue
Block a user