From 432c427f3249e0d41ed7f3a5749d09a4e7788e99 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sun, 16 Apr 2023 22:33:38 +0200 Subject: [PATCH 01/13] Implement basic exposure settings --- crates/bevy_pbr/src/render/light.rs | 14 +------- crates/bevy_pbr/src/render/pbr_functions.wgsl | 2 +- crates/bevy_render/src/camera/camera.rs | 36 +++++++++++++++++++ crates/bevy_render/src/view/mod.rs | 27 +++++++++----- crates/bevy_render/src/view/view.wgsl | 1 + 5 files changed, 57 insertions(+), 23 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index dc28bdfc0690b..f681ddf1778c3 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -854,18 +854,6 @@ pub fn prepare_lights( flags |= DirectionalLightFlags::SHADOWS_ENABLED; } - // convert from illuminance (lux) to candelas - // - // exposure is hard coded at the moment but should be replaced - // by values coming from the camera - // see: https://google.github.io/filament/Filament.html#imagingpipeline/physicallybasedcamera/exposuresettings - const APERTURE: f32 = 4.0; - const SHUTTER_SPEED: f32 = 1.0 / 250.0; - const SENSITIVITY: f32 = 100.0; - let ev100 = f32::log2(APERTURE * APERTURE / SHUTTER_SPEED) - f32::log2(SENSITIVITY / 100.0); - let exposure = 1.0 / (f32::powf(2.0, ev100) * 1.2); - let intensity = light.illuminance * exposure; - let num_cascades = light .cascade_shadow_config .bounds @@ -876,7 +864,7 @@ pub fn prepare_lights( cascades: [GpuDirectionalCascade::default(); MAX_CASCADES_PER_LIGHT], // premultiply color by intensity // we don't use the alpha at all, so no reason to multiply only [0..3] - color: Vec4::from_slice(&light.color.as_linear_rgba_f32()) * intensity, + color: Vec4::from_slice(&light.color.as_linear_rgba_f32()) * light.illuminance, // direction is negated to be ready for N.L dir_to_light: light.transform.back(), flags: flags.bits, diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index 1bc782fc19f3d..02474cd43fe8e 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -254,7 +254,7 @@ fn pbr( // Total light output_color = vec4( - direct_light + indirect_light + emissive_light, + view.exposure * (direct_light + indirect_light + emissive_light), output_color.a ); diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 851783a3f12b6..75d09a53983c0 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -76,6 +76,36 @@ pub struct ComputedCameraValues { old_viewport_size: Option, } +#[derive(Component)] +pub struct ExposureSettings { + pub aperture_f_stops: f32, + pub shutter_speed_s: f32, + pub sensitivity_iso: f32, +} + +impl Default for ExposureSettings { + fn default() -> Self { + Self { + aperture_f_stops: 4.0, + shutter_speed_s: 1.0 / 250.0, + sensitivity_iso: 100.0, + } + } +} + +impl ExposureSettings { + #[inline] + pub fn ev100(&self) -> f32 { + (self.aperture_f_stops * self.aperture_f_stops / self.shutter_speed_s).log2() + - (self.sensitivity_iso / 100.0).log2() + } + + #[inline] + pub fn exposure(&self) -> f32 { + 1.0 / (2.0f32.powf(self.ev100()) * 1.2) + } +} + /// The defining component for camera entities, storing information about how and what to render /// through this camera. /// @@ -573,6 +603,7 @@ pub struct ExtractedCamera { pub output_mode: CameraOutputMode, pub msaa_writeback: bool, pub sorted_camera_index_for_target: usize, + pub exposure: f32, } pub fn extract_cameras( @@ -585,6 +616,7 @@ pub fn extract_cameras( &GlobalTransform, &VisibleEntities, Option<&ColorGrading>, + Option<&ExposureSettings>, Option<&TemporalJitter>, )>, >, @@ -598,6 +630,7 @@ pub fn extract_cameras( transform, visible_entities, color_grading, + exposure_settings, temporal_jitter, ) in query.iter() { @@ -630,6 +663,9 @@ pub fn extract_cameras( msaa_writeback: camera.msaa_writeback, // this will be set in sort_cameras sorted_camera_index_for_target: 0, + exposure: exposure_settings + .map(|e| e.exposure()) + .unwrap_or_else(|| ExposureSettings::default().exposure()), }, ExtractedView { projection: camera.projection_matrix(), diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index c602dd7628f12..27663eb437262 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -6,7 +6,7 @@ pub use visibility::*; pub use window::*; use crate::{ - camera::{ExtractedCamera, TemporalJitter}, + camera::{ExposureSettings, ExtractedCamera, TemporalJitter}, extract_resource::{ExtractResource, ExtractResourcePlugin}, prelude::{Image, Shader}, render_asset::RenderAssets, @@ -168,6 +168,7 @@ pub struct ViewUniform { projection: Mat4, inverse_projection: Mat4, world_position: Vec3, + exposure: f32, // viewport(x_origin, y_origin, width, height) viewport: Vec4, color_grading: ColorGrading, @@ -313,13 +314,18 @@ pub fn prepare_view_uniforms( render_device: Res, render_queue: Res, mut view_uniforms: ResMut, - views: Query<(Entity, &ExtractedView, Option<&TemporalJitter>)>, + views: Query<( + Entity, + Option<&ExtractedCamera>, + &ExtractedView, + Option<&TemporalJitter>, + )>, ) { view_uniforms.uniforms.clear(); - for (entity, camera, temporal_jitter) in &views { - let viewport = camera.viewport.as_vec4(); - let unjittered_projection = camera.projection; + for (entity, extracted_camera, extracted_view, temporal_jitter) in &views { + let viewport = extracted_view.viewport.as_vec4(); + let unjittered_projection = extracted_view.projection; let mut projection = unjittered_projection; if let Some(temporal_jitter) = temporal_jitter { @@ -327,12 +333,12 @@ pub fn prepare_view_uniforms( } let inverse_projection = projection.inverse(); - let view = camera.transform.compute_matrix(); + let view = extracted_view.transform.compute_matrix(); let inverse_view = view.inverse(); let view_uniforms = ViewUniformOffset { offset: view_uniforms.uniforms.push(ViewUniform { - view_proj: camera + view_proj: extracted_view .view_projection .unwrap_or_else(|| projection * inverse_view), unjittered_view_proj: unjittered_projection * inverse_view, @@ -341,9 +347,12 @@ pub fn prepare_view_uniforms( inverse_view, projection, inverse_projection, - world_position: camera.transform.translation(), + world_position: extracted_view.transform.translation(), + exposure: extracted_camera + .map(|c| c.exposure) + .unwrap_or_else(|| ExposureSettings::default().exposure()), viewport, - color_grading: camera.color_grading, + color_grading: extracted_view.color_grading, }), }; diff --git a/crates/bevy_render/src/view/view.wgsl b/crates/bevy_render/src/view/view.wgsl index ce50833e66f80..92349d333762b 100644 --- a/crates/bevy_render/src/view/view.wgsl +++ b/crates/bevy_render/src/view/view.wgsl @@ -16,6 +16,7 @@ struct View { projection: mat4x4, inverse_projection: mat4x4, world_position: vec3, + exposure: f32, // viewport(x_origin, y_origin, width, height) viewport: vec4, color_grading: ColorGrading, From 8fa78a92e2aa5f434013baaffada965e70bdd2dd Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sun, 16 Apr 2023 22:34:05 +0200 Subject: [PATCH 02/13] WIP: Test exposure settings in lighting example --- examples/3d/lighting.rs | 103 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/examples/3d/lighting.rs b/examples/3d/lighting.rs index 4757f0e3dacb3..74381c83eace0 100644 --- a/examples/3d/lighting.rs +++ b/examples/3d/lighting.rs @@ -3,13 +3,13 @@ use std::f32::consts::PI; -use bevy::{pbr::CascadeShadowConfigBuilder, prelude::*}; +use bevy::{pbr::CascadeShadowConfigBuilder, prelude::*, render::camera::ExposureSettings}; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) - .add_systems(Update, (movement, animate_light_direction)) + .add_systems(Update, (update_exposure, movement, animate_light_direction)) .run(); } @@ -207,6 +207,7 @@ fn setup( // directional 'sun' light commands.spawn(DirectionalLightBundle { directional_light: DirectionalLight { + illuminance: 1000.0, shadows_enabled: true, ..default() }, @@ -227,11 +228,101 @@ fn setup( ..default() }); + let exposure_settings = ExposureSettings::default(); + let style = TextStyle { + font: asset_server.load("fonts/FiraMono-Medium.ttf"), + font_size: 18.0, + color: Color::WHITE, + }; + + commands.spawn( + TextBundle::from_sections(vec![ + TextSection::new( + format!("Aperture: f/{:.0}\n", exposure_settings.aperture_f_stops), + style.clone(), + ), + TextSection::new( + format!( + "Shutter speed: 1/{:.0}s\n", + 1.0 / exposure_settings.shutter_speed_s + ), + style.clone(), + ), + TextSection::new( + format!( + "Sensitivity: ISO {:.0}\n", + exposure_settings.sensitivity_iso + ), + style.clone(), + ), + TextSection::new("\n\n", style.clone()), + TextSection::new("Controls\n", style.clone()), + TextSection::new("---------------\n", style.clone()), + TextSection::new("1/2 - Decrease/Increase aperture\n", style.clone()), + TextSection::new("3/4 - Decrease/Increase shutter speed\n", style.clone()), + TextSection::new("5/6 - Decrease/Increase sensitivity\n", style), + ]) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(10.0), + left: Val::Px(10.0), + ..default() + }), + ); + // camera - commands.spawn(Camera3dBundle { - transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), - ..default() - }); + commands.spawn(( + Camera3dBundle { + transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }, + exposure_settings, + )); +} + +fn update_exposure( + key_input: Res>, + mut query: Query<&mut ExposureSettings>, + mut text: Query<&mut Text>, +) { + let mut text = text.single_mut(); + let mut exposure_settings = query.single_mut(); + if key_input.just_pressed(KeyCode::Key2) { + exposure_settings.aperture_f_stops *= 2.0; + text.sections[0].value = format!("Aperture: f/{:.0}\n", exposure_settings.aperture_f_stops); + } else if key_input.just_pressed(KeyCode::Key1) { + exposure_settings.aperture_f_stops *= 0.5; + text.sections[0].value = format!("Aperture: f/{:.0}\n", exposure_settings.aperture_f_stops); + } + if key_input.just_pressed(KeyCode::Key4) { + exposure_settings.shutter_speed_s *= 2.0; + text.sections[1].value = format!( + "Shutter speed: 1/{:.0}s\n", + 1.0 / exposure_settings.shutter_speed_s + ); + } else if key_input.just_pressed(KeyCode::Key3) { + exposure_settings.shutter_speed_s *= 0.5; + text.sections[1].value = format!( + "Shutter speed: 1/{:.0}s\n", + 1.0 / exposure_settings.shutter_speed_s + ); + } + if key_input.just_pressed(KeyCode::Key6) { + exposure_settings.sensitivity_iso += 100.0; + text.sections[2].value = format!( + "Sensitivity: ISO {:.0}\n", + exposure_settings.sensitivity_iso + ); + } else if key_input.just_pressed(KeyCode::Key5) { + exposure_settings.sensitivity_iso -= 100.0; + text.sections[2].value = format!( + "Sensitivity: ISO {:.0}\n", + exposure_settings.sensitivity_iso + ); + } + if key_input.just_pressed(KeyCode::D) { + *exposure_settings = ExposureSettings::default(); + } } fn animate_light_direction( From 0cfbfd7a30ff6e16b425725fb5c1f3b6a7938a00 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 21 Aug 2023 21:18:38 -0700 Subject: [PATCH 03/13] Rebase fixes --- crates/bevy_pbr/src/render/pbr_functions.wgsl | 2 +- crates/bevy_render/src/view/mod.rs | 5 ++-- examples/3d/lighting.rs | 30 +++++-------------- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index 216719080d993..69d3f49ffbf20 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -272,7 +272,7 @@ fn pbr( // Total light output_color = vec4( - view.exposure * (direct_light + indirect_light + emissive_light), + view_bindings::view.exposure * (direct_light + indirect_light + emissive_light), output_color.a ); diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 3900422399ff8..ce7a87d8ce3d2 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -359,6 +359,7 @@ pub fn prepare_view_uniforms( Option<&ExtractedCamera>, &ExtractedView, Option<&TemporalJitter>, + Option<&MipBias>, )>, ) { view_uniforms.uniforms.clear(); @@ -386,9 +387,7 @@ pub fn prepare_view_uniforms( let view_uniforms = ViewUniformOffset { offset: view_uniforms.uniforms.push(ViewUniform { - view_proj: extracted_view - .view_projection - .unwrap_or_else(|| projection * inverse_view), + view_proj, unjittered_view_proj: unjittered_projection * inverse_view, inverse_view_proj: view * inverse_projection, view, diff --git a/examples/3d/lighting.rs b/examples/3d/lighting.rs index 06c5f962d09ef..ea739b53f66dd 100644 --- a/examples/3d/lighting.rs +++ b/examples/3d/lighting.rs @@ -23,23 +23,6 @@ fn setup( mut materials: ResMut>, asset_server: Res, ) { - // example instructions - commands.spawn( - TextBundle::from_section( - "Use arrow keys to move objects", - TextStyle { - font_size: 20.0, - ..default() - }, - ) - .with_style(Style { - position_type: PositionType::Absolute, - top: Val::Px(12.0), - left: Val::Px(12.0), - ..default() - }), - ); - // ground plane commands.spawn(PbrBundle { mesh: meshes.add(shape::Plane::from_size(10.0).into()), @@ -246,12 +229,12 @@ fn setup( }); let exposure_settings = ExposureSettings::default(); + + // example instructions let style = TextStyle { - font: asset_server.load("fonts/FiraMono-Medium.ttf"), - font_size: 18.0, - color: Color::WHITE, + font_size: 20.0, + ..default() }; - commands.spawn( TextBundle::from_sections(vec![ TextSection::new( @@ -275,14 +258,15 @@ fn setup( TextSection::new("\n\n", style.clone()), TextSection::new("Controls\n", style.clone()), TextSection::new("---------------\n", style.clone()), + TextSection::new("Arrow keys - Move objects\n", style.clone()), TextSection::new("1/2 - Decrease/Increase aperture\n", style.clone()), TextSection::new("3/4 - Decrease/Increase shutter speed\n", style.clone()), TextSection::new("5/6 - Decrease/Increase sensitivity\n", style), ]) .with_style(Style { position_type: PositionType::Absolute, - top: Val::Px(10.0), - left: Val::Px(10.0), + top: Val::Px(12.0), + left: Val::Px(12.0), ..default() }), ); From ef62d30d9200f3cd95def90caece718900b9e10b Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 21 Aug 2023 21:47:39 -0700 Subject: [PATCH 04/13] Switch ExposureSettings to hold an EV100 value, improve example. --- crates/bevy_render/src/camera/camera.rs | 50 ++++++++++++----- examples/3d/lighting.rs | 73 ++++++++++++------------- 2 files changed, 71 insertions(+), 52 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index bfa2e2376eed1..175aa9d5fd553 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -75,14 +75,49 @@ pub struct ComputedCameraValues { old_viewport_size: Option, } +/// How much energy a [`Camera3d`] absorbs from incoming light. +/// +/// #[derive(Component)] pub struct ExposureSettings { + /// + pub ev100: f32, +} + +impl ExposureSettings { + pub fn from_physical_camera(physical_camera_parameters: PhysicalCameraParameters) -> Self { + let p = physical_camera_parameters; + Self { + ev100: (p.aperture_f_stops * p.aperture_f_stops / p.shutter_speed_s).log2() + - (p.sensitivity_iso / 100.0).log2(), + } + } + + /// TODO: Docs + #[inline] + pub fn exposure(&self) -> f32 { + 1.0 / (2.0f32.powf(self.ev100) * 1.2) + } +} + +impl Default for ExposureSettings { + fn default() -> Self { + Self::from_physical_camera(PhysicalCameraParameters::default()) + } +} + +/// TODO: Docs (and double check these links / find better links) +#[derive(Clone, Copy)] +pub struct PhysicalCameraParameters { + /// pub aperture_f_stops: f32, + /// pub shutter_speed_s: f32, + /// pub sensitivity_iso: f32, } -impl Default for ExposureSettings { +impl Default for PhysicalCameraParameters { fn default() -> Self { Self { aperture_f_stops: 4.0, @@ -92,19 +127,6 @@ impl Default for ExposureSettings { } } -impl ExposureSettings { - #[inline] - pub fn ev100(&self) -> f32 { - (self.aperture_f_stops * self.aperture_f_stops / self.shutter_speed_s).log2() - - (self.sensitivity_iso / 100.0).log2() - } - - #[inline] - pub fn exposure(&self) -> f32 { - 1.0 / (2.0f32.powf(self.ev100()) * 1.2) - } -} - /// The defining [`Component`] for camera entities, /// storing information about how and what to render through this camera. /// diff --git a/examples/3d/lighting.rs b/examples/3d/lighting.rs index ea739b53f66dd..f402a08770203 100644 --- a/examples/3d/lighting.rs +++ b/examples/3d/lighting.rs @@ -3,21 +3,30 @@ use std::f32::consts::PI; -use bevy::{pbr::CascadeShadowConfigBuilder, prelude::*, render::camera::ExposureSettings}; +use bevy::{ + pbr::CascadeShadowConfigBuilder, + prelude::*, + render::camera::{ExposureSettings, PhysicalCameraParameters}, +}; fn main() { App::new() .add_plugins(DefaultPlugins) + .init_resource::() .add_systems(Startup, setup) .add_systems(Update, (update_exposure, movement, animate_light_direction)) .run(); } +#[derive(Resource, Default, Deref, DerefMut)] +struct Parameters(PhysicalCameraParameters); + #[derive(Component)] struct Movable; /// set up a simple 3D scene fn setup( + parameters: Res, mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, @@ -228,8 +237,6 @@ fn setup( ..default() }); - let exposure_settings = ExposureSettings::default(); - // example instructions let style = TextStyle { font_size: 20.0, @@ -238,21 +245,18 @@ fn setup( commands.spawn( TextBundle::from_sections(vec![ TextSection::new( - format!("Aperture: f/{:.0}\n", exposure_settings.aperture_f_stops), + format!("Aperture: f/{:.0}\n", parameters.aperture_f_stops), style.clone(), ), TextSection::new( format!( "Shutter speed: 1/{:.0}s\n", - 1.0 / exposure_settings.shutter_speed_s + 1.0 / parameters.shutter_speed_s ), style.clone(), ), TextSection::new( - format!( - "Sensitivity: ISO {:.0}\n", - exposure_settings.sensitivity_iso - ), + format!("Sensitivity: ISO {:.0}\n", parameters.sensitivity_iso), style.clone(), ), TextSection::new("\n\n", style.clone()), @@ -261,7 +265,8 @@ fn setup( TextSection::new("Arrow keys - Move objects\n", style.clone()), TextSection::new("1/2 - Decrease/Increase aperture\n", style.clone()), TextSection::new("3/4 - Decrease/Increase shutter speed\n", style.clone()), - TextSection::new("5/6 - Decrease/Increase sensitivity\n", style), + TextSection::new("5/6 - Decrease/Increase sensitivity\n", style.clone()), + TextSection::new("R - Reset exposure", style), ]) .with_style(Style { position_type: PositionType::Absolute, @@ -277,53 +282,45 @@ fn setup( transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), ..default() }, - exposure_settings, + ExposureSettings::from_physical_camera(**parameters), )); } fn update_exposure( key_input: Res>, + mut parameters: ResMut, mut query: Query<&mut ExposureSettings>, mut text: Query<&mut Text>, ) { + // TODO: Clamp values to a reasonable range let mut text = text.single_mut(); - let mut exposure_settings = query.single_mut(); if key_input.just_pressed(KeyCode::Key2) { - exposure_settings.aperture_f_stops *= 2.0; - text.sections[0].value = format!("Aperture: f/{:.0}\n", exposure_settings.aperture_f_stops); + parameters.aperture_f_stops *= 2.0; } else if key_input.just_pressed(KeyCode::Key1) { - exposure_settings.aperture_f_stops *= 0.5; - text.sections[0].value = format!("Aperture: f/{:.0}\n", exposure_settings.aperture_f_stops); + parameters.aperture_f_stops *= 0.5; } if key_input.just_pressed(KeyCode::Key4) { - exposure_settings.shutter_speed_s *= 2.0; - text.sections[1].value = format!( - "Shutter speed: 1/{:.0}s\n", - 1.0 / exposure_settings.shutter_speed_s - ); + parameters.shutter_speed_s *= 2.0; } else if key_input.just_pressed(KeyCode::Key3) { - exposure_settings.shutter_speed_s *= 0.5; - text.sections[1].value = format!( - "Shutter speed: 1/{:.0}s\n", - 1.0 / exposure_settings.shutter_speed_s - ); + parameters.shutter_speed_s *= 0.5; } if key_input.just_pressed(KeyCode::Key6) { - exposure_settings.sensitivity_iso += 100.0; - text.sections[2].value = format!( - "Sensitivity: ISO {:.0}\n", - exposure_settings.sensitivity_iso - ); + parameters.sensitivity_iso += 100.0; } else if key_input.just_pressed(KeyCode::Key5) { - exposure_settings.sensitivity_iso -= 100.0; - text.sections[2].value = format!( - "Sensitivity: ISO {:.0}\n", - exposure_settings.sensitivity_iso - ); + parameters.sensitivity_iso -= 100.0; } - if key_input.just_pressed(KeyCode::D) { - *exposure_settings = ExposureSettings::default(); + if key_input.just_pressed(KeyCode::R) { + *parameters = Parameters::default(); } + + text.sections[0].value = format!("Aperture: f/{:.0}\n", parameters.aperture_f_stops); + text.sections[1].value = format!( + "Shutter speed: 1/{:.0}s\n", + 1.0 / parameters.shutter_speed_s + ); + text.sections[2].value = format!("Sensitivity: ISO {:.0}\n", parameters.sensitivity_iso); + + *query.single_mut() = ExposureSettings::from_physical_camera(**parameters); } fn animate_light_direction( From f5c4166c4c025dd24ea737244d76739b398d1a55 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 21 Aug 2023 21:51:27 -0700 Subject: [PATCH 05/13] Misc --- crates/bevy_pbr/src/render/light.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 2d0d980e7522c..fb53f0bd13767 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -865,6 +865,7 @@ pub fn prepare_lights( gpu_directional_lights[index] = GpuDirectionalLight { // Filled in later. cascades: [GpuDirectionalCascade::default(); MAX_CASCADES_PER_LIGHT], + // TODO: Is the below comment still valid? // premultiply color by intensity // we don't use the alpha at all, so no reason to multiply only [0..3] color: Vec4::from_slice(&light.color.as_linear_rgba_f32()) * light.illuminance, From ce69ab2363fd2fa72f744c6b4cbf59ffc96b5c9c Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 22 Aug 2023 19:16:35 -0700 Subject: [PATCH 06/13] Apply PR feedback --- crates/bevy_render/src/camera/camera.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 175aa9d5fd553..0fdf9af27b883 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -85,11 +85,13 @@ pub struct ExposureSettings { } impl ExposureSettings { + pub const EV100_SUNLIGHT: f32 = 15.0; + pub const EV100_OVERCAST: f32 = 12.0; + pub const EV100_INDOOR: f32 = 7.0; + pub fn from_physical_camera(physical_camera_parameters: PhysicalCameraParameters) -> Self { - let p = physical_camera_parameters; Self { - ev100: (p.aperture_f_stops * p.aperture_f_stops / p.shutter_speed_s).log2() - - (p.sensitivity_iso / 100.0).log2(), + ev100: physical_camera_parameters.ev100(), } } @@ -102,7 +104,9 @@ impl ExposureSettings { impl Default for ExposureSettings { fn default() -> Self { - Self::from_physical_camera(PhysicalCameraParameters::default()) + Self { + ev100: Self::EV100_OVERCAST, + } } } @@ -117,6 +121,14 @@ pub struct PhysicalCameraParameters { pub sensitivity_iso: f32, } +impl PhysicalCameraParameters { + /// Calculate the [EV100](https://en.wikipedia.org/wiki/Exposure_value). + pub fn ev100(&self) -> f32 { + (self.aperture_f_stops * self.aperture_f_stops / self.shutter_speed_s).log2() + - (self.sensitivity_iso / 100.0).log2() + } +} + impl Default for PhysicalCameraParameters { fn default() -> Self { Self { From e1448158155a4c379ddb3f94b544dc83a94913ec Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 22 Aug 2023 19:38:10 -0700 Subject: [PATCH 07/13] Add brightness control to Skybox --- .../src/core_3d/main_opaque_pass_3d_node.rs | 8 +- crates/bevy_core_pipeline/src/skybox/mod.rs | 73 ++++++++++++++++--- .../bevy_core_pipeline/src/skybox/skybox.wgsl | 4 +- examples/3d/skybox.rs | 7 +- 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index 34d8c299c94c9..db951d2cd5556 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -116,13 +116,17 @@ impl ViewNode for MainOpaquePass3dNode { } // Draw the skybox using a fullscreen triangle - if let (Some(skybox_pipeline), Some(skybox_bind_group)) = + if let (Some(skybox_pipeline), Some(SkyboxBindGroup(skybox_bind_group))) = (skybox_pipeline, skybox_bind_group) { let pipeline_cache = world.resource::(); if let Some(pipeline) = pipeline_cache.get_render_pipeline(skybox_pipeline.0) { render_pass.set_render_pipeline(pipeline); - render_pass.set_bind_group(0, &skybox_bind_group.0, &[view_uniform_offset.offset]); + render_pass.set_bind_group( + 0, + &skybox_bind_group.0, + &[view_uniform_offset.offset, skybox_bind_group.1], + ); render_pass.draw(0..3, 0..1); } } diff --git a/crates/bevy_core_pipeline/src/skybox/mod.rs b/crates/bevy_core_pipeline/src/skybox/mod.rs index ccf21334aa10a..f3cdf9b024f10 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -2,13 +2,16 @@ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Handle, HandleUntyped}; use bevy_ecs::{ prelude::{Component, Entity}, - query::With, + query::{QueryItem, With}, schedule::IntoSystemConfigs, system::{Commands, Query, Res, ResMut, Resource}, }; use bevy_reflect::TypeUuid; use bevy_render::{ - extract_component::{ExtractComponent, ExtractComponentPlugin}, + extract_component::{ + ComponentUniforms, DynamicUniformIndex, ExtractComponent, ExtractComponentPlugin, + UniformComponentPlugin, + }, render_asset::RenderAssets, render_resource::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, @@ -34,7 +37,10 @@ impl Plugin for SkyboxPlugin { fn build(&self, app: &mut App) { load_internal_asset!(app, SKYBOX_SHADER_HANDLE, "skybox.wgsl", Shader::from_wgsl); - app.add_plugins(ExtractComponentPlugin::::default()); + app.add_plugins(( + ExtractComponentPlugin::::default(), + UniformComponentPlugin::::default(), + )); let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, @@ -70,8 +76,32 @@ impl Plugin for SkyboxPlugin { /// To do so, use `EnvironmentMapLight` alongside this component. /// /// See also . -#[derive(Component, ExtractComponent, Clone)] -pub struct Skybox(pub Handle); +#[derive(Component, Clone)] +pub struct Skybox { + pub image: Handle, + /// TODO: Docs + pub brightness: f32, +} + +impl ExtractComponent for Skybox { + type Query = &'static Self; + type Filter = (); + type Out = (Self, SkyboxUniforms); + + fn extract_component(item: QueryItem<'_, Self::Query>) -> Option { + Some(( + item.clone(), + SkyboxUniforms { + brightness: item.brightness, + }, + )) + } +} + +#[derive(Component, ShaderType, Clone)] +pub struct SkyboxUniforms { + brightness: f32, +} #[derive(Resource)] struct SkyboxPipeline { @@ -109,6 +139,16 @@ impl SkyboxPipeline { }, count: None, }, + BindGroupLayoutEntry { + binding: 3, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(SkyboxUniforms::min_size()), + }, + count: None, + }, ], }; @@ -207,20 +247,23 @@ fn prepare_skybox_pipelines( } #[derive(Component)] -pub struct SkyboxBindGroup(pub BindGroup); +pub struct SkyboxBindGroup(pub (BindGroup, u32)); fn queue_skybox_bind_groups( mut commands: Commands, pipeline: Res, view_uniforms: Res, + skybox_uniforms: Res>, images: Res>, render_device: Res, - views: Query<(Entity, &Skybox)>, + views: Query<(Entity, &Skybox, &DynamicUniformIndex)>, ) { - for (entity, skybox) in &views { - if let (Some(skybox), Some(view_uniforms)) = - (images.get(&skybox.0), view_uniforms.uniforms.binding()) - { + for (entity, skybox, skybox_uniform_index) in &views { + if let (Some(skybox), Some(view_uniforms), Some(skybox_uniforms)) = ( + images.get(&skybox.image), + view_uniforms.uniforms.binding(), + skybox_uniforms.binding(), + ) { let bind_group = render_device.create_bind_group(&BindGroupDescriptor { label: Some("skybox_bind_group"), layout: &pipeline.bind_group_layout, @@ -237,10 +280,16 @@ fn queue_skybox_bind_groups( binding: 2, resource: view_uniforms, }, + BindGroupEntry { + binding: 3, + resource: skybox_uniforms, + }, ], }); - commands.entity(entity).insert(SkyboxBindGroup(bind_group)); + commands + .entity(entity) + .insert(SkyboxBindGroup((bind_group, skybox_uniform_index.index()))); } } } diff --git a/crates/bevy_core_pipeline/src/skybox/skybox.wgsl b/crates/bevy_core_pipeline/src/skybox/skybox.wgsl index 5f31b753072ea..15034c38cf388 100644 --- a/crates/bevy_core_pipeline/src/skybox/skybox.wgsl +++ b/crates/bevy_core_pipeline/src/skybox/skybox.wgsl @@ -6,6 +6,8 @@ var skybox: texture_cube; var skybox_sampler: sampler; @group(0) @binding(2) var view: View; +@group(0) @binding(3) +var brightness: f32; struct VertexOutput { @builtin(position) clip_position: vec4, @@ -48,5 +50,5 @@ fn skybox_fragment(in: VertexOutput) -> @location(0) vec4 { // position, to the fragment world position on the near clipping plane let ray_direction = in.world_position - view.world_position; // cube maps are left-handed so we negate the z coordinate - return textureSample(skybox, skybox_sampler, ray_direction * vec3(1.0, 1.0, -1.0)); + return textureSample(skybox, skybox_sampler, ray_direction * vec3(1.0, 1.0, -1.0)) * brightness; } diff --git a/examples/3d/skybox.rs b/examples/3d/skybox.rs index 7cc6b91c4b5f6..cb65ece5a2b8a 100644 --- a/examples/3d/skybox.rs +++ b/examples/3d/skybox.rs @@ -76,7 +76,10 @@ fn setup(mut commands: Commands, asset_server: Res) { ..default() }, CameraController::default(), - Skybox(skybox_handle.clone()), + Skybox { + image: skybox_handle.clone(), + brightness: 1.0, + }, )); // ambient light @@ -159,7 +162,7 @@ fn asset_loaded( } for mut skybox in &mut skyboxes { - skybox.0 = cubemap.image_handle.clone(); + skybox.image = cubemap.image_handle.clone(); } cubemap.is_loaded = true; From 56bb844b6fd89b415375a49ed3a8d1bde0e47e69 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 22 Aug 2023 19:47:01 -0700 Subject: [PATCH 08/13] Apply exposure to skybox --- crates/bevy_core_pipeline/src/skybox/mod.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/bevy_core_pipeline/src/skybox/mod.rs b/crates/bevy_core_pipeline/src/skybox/mod.rs index f3cdf9b024f10..5b6f403396e68 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -8,6 +8,7 @@ use bevy_ecs::{ }; use bevy_reflect::TypeUuid; use bevy_render::{ + camera::ExposureSettings, extract_component::{ ComponentUniforms, DynamicUniformIndex, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin, @@ -84,15 +85,21 @@ pub struct Skybox { } impl ExtractComponent for Skybox { - type Query = &'static Self; + type Query = (&'static Self, Option<&'static ExposureSettings>); type Filter = (); type Out = (Self, SkyboxUniforms); - fn extract_component(item: QueryItem<'_, Self::Query>) -> Option { + fn extract_component( + (skybox, exposure_settings): QueryItem<'_, Self::Query>, + ) -> Option { + let exposure = exposure_settings + .map(|e| e.exposure()) + .unwrap_or_else(|| ExposureSettings::default().exposure()); + Some(( - item.clone(), + skybox.clone(), SkyboxUniforms { - brightness: item.brightness, + brightness: skybox.brightness * exposure, }, )) } From 4dd2ede25e29cbe35c1b3dcff68cfa0349c1eb3c Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 22 Aug 2023 19:52:56 -0700 Subject: [PATCH 09/13] Add EnvironmentMapLight::brightness --- crates/bevy_pbr/src/environment_map/environment_map.wgsl | 4 ++-- crates/bevy_pbr/src/environment_map/mod.rs | 3 +++ crates/bevy_pbr/src/render/light.rs | 4 ++++ crates/bevy_pbr/src/render/mesh_view_types.wgsl | 1 + examples/3d/pbr.rs | 1 + 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/bevy_pbr/src/environment_map/environment_map.wgsl b/crates/bevy_pbr/src/environment_map/environment_map.wgsl index ed10e2d0ba894..95cabaa5f9c7a 100644 --- a/crates/bevy_pbr/src/environment_map/environment_map.wgsl +++ b/crates/bevy_pbr/src/environment_map/environment_map.wgsl @@ -39,7 +39,7 @@ fn environment_map_light( let kD = diffuse_color * Edss; var out: EnvironmentMapLight; - out.diffuse = (FmsEms + kD) * irradiance; - out.specular = FssEss * radiance; + out.diffuse = (FmsEms + kD) * irradiance * bindings::lights.environment_map_brightness; + out.specular = FssEss * radiance * bindings::lights.environment_map_brightness; return out; } diff --git a/crates/bevy_pbr/src/environment_map/mod.rs b/crates/bevy_pbr/src/environment_map/mod.rs index 7e6e8732028eb..6faa02de9d57a 100644 --- a/crates/bevy_pbr/src/environment_map/mod.rs +++ b/crates/bevy_pbr/src/environment_map/mod.rs @@ -50,6 +50,9 @@ impl Plugin for EnvironmentMapPlugin { pub struct EnvironmentMapLight { pub diffuse_map: Handle, pub specular_map: Handle, + /// TODO: Docs + // TODO: What to call this? + pub brightness: f32, } impl EnvironmentMapLight { diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index fb53f0bd13767..1d6996ba92746 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -212,6 +212,7 @@ pub struct GpuLights { // offset from spot light's light index to spot light's shadow map index spot_light_shadowmap_offset: i32, environment_map_smallest_specular_mip_level: u32, + environment_map_brightness: f32, } // NOTE: this must be kept in sync with the same constants in pbr.frag @@ -960,6 +961,9 @@ pub fn prepare_lights( .and_then(|env_map| images.get(&env_map.specular_map)) .map(|specular_map| specular_map.mip_level_count - 1) .unwrap_or(0), + environment_map_brightness: environment_map + .map(|env_map| env_map.brightness) + .unwrap_or(1.0), }; // TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query diff --git a/crates/bevy_pbr/src/render/mesh_view_types.wgsl b/crates/bevy_pbr/src/render/mesh_view_types.wgsl index b944aa2792e2f..9c2164750a774 100644 --- a/crates/bevy_pbr/src/render/mesh_view_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_types.wgsl @@ -60,6 +60,7 @@ struct Lights { n_directional_lights: u32, spot_light_shadowmap_offset: i32, environment_map_smallest_specular_mip_level: u32, + environment_map_brightness: f32, }; struct Fog { diff --git a/examples/3d/pbr.rs b/examples/3d/pbr.rs index 2bbc62a5edc51..3e70789e3771c 100644 --- a/examples/3d/pbr.rs +++ b/examples/3d/pbr.rs @@ -140,6 +140,7 @@ fn setup( 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"), + brightness: 1.0, }, )); } From 64bcfa8dbd9bc58a4547a2d09aa3a0fc174b8531 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 22 Aug 2023 19:56:02 -0700 Subject: [PATCH 10/13] Add misc todo --- crates/bevy_core_pipeline/src/skybox/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_core_pipeline/src/skybox/mod.rs b/crates/bevy_core_pipeline/src/skybox/mod.rs index 5b6f403396e68..0c2c5576b8f83 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -105,6 +105,7 @@ impl ExtractComponent for Skybox { } } +// TODO: Replace with a push constant once WebGPU gets support for that #[derive(Component, ShaderType, Clone)] pub struct SkyboxUniforms { brightness: f32, From f9e07308324b26f04942067b3719e4ec04c5bb82 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 22 Aug 2023 20:22:47 -0700 Subject: [PATCH 11/13] Misc rename, example fixes --- .../bevy_pbr/src/environment_map/environment_map.wgsl | 10 +++++----- crates/bevy_pbr/src/environment_map/mod.rs | 3 +-- crates/bevy_pbr/src/render/light.rs | 6 +++--- crates/bevy_pbr/src/render/mesh_view_types.wgsl | 2 +- examples/3d/load_gltf.rs | 1 + examples/3d/pbr.rs | 2 +- examples/3d/tonemapping.rs | 1 + examples/tools/scene_viewer/main.rs | 1 + 8 files changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/bevy_pbr/src/environment_map/environment_map.wgsl b/crates/bevy_pbr/src/environment_map/environment_map.wgsl index 95cabaa5f9c7a..e55e7ca3920ea 100644 --- a/crates/bevy_pbr/src/environment_map/environment_map.wgsl +++ b/crates/bevy_pbr/src/environment_map/environment_map.wgsl @@ -21,9 +21,9 @@ fn environment_map_light( // Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf // Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform // because textureNumLevels() does not work on WebGL2 - let radiance_level = perceptual_roughness * f32(bindings::lights.environment_map_smallest_specular_mip_level); - let irradiance = textureSample(bindings::environment_map_diffuse, bindings::environment_map_sampler, vec3(N.xy, -N.z)).rgb; - let radiance = textureSampleLevel(bindings::environment_map_specular, bindings::environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb; + let radiance_level = perceptual_roughness * f32(bindings,:: lights.environment_map_smallest_specular_mip_level); + let irradiance = textureSample(bindings,:: environment_map_diffuse, bindings,:: environment_map_sampler, vec3(N.xy, -N.z)).rgb; + let radiance = textureSampleLevel(bindings,:: environment_map_specular, bindings,:: environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb; // Multiscattering approximation: https://www.jcgt.org/published/0008/01/03/paper.pdf // Useful reference: https://bruop.github.io/ibl @@ -39,7 +39,7 @@ fn environment_map_light( let kD = diffuse_color * Edss; var out: EnvironmentMapLight; - out.diffuse = (FmsEms + kD) * irradiance * bindings::lights.environment_map_brightness; - out.specular = FssEss * radiance * bindings::lights.environment_map_brightness; + out.diffuse = (FmsEms + kD) * irradiance * bindings::lights.environment_map_intensity; + out.specular = FssEss * radiance * bindings::lights.environment_map_intensity; return out; } diff --git a/crates/bevy_pbr/src/environment_map/mod.rs b/crates/bevy_pbr/src/environment_map/mod.rs index 6faa02de9d57a..e691017e67aeb 100644 --- a/crates/bevy_pbr/src/environment_map/mod.rs +++ b/crates/bevy_pbr/src/environment_map/mod.rs @@ -51,8 +51,7 @@ pub struct EnvironmentMapLight { pub diffuse_map: Handle, pub specular_map: Handle, /// TODO: Docs - // TODO: What to call this? - pub brightness: f32, + pub intensity: f32, } impl EnvironmentMapLight { diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 1d6996ba92746..7ad233438123c 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -212,7 +212,7 @@ pub struct GpuLights { // offset from spot light's light index to spot light's shadow map index spot_light_shadowmap_offset: i32, environment_map_smallest_specular_mip_level: u32, - environment_map_brightness: f32, + environment_map_intensity: f32, } // NOTE: this must be kept in sync with the same constants in pbr.frag @@ -961,8 +961,8 @@ pub fn prepare_lights( .and_then(|env_map| images.get(&env_map.specular_map)) .map(|specular_map| specular_map.mip_level_count - 1) .unwrap_or(0), - environment_map_brightness: environment_map - .map(|env_map| env_map.brightness) + environment_map_intensity: environment_map + .map(|env_map| env_map.intensity) .unwrap_or(1.0), }; diff --git a/crates/bevy_pbr/src/render/mesh_view_types.wgsl b/crates/bevy_pbr/src/render/mesh_view_types.wgsl index 9c2164750a774..fe6fef99cb6a2 100644 --- a/crates/bevy_pbr/src/render/mesh_view_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_types.wgsl @@ -60,7 +60,7 @@ struct Lights { n_directional_lights: u32, spot_light_shadowmap_offset: i32, environment_map_smallest_specular_mip_level: u32, - environment_map_brightness: f32, + environment_map_intensity: f32, }; struct Fog { diff --git a/examples/3d/load_gltf.rs b/examples/3d/load_gltf.rs index 33b1559907f0f..0b85e5a190ad9 100644 --- a/examples/3d/load_gltf.rs +++ b/examples/3d/load_gltf.rs @@ -30,6 +30,7 @@ fn setup(mut commands: Commands, asset_server: Res) { 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: 1.0, }, )); diff --git a/examples/3d/pbr.rs b/examples/3d/pbr.rs index 3e70789e3771c..7334d65a90597 100644 --- a/examples/3d/pbr.rs +++ b/examples/3d/pbr.rs @@ -140,7 +140,7 @@ fn setup( 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"), - brightness: 1.0, + intensity: 1.0, }, )); } diff --git a/examples/3d/tonemapping.rs b/examples/3d/tonemapping.rs index d2f87b079a257..4d316eb61729f 100644 --- a/examples/3d/tonemapping.rs +++ b/examples/3d/tonemapping.rs @@ -69,6 +69,7 @@ fn setup( 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: 1.0, }, )); diff --git a/examples/tools/scene_viewer/main.rs b/examples/tools/scene_viewer/main.rs index 69df243382648..f1be1ac372d7e 100644 --- a/examples/tools/scene_viewer/main.rs +++ b/examples/tools/scene_viewer/main.rs @@ -143,6 +143,7 @@ fn setup_scene_after_load( .load("assets/environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), specular_map: asset_server .load("assets/environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), + intensity: 1.0, }, camera_controller, )); From c42824e3e01fe7c222c737301f47e0b5f76618ed Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:54:21 -0700 Subject: [PATCH 12/13] Docs --- crates/bevy_core_pipeline/src/skybox/mod.rs | 4 +++- crates/bevy_pbr/src/environment_map/mod.rs | 4 +++- crates/bevy_pbr/src/render/light.rs | 3 +-- crates/bevy_render/src/camera/camera.rs | 5 +++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/bevy_core_pipeline/src/skybox/mod.rs b/crates/bevy_core_pipeline/src/skybox/mod.rs index 0c2c5576b8f83..acb123017fab2 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -80,7 +80,9 @@ impl Plugin for SkyboxPlugin { #[derive(Component, Clone)] pub struct Skybox { pub image: Handle, - /// TODO: Docs + /// Scale factor applied to the skybox image. + /// After applying this multiplier to the image samples, the resulting values should + /// be in units of [cd/m^2](https://en.wikipedia.org/wiki/Candela_per_square_metre). pub brightness: f32, } diff --git a/crates/bevy_pbr/src/environment_map/mod.rs b/crates/bevy_pbr/src/environment_map/mod.rs index e691017e67aeb..953b30344d8cc 100644 --- a/crates/bevy_pbr/src/environment_map/mod.rs +++ b/crates/bevy_pbr/src/environment_map/mod.rs @@ -50,7 +50,9 @@ impl Plugin for EnvironmentMapPlugin { pub struct EnvironmentMapLight { pub diffuse_map: Handle, pub specular_map: Handle, - /// TODO: Docs + /// Scale factor applied to both the diffuse and specular cubemap. + /// After applying this multiplier to the image samples, the resulting values should + /// be in units of [cd/m^2](https://en.wikipedia.org/wiki/Candela_per_square_metre). pub intensity: f32, } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 7ad233438123c..32d56b8fba1ac 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -866,8 +866,7 @@ pub fn prepare_lights( gpu_directional_lights[index] = GpuDirectionalLight { // Filled in later. cascades: [GpuDirectionalCascade::default(); MAX_CASCADES_PER_LIGHT], - // TODO: Is the below comment still valid? - // premultiply color by intensity + // premultiply color by illuminance // we don't use the alpha at all, so no reason to multiply only [0..3] color: Vec4::from_slice(&light.color.as_linear_rgba_f32()) * light.illuminance, // direction is negated to be ready for N.L diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 0fdf9af27b883..ba5c2cd5f30cb 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -95,7 +95,7 @@ impl ExposureSettings { } } - /// TODO: Docs + /// Converts EV100 values to exposure values. #[inline] pub fn exposure(&self) -> f32 { 1.0 / (2.0f32.powf(self.ev100) * 1.2) @@ -110,7 +110,8 @@ impl Default for ExposureSettings { } } -/// TODO: Docs (and double check these links / find better links) +/// Parameters based on physical camera characteristics for calculating +/// EV100 values for use with [`ExposureSettings`]. #[derive(Clone, Copy)] pub struct PhysicalCameraParameters { /// From 71860bbac40606a91512ef6a87790b6c44da29b9 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sat, 26 Aug 2023 14:00:40 -0700 Subject: [PATCH 13/13] Fix shader --- crates/bevy_pbr/src/environment_map/environment_map.wgsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/environment_map/environment_map.wgsl b/crates/bevy_pbr/src/environment_map/environment_map.wgsl index e55e7ca3920ea..7b5f88d609937 100644 --- a/crates/bevy_pbr/src/environment_map/environment_map.wgsl +++ b/crates/bevy_pbr/src/environment_map/environment_map.wgsl @@ -21,9 +21,9 @@ fn environment_map_light( // Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf // Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform // because textureNumLevels() does not work on WebGL2 - let radiance_level = perceptual_roughness * f32(bindings,:: lights.environment_map_smallest_specular_mip_level); - let irradiance = textureSample(bindings,:: environment_map_diffuse, bindings,:: environment_map_sampler, vec3(N.xy, -N.z)).rgb; - let radiance = textureSampleLevel(bindings,:: environment_map_specular, bindings,:: environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb; + let radiance_level = perceptual_roughness * f32(bindings::lights.environment_map_smallest_specular_mip_level); + let irradiance = textureSample(bindings::environment_map_diffuse, bindings::environment_map_sampler, vec3(N.xy, -N.z)).rgb; + let radiance = textureSampleLevel(bindings::environment_map_specular, bindings::environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb; // Multiscattering approximation: https://www.jcgt.org/published/0008/01/03/paper.pdf // Useful reference: https://bruop.github.io/ibl