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 86fa28aa8a530..e66f29b8137ca 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -2,13 +2,17 @@ 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}, + camera::ExposureSettings, + extract_component::{ + ComponentUniforms, DynamicUniformIndex, ExtractComponent, ExtractComponentPlugin, + UniformComponentPlugin, + }, render_asset::RenderAssets, render_resource::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, @@ -34,7 +38,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 +77,41 @@ 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, + /// 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 Query = (&'static Self, Option<&'static ExposureSettings>); + type Filter = (); + type Out = (Self, SkyboxUniforms); + + 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(( + 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 { @@ -109,6 +149,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 +257,23 @@ 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, 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 +290,16 @@ fn prepare_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/crates/bevy_pbr/src/environment_map/environment_map.wgsl b/crates/bevy_pbr/src/environment_map/environment_map.wgsl index ed10e2d0ba894..7b5f88d609937 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_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 7e6e8732028eb..953b30344d8cc 100644 --- a/crates/bevy_pbr/src/environment_map/mod.rs +++ b/crates/bevy_pbr/src/environment_map/mod.rs @@ -50,6 +50,10 @@ impl Plugin for EnvironmentMapPlugin { pub struct EnvironmentMapLight { pub diffuse_map: Handle, pub specular_map: Handle, + /// 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, } impl EnvironmentMapLight { diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index d8469f527a4be..47177e989c8dc 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -203,6 +203,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 @@ -848,18 +849,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 @@ -868,9 +857,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(), @@ -962,6 +951,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 diff --git a/crates/bevy_pbr/src/render/mesh_view_types.wgsl b/crates/bevy_pbr/src/render/mesh_view_types.wgsl index b944aa2792e2f..fe6fef99cb6a2 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_intensity: f32, }; struct Fog { diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index 6677eb70e5906..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( - 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/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 5fbf037d1fe1d..34f4a5abae363 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -75,6 +75,71 @@ 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 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 { + Self { + ev100: physical_camera_parameters.ev100(), + } + } + + /// Converts EV100 values to exposure values. + #[inline] + pub fn exposure(&self) -> f32 { + 1.0 / (2.0f32.powf(self.ev100) * 1.2) + } +} + +impl Default for ExposureSettings { + fn default() -> Self { + Self { + ev100: Self::EV100_OVERCAST, + } + } +} + +/// Parameters based on physical camera characteristics for calculating +/// EV100 values for use with [`ExposureSettings`]. +#[derive(Clone, Copy)] +pub struct PhysicalCameraParameters { + /// + pub aperture_f_stops: f32, + /// + pub shutter_speed_s: f32, + /// + 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 { + aperture_f_stops: 4.0, + shutter_speed_s: 1.0 / 250.0, + sensitivity_iso: 100.0, + } + } +} + /// The defining [`Component`] for camera entities, /// storing information about how and what to render through this camera. /// @@ -630,6 +695,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( @@ -642,6 +708,7 @@ pub fn extract_cameras( &GlobalTransform, &VisibleEntities, Option<&ColorGrading>, + Option<&ExposureSettings>, Option<&TemporalJitter>, Option<&RenderLayers>, )>, @@ -656,6 +723,7 @@ pub fn extract_cameras( transform, visible_entities, color_grading, + exposure_settings, temporal_jitter, render_layers, ) in query.iter() @@ -696,6 +764,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 487c3ef415479..e778ae8980191 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, ManualTextureViews, MipBias, TemporalJitter}, + camera::{ExposureSettings, ExtractedCamera, ManualTextureViews, MipBias, TemporalJitter}, extract_resource::{ExtractResource, ExtractResourcePlugin}, prelude::{Image, Shader}, render_asset::RenderAssets, @@ -167,6 +167,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, @@ -352,6 +353,7 @@ pub fn prepare_view_uniforms( mut view_uniforms: ResMut, views: Query<( Entity, + Option<&ExtractedCamera>, &ExtractedView, Option<&TemporalJitter>, Option<&MipBias>, @@ -359,9 +361,9 @@ pub fn prepare_view_uniforms( ) { view_uniforms.uniforms.clear(); - for (entity, camera, temporal_jitter, mip_bias) in &views { - let viewport = camera.viewport.as_vec4(); - let unjittered_projection = camera.projection; + for (entity, extracted_camera, extracted_view, temporal_jitter, mip_bias) 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 { @@ -369,13 +371,13 @@ 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_proj = if temporal_jitter.is_some() { projection * inverse_view } else { - camera + extracted_view .view_projection .unwrap_or_else(|| projection * inverse_view) }; @@ -389,9 +391,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, mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0, }), }; diff --git a/crates/bevy_render/src/view/view.wgsl b/crates/bevy_render/src/view/view.wgsl index 86ae3359f72cc..b40b057f02685 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, diff --git a/examples/3d/lighting.rs b/examples/3d/lighting.rs index 843099f052c19..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::*}; +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, (movement, animate_light_direction)) + .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>, @@ -207,6 +216,7 @@ fn setup( // directional 'sun' light commands.spawn(DirectionalLightBundle { directional_light: DirectionalLight { + illuminance: 1000.0, shadows_enabled: true, ..default() }, @@ -227,21 +237,37 @@ fn setup( ..default() }); - // camera - commands.spawn(Camera3dBundle { - transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), - ..default() - }); - // example instructions + let style = TextStyle { + font_size: 20.0, + ..default() + }; commands.spawn( - TextBundle::from_section( - "Use arrow keys to move objects", - TextStyle { - font_size: 20.0, - ..default() - }, - ) + TextBundle::from_sections(vec![ + TextSection::new( + format!("Aperture: f/{:.0}\n", parameters.aperture_f_stops), + style.clone(), + ), + TextSection::new( + format!( + "Shutter speed: 1/{:.0}s\n", + 1.0 / parameters.shutter_speed_s + ), + style.clone(), + ), + TextSection::new( + format!("Sensitivity: ISO {:.0}\n", parameters.sensitivity_iso), + style.clone(), + ), + 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.clone()), + TextSection::new("R - Reset exposure", style), + ]) .with_style(Style { position_type: PositionType::Absolute, top: Val::Px(12.0), @@ -249,6 +275,52 @@ fn setup( ..default() }), ); + + // camera + commands.spawn(( + Camera3dBundle { + transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }, + 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(); + if key_input.just_pressed(KeyCode::Key2) { + parameters.aperture_f_stops *= 2.0; + } else if key_input.just_pressed(KeyCode::Key1) { + parameters.aperture_f_stops *= 0.5; + } + if key_input.just_pressed(KeyCode::Key4) { + parameters.shutter_speed_s *= 2.0; + } else if key_input.just_pressed(KeyCode::Key3) { + parameters.shutter_speed_s *= 0.5; + } + if key_input.just_pressed(KeyCode::Key6) { + parameters.sensitivity_iso += 100.0; + } else if key_input.just_pressed(KeyCode::Key5) { + parameters.sensitivity_iso -= 100.0; + } + 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( 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 b35e4bd8e4682..147a33a119f61 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"), + intensity: 1.0, }, )); } diff --git a/examples/3d/skybox.rs b/examples/3d/skybox.rs index 619550f56b4d5..7006bbb9b51fa 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; diff --git a/examples/3d/tonemapping.rs b/examples/3d/tonemapping.rs index 687efaaef5845..d7f224167f194 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, ));