//! This examples compares Tonemapping options use bevy::{ core_pipeline::tonemapping::Tonemapping, pbr::CascadeShadowConfigBuilder, prelude::*, reflect::TypePath, render::{ render_resource::{AsBindGroup, ShaderRef}, view::{ColorGrading, ColorGradingGlobal, ColorGradingSection}, }, utils::HashMap, }; use std::f32::consts::PI; /// This example uses a shader source file from the assets subdirectory const SHADER_ASSET_PATH: &str = "shaders/tonemapping_test_patterns.wgsl"; fn main() { App::new() .add_plugins(( DefaultPlugins, MaterialPlugin::::default(), )) .insert_resource(CameraTransform( Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), )) .init_resource::() .insert_resource(CurrentScene(1)) .insert_resource(SelectedParameter { value: 0, max: 4 }) .add_systems( Startup, ( setup, setup_basic_scene, setup_color_gradient_scene, setup_image_viewer_scene, ), ) .add_systems( Update, ( drag_drop_image, resize_image, toggle_scene, toggle_tonemapping_method, update_color_grading_settings, update_ui, ), ) .run(); } fn setup( mut commands: Commands, asset_server: Res, camera_transform: Res, ) { // camera commands.spawn(( Camera3d::default(), Camera { hdr: true, ..default() }, camera_transform.0, DistanceFog { color: Color::srgb_u8(43, 44, 47), falloff: FogFalloff::Linear { start: 1.0, end: 8.0, }, ..default() }, EnvironmentMapLight { diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), intensity: 2000.0, ..default() }, )); // ui commands.spawn(( Text::default(), Node { position_type: PositionType::Absolute, top: Val::Px(12.0), left: Val::Px(12.0), ..default() }, )); } fn setup_basic_scene(mut commands: Commands, asset_server: Res) { // Main scene commands.spawn(( SceneRoot(asset_server.load( GltfAssetLabel::Scene(0).from_asset("models/TonemappingTest/TonemappingTest.gltf"), )), SceneNumber(1), )); // Flight Helmet commands.spawn(( SceneRoot( asset_server .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")), ), Transform::from_xyz(0.5, 0.0, -0.5).with_rotation(Quat::from_rotation_y(-0.15 * PI)), SceneNumber(1), )); // light commands.spawn(( DirectionalLight { illuminance: 15_000., shadows_enabled: true, ..default() }, Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)), CascadeShadowConfigBuilder { maximum_distance: 3.0, first_cascade_far_bound: 0.9, ..default() } .build(), SceneNumber(1), )); } fn setup_color_gradient_scene( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, camera_transform: Res, ) { let mut transform = camera_transform.0; transform.translation += *transform.forward(); commands.spawn(( Mesh3d(meshes.add(Rectangle::new(0.7, 0.7))), MeshMaterial3d(materials.add(ColorGradientMaterial {})), transform, Visibility::Hidden, SceneNumber(2), )); } fn setup_image_viewer_scene( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, camera_transform: Res, ) { let mut transform = camera_transform.0; transform.translation += *transform.forward(); // exr/hdr viewer (exr requires enabling bevy feature) commands.spawn(( Mesh3d(meshes.add(Rectangle::default())), MeshMaterial3d(materials.add(StandardMaterial { base_color_texture: None, unlit: true, ..default() })), transform, Visibility::Hidden, SceneNumber(3), HDRViewer, )); commands.spawn(( Text::new("Drag and drop an HDR or EXR file"), TextFont { font_size: 36.0, ..default() }, TextColor(Color::BLACK), TextLayout::new_with_justify(JustifyText::Center), Node { align_self: AlignSelf::Center, margin: UiRect::all(Val::Auto), ..default() }, SceneNumber(3), Visibility::Hidden, )); } // ---------------------------------------------------------------------------- fn drag_drop_image( image_mat: Query<&MeshMaterial3d, With>, text: Query, With)>, mut materials: ResMut>, mut drop_events: EventReader, asset_server: Res, mut commands: Commands, ) { let Some(new_image) = drop_events.read().find_map(|e| match e { FileDragAndDrop::DroppedFile { path_buf, .. } => { Some(asset_server.load(path_buf.to_string_lossy().to_string())) } _ => None, }) else { return; }; for mat_h in &image_mat { if let Some(mat) = materials.get_mut(mat_h) { mat.base_color_texture = Some(new_image.clone()); // Despawn the image viewer instructions if let Ok(text_entity) = text.get_single() { commands.entity(text_entity).despawn(); } } } } fn resize_image( image_mesh: Query<(&MeshMaterial3d, &Mesh3d), With>, materials: Res>, mut meshes: ResMut>, images: Res>, mut image_events: EventReader>, ) { for event in image_events.read() { let (AssetEvent::Added { id } | AssetEvent::Modified { id }) = event else { continue; }; for (mat_h, mesh_h) in &image_mesh { let Some(mat) = materials.get(mat_h) else { continue; }; let Some(ref base_color_texture) = mat.base_color_texture else { continue; }; if *id != base_color_texture.id() { continue; }; let Some(image_changed) = images.get(*id) else { continue; }; let size = image_changed.size_f32().normalize_or_zero() * 1.4; // Resize Mesh let quad = Mesh::from(Rectangle::from_size(size)); meshes.insert(mesh_h, quad); } } } fn toggle_scene( keys: Res>, mut query: Query<(&mut Visibility, &SceneNumber)>, mut current_scene: ResMut, ) { let mut pressed = None; if keys.just_pressed(KeyCode::KeyQ) { pressed = Some(1); } else if keys.just_pressed(KeyCode::KeyW) { pressed = Some(2); } else if keys.just_pressed(KeyCode::KeyE) { pressed = Some(3); } if let Some(pressed) = pressed { current_scene.0 = pressed; for (mut visibility, scene) in query.iter_mut() { if scene.0 == pressed { *visibility = Visibility::Visible; } else { *visibility = Visibility::Hidden; } } } } fn toggle_tonemapping_method( keys: Res>, mut tonemapping: Single<&mut Tonemapping>, mut color_grading: Single<&mut ColorGrading>, per_method_settings: Res, ) { if keys.just_pressed(KeyCode::Digit1) { **tonemapping = Tonemapping::None; } else if keys.just_pressed(KeyCode::Digit2) { **tonemapping = Tonemapping::Reinhard; } else if keys.just_pressed(KeyCode::Digit3) { **tonemapping = Tonemapping::ReinhardLuminance; } else if keys.just_pressed(KeyCode::Digit4) { **tonemapping = Tonemapping::AcesFitted; } else if keys.just_pressed(KeyCode::Digit5) { **tonemapping = Tonemapping::AgX; } else if keys.just_pressed(KeyCode::Digit6) { **tonemapping = Tonemapping::SomewhatBoringDisplayTransform; } else if keys.just_pressed(KeyCode::Digit7) { **tonemapping = Tonemapping::TonyMcMapface; } else if keys.just_pressed(KeyCode::Digit8) { **tonemapping = Tonemapping::BlenderFilmic; } **color_grading = (*per_method_settings .settings .get::(&tonemapping) .as_ref() .unwrap()) .clone(); } #[derive(Resource)] struct SelectedParameter { value: i32, max: i32, } impl SelectedParameter { fn next(&mut self) { self.value = (self.value + 1).rem_euclid(self.max); } fn prev(&mut self) { self.value = (self.value - 1).rem_euclid(self.max); } } fn update_color_grading_settings( keys: Res>, time: Res