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

Re-introduce "Replace Ambient Lights with Environment Map Lights (#17482)" #18207

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
22 changes: 14 additions & 8 deletions crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,16 @@ pub use volumetric_fog::{FogVolume, VolumetricFog, VolumetricFogPlugin, Volumetr
///
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
#[expect(
deprecated,
reason = "AmbientLight has been replaced by EnvironmentMapLight"
)]
#[doc(hidden)]
pub use crate::light::AmbientLight;
#[doc(hidden)]
pub use crate::{
fog::{DistanceFog, FogFalloff},
light::{light_consts, AmbientLight, DirectionalLight, PointLight, SpotLight},
light::{light_consts, DirectionalLight, PointLight, SpotLight},
light_probe::{environment_map::EnvironmentMapLight, LightProbe},
material::{Material, MaterialPlugin},
mesh_material::MeshMaterial3d,
Expand Down Expand Up @@ -165,7 +171,6 @@ pub const PBR_PREPASS_SHADER_HANDLE: Handle<Shader> =
weak_handle!("9afeaeab-7c45-43ce-b322-4b97799eaeb9");
pub const PBR_FUNCTIONS_HANDLE: Handle<Shader> =
weak_handle!("815b8618-f557-4a96-91a5-a2fb7e249fb0");
pub const PBR_AMBIENT_HANDLE: Handle<Shader> = weak_handle!("4a90b95b-112a-4a10-9145-7590d6f14260");
pub const PARALLAX_MAPPING_SHADER_HANDLE: Handle<Shader> =
weak_handle!("6cf57d9f-222a-429a-bba4-55ba9586e1d4");
pub const VIEW_TRANSFORMATIONS_SHADER_HANDLE: Handle<Shader> =
Expand Down Expand Up @@ -280,12 +285,6 @@ impl Plugin for PbrPlugin {
"render/rgb9e5.wgsl",
Shader::from_wgsl
);
load_internal_asset!(
app,
PBR_AMBIENT_HANDLE,
"render/pbr_ambient.wgsl",
Shader::from_wgsl
);
load_internal_asset!(
app,
PBR_FRAGMENT_HANDLE,
Expand Down Expand Up @@ -325,6 +324,10 @@ impl Plugin for PbrPlugin {
Shader::from_wgsl
);

#[expect(
deprecated,
reason = "AmbientLight has been replaced by EnvironmentMapLight"
)]
app.register_asset_reflect::<StandardMaterial>()
.register_type::<AmbientLight>()
.register_type::<CascadeShadowConfig>()
Expand Down Expand Up @@ -401,6 +404,9 @@ impl Plugin for PbrPlugin {
.add_systems(
PostUpdate,
(
map_ambient_lights
.in_set(SimulationLightSystems::MapAmbientLights)
.after(CameraUpdateSystem),
add_clusters
.in_set(SimulationLightSystems::AddClusters)
.after(CameraUpdateSystem),
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_pbr/src/light/ambient_light.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#![deprecated(
since = "0.16.0",
note = "Use `EnvironmentMapLight::solid_color` instead"
)]

use super::*;

/// An ambient light, which lights the entire scene equally.
Expand Down
59 changes: 58 additions & 1 deletion crates/bevy_pbr/src/light/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ use bevy_render::{
use bevy_transform::components::{GlobalTransform, Transform};
use bevy_utils::Parallel;

use crate::*;
use crate::{prelude::EnvironmentMapLight, *};

mod ambient_light;
#[expect(
deprecated,
reason = "AmbientLight has been replaced by EnvironmentMapLight"
)]
pub use ambient_light::AmbientLight;

mod point_light;
Expand Down Expand Up @@ -509,6 +513,7 @@ pub struct LightVisibilityClass;
/// System sets used to run light-related systems.
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
pub enum SimulationLightSystems {
MapAmbientLights,
AddClusters,
AssignLightsToClusters,
/// System order ambiguities between systems in this set are ignored:
Expand All @@ -522,6 +527,58 @@ pub enum SimulationLightSystems {
CheckLightVisibility,
}

#[derive(Component)]
pub struct EnvironmentMapLightFromAmbientLight;

#[expect(
deprecated,
reason = "AmbientLight has been replaced by EnvironmentMapLight"
)]
pub fn map_ambient_lights(
mut commands: Commands,
mut image_assets: ResMut<Assets<Image>>,
ambient_light: Res<AmbientLight>,
new_views: Query<
(Entity, Option<Ref<AmbientLight>>),
(
With<Camera>,
Without<EnvironmentMapLight>,
Without<EnvironmentMapLightFromAmbientLight>,
),
>,
mut managed_views: Query<
(&mut EnvironmentMapLight, Option<Ref<AmbientLight>>),
With<EnvironmentMapLightFromAmbientLight>,
>,
) {
let ambient_light = ambient_light.into();
for (entity, ambient_override) in new_views.iter() {
let ambient = ambient_override.as_ref().unwrap_or(&ambient_light);
let ambient_required = ambient.brightness > 0.0 && ambient.color != Color::BLACK;
if ambient_required && ambient.is_changed() {
commands
.entity(entity)
.insert(EnvironmentMapLight {
intensity: ambient.brightness,
affects_lightmapped_mesh_diffuse: ambient.affects_lightmapped_meshes,
..EnvironmentMapLight::solid_color(image_assets.as_mut(), ambient.color)
})
.insert(EnvironmentMapLightFromAmbientLight);
}
}
for (mut env_map, ambient_override) in managed_views.iter_mut() {
let ambient = ambient_override.as_ref().unwrap_or(&ambient_light);
let ambient_required = ambient.brightness > 0.0 && ambient.color != Color::BLACK;
if ambient_required && ambient.is_changed() {
*env_map = EnvironmentMapLight {
intensity: ambient.brightness,
affects_lightmapped_mesh_diffuse: ambient.affects_lightmapped_meshes,
..EnvironmentMapLight::solid_color(image_assets.as_mut(), ambient.color)
};
}
}
}

pub fn update_directional_light_frusta(
mut views: Query<
(
Expand Down
85 changes: 82 additions & 3 deletions crates/bevy_pbr/src/light_probe/environment_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
//!
//! [several pre-filtered environment maps]: https://github.com/KhronosGroup/glTF-Sample-Environments

use bevy_asset::{weak_handle, AssetId, Handle};
use bevy_asset::{weak_handle, AssetId, Assets, Handle, RenderAssetUsages};
use bevy_color::{Color, ColorToPacked, Srgba};
use bevy_ecs::{
component::Component, query::QueryItem, reflect::ReflectComponent, system::lifetimeless::Read,
};
Expand All @@ -56,8 +57,9 @@ use bevy_render::{
render_asset::RenderAssets,
render_resource::{
binding_types::{self, uniform_buffer},
BindGroupLayoutEntryBuilder, Sampler, SamplerBindingType, Shader, ShaderStages,
TextureSampleType, TextureView,
BindGroupLayoutEntryBuilder, Extent3d, Sampler, SamplerBindingType, Shader, ShaderStages,
TextureDimension, TextureFormat, TextureSampleType, TextureView, TextureViewDescriptor,
TextureViewDimension,
},
renderer::{RenderAdapter, RenderDevice},
texture::{FallbackImage, GpuImage},
Expand Down Expand Up @@ -114,6 +116,83 @@ pub struct EnvironmentMapLight {
pub affects_lightmapped_mesh_diffuse: bool,
}

impl EnvironmentMapLight {
/// An environment map with a uniform color, useful for uniform ambient lighting.
pub fn solid_color(assets: &mut Assets<Image>, color: Color) -> Self {
let color: Srgba = color.into();
let image = Image {
texture_view_descriptor: Some(TextureViewDescriptor {
dimension: Some(TextureViewDimension::Cube),
..Default::default()
}),
..Image::new_fill(
Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 6,
},
TextureDimension::D2,
&color.to_u8_array(),
TextureFormat::Rgba8UnormSrgb,
RenderAssetUsages::RENDER_WORLD,
)
};
let handle = assets.add(image);

Self {
diffuse_map: handle.clone(),
specular_map: handle,
..Default::default()
}
}

/// An environment map with a hemispherical gradient, fading between the sky and ground colors
/// at the horizon. Useful as a very simple 'sky'.
pub fn hemispherical_gradient(
assets: &mut Assets<Image>,
top_color: Color,
bottom_color: Color,
) -> Self {
let top_color: Srgba = top_color.into();
let bottom_color: Srgba = bottom_color.into();
let mid_color = (top_color + bottom_color) / 2.0;
let image = Image {
texture_view_descriptor: Some(TextureViewDescriptor {
dimension: Some(TextureViewDimension::Cube),
..Default::default()
}),
..Image::new(
Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 6,
},
TextureDimension::D2,
[
mid_color,
mid_color,
top_color,
bottom_color,
mid_color,
mid_color,
]
.into_iter()
.flat_map(Srgba::to_u8_array)
.collect(),
TextureFormat::Rgba8UnormSrgb,
RenderAssetUsages::RENDER_WORLD,
)
};
let handle = assets.add(image);

Self {
diffuse_map: handle.clone(),
specular_map: handle,
..Default::default()
}
}
}

impl Default for EnvironmentMapLight {
fn default() -> Self {
EnvironmentMapLight {
Expand Down
26 changes: 5 additions & 21 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ bitflags::bitflags! {
#[derive(Copy, Clone, Debug, ShaderType)]
pub struct GpuLights {
directional_lights: [GpuDirectionalLight; MAX_DIRECTIONAL_LIGHTS],
ambient_color: Vec4,
// xyz are x/y/z cluster dimensions and w is the number of clusters
cluster_dimensions: UVec4,
// xy are vec2<f32>(cluster_dimensions.xy) / vec2<f32>(view.width, view.height)
Expand All @@ -149,7 +148,6 @@ pub struct GpuLights {
n_directional_lights: u32,
// offset from spot light's light index to spot light's shadow map index
spot_light_shadowmap_offset: i32,
ambient_light_affects_lightmapped_meshes: u32,
}

// NOTE: When running bevy on Adreno GPU chipsets in WebGL, any value above 1 will result in a crash
Expand Down Expand Up @@ -729,11 +727,9 @@ pub fn prepare_lights(
&ExtractedClusterConfig,
Option<&RenderLayers>,
Has<NoIndirectDrawing>,
Option<&AmbientLight>,
),
With<Camera3d>,
>,
ambient_light: Res<AmbientLight>,
point_light_shadow_map: Res<PointLightShadowMap>,
directional_light_shadow_map: Res<DirectionalLightShadowMap>,
mut shadow_render_phases: ResMut<ViewBinnedRenderPhases<Shadow>>,
Expand Down Expand Up @@ -1142,18 +1138,11 @@ pub fn prepare_lights(
let mut live_views = EntityHashSet::with_capacity(views_count);

// set up light data for each view
for (
entity,
camera_main_entity,
extracted_view,
clusters,
maybe_layers,
no_indirect_drawing,
maybe_ambient_override,
) in sorted_cameras
.0
.iter()
.filter_map(|sorted_camera| views.get(sorted_camera.entity).ok())
for (entity, camera_main_entity, extracted_view, clusters, maybe_layers, no_indirect_drawing) in
sorted_cameras
.0
.iter()
.filter_map(|sorted_camera| views.get(sorted_camera.entity).ok())
{
live_views.insert(entity);

Expand All @@ -1175,11 +1164,8 @@ pub fn prepare_lights(
);

let n_clusters = clusters.dimensions.x * clusters.dimensions.y * clusters.dimensions.z;
let ambient_light = maybe_ambient_override.unwrap_or(&ambient_light);
let mut gpu_lights = GpuLights {
directional_lights: gpu_directional_lights,
ambient_color: Vec4::from_slice(&LinearRgba::from(ambient_light.color).to_f32_array())
* ambient_light.brightness,
cluster_factors: Vec4::new(
clusters.dimensions.x as f32 / extracted_view.viewport.z as f32,
clusters.dimensions.y as f32 / extracted_view.viewport.w as f32,
Expand All @@ -1194,8 +1180,6 @@ pub fn prepare_lights(
// index to shadow map index, we need to subtract point light count and add directional shadowmap count.
spot_light_shadowmap_offset: num_directional_cascades_enabled as i32
- point_light_count as i32,
ambient_light_affects_lightmapped_meshes: ambient_light.affects_lightmapped_meshes
as u32,
};

// 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: 0 additions & 1 deletion crates/bevy_pbr/src/render/mesh_view_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ const DIRECTIONAL_LIGHT_FLAGS_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE_BIT: u32 = 4u;
struct Lights {
// NOTE: this array size must be kept in sync with the constants defined in bevy_pbr/src/render/light.rs
directional_lights: array<DirectionalLight, #{MAX_DIRECTIONAL_LIGHTS}u>,
ambient_color: vec4<f32>,
// x/y/z dimensions and n_clusters in w
cluster_dimensions: vec4<u32>,
// xy are vec2<f32>(cluster_dimensions.xy) / vec2<f32>(view.width, view.height)
Expand Down
29 changes: 0 additions & 29 deletions crates/bevy_pbr/src/render/pbr_ambient.wgsl

This file was deleted.

15 changes: 0 additions & 15 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -561,18 +561,6 @@ fn apply_pbr_lighting(
#endif
}

#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
// NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated
// world position, inverted normal and view vectors, and the following simplified
// values for a fully diffuse transmitted light contribution approximation:
//
// perceptual_roughness = 1.0;
// NdotV = 1.0;
// F0 = vec3<f32>(0.0)
// diffuse_occlusion = vec3<f32>(1.0)
transmitted_light += ambient::ambient_light(diffuse_transmissive_lobe_world_position, -in.N, -in.V, 1.0, diffuse_transmissive_color, vec3<f32>(0.0), 1.0, vec3<f32>(1.0));
#endif

// Diffuse indirect lighting can come from a variety of sources. The
// priority goes like this:
//
Expand Down Expand Up @@ -644,9 +632,6 @@ fn apply_pbr_lighting(

#endif // ENVIRONMENT_MAP

// Ambient light (indirect)
indirect_light += ambient::ambient_light(in.world_position, in.N, in.V, NdotV, diffuse_color, F0, perceptual_roughness, diffuse_occlusion);

// we'll use the specular component of the transmitted environment
// light in the call to `specular_transmissive_light()` below
var specular_transmitted_environment_light = vec3<f32>(0.0);
Expand Down
Loading