This commit is contained in:
Jérôme Bousquié
2026-09-25 11:20:20 +02:00
parent 35aeb769a8
commit 9614156848
15 changed files with 822 additions and 333 deletions
+32 -1
View File
@@ -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`,
+2 -2
View File
@@ -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()
+151
View File
@@ -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,
})
}