MSAA
This commit is contained in:
+32
-1
@@ -16,7 +16,8 @@ cargo run -p wsg-lib --example <nom>
|
||||
| `hdr` | HDR + Tone Mapping (ACES) + contrôle d'exposition |
|
||||
| `emissive` | Matériaux émissifs (intensités croissantes 0 → 4.0) |
|
||||
| `shadow` | Shadow mapping (ombre portée directionnelle) |
|
||||
| `culling` | Culling GPU-driven (grille 20×20, objets hors frustum ignorés) |
|
||||
| `culling` | Culling GPU-driven (grille 15×15, objets hors frustum ignorés) |
|
||||
| `msaa` | MSAA 4× (anti-aliasing multi-échantillons, arêtes lisses) |
|
||||
| `manual` | Workflow bas niveau (Context + Renderer + PipelineCache) |
|
||||
| `import` | Import de fichier OBJ (non graphique, stdout) |
|
||||
|
||||
@@ -220,6 +221,36 @@ cargo run -p wsg-lib --example culling
|
||||
|
||||
---
|
||||
|
||||
## `msaa` — MSAA 4× (Anti-aliasing)
|
||||
|
||||
Démontre l'anti-aliasing multi-échantillons : les arêtes des objets (cube, sphère)
|
||||
sont lisses au lieu d'être "en escalier". La scène contient un cube (arêtes nettes),
|
||||
une sphère (silhouette courbe) et un petit cube près de la caméra (aliasing maximal).
|
||||
|
||||
```sh
|
||||
cargo run -p wsg-lib --example msaa
|
||||
```
|
||||
|
||||
### Touches
|
||||
|
||||
| Touche | Action |
|
||||
|--------|--------|
|
||||
| Glisser (LMB) | Orbiter la caméra |
|
||||
| Molette | Zoom |
|
||||
| `R` | Reset caméra |
|
||||
| `M` | Afficher le nombre d'échantillons |
|
||||
|
||||
### Pour comparer avec/sans MSAA
|
||||
|
||||
Supprimer la ligne `.with_msaa(4)` dans le source et recompiler : la scène est
|
||||
identique, seules les arêtes diffèrent (escaler vs lisse).
|
||||
|
||||
> **Note** : MSAA est un réglage de build-time (allocation de textures multi-échantillons).
|
||||
> Il fonctionne indépendamment de HDR : avec HDR, la texture MSAA est `Rgba16Float`
|
||||
> et résout dans la texture HDR avant bloom/TM.
|
||||
|
||||
---
|
||||
|
||||
## `manual` — Workflow bas niveau
|
||||
|
||||
Démontre l'API **sans** la façade `App` : utilisation directe de `Context`,
|
||||
|
||||
@@ -56,14 +56,14 @@ impl ApplicationHandler for App {
|
||||
|
||||
// 2. Renderer initialization (it retrieves everything it needs)
|
||||
let device = Arc::new(context.device.clone());
|
||||
let mut cache = PipelineCache::new(device, context.queue.clone());
|
||||
let mut cache = PipelineCache::new(device, context.queue.clone(), 1);
|
||||
cache
|
||||
.register_shader("standard", utils::STANDARD_SHADER_PATH)
|
||||
.unwrap();
|
||||
|
||||
// Flat 2D rendering: `standard` in unlit mode (the frame+object bind groups are set by
|
||||
// draw_entity, the default frame matrix is the identity → NDC positions unchanged).
|
||||
let mut renderer = Renderer::new(&context, format, 800, 600, &ShadowConfig::default(), None, None);
|
||||
let mut renderer = Renderer::new(&context, format, 800, 600, &ShadowConfig::default(), None, None, None);
|
||||
renderer.set_unlit(true);
|
||||
|
||||
// 3. Material: uses renderer.device() and renderer.format()
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
//! **MSAA (Multi-Sample Anti-Aliasing)** — demonstrates 4× MSAA edge smoothing.
|
||||
//!
|
||||
//! Shows how MSAA eliminates the jagged "staircase" artifacts (aliasing) along
|
||||
//! sharp edges. The scene contains a cube (sharp edges), a sphere (curved surface),
|
||||
//! and a ground plane — all with high-contrast edges where aliasing is most visible.
|
||||
//!
|
||||
//! To compare with/without MSAA: remove the `.with_msaa(4)` line from the builder
|
||||
//! below and rebuild. The scene and lighting are identical — only the edge
|
||||
//! smoothness differs.
|
||||
//!
|
||||
//! ## Pipeline (MSAA + HDR)
|
||||
//! ```text
|
||||
//! Main pass → MSAA texture (4 samples, Rgba16Float)
|
||||
//! ↓ resolve (average 4 samples → 1)
|
||||
//! HDR texture (single sample)
|
||||
//! ↓
|
||||
//! Tone Mapping → surface
|
||||
//! ```
|
||||
//!
|
||||
//! ## Controls
|
||||
//! | Key | Action |
|
||||
//! |-----|--------|
|
||||
//! | Drag (LMB) | Orbit camera |
|
||||
//! | Wheel | Zoom |
|
||||
//! | `R` | Reset camera |
|
||||
//! | `M` | Toggle MSAA info (shows sample count) |
|
||||
//!
|
||||
//! ## Build & Run
|
||||
//! ```sh
|
||||
//! cargo run -p wsg-lib --example msaa
|
||||
//! ```
|
||||
|
||||
use glam::Vec3;
|
||||
use winit::event::MouseButton;
|
||||
use winit::keyboard::KeyCode;
|
||||
use wsg_lib::app::AppBuilder;
|
||||
use wsg_lib::camera::CameraController;
|
||||
use wsg_lib::core::{Transform, ToneMapper};
|
||||
use wsg_lib::mesh::{cube, icosphere, plane};
|
||||
use wsg_lib::AppHandler;
|
||||
use wsg_lib::utils::WsgError;
|
||||
|
||||
struct MsaaDemo {
|
||||
camera: CameraController,
|
||||
show_info: bool,
|
||||
}
|
||||
|
||||
impl AppHandler for MsaaDemo {
|
||||
fn setup(&mut self, app: &mut wsg_lib::App) {
|
||||
app.scene
|
||||
.register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH)
|
||||
.unwrap();
|
||||
|
||||
// Ground plane.
|
||||
app.scene
|
||||
.create_mesh("ground_mesh", plane(10.0, 10.0, 1, 1), None)
|
||||
.unwrap();
|
||||
app.scene.add_entity("ground", "ground_mesh").unwrap();
|
||||
|
||||
// Cube — sharp edges make aliasing very visible.
|
||||
app.scene
|
||||
.create_mesh("cube_mesh", cube(1.0), None)
|
||||
.unwrap();
|
||||
let mut cube_tf = Transform::identity();
|
||||
cube_tf.translation = Vec3::new(1.5, 0.5, 0.0);
|
||||
app.scene
|
||||
.add_entity_with_transform("cube", "cube_mesh", cube_tf)
|
||||
.unwrap();
|
||||
|
||||
// Sphere — curved surface, aliasing visible on the silhouette.
|
||||
app.scene
|
||||
.create_mesh("sphere_mesh", icosphere(0.6, 3), None)
|
||||
.unwrap();
|
||||
let mut sphere_tf = Transform::identity();
|
||||
sphere_tf.translation = Vec3::new(-1.5, 0.6, 0.0);
|
||||
app.scene
|
||||
.add_entity_with_transform("sphere", "sphere_mesh", sphere_tf)
|
||||
.unwrap();
|
||||
|
||||
// Small cube near the camera — very close edges, maximum aliasing.
|
||||
app.scene
|
||||
.create_mesh("small_cube_mesh", cube(0.3), None)
|
||||
.unwrap();
|
||||
let mut small_tf = Transform::identity();
|
||||
small_tf.translation = Vec3::new(0.0, 0.15, 1.5);
|
||||
app.scene
|
||||
.add_entity_with_transform("small_cube", "small_cube_mesh", small_tf)
|
||||
.unwrap();
|
||||
|
||||
// Directional light (strong, creates high-contrast edges).
|
||||
let light_dir = Vec3::new(0.5, 1.0, 0.3).normalize();
|
||||
app.scene
|
||||
.add_directional_light(light_dir, [1.0, 0.95, 0.85], 1.5)
|
||||
.unwrap();
|
||||
app.scene.set_ambient([0.08, 0.08, 0.1]);
|
||||
|
||||
// Camera.
|
||||
self.camera.yaw = 0.4;
|
||||
self.camera.pitch = 0.2;
|
||||
self.camera.distance = 4.0;
|
||||
self.camera.target = Vec3::new(0.0, 0.4, 0.0);
|
||||
self.camera.apply_to(app.scene.camera_mut());
|
||||
|
||||
// Print MSAA status.
|
||||
let sc = app.renderer().msaa_sample_count();
|
||||
eprintln!("[MSAA] sample_count = {} ({})", sc, if sc > 1 { "active" } else { "disabled" });
|
||||
}
|
||||
|
||||
fn update(&mut self, app: &mut wsg_lib::App) {
|
||||
// Orbit camera.
|
||||
let (dx, dy) = app.input.mouse_delta();
|
||||
if app.input.mouse_button_held(MouseButton::Left) {
|
||||
self.camera.orbit(dx, dy);
|
||||
}
|
||||
let (_, sy) = app.input.scroll_delta();
|
||||
self.camera.zoom(sy);
|
||||
|
||||
if app.input.key_pressed(KeyCode::KeyR) {
|
||||
self.camera.yaw = 0.4;
|
||||
self.camera.pitch = 0.2;
|
||||
self.camera.distance = 4.0;
|
||||
}
|
||||
self.camera.apply_to(app.scene.camera_mut());
|
||||
|
||||
// Toggle info display.
|
||||
if app.input.key_pressed(KeyCode::KeyM) {
|
||||
self.show_info = !self.show_info;
|
||||
let sc = app.renderer().msaa_sample_count();
|
||||
eprintln!("[MSAA] {}× {}", sc, if sc > 1 { "enabled" } else { "disabled (single sample)" });
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, app: &mut wsg_lib::App, frame: &wsg_lib::core::Frame) {
|
||||
app.render_scene(frame.view());
|
||||
}
|
||||
}
|
||||
|
||||
#[pollster::main]
|
||||
async fn main() -> Result<(), WsgError> {
|
||||
let app = AppBuilder::new()
|
||||
.title("WSG MSAA 4×")
|
||||
.size(960, 640)
|
||||
.with_msaa(4) // ← Enable 4× MSAA (remove for comparison)
|
||||
.with_hdr(ToneMapper::Aces) // MSAA works with or without HDR
|
||||
.build()
|
||||
.await?;
|
||||
app.run(MsaaDemo {
|
||||
camera: CameraController::default(),
|
||||
show_info: false,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user