Files
wsg/lib/examples/msaa.rs
T
Jérôme Bousquié 9614156848 MSAA
2026-09-25 11:20:20 +02:00

152 lines
5.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! **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,
})
}