Skip to content

Commit

Permalink
New Exposure and Lighting Defaults (and calibrate examples) (#11868)
Browse files Browse the repository at this point in the history
# Objective

After adding configurable exposure, we set the default ev100 value to
`7` (indoor). This brought us out of sync with Blender's configuration
and defaults. This PR changes the default to `9.7` (bright indoor or
very overcast outdoors), as I calibrated in #11577. This feels like a
very reasonable default.

The other changes generally center around tweaking Bevy's lighting
defaults and examples to play nicely with this number, alongside a few
other tweaks and improvements.

Note that for artistic reasons I have reverted some examples, which
changed to directional lights in #11581, back to point lights.
 
Fixes #11577 

---

## Changelog

- Changed `Exposure::ev100` from `7` to `9.7` to better match Blender
- Renamed `ExposureSettings` to `Exposure`
- `Camera3dBundle` now includes `Exposure` for discoverability
- Bumped `FULL_DAYLIGHT ` and `DIRECT_SUNLIGHT` to represent the
middle-to-top of those ranges instead of near the bottom
- Added new `AMBIENT_DAYLIGHT` constant and set that as the new
`DirectionalLight` default illuminance.
- `PointLight` and `SpotLight` now have a default `intensity` of
1,000,000 lumens. This makes them actually useful in the context of the
new "semi-outdoor" exposure and puts them in the "cinema lighting"
category instead of the "common household light" category. They are also
reasonably close to the Blender default.
- `AmbientLight` default has been bumped from `20` to `80`.

## Migration Guide

- The increased `Exposure::ev100` means that all existing 3D lighting
will need to be adjusted to match (DirectionalLights, PointLights,
SpotLights, EnvironmentMapLights, etc). Or alternatively, you can adjust
the `Exposure::ev100` on your cameras to work nicely with your current
lighting values. If you are currently relying on default intensity
values, you might need to change the intensity to achieve the same
effect. Note that in Bevy 0.12, point/spot lights had a different hard
coded ev100 value than directional lights. In Bevy 0.13, they use the
same ev100, so if you have both in your scene, the _scale_ between these
light types has changed and you will likely need to adjust one or both
of them.
  • Loading branch information
cart authored Feb 15, 2024
1 parent 1d5388e commit dd619a1
Show file tree
Hide file tree
Showing 52 changed files with 161 additions and 175 deletions.
8 changes: 5 additions & 3 deletions crates/bevy_core_pipeline/src/core_3d/camera_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::tonemapping::{DebandDither, Tonemapping};
use bevy_ecs::prelude::*;
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::{
camera::{Camera, CameraMainTextureUsages, CameraRenderGraph, Projection},
camera::{Camera, CameraMainTextureUsages, CameraRenderGraph, Exposure, Projection},
extract_component::ExtractComponent,
primitives::Frustum,
render_resource::{LoadOp, TextureUsages},
Expand Down Expand Up @@ -145,6 +145,7 @@ pub struct Camera3dBundle {
pub tonemapping: Tonemapping,
pub dither: DebandDither,
pub color_grading: ColorGrading,
pub exposure: Exposure,
pub main_texture_usages: CameraMainTextureUsages,
}

Expand All @@ -161,9 +162,10 @@ impl Default for Camera3dBundle {
global_transform: Default::default(),
camera_3d: Default::default(),
tonemapping: Default::default(),
dither: DebandDither::Enabled,
color_grading: ColorGrading::default(),
color_grading: Default::default(),
exposure: Default::default(),
main_texture_usages: Default::default(),
dither: DebandDither::Enabled,
}
}
}
12 changes: 5 additions & 7 deletions crates/bevy_core_pipeline/src/skybox/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use bevy_ecs::{
system::{Commands, Query, Res, ResMut, Resource},
};
use bevy_render::{
camera::ExposureSettings,
camera::Exposure,
extract_component::{
ComponentUniforms, DynamicUniformIndex, ExtractComponent, ExtractComponentPlugin,
UniformComponentPlugin,
Expand Down Expand Up @@ -80,16 +80,14 @@ pub struct Skybox {
}

impl ExtractComponent for Skybox {
type QueryData = (&'static Self, Option<&'static ExposureSettings>);
type QueryData = (&'static Self, Option<&'static Exposure>);
type QueryFilter = ();
type Out = (Self, SkyboxUniforms);

fn extract_component(
(skybox, exposure_settings): QueryItem<'_, Self::QueryData>,
) -> Option<Self::Out> {
let exposure = exposure_settings
fn extract_component((skybox, exposure): QueryItem<'_, Self::QueryData>) -> Option<Self::Out> {
let exposure = exposure
.map(|e| e.exposure())
.unwrap_or_else(|| ExposureSettings::default().exposure());
.unwrap_or_else(|| Exposure::default().exposure());

Some((
skybox.clone(),
Expand Down
20 changes: 14 additions & 6 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ pub mod light_consts {
pub const CLEAR_SUNRISE: f32 = 400.;
/// The amount of light (lux) on a overcast day; typical TV studio lighting
pub const OVERCAST_DAY: f32 = 1000.;
/// The amount of light (lux) from ambient daylight (not direct sunlight).
pub const AMBIENT_DAYLIGHT: f32 = 10_000.;
/// The amount of light (lux) in full daylight (not direct sun).
pub const FULL_DAYLIGHT: f32 = 10_000.;
pub const FULL_DAYLIGHT: f32 = 20_000.;
/// The amount of light (lux) in direct sunlight.
pub const DIRECT_SUNLIGHT: f32 = 50_000.;
pub const DIRECT_SUNLIGHT: f32 = 100_000.;
}
}

Expand Down Expand Up @@ -113,7 +115,10 @@ impl Default for PointLight {
fn default() -> Self {
PointLight {
color: Color::rgb(1.0, 1.0, 1.0),
intensity: 2000.0, // Roughly a 20-watt LED bulb
// 1,000,000 lumens is a very large "cinema light" capable of registering brightly at Bevy's
// default "very overcast day" exposure level. For "indoor lighting" with a lower exposure,
// this would be way too bright.
intensity: 1_000_000.0,
range: 20.0,
radius: 0.0,
shadows_enabled: false,
Expand Down Expand Up @@ -181,7 +186,10 @@ impl Default for SpotLight {
// a quarter arc attenuating from the center
Self {
color: Color::rgb(1.0, 1.0, 1.0),
intensity: 2000.0, // Roughly a 20-watt LED bulb
// 1,000,000 lumens is a very large "cinema light" capable of registering brightly at Bevy's
// default "very overcast day" exposure level. For "indoor lighting" with a lower exposure,
// this would be way too bright.
intensity: 1_000_000.0,
range: 20.0,
radius: 0.0,
shadows_enabled: false,
Expand Down Expand Up @@ -262,7 +270,7 @@ impl Default for DirectionalLight {
fn default() -> Self {
DirectionalLight {
color: Color::rgb(1.0, 1.0, 1.0),
illuminance: light_consts::lux::OVERCAST_DAY,
illuminance: light_consts::lux::AMBIENT_DAYLIGHT,
shadows_enabled: false,
shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
Expand Down Expand Up @@ -637,7 +645,7 @@ impl Default for AmbientLight {
fn default() -> Self {
Self {
color: Color::WHITE,
brightness: 20.0,
brightness: 80.0,
}
}
}
Expand Down
31 changes: 22 additions & 9 deletions crates/bevy_render/src/camera/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ pub struct ComputedCameraValues {
///
/// <https://en.wikipedia.org/wiki/Exposure_(photography)>
#[derive(Component)]
pub struct ExposureSettings {
pub struct Exposure {
/// <https://en.wikipedia.org/wiki/Exposure_value#Tabulated_exposure_values>
pub ev100: f32,
}

impl ExposureSettings {
impl Exposure {
pub const SUNLIGHT: Self = Self {
ev100: Self::EV100_SUNLIGHT,
};
Expand All @@ -105,11 +105,24 @@ impl ExposureSettings {
pub const INDOOR: Self = Self {
ev100: Self::EV100_INDOOR,
};
/// This value was calibrated to match Blender's implicit/default exposure as closely as possible.
/// It also happens to be a reasonable default.
///
/// See <https:/bevyengine/bevy/issues/11577> for details.
pub const BLENDER: Self = Self {
ev100: Self::EV100_BLENDER,
};

pub const EV100_SUNLIGHT: f32 = 15.0;
pub const EV100_OVERCAST: f32 = 12.0;
pub const EV100_INDOOR: f32 = 7.0;

/// This value was calibrated to match Blender's implicit/default exposure as closely as possible.
/// It also happens to be a reasonable default.
///
/// See <https:/bevyengine/bevy/issues/11577> for details.
pub const EV100_BLENDER: f32 = 9.7;

pub fn from_physical_camera(physical_camera_parameters: PhysicalCameraParameters) -> Self {
Self {
ev100: physical_camera_parameters.ev100(),
Expand All @@ -124,14 +137,14 @@ impl ExposureSettings {
}
}

impl Default for ExposureSettings {
impl Default for Exposure {
fn default() -> Self {
Self::INDOOR
Self::BLENDER
}
}

/// Parameters based on physical camera characteristics for calculating
/// EV100 values for use with [`ExposureSettings`].
/// EV100 values for use with [`Exposure`].
#[derive(Clone, Copy)]
pub struct PhysicalCameraParameters {
/// <https://en.wikipedia.org/wiki/F-number>
Expand Down Expand Up @@ -798,7 +811,7 @@ pub fn extract_cameras(
&VisibleEntities,
&Frustum,
Option<&ColorGrading>,
Option<&ExposureSettings>,
Option<&Exposure>,
Option<&TemporalJitter>,
Option<&RenderLayers>,
Option<&Projection>,
Expand All @@ -815,7 +828,7 @@ pub fn extract_cameras(
visible_entities,
frustum,
color_grading,
exposure_settings,
exposure,
temporal_jitter,
render_layers,
projection,
Expand Down Expand Up @@ -858,9 +871,9 @@ pub fn extract_cameras(
clear_color: camera.clear_color.clone(),
// this will be set in sort_cameras
sorted_camera_index_for_target: 0,
exposure: exposure_settings
exposure: exposure
.map(|e| e.exposure())
.unwrap_or_else(|| ExposureSettings::default().exposure()),
.unwrap_or_else(|| Exposure::default().exposure()),
},
ExtractedView {
projection: camera.projection_matrix(),
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_render/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub use window::*;

use crate::{
camera::{
CameraMainTextureUsages, ClearColor, ClearColorConfig, ExposureSettings, ExtractedCamera,
CameraMainTextureUsages, ClearColor, ClearColorConfig, Exposure, ExtractedCamera,
ManualTextureViews, MipBias, TemporalJitter,
},
extract_resource::{ExtractResource, ExtractResourcePlugin},
Expand Down Expand Up @@ -434,7 +434,7 @@ pub fn prepare_view_uniforms(
world_position: extracted_view.transform.translation(),
exposure: extracted_camera
.map(|c| c.exposure)
.unwrap_or_else(|| ExposureSettings::default().exposure()),
.unwrap_or_else(|| Exposure::default().exposure()),
viewport,
frustum,
color_grading: extracted_view.color_grading,
Expand Down
7 changes: 3 additions & 4 deletions examples/3d/3d_gizmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,12 @@ fn setup(
..default()
});
// light
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: light_consts::lux::OVERCAST_DAY,
commands.spawn(PointLightBundle {
point_light: PointLight {
shadows_enabled: true,
..default()
},
transform: Transform::from_xyz(4.0, 8.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
transform: Transform::from_xyz(4.0, 8.0, 4.0),
..default()
});

Expand Down
7 changes: 3 additions & 4 deletions examples/3d/3d_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,12 @@ fn setup(
..default()
});
// light
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: light_consts::lux::OVERCAST_DAY,
commands.spawn(PointLightBundle {
point_light: PointLight {
shadows_enabled: true,
..default()
},
transform: Transform::from_xyz(4.0, 8.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
transform: Transform::from_xyz(4.0, 8.0, 4.0),
..default()
});
// camera
Expand Down
9 changes: 5 additions & 4 deletions examples/3d/3d_shapes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ fn setup(
));
}

commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: light_consts::lux::OVERCAST_DAY,
commands.spawn(PointLightBundle {
point_light: PointLight {
shadows_enabled: true,
intensity: 10_000_000.,
range: 100.0,
..default()
},
transform: Transform::from_xyz(8.0, 16.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y),
transform: Transform::from_xyz(8.0, 16.0, 8.0),
..default()
});

Expand Down
1 change: 0 additions & 1 deletion examples/3d/3d_viewport_to_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ fn setup(
// light
commands.spawn(DirectionalLightBundle {
transform: Transform::from_translation(Vec3::ONE).looking_at(Vec3::ZERO, Vec3::Y),
directional_light: DirectionalLight::default(),
..default()
});

Expand Down
2 changes: 1 addition & 1 deletion examples/3d/animated_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,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_000.0,
intensity: 2_000.0,
},
));

Expand Down
2 changes: 1 addition & 1 deletion examples/3d/anti_aliasing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ fn setup(
// Light
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: light_consts::lux::OVERCAST_DAY,
illuminance: light_consts::lux::FULL_DAYLIGHT,
shadows_enabled: true,
..default()
},
Expand Down
1 change: 0 additions & 1 deletion examples/3d/atmospheric_fog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ fn setup_terrain_scene(
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
color: Color::rgb(0.98, 0.95, 0.82),
illuminance: light_consts::lux::OVERCAST_DAY,
shadows_enabled: true,
..default()
},
Expand Down
8 changes: 2 additions & 6 deletions examples/3d/blend_modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,8 @@ fn setup(
}

// Light
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: light_consts::lux::OVERCAST_DAY,
..default()
},
transform: Transform::from_xyz(4.0, 8.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
commands.spawn(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0),
..default()
});

Expand Down
6 changes: 3 additions & 3 deletions examples/3d/deferred_rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,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: 250.0,
intensity: 2000.0,
},
DepthPrepass,
MotionVectorPrepass,
Expand All @@ -68,7 +68,7 @@ fn setup(

commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: light_consts::lux::OVERCAST_DAY,
illuminance: 15_000.,
shadows_enabled: true,
..default()
},
Expand Down Expand Up @@ -140,7 +140,7 @@ fn setup(
// Light
commands.spawn(PointLightBundle {
point_light: PointLight {
intensity: 150.0,
intensity: 800.0,
radius: 0.125,
shadows_enabled: true,
color: sphere_color,
Expand Down
5 changes: 0 additions & 5 deletions examples/3d/fog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
use bevy::{
pbr::{NotShadowCaster, NotShadowReceiver},
prelude::*,
render::camera::ExposureSettings,
};

fn main() {
Expand All @@ -43,9 +42,6 @@ fn setup_camera_fog(mut commands: Commands) {
},
..default()
},
// This is a dark scene,
// increasing the exposure makes it easier to see
ExposureSettings { ev100: 4.0 },
));
}

Expand Down Expand Up @@ -119,7 +115,6 @@ fn setup_pyramid_scene(
commands.spawn(PointLightBundle {
transform: Transform::from_xyz(0.0, 1.0, 0.0),
point_light: PointLight {
intensity: 4_000.,
shadows_enabled: true,
..default()
},
Expand Down
6 changes: 1 addition & 5 deletions examples/3d/generate_custom_mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,7 @@ fn setup(
});

// Light up the scene.
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: light_consts::lux::OVERCAST_DAY,
..default()
},
commands.spawn(PointLightBundle {
transform: camera_and_light_transform,
..default()
});
Expand Down
Loading

0 comments on commit dd619a1

Please sign in to comment.