anti_aliasing/
anti_aliasing.rs

1//! Compares different anti-aliasing techniques supported by Bevy.
2
3use std::{f32::consts::PI, fmt::Write};
4
5use bevy::{
6    anti_alias::{
7        contrast_adaptive_sharpening::ContrastAdaptiveSharpening,
8        fxaa::{Fxaa, Sensitivity},
9        smaa::{Smaa, SmaaPreset},
10        taa::TemporalAntiAliasing,
11    },
12    asset::RenderAssetUsages,
13    core_pipeline::prepass::{DepthPrepass, MotionVectorPrepass},
14    image::{ImageSampler, ImageSamplerDescriptor},
15    light::CascadeShadowConfigBuilder,
16    prelude::*,
17    render::{
18        camera::{MipBias, TemporalJitter},
19        render_resource::{Extent3d, TextureDimension, TextureFormat},
20        view::Hdr,
21    },
22};
23
24#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
25use bevy::anti_alias::dlss::{
26    Dlss, DlssPerfQualityMode, DlssProjectId, DlssSuperResolutionSupported,
27};
28
29fn main() {
30    let mut app = App::new();
31
32    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
33    app.insert_resource(DlssProjectId(bevy_asset::uuid::uuid!(
34        "5417916c-0291-4e3f-8f65-326c1858ab96" // Don't copy paste this - generate your own UUID!
35    )));
36
37    app.add_plugins(DefaultPlugins)
38        .add_systems(Startup, setup)
39        .add_systems(
40            Update,
41            (modify_aa, modify_sharpening, modify_projection, update_ui),
42        );
43
44    app.run();
45}
46
47type TaaComponents = (
48    TemporalAntiAliasing,
49    TemporalJitter,
50    MipBias,
51    DepthPrepass,
52    MotionVectorPrepass,
53);
54
55#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
56type DlssComponents = (
57    Dlss,
58    TemporalJitter,
59    MipBias,
60    DepthPrepass,
61    MotionVectorPrepass,
62);
63#[cfg(any(not(feature = "dlss"), feature = "force_disable_dlss"))]
64type DlssComponents = ();
65
66fn modify_aa(
67    keys: Res<ButtonInput<KeyCode>>,
68    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] camera: Single<
69        (
70            Entity,
71            Option<&mut Fxaa>,
72            Option<&mut Smaa>,
73            Option<&TemporalAntiAliasing>,
74            &mut Msaa,
75            Option<&mut Dlss>,
76        ),
77        With<Camera>,
78    >,
79    #[cfg(any(not(feature = "dlss"), feature = "force_disable_dlss"))] camera: Single<
80        (
81            Entity,
82            Option<&mut Fxaa>,
83            Option<&mut Smaa>,
84            Option<&TemporalAntiAliasing>,
85            &mut Msaa,
86        ),
87        With<Camera>,
88    >,
89    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] dlss_supported: Option<
90        Res<DlssSuperResolutionSupported>,
91    >,
92    mut commands: Commands,
93) {
94    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
95    let (camera_entity, fxaa, smaa, taa, mut msaa, dlss) = camera.into_inner();
96    #[cfg(any(not(feature = "dlss"), feature = "force_disable_dlss"))]
97    let (camera_entity, fxaa, smaa, taa, mut msaa) = camera.into_inner();
98    let mut camera = commands.entity(camera_entity);
99
100    // No AA
101    if keys.just_pressed(KeyCode::Digit1) {
102        *msaa = Msaa::Off;
103        camera
104            .remove::<Fxaa>()
105            .remove::<Smaa>()
106            .remove::<TaaComponents>()
107            .remove::<DlssComponents>();
108    }
109
110    // MSAA
111    if keys.just_pressed(KeyCode::Digit2) && *msaa == Msaa::Off {
112        camera
113            .remove::<Fxaa>()
114            .remove::<Smaa>()
115            .remove::<TaaComponents>()
116            .remove::<DlssComponents>();
117
118        *msaa = Msaa::Sample4;
119    }
120
121    // MSAA Sample Count
122    if *msaa != Msaa::Off {
123        if keys.just_pressed(KeyCode::KeyQ) {
124            *msaa = Msaa::Sample2;
125        }
126        if keys.just_pressed(KeyCode::KeyW) {
127            *msaa = Msaa::Sample4;
128        }
129        if keys.just_pressed(KeyCode::KeyE) {
130            *msaa = Msaa::Sample8;
131        }
132    }
133
134    // FXAA
135    if keys.just_pressed(KeyCode::Digit3) && fxaa.is_none() {
136        *msaa = Msaa::Off;
137        camera
138            .remove::<Smaa>()
139            .remove::<TaaComponents>()
140            .remove::<DlssComponents>()
141            .insert(Fxaa::default());
142    }
143
144    // FXAA Settings
145    if let Some(mut fxaa) = fxaa {
146        if keys.just_pressed(KeyCode::KeyQ) {
147            fxaa.edge_threshold = Sensitivity::Low;
148            fxaa.edge_threshold_min = Sensitivity::Low;
149        }
150        if keys.just_pressed(KeyCode::KeyW) {
151            fxaa.edge_threshold = Sensitivity::Medium;
152            fxaa.edge_threshold_min = Sensitivity::Medium;
153        }
154        if keys.just_pressed(KeyCode::KeyE) {
155            fxaa.edge_threshold = Sensitivity::High;
156            fxaa.edge_threshold_min = Sensitivity::High;
157        }
158        if keys.just_pressed(KeyCode::KeyR) {
159            fxaa.edge_threshold = Sensitivity::Ultra;
160            fxaa.edge_threshold_min = Sensitivity::Ultra;
161        }
162        if keys.just_pressed(KeyCode::KeyT) {
163            fxaa.edge_threshold = Sensitivity::Extreme;
164            fxaa.edge_threshold_min = Sensitivity::Extreme;
165        }
166    }
167
168    // SMAA
169    if keys.just_pressed(KeyCode::Digit4) && smaa.is_none() {
170        *msaa = Msaa::Off;
171        camera
172            .remove::<Fxaa>()
173            .remove::<TaaComponents>()
174            .remove::<DlssComponents>()
175            .insert(Smaa::default());
176    }
177
178    // SMAA Settings
179    if let Some(mut smaa) = smaa {
180        if keys.just_pressed(KeyCode::KeyQ) {
181            smaa.preset = SmaaPreset::Low;
182        }
183        if keys.just_pressed(KeyCode::KeyW) {
184            smaa.preset = SmaaPreset::Medium;
185        }
186        if keys.just_pressed(KeyCode::KeyE) {
187            smaa.preset = SmaaPreset::High;
188        }
189        if keys.just_pressed(KeyCode::KeyR) {
190            smaa.preset = SmaaPreset::Ultra;
191        }
192    }
193
194    // TAA
195    if keys.just_pressed(KeyCode::Digit5) && taa.is_none() {
196        *msaa = Msaa::Off;
197        camera
198            .remove::<Fxaa>()
199            .remove::<Smaa>()
200            .remove::<DlssComponents>()
201            .insert(TemporalAntiAliasing::default());
202    }
203
204    // DLSS
205    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
206    if keys.just_pressed(KeyCode::Digit6) && dlss.is_none() && dlss_supported.is_some() {
207        *msaa = Msaa::Off;
208        camera
209            .remove::<Fxaa>()
210            .remove::<Smaa>()
211            .remove::<TaaComponents>()
212            .insert(Dlss::default());
213    }
214
215    // DLSS Settings
216    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
217    if let Some(mut dlss) = dlss {
218        if keys.just_pressed(KeyCode::KeyZ) {
219            dlss.perf_quality_mode = DlssPerfQualityMode::Auto;
220        }
221        if keys.just_pressed(KeyCode::KeyX) {
222            dlss.perf_quality_mode = DlssPerfQualityMode::UltraPerformance;
223        }
224        if keys.just_pressed(KeyCode::KeyC) {
225            dlss.perf_quality_mode = DlssPerfQualityMode::Performance;
226        }
227        if keys.just_pressed(KeyCode::KeyV) {
228            dlss.perf_quality_mode = DlssPerfQualityMode::Balanced;
229        }
230        if keys.just_pressed(KeyCode::KeyB) {
231            dlss.perf_quality_mode = DlssPerfQualityMode::Quality;
232        }
233        if keys.just_pressed(KeyCode::KeyN) {
234            dlss.perf_quality_mode = DlssPerfQualityMode::Dlaa;
235        }
236    }
237}
238
239fn modify_sharpening(
240    keys: Res<ButtonInput<KeyCode>>,
241    mut query: Query<&mut ContrastAdaptiveSharpening>,
242) {
243    for mut cas in &mut query {
244        if keys.just_pressed(KeyCode::Digit0) {
245            cas.enabled = !cas.enabled;
246        }
247        if cas.enabled {
248            if keys.just_pressed(KeyCode::Minus) {
249                cas.sharpening_strength -= 0.1;
250                cas.sharpening_strength = cas.sharpening_strength.clamp(0.0, 1.0);
251            }
252            if keys.just_pressed(KeyCode::Equal) {
253                cas.sharpening_strength += 0.1;
254                cas.sharpening_strength = cas.sharpening_strength.clamp(0.0, 1.0);
255            }
256            if keys.just_pressed(KeyCode::KeyD) {
257                cas.denoise = !cas.denoise;
258            }
259        }
260    }
261}
262
263fn modify_projection(keys: Res<ButtonInput<KeyCode>>, mut query: Query<&mut Projection>) {
264    for mut projection in &mut query {
265        if keys.just_pressed(KeyCode::KeyO) {
266            match *projection {
267                Projection::Perspective(_) => {
268                    *projection = Projection::Orthographic(OrthographicProjection {
269                        scale: 0.002,
270                        ..OrthographicProjection::default_3d()
271                    });
272                }
273                _ => {
274                    *projection = Projection::Perspective(PerspectiveProjection::default());
275                }
276            }
277        }
278    }
279}
280
281fn update_ui(
282    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] camera: Single<
283        (
284            &Projection,
285            Option<&Fxaa>,
286            Option<&Smaa>,
287            Option<&TemporalAntiAliasing>,
288            &ContrastAdaptiveSharpening,
289            &Msaa,
290            Option<&Dlss>,
291        ),
292        With<Camera>,
293    >,
294    #[cfg(any(not(feature = "dlss"), feature = "force_disable_dlss"))] camera: Single<
295        (
296            &Projection,
297            Option<&Fxaa>,
298            Option<&Smaa>,
299            Option<&TemporalAntiAliasing>,
300            &ContrastAdaptiveSharpening,
301            &Msaa,
302        ),
303        With<Camera>,
304    >,
305    mut ui: Single<&mut Text>,
306    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] dlss_supported: Option<
307        Res<DlssSuperResolutionSupported>,
308    >,
309) {
310    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
311    let (projection, fxaa, smaa, taa, cas, msaa, dlss) = *camera;
312    #[cfg(any(not(feature = "dlss"), feature = "force_disable_dlss"))]
313    let (projection, fxaa, smaa, taa, cas, msaa) = *camera;
314
315    let ui = &mut ui.0;
316    *ui = "Antialias Method\n".to_string();
317
318    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
319    let dlss_none = dlss.is_none();
320    #[cfg(any(not(feature = "dlss"), feature = "force_disable_dlss"))]
321    let dlss_none = true;
322
323    draw_selectable_menu_item(
324        ui,
325        "No AA",
326        '1',
327        *msaa == Msaa::Off && fxaa.is_none() && taa.is_none() && smaa.is_none() && dlss_none,
328    );
329    draw_selectable_menu_item(ui, "MSAA", '2', *msaa != Msaa::Off);
330    draw_selectable_menu_item(ui, "FXAA", '3', fxaa.is_some());
331    draw_selectable_menu_item(ui, "SMAA", '4', smaa.is_some());
332    draw_selectable_menu_item(ui, "TAA", '5', taa.is_some());
333    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
334    if dlss_supported.is_some() {
335        draw_selectable_menu_item(ui, "DLSS", '6', dlss.is_some());
336    }
337
338    if *msaa != Msaa::Off {
339        ui.push_str("\n----------\n\nSample Count\n");
340        draw_selectable_menu_item(ui, "2", 'Q', *msaa == Msaa::Sample2);
341        draw_selectable_menu_item(ui, "4", 'W', *msaa == Msaa::Sample4);
342        draw_selectable_menu_item(ui, "8", 'E', *msaa == Msaa::Sample8);
343    }
344
345    if let Some(fxaa) = fxaa {
346        ui.push_str("\n----------\n\nSensitivity\n");
347        draw_selectable_menu_item(ui, "Low", 'Q', fxaa.edge_threshold == Sensitivity::Low);
348        draw_selectable_menu_item(
349            ui,
350            "Medium",
351            'W',
352            fxaa.edge_threshold == Sensitivity::Medium,
353        );
354        draw_selectable_menu_item(ui, "High", 'E', fxaa.edge_threshold == Sensitivity::High);
355        draw_selectable_menu_item(ui, "Ultra", 'R', fxaa.edge_threshold == Sensitivity::Ultra);
356        draw_selectable_menu_item(
357            ui,
358            "Extreme",
359            'T',
360            fxaa.edge_threshold == Sensitivity::Extreme,
361        );
362    }
363
364    if let Some(smaa) = smaa {
365        ui.push_str("\n----------\n\nQuality\n");
366        draw_selectable_menu_item(ui, "Low", 'Q', smaa.preset == SmaaPreset::Low);
367        draw_selectable_menu_item(ui, "Medium", 'W', smaa.preset == SmaaPreset::Medium);
368        draw_selectable_menu_item(ui, "High", 'E', smaa.preset == SmaaPreset::High);
369        draw_selectable_menu_item(ui, "Ultra", 'R', smaa.preset == SmaaPreset::Ultra);
370    }
371
372    #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
373    if let Some(dlss) = dlss {
374        let pqm = dlss.perf_quality_mode;
375        ui.push_str("\n----------\n\nQuality\n");
376        draw_selectable_menu_item(ui, "Auto", 'Z', pqm == DlssPerfQualityMode::Auto);
377        draw_selectable_menu_item(
378            ui,
379            "UltraPerformance",
380            'X',
381            pqm == DlssPerfQualityMode::UltraPerformance,
382        );
383        draw_selectable_menu_item(
384            ui,
385            "Performance",
386            'C',
387            pqm == DlssPerfQualityMode::Performance,
388        );
389        draw_selectable_menu_item(ui, "Balanced", 'V', pqm == DlssPerfQualityMode::Balanced);
390        draw_selectable_menu_item(ui, "Quality", 'B', pqm == DlssPerfQualityMode::Quality);
391        draw_selectable_menu_item(ui, "DLAA", 'N', pqm == DlssPerfQualityMode::Dlaa);
392    }
393
394    ui.push_str("\n----------\n\n");
395    draw_selectable_menu_item(ui, "Sharpening", '0', cas.enabled);
396
397    if cas.enabled {
398        ui.push_str(&format!("(-/+) Strength: {:.1}\n", cas.sharpening_strength));
399        draw_selectable_menu_item(ui, "Denoising", 'D', cas.denoise);
400    }
401
402    ui.push_str("\n----------\n\n");
403    draw_selectable_menu_item(
404        ui,
405        "Orthographic",
406        'O',
407        matches!(projection, Projection::Orthographic(_)),
408    );
409}
410
411/// Set up a simple 3D scene
412fn setup(
413    mut commands: Commands,
414    mut meshes: ResMut<Assets<Mesh>>,
415    mut materials: ResMut<Assets<StandardMaterial>>,
416    mut images: ResMut<Assets<Image>>,
417    asset_server: Res<AssetServer>,
418) {
419    // Plane
420    commands.spawn((
421        Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))),
422        MeshMaterial3d(materials.add(Color::srgb(0.1, 0.2, 0.1))),
423    ));
424
425    let cube_material = materials.add(StandardMaterial {
426        base_color_texture: Some(images.add(uv_debug_texture())),
427        ..default()
428    });
429
430    // Cubes
431    for i in 0..5 {
432        commands.spawn((
433            Mesh3d(meshes.add(Cuboid::new(0.25, 0.25, 0.25))),
434            MeshMaterial3d(cube_material.clone()),
435            Transform::from_xyz(i as f32 * 0.25 - 1.0, 0.125, -i as f32 * 0.5),
436        ));
437    }
438
439    // Flight Helmet
440    commands.spawn(SceneRoot(asset_server.load(
441        GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
442    )));
443
444    // Light
445    commands.spawn((
446        DirectionalLight {
447            illuminance: light_consts::lux::FULL_DAYLIGHT,
448            shadows_enabled: true,
449            ..default()
450        },
451        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)),
452        CascadeShadowConfigBuilder {
453            maximum_distance: 3.0,
454            first_cascade_far_bound: 0.9,
455            ..default()
456        }
457        .build(),
458    ));
459
460    // Camera
461    commands.spawn((
462        Camera3d::default(),
463        Hdr,
464        Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
465        ContrastAdaptiveSharpening {
466            enabled: false,
467            ..default()
468        },
469        EnvironmentMapLight {
470            diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
471            specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
472            intensity: 150.0,
473            ..default()
474        },
475        DistanceFog {
476            color: Color::srgba_u8(43, 44, 47, 255),
477            falloff: FogFalloff::Linear {
478                start: 1.0,
479                end: 4.0,
480            },
481            ..default()
482        },
483    ));
484
485    // example instructions
486    commands.spawn((
487        Text::default(),
488        Node {
489            position_type: PositionType::Absolute,
490            top: px(12),
491            left: px(12),
492            ..default()
493        },
494    ));
495}
496
497/// Writes a simple menu item that can be on or off.
498fn draw_selectable_menu_item(ui: &mut String, label: &str, shortcut: char, enabled: bool) {
499    let star = if enabled { "*" } else { "" };
500    let _ = writeln!(*ui, "({shortcut}) {star}{label}{star}");
501}
502
503/// Creates a colorful test pattern
504fn uv_debug_texture() -> Image {
505    const TEXTURE_SIZE: usize = 8;
506
507    let mut palette: [u8; 32] = [
508        255, 102, 159, 255, 255, 159, 102, 255, 236, 255, 102, 255, 121, 255, 102, 255, 102, 255,
509        198, 255, 102, 198, 255, 255, 121, 102, 255, 255, 236, 102, 255, 255,
510    ];
511
512    let mut texture_data = [0; TEXTURE_SIZE * TEXTURE_SIZE * 4];
513    for y in 0..TEXTURE_SIZE {
514        let offset = TEXTURE_SIZE * y * 4;
515        texture_data[offset..(offset + TEXTURE_SIZE * 4)].copy_from_slice(&palette);
516        palette.rotate_right(4);
517    }
518
519    let mut img = Image::new_fill(
520        Extent3d {
521            width: TEXTURE_SIZE as u32,
522            height: TEXTURE_SIZE as u32,
523            depth_or_array_layers: 1,
524        },
525        TextureDimension::D2,
526        &texture_data,
527        TextureFormat::Rgba8UnormSrgb,
528        RenderAssetUsages::RENDER_WORLD,
529    );
530    img.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor::default());
531    img
532}