//! **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, }) }