Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exposure settings (adopted) #11347

Merged
merged 59 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
432c427
Implement basic exposure settings
superdump Apr 16, 2023
8fa78a9
WIP: Test exposure settings in lighting example
superdump Apr 16, 2023
cbeed57
Merge commit 'af0323a55e9cbfe28262fd500fbe21f8cf4c8ee4' into exposure…
JMS55 Aug 22, 2023
0cfbfd7
Rebase fixes
JMS55 Aug 22, 2023
ef62d30
Switch ExposureSettings to hold an EV100 value, improve example.
JMS55 Aug 22, 2023
f5c4166
Misc
JMS55 Aug 22, 2023
ce69ab2
Apply PR feedback
JMS55 Aug 23, 2023
e144815
Add brightness control to Skybox
JMS55 Aug 23, 2023
56bb844
Apply exposure to skybox
JMS55 Aug 23, 2023
4dd2ede
Add EnvironmentMapLight::brightness
JMS55 Aug 23, 2023
64bcfa8
Add misc todo
JMS55 Aug 23, 2023
f9e0730
Misc rename, example fixes
JMS55 Aug 23, 2023
c42824e
Docs
JMS55 Aug 23, 2023
71860bb
Fix shader
JMS55 Aug 26, 2023
7dc7966
Merge commit '02025eff0b25bb8c18103dbafc33a764e1989ad4' into exposure…
JMS55 Sep 1, 2023
f78a0af
Merge pull request #32 from JMS55/exposure_jms
superdump Sep 1, 2023
f0ee3d6
Merge commit '35073cf7aa9eaf748e167c103921a6764fcedfb1' into exposure…
JMS55 Oct 14, 2023
7544e2f
Merge commit 'f0ee3d6aa53b6e03229dccee44eeadc1bf03a351' into exposure…
JMS55 Oct 14, 2023
0e37bed
Merge commit 'ca1874e6c612d73306173065242aaaa8733c9b20' into exposure…
JMS55 Dec 15, 2023
e325da3
Rebase fixes
JMS55 Dec 15, 2023
ac2c148
Input -> ButtonInput
GitGhillie Dec 16, 2023
b1c0ddd
Change default exposure to indoor
GitGhillie Dec 16, 2023
540ab66
Adjust a few examples
GitGhillie Dec 16, 2023
d04ad9a
Adjust PhysicalCameraParameters to an exposure of roughly 7 as well
GitGhillie Dec 16, 2023
dc61b42
adjust lighting example by changing the initial exposure
GitGhillie Dec 16, 2023
479223a
increase point light intensity in lighting example
GitGhillie Dec 17, 2023
6cf0423
Adjust default ambient light
GitGhillie Dec 18, 2023
f67e704
Adjust 3d examples
GitGhillie Dec 18, 2023
2d2f95d
Adjust some more 3d examples
GitGhillie Dec 19, 2023
81e7c25
Adjust some more 3d examples part 3
GitGhillie Dec 20, 2023
418ec05
Adjust some more 3d examples part 4
GitGhillie Dec 25, 2023
ec1ddfb
Adjust some more 3d examples part 5
GitGhillie Dec 26, 2023
a7e0831
Adjust some more 3d examples part 6
GitGhillie Dec 26, 2023
ad4fe6d
Merge commit '8067e46049f222d37ac394745805bad98979980f' into exposure…
JMS55 Dec 28, 2023
e969d5e
Compensate for exposure after fetching transmission background color
coreh Dec 29, 2023
f743dce
Adjust transmission example
coreh Dec 29, 2023
874ea8c
Remove `ColorGrading` exposure as a workaround for `EnvironmentMapLig…
coreh Dec 29, 2023
0d4006e
Also tweak look of emissive flame in transmission example
coreh Dec 29, 2023
edfb721
Adjust some more 3d examples part 7
GitGhillie Dec 30, 2023
404a98d
Merge pull request #1 from coreh/exposure_jms
GitGhillie Dec 31, 2023
5f92b44
Adjust some more 3d examples part 8
GitGhillie Jan 4, 2024
9968173
Adjust some more 3d examples part 9 (fin)
GitGhillie Jan 4, 2024
bbd6532
fmt
GitGhillie Jan 4, 2024
a6d9076
Take exposure into account for directional light influence in fog
coreh Jan 6, 2024
c276a80
Merge pull request #2 from coreh/exposure-fog-fix
GitGhillie Jan 6, 2024
012d33e
Merge commit 'c276a80e3df0ca13bf38154f3d6b2667b96af7c1' into exposure…
JMS55 Jan 14, 2024
da12108
Merge commit '8a523de8db28bfdc5b2a4c67c2022cc957075de6' into exposure…
JMS55 Jan 14, 2024
e00f0ad
Fix doc
JMS55 Jan 14, 2024
5cd197b
Fix example keycodes
JMS55 Jan 14, 2024
d892d5d
Update crates/bevy_render/src/camera/camera.rs
JMS55 Jan 14, 2024
07ccccd
Format
JMS55 Jan 14, 2024
24c9953
Update crates/bevy_render/src/camera/camera.rs
JMS55 Jan 15, 2024
2a52b6a
Update crates/bevy_render/src/camera/camera.rs
JMS55 Jan 15, 2024
fab6f87
Improve doc comment
JMS55 Jan 15, 2024
fd91b58
Fix lightmaps example
atlv24 Jan 16, 2024
cc11b23
Merge pull request #20 from rodolphito/exposure
JMS55 Jan 16, 2024
e0f3e9c
more fixing ...
atlv24 Jan 16, 2024
55444b4
Fix fix fix
atlv24 Jan 16, 2024
a7b22be
Merge pull request #21 from rodolphito/exposure2
JMS55 Jan 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,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::<PipelineCache>();
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);
}
}
Expand Down
78 changes: 60 additions & 18 deletions crates/bevy_core_pipeline/src/skybox/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, Handle};
use bevy_ecs::{
prelude::{Component, Entity},
query::With,
query::{QueryItem, With},
schedule::IntoSystemConfigs,
system::{Commands, Query, Res, ResMut, Resource},
};
use bevy_render::{
extract_component::{ExtractComponent, ExtractComponentPlugin},
camera::ExposureSettings,
extract_component::{
ComponentUniforms, DynamicUniformIndex, ExtractComponent, ExtractComponentPlugin,
UniformComponentPlugin,
},
render_asset::RenderAssets,
render_resource::{
binding_types::{sampler, texture_cube, uniform_buffer},
BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutEntries,
CachedRenderPipelineId, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState,
DepthStencilState, FragmentState, MultisampleState, PipelineCache, PrimitiveState,
RenderPipelineDescriptor, SamplerBindingType, Shader, ShaderStages,
SpecializedRenderPipeline, SpecializedRenderPipelines, StencilFaceState, StencilState,
TextureFormat, TextureSampleType, VertexState,
*,
},
renderer::RenderDevice,
texture::{BevyDefault, Image},
Expand All @@ -34,7 +33,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::<Skybox>::default());
app.add_plugins((
ExtractComponentPlugin::<Skybox>::default(),
UniformComponentPlugin::<SkyboxUniforms>::default(),
));

let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
Expand Down Expand Up @@ -68,8 +70,41 @@ impl Plugin for SkyboxPlugin {
/// To do so, use `EnvironmentMapLight` alongside this component.
///
/// See also <https://en.wikipedia.org/wiki/Skybox_(video_games)>.
#[derive(Component, ExtractComponent, Clone)]
pub struct Skybox(pub Handle<Image>);
#[derive(Component, Clone)]
pub struct Skybox {
pub image: Handle<Image>,
/// 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,
}

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

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

Some((
skybox.clone(),
SkyboxUniforms {
brightness: skybox.brightness * exposure,
},
))
}
}

// TODO: Replace with a push constant once WebGPU gets support for that
#[derive(Component, ShaderType, Clone)]
pub struct SkyboxUniforms {
brightness: f32,
}

#[derive(Resource)]
struct SkyboxPipeline {
Expand All @@ -88,6 +123,7 @@ impl SkyboxPipeline {
sampler(SamplerBindingType::Filtering),
uniform_buffer::<ViewUniform>(true)
.visibility(ShaderStages::VERTEX_FRAGMENT),
uniform_buffer::<SkyboxUniforms>(true),
),
),
),
Expand Down Expand Up @@ -186,31 +222,37 @@ fn prepare_skybox_pipelines(
}

#[derive(Component)]
pub struct SkyboxBindGroup(pub BindGroup);
pub struct SkyboxBindGroup(pub (BindGroup, u32));

fn prepare_skybox_bind_groups(
mut commands: Commands,
pipeline: Res<SkyboxPipeline>,
view_uniforms: Res<ViewUniforms>,
skybox_uniforms: Res<ComponentUniforms<SkyboxUniforms>>,
images: Res<RenderAssets<Image>>,
render_device: Res<RenderDevice>,
views: Query<(Entity, &Skybox)>,
views: Query<(Entity, &Skybox, &DynamicUniformIndex<SkyboxUniforms>)>,
) {
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(
"skybox_bind_group",
&pipeline.bind_group_layout,
&BindGroupEntries::sequential((
&skybox.texture_view,
&skybox.sampler,
view_uniforms,
skybox_uniforms,
)),
);

commands.entity(entity).insert(SkyboxBindGroup(bind_group));
commands
.entity(entity)
.insert(SkyboxBindGroup((bind_group, skybox_uniform_index.index())));
}
}
}
3 changes: 2 additions & 1 deletion crates/bevy_core_pipeline/src/skybox/skybox.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@group(0) @binding(0) var skybox: texture_cube<f32>;
@group(0) @binding(1) var skybox_sampler: sampler;
@group(0) @binding(2) var<uniform> view: View;
@group(0) @binding(3) var<uniform> brightness: f32;

fn coords_to_ray_direction(position: vec2<f32>, viewport: vec4<f32>) -> vec3<f32> {
// Using world positions of the fragment and camera to calculate a ray direction
Expand Down Expand Up @@ -62,5 +63,5 @@ fn skybox_fragment(in: VertexOutput) -> @location(0) vec4<f32> {
let ray_direction = coords_to_ray_direction(in.position.xy, view.viewport);

// 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;
}
4 changes: 2 additions & 2 deletions crates/bevy_pbr/src/environment_map/environment_map.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,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_intensity;
out.specular = FssEss * radiance * bindings::lights.environment_map_intensity;
return out;
}
4 changes: 4 additions & 0 deletions crates/bevy_pbr/src/environment_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ impl Plugin for EnvironmentMapPlugin {
pub struct EnvironmentMapLight {
pub diffuse_map: Handle<Image>,
pub specular_map: Handle<Image>,
/// 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).
JMS55 marked this conversation as resolved.
Show resolved Hide resolved
pub intensity: f32,
}

impl EnvironmentMapLight {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ fn calculate_cascade(
/// # use bevy_ecs::system::ResMut;
/// # use bevy_pbr::AmbientLight;
/// fn setup_ambient_light(mut ambient_light: ResMut<AmbientLight>) {
/// ambient_light.brightness = 0.3;
/// ambient_light.brightness = 20.0;
/// }
/// ```
#[derive(Resource, Clone, Debug, ExtractResource, Reflect)]
Expand All @@ -572,7 +572,7 @@ impl Default for AmbientLight {
fn default() -> Self {
Self {
color: Color::rgb(1.0, 1.0, 1.0),
brightness: 0.05,
brightness: 8.0,
}
}
}
Expand Down
20 changes: 6 additions & 14 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,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_intensity: f32,
}

// NOTE: this must be kept in sync with the same constants in pbr.frag
Expand Down Expand Up @@ -857,18 +858,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
Expand All @@ -877,9 +866,9 @@ pub fn prepare_lights(
gpu_directional_lights[index] = GpuDirectionalLight {
// Filled in later.
cascades: [GpuDirectionalCascade::default(); MAX_CASCADES_PER_LIGHT],
// 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()) * 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(),
Expand Down Expand Up @@ -972,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_intensity: environment_map
.map(|env_map| env_map.intensity)
.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
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/render/mesh_view_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct Lights {
n_directional_lights: u32,
spot_light_shadowmap_offset: i32,
environment_map_smallest_specular_mip_level: u32,
environment_map_intensity: f32,
};

struct Fog {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ fn apply_pbr_lighting(

// Total light
output_color = vec4<f32>(
transmitted_light + direct_light + indirect_light + emissive_light,
view_bindings::view.exposure * (transmitted_light + direct_light + indirect_light + emissive_light),
output_color.a
);

Expand Down Expand Up @@ -422,7 +422,7 @@ fn apply_fog(fog_params: mesh_view_types::Fog, input_color: vec4<f32>, fragment_
0.0
),
fog_params.directional_light_exponent
) * light.color.rgb;
) * light.color.rgb * view_bindings::view.exposure;
}
}

Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_pbr/src/render/pbr_transmission.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ fn specular_transmissive_light(world_position: vec4<f32>, frag_coord: vec3<f32>,
background_color = fetch_transmissive_background(offset_position, frag_coord, view_z, perceptual_roughness);
}

// Compensate for exposure, since the background color is coming from an already exposure-adjusted texture
background_color = vec4(background_color.rgb / view_bindings::view.exposure, background_color.a);

// Dot product of the refracted direction with the exit normal (Note: We assume the exit normal is the entry normal but inverted)
let MinusNdotT = dot(-N, T);

Expand Down
Loading