diff --git a/_release-content/migration-guides/environment_map_uniform_removed.md b/_release-content/migration-guides/environment_map_uniform_removed.md new file mode 100644 index 0000000000000..6b577a8cb71b3 --- /dev/null +++ b/_release-content/migration-guides/environment_map_uniform_removed.md @@ -0,0 +1,6 @@ +--- +title: "`EnvironmentMapUniform` is removed" +pull_requests: [24095] +--- + +`EnvironmentMapUniform` has been removed. It previously stored the rotation transformation matrix of view environment maps. Now the rotation is stored as a quaternion in `LightProbesUniform::view_rotation`. diff --git a/crates/bevy_light/src/probe.rs b/crates/bevy_light/src/probe.rs index 583689c0c9cff..b5ba960c99076 100644 --- a/crates/bevy_light/src/probe.rs +++ b/crates/bevy_light/src/probe.rs @@ -123,6 +123,8 @@ pub struct EnvironmentMapLight { /// World space rotation applied to the environment light cubemaps. /// This is useful for users who require a different axis, such as the Z-axis, to serve /// as the vertical axis. + /// + /// Note: This only has an effect if attached to a view. pub rotation: Quat, /// Whether the light from this environment map contributes diffuse lighting diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index a734d08dbef29..e7eaee6e2c958 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -127,9 +127,6 @@ fn shader_ref(path: PathBuf) -> ShaderRef { ShaderRef::Path(AssetPath::from_path_buf(path).with_source("embedded")) } -pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 19; -pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 20; - /// Sets up the entire PBR infrastructure of bevy. pub struct PbrPlugin { /// Controls if the prepass is enabled for the [`StandardMaterial`]. diff --git a/crates/bevy_pbr/src/light_probe/environment_map.rs b/crates/bevy_pbr/src/light_probe/environment_map.rs index c4c62fbec3e8e..3ba44e3247fb4 100644 --- a/crates/bevy_pbr/src/light_probe/environment_map.rs +++ b/crates/bevy_pbr/src/light_probe/environment_map.rs @@ -51,13 +51,12 @@ use bevy_ecs::{ }; use bevy_image::Image; use bevy_light::{EnvironmentMapLight, ParallaxCorrection}; -use bevy_math::{Affine3A, Vec3}; +use bevy_math::{Affine3A, Quat, Vec3}; use bevy_render::{ extract_instances::ExtractInstance, render_asset::RenderAssets, render_resource::{ - binding_types::{self, uniform_buffer}, - BindGroupLayoutEntryBuilder, Sampler, SamplerBindingType, ShaderStages, TextureSampleType, + binding_types, BindGroupLayoutEntryBuilder, Sampler, SamplerBindingType, TextureSampleType, TextureView, }, renderer::{RenderAdapter, RenderDevice}, @@ -67,8 +66,8 @@ use bevy_render::{ use core::{num::NonZero, ops::Deref}; use crate::{ - add_cubemap_texture_view, binding_arrays_are_usable, EnvironmentMapUniform, - RenderLightProbeFlags, MAX_VIEW_LIGHT_PROBES, + add_cubemap_texture_view, binding_arrays_are_usable, RenderLightProbeFlags, + MAX_VIEW_LIGHT_PROBES, }; use super::{LightProbeComponent, RenderViewLightProbes}; @@ -136,6 +135,8 @@ pub struct EnvironmentMapViewLightProbeInfo { /// Whether this lightmap affects the diffuse lighting of lightmapped /// meshes. pub(crate) affects_lightmapped_mesh_diffuse: bool, + /// World space rotation applied to the environment light cubemaps. + pub(crate) rotation: Quat, } impl ExtractInstance for EnvironmentMapIds { @@ -156,7 +157,7 @@ impl ExtractInstance for EnvironmentMapIds { pub(crate) fn get_bind_group_layout_entries( render_device: &RenderDevice, render_adapter: &RenderAdapter, -) -> [BindGroupLayoutEntryBuilder; 4] { +) -> [BindGroupLayoutEntryBuilder; 3] { let mut texture_cube_binding = binding_types::texture_cube(TextureSampleType::Float { filterable: true }); if binding_arrays_are_usable(render_device, render_adapter) { @@ -168,7 +169,6 @@ pub(crate) fn get_bind_group_layout_entries( texture_cube_binding, texture_cube_binding, binding_types::sampler(SamplerBindingType::Filtering), - uniform_buffer::(true).visibility(ShaderStages::FRAGMENT), ] } @@ -295,6 +295,7 @@ impl LightProbeComponent for EnvironmentMapLight { specular_map: specular_map_handle, intensity, affects_lightmapped_mesh_diffuse, + rotation, .. }) = view_component && let (Some(_), Some(specular_map)) = ( @@ -314,6 +315,7 @@ impl LightProbeComponent for EnvironmentMapLight { - 1, intensity: *intensity, affects_lightmapped_mesh_diffuse: *affects_lightmapped_mesh_diffuse, + rotation: *rotation, }); }; @@ -344,6 +346,7 @@ impl Default for EnvironmentMapViewLightProbeInfo { smallest_specular_mip_level: 0, intensity: 1.0, affects_lightmapped_mesh_diffuse: true, + rotation: Quat::IDENTITY, } } } diff --git a/crates/bevy_pbr/src/light_probe/environment_map.wgsl b/crates/bevy_pbr/src/light_probe/environment_map.wgsl index 8228d41b2f52e..15e7aaec33fb3 100644 --- a/crates/bevy_pbr/src/light_probe/environment_map.wgsl +++ b/crates/bevy_pbr/src/light_probe/environment_map.wgsl @@ -3,7 +3,6 @@ #import bevy_pbr::light_probe::{light_probe_iterator_new, light_probe_iterator_next} #import bevy_pbr::mesh_view_bindings as bindings #import bevy_pbr::mesh_view_bindings::light_probes -#import bevy_pbr::mesh_view_bindings::environment_map_uniform #import bevy_pbr::mesh_view_types::{ LIGHT_PROBE_FLAG_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE, LIGHT_PROBE_FLAG_PARALLAX_CORRECT } @@ -45,7 +44,8 @@ fn compute_cubemap_sample_dir( world_ray_direction: vec3, light_from_world: mat4x4, parallax_correction_bounds: vec3, - parallax_correct: bool + parallax_correct: bool, + view_rotation: vec4, ) -> vec3 { var sample_dir: vec3; @@ -85,6 +85,10 @@ fn compute_cubemap_sample_dir( sample_dir = (light_from_world * vec4(world_ray_direction, 0.0)).xyz; } + // Rotating the world space ray direction by the environment map light view rotation, + // it is equivalent to rotating the environment cubemap itself. + sample_dir = quat_rotate(view_rotation, sample_dir); + // Cubemaps are left-handed, so we negate the Z coordinate. sample_dir.z = -sample_dir.z; return sample_dir; @@ -124,6 +128,7 @@ fn compute_radiances( while (true) { var query_result = light_probe_iterator_next(&iterator); + var view_rotation = vec4(0.0, 0.0, 0.0, 1.0); // If we reached the end of the light probe list, and we didn't find // enough reflection probes to reach a weight of 1.0, use the view @@ -132,6 +137,7 @@ fn compute_radiances( if (query_result.texture_index < 0 && total_weight < 0.9999) { query_result.texture_index = light_probes.view_cubemap_index; query_result.intensity = light_probes.intensity_for_view; + view_rotation = light_probes.view_rotation; query_result.light_from_world = mat4x4( vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), @@ -172,7 +178,8 @@ fn compute_radiances( N, query_result.light_from_world, query_result.parallax_correction_bounds, - parallax_correct + parallax_correct, + view_rotation, ); radiances.irradiance = textureSampleLevel( bindings::diffuse_environment_maps[query_result.texture_index], @@ -187,7 +194,8 @@ fn compute_radiances( radiance_sample_dir, query_result.light_from_world, query_result.parallax_correction_bounds, - parallax_correct + parallax_correct, + view_rotation, ); radiances.radiance += textureSampleLevel( @@ -247,9 +255,9 @@ fn compute_radiances( if (enable_diffuse) { var irradiance_sample_dir = N; - // Rotating the world space ray direction by the environment light map transform matrix, it is - // equivalent to rotating the diffuse environment cubemap itself. - irradiance_sample_dir = (environment_map_uniform.transform * vec4(irradiance_sample_dir, 1.0)).xyz; + // Rotating the world space ray direction by the environment map light view rotation, + // it is equivalent to rotating the environment cubemap itself. + irradiance_sample_dir = quat_rotate(light_probes.view_rotation, irradiance_sample_dir); // Cube maps are left-handed so we negate the z coordinate. irradiance_sample_dir.z = -irradiance_sample_dir.z; radiances.irradiance = textureSampleLevel( @@ -260,9 +268,9 @@ fn compute_radiances( } var radiance_sample_dir = radiance_sample_direction(N, R, roughness); - // Rotating the world space ray direction by the environment light map transform matrix, it is - // equivalent to rotating the specular environment cubemap itself. - radiance_sample_dir = (environment_map_uniform.transform * vec4(radiance_sample_dir, 1.0)).xyz; + // Rotating the world space ray direction by the environment map light view rotation, + // it is equivalent to rotating the environment cubemap itself. + radiance_sample_dir = quat_rotate(light_probes.view_rotation, radiance_sample_dir); // Cube maps are left-handed so we negate the z coordinate. radiance_sample_dir.z = -radiance_sample_dir.z; radiances.radiance = textureSampleLevel( @@ -367,7 +375,7 @@ fn environment_map_light( let F0_surface = mix(F0_dielectric, F0_metallic, metallic); let specular_occlusion = saturate(dot(F0_surface, vec3(50.0 * 0.33))); - // Compute per-material (dielectric and metallic separately) then mix the results. + // Compute per-material (dielectric and metallic separately) then mix the results. // We can't use F0 directly as the multiscattering term is nonlinear. let Ems = 1.0 - (F_ab.x + F_ab.y); @@ -405,3 +413,8 @@ fn radiance_sample_direction(N: vec3, R: vec3, roughness: f32) -> vec3 let lerp_factor = smoothness * (sqrt(smoothness) + roughness); return mix(N, R, lerp_factor); } + +fn quat_rotate(q: vec4, dir: vec3) -> vec3 { + let t = 2.0 * cross(q.xyz, dir); + return dir + q.w * t + cross(q.xyz, t); +} diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index a57ee1e64c247..c098b5be78d6d 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -16,7 +16,7 @@ use bevy_image::Image; use bevy_light::{ cluster::ClusterVisibilityClass, EnvironmentMapLight, IrradianceVolume, LightProbe, }; -use bevy_math::{Affine3A, FloatOrd, Mat4, Vec3, Vec4}; +use bevy_math::{Affine3A, FloatOrd, Mat4, Quat, Vec3, Vec4}; use bevy_platform::collections::HashMap; use bevy_render::{ extract_instances::ExtractInstancesPlugin, @@ -30,7 +30,7 @@ use bevy_render::{ Extract, ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderSystems, }; use bevy_shader::load_shader_library; -use bevy_transform::{components::Transform, prelude::GlobalTransform}; +use bevy_transform::prelude::GlobalTransform; use bitflags::bitflags; use tracing::error; @@ -143,6 +143,9 @@ pub struct LightProbesUniform { /// associated with the view. smallest_specular_mip_level_for_view: u32, + /// World space rotation applied to environment map cubemaps associated with the view itself. + view_rotation: Vec4, + /// The intensity of the environment cubemap associated with the view. /// /// See the comment in [`EnvironmentMapLight`] for details. @@ -367,31 +370,6 @@ pub trait LightProbeComponent: Send + Sync + Component + Sized { } } -/// The uniform struct extracted from [`EnvironmentMapLight`]. -/// Will be available for use in the Environment Map shader. -#[derive(Component, ShaderType, Clone)] -pub struct EnvironmentMapUniform { - /// The world space transformation matrix of the sample ray for environment cubemaps. - transform: Mat4, -} - -impl Default for EnvironmentMapUniform { - fn default() -> Self { - EnvironmentMapUniform { - transform: Mat4::IDENTITY, - } - } -} - -/// A GPU buffer that stores the environment map settings for each view. -#[derive(Resource, Default, Deref, DerefMut)] -pub struct EnvironmentMapUniformBuffer(pub DynamicUniformBuffer); - -/// A component that stores the offset within the -/// [`EnvironmentMapUniformBuffer`] for each view. -#[derive(Component, Default, Deref, DerefMut)] -pub struct ViewEnvironmentMapUniformOffset(u32); - impl Plugin for LightProbePlugin { fn build(&self, app: &mut App) { load_shader_library!(app, "light_probe.wgsl"); @@ -409,8 +387,6 @@ impl Plugin for LightProbePlugin { render_app .init_gpu_resource::() - .init_gpu_resource::() - .add_systems(ExtractSchedule, gather_environment_map_uniform) .add_systems( ExtractSchedule, gather_light_probes:: @@ -425,33 +401,11 @@ impl Plugin for LightProbePlugin { ) .add_systems( Render, - (upload_light_probes, prepare_environment_uniform_buffer) - .in_set(RenderSystems::PrepareResources), + upload_light_probes.in_set(RenderSystems::PrepareResources), ); } } -/// Extracts [`EnvironmentMapLight`] from views and creates [`EnvironmentMapUniform`] for them. -/// -/// Compared to the `ExtractComponentPlugin`, this implementation will create a default instance -/// if one does not already exist. -fn gather_environment_map_uniform( - view_query: Extract>>, - mut commands: Commands, -) { - for (view_entity, environment_map_light) in view_query.iter() { - let environment_map_uniform = EnvironmentMapUniform { - transform: Transform::from_rotation(environment_map_light.rotation) - .to_matrix() - .inverse(), - }; - commands - .get_entity(view_entity) - .expect("Environment map light entity wasn't synced.") - .insert(environment_map_uniform); - } -} - /// Gathers up all light probes of a single type in the scene and assigns them /// to views, performing frustum culling and distance sorting in the process. fn gather_light_probes( @@ -511,29 +465,6 @@ fn gather_light_probes( } } -/// Gathers up environment map settings for each applicable view and -/// writes them into a GPU buffer. -pub fn prepare_environment_uniform_buffer( - mut commands: Commands, - views: Query<(Entity, &EnvironmentMapUniform), With>, - mut environment_uniform_buffer: ResMut, - render_device: Res, - render_queue: Res, -) { - let Some(mut writer) = - environment_uniform_buffer.get_writer(views.iter().len(), &render_device, &render_queue) - else { - return; - }; - - for (view, environment_uniform) in views.iter() { - let uniform_offset = writer.write(environment_uniform); - commands - .entity(view) - .insert(ViewEnvironmentMapUniformOffset(uniform_offset)); - } -} - // A system that runs after [`gather_light_probes`] and populates the GPU // uniforms with the results. // @@ -606,6 +537,10 @@ fn upload_light_probes( } None => 1, }, + view_rotation: match maybe_view_light_probe_info { + Some(view_light_probe_info) => view_light_probe_info.rotation.inverse().into(), + None => Quat::IDENTITY.into(), + }, }; // Add any environment maps that [`gather_light_probes`] found to the @@ -646,6 +581,7 @@ impl Default for LightProbesUniform { smallest_specular_mip_level_for_view: 0, intensity_for_view: 1.0, view_environment_map_affects_lightmapped_mesh_diffuse: 1, + view_rotation: Quat::IDENTITY.into(), } } } diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.rs b/crates/bevy_pbr/src/render/mesh_view_bindings.rs index ecec2a56200c1..8cb717eaaf2d1 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.rs +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.rs @@ -1,7 +1,8 @@ use crate::{ - AreaLightLuts, DfgLut, ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, - ViewLightProbesUniformOffset, ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset, + AreaLightLuts, DfgLut, ViewFogUniformOffset, ViewLightProbesUniformOffset, + ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset, }; +use arrayvec::ArrayVec; use bevy_core_pipeline::{ oit::{ OitBuffers, OrderIndependentTransparencySettings, @@ -36,7 +37,6 @@ use bevy_render::{ }; use core::fmt::Write; use core::num::NonZero; -use smallvec::{smallvec, SmallVec}; use crate::{ contact_shadows::{ @@ -54,12 +54,12 @@ use crate::{ }, prepass, resources::{AtmosphereBuffer, AtmosphereSampler, AtmosphereTextures, GpuAtmosphere}, - Bluenoise, EnvironmentMapUniformBuffer, ExtractedAtmosphere, FogMeta, - GlobalClusterableObjectMeta, GpuClusteredLights, GpuFog, GpuLights, LightMeta, - LightProbesBuffer, LightProbesUniform, MeshPipeline, MeshPipelineKey, RenderViewLightProbes, - ScreenSpaceAmbientOcclusionResources, ScreenSpaceReflectionsBuffer, - ScreenSpaceReflectionsUniform, ShadowSamplers, ViewClusterBindings, ViewShadowBindings, - ViewTransmissionTexture, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, + Bluenoise, ExtractedAtmosphere, FogMeta, GlobalClusterableObjectMeta, GpuClusteredLights, + GpuFog, GpuLights, LightMeta, LightProbesBuffer, LightProbesUniform, MeshPipeline, + MeshPipelineKey, RenderViewLightProbes, ScreenSpaceAmbientOcclusionResources, + ScreenSpaceReflectionsBuffer, ScreenSpaceReflectionsUniform, ShadowSamplers, + ViewClusterBindings, ViewShadowBindings, ViewTransmissionTexture, + CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, }; #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] @@ -68,6 +68,9 @@ use bevy_render::render_resource::binding_types::texture_cube; #[cfg(debug_assertions)] use {crate::MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES, bevy_utils::once, tracing::warn}; +pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 18; +pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 19; + #[derive(Clone)] pub struct MeshPipelineViewLayout { pub main_layout: BindGroupLayoutDescriptor, @@ -110,12 +113,14 @@ impl MeshPipelineViewLayoutKey { let sep = ","; let mut result = String::with_capacity(sep.len() * lower); - for name in iter { - result.push_str(sep); + for (i, name) in iter.enumerate() { + if i != 0 { + result.push_str(sep); + } write!(&mut result, "{}", name).unwrap(); } - format!("mesh_view_layout_{}", result) + format!("mesh_view_layout:{}", result) } } @@ -376,16 +381,18 @@ fn layout_entries( )); } - if layout_key.contains(MeshPipelineViewLayoutKey::ENVIRONMENT_MAP) { - entries = entries.extend_with_indices(((18, environment_map_entries[3]),)); - } - if layout_key.contains(MeshPipelineViewLayoutKey::TONEMAP_IN_SHADER) { // Tonemapping let tonemapping_lut_entries = get_lut_bind_group_layout_entries(); entries = entries.extend_with_indices(( - (19, tonemapping_lut_entries[0]), - (20, tonemapping_lut_entries[1]), + ( + TONEMAPPING_LUT_TEXTURE_BINDING_INDEX, + tonemapping_lut_entries[0], + ), + ( + TONEMAPPING_LUT_SAMPLER_BINDING_INDEX, + tonemapping_lut_entries[1], + ), )); } @@ -395,7 +402,7 @@ fn layout_entries( { for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key) .iter() - .zip([21, 22, 23, 24]) + .zip([20, 21, 22, 23]) { if let Some(entry) = entry { entries = entries.extend_with_indices(((binding as u32, *entry),)); @@ -406,10 +413,10 @@ fn layout_entries( // View Transmission Texture entries = entries.extend_with_indices(( ( - 25, + 24, texture_2d(TextureSampleType::Float { filterable: true }), ), - (26, sampler(SamplerBindingType::Filtering)), + (25, sampler(SamplerBindingType::Filtering)), )); // OIT @@ -420,18 +427,18 @@ fn layout_entries( if is_oit_supported { entries = entries.extend_with_indices(( ( - 27, + 26, uniform_buffer::(true), ), // oit_nodes_capacity - (28, uniform_buffer::(false)), + (27, uniform_buffer::(false)), // oit_nodes - (29, storage_buffer_sized(false, None)), + (28, storage_buffer_sized(false, None)), // oit_heads, - (30, storage_buffer_sized(false, None)), + (29, storage_buffer_sized(false, None)), // oit_atomic_counter ( - 31, + 30, storage_buffer_sized(false, NonZero::::new(size_of::() as u64)), ), )); @@ -443,19 +450,19 @@ fn layout_entries( entries = entries.extend_with_indices(( // transmittance LUT ( - 32, + 31, texture_2d(TextureSampleType::Float { filterable: true }), ), - (33, sampler(SamplerBindingType::Filtering)), + (32, sampler(SamplerBindingType::Filtering)), // atmosphere data buffer - (34, storage_buffer_read_only::(false)), + (33, storage_buffer_read_only::(false)), )); } // Blue noise if layout_key.contains(MeshPipelineViewLayoutKey::STBN) { entries = entries.extend_with_indices((( - 35, + 34, texture_2d_array(TextureSampleType::Float { filterable: false }), ),)); } @@ -463,20 +470,20 @@ fn layout_entries( if cfg!(feature = "area_light_luts") { entries = entries.extend_with_indices(( ( - 36, + 35, texture_2d_array(TextureSampleType::Float { filterable: true }), ), - (37, sampler(SamplerBindingType::Filtering)), + (36, sampler(SamplerBindingType::Filtering)), )); } // DFG LUT if cfg!(feature = "dfg_lut") { entries = entries.extend_with_indices(( ( - 38, + 37, texture_2d(TextureSampleType::Float { filterable: true }), ), - (39, sampler(SamplerBindingType::Filtering)), + (38, sampler(SamplerBindingType::Filtering)), )); } @@ -516,7 +523,7 @@ fn layout_entries( struct MeshPipelineViewLayoutParams { clustered_forward_buffer_binding_type: BufferBindingType, visibility_ranges_buffer_binding_type: BufferBindingType, - environment_map_entries: [BindGroupLayoutEntryBuilder; 4], + environment_map_entries: [BindGroupLayoutEntryBuilder; 3], irradiance_volume_entries: [BindGroupLayoutEntryBuilder; 2], clustered_decal_entries: Option<[BindGroupLayoutEntryBuilder; 3]>, is_oit_supported: bool, @@ -594,11 +601,14 @@ impl MeshPipelineViewLayouts { entries: core::mem::take(&mut entries[0]), }, binding_array_layout: BindGroupLayoutDescriptor { - label: format!("{}_binding_array", layout_key.label()).into(), + label: layout_key + .label() + .replace("mesh_view_layout:", "mesh_view_layout_binding_array:") + .into(), entries: core::mem::take(&mut entries[1]), }, empty_layout: BindGroupLayoutDescriptor { - label: format!("{}_empty", layout_key.label()).into(), + label: "mesh_view_layout_empty".into(), entries: vec![], }, } @@ -608,7 +618,7 @@ impl MeshPipelineViewLayouts { #[derive(Component)] pub struct MeshViewBindGroup { pub main: BindGroup, - pub main_offsets: SmallVec<[u32; 8]>, + pub main_offsets: ArrayVec, pub binding_array: BindGroup, pub empty: BindGroup, } @@ -622,10 +632,11 @@ pub fn prepare_mesh_view_bind_groups( ), mesh_pipeline: Res, shadow_samplers: Res, - (light_meta, global_clusterable_object_meta,fog_meta,view_uniforms, environment_map_uniform): ( + (light_meta, global_clusterable_object_meta, fog_meta, view_uniforms): ( Res, - Res,Res, - Res, Res + Res, + Res, + Res, ), views: Query<( Entity, @@ -650,7 +661,6 @@ pub fn prepare_mesh_view_bind_groups( Option<&ViewFogUniformOffset>, Option<&ViewScreenSpaceReflectionsUniformOffset>, Option<&ViewContactShadowsUniformOffset>, - Option<&ViewEnvironmentMapUniformOffset>, Option<&OrderIndependentTransparencySettingsOffset>, ), )>, @@ -729,7 +739,6 @@ pub fn prepare_mesh_view_bind_groups( view_fog_offset, view_ssr_offset, view_contact_shadows_offset, - view_environment_map_offset, view_oit_settings_offset, ), ) in &views @@ -753,11 +762,11 @@ pub fn prepare_mesh_view_bind_groups( let tonemap_in_shader = camera.is_none_or(|camera| !camera.hdr); let mut layout_key = MeshPipelineViewLayoutKey::from(*msaa) | MeshPipelineViewLayoutKey::from(prepass_textures); - let mut offsets: SmallVec<[u32; 8]> = smallvec![ + let mut offsets = ArrayVec::from_iter([ view_uniform_offset.offset, view_lights_offset.offset, - **view_light_probes_offset - ]; + **view_light_probes_offset, + ]); entries = entries.extend_with_indices(( (0, view_binding.clone()), @@ -803,24 +812,15 @@ pub fn prepare_mesh_view_bind_groups( .extend_with_indices(((16, contact_shadows_buffer.0.binding().unwrap()),)); } - if let Some(view_environment_map_offset) = view_environment_map_offset - && render_view_environment_maps.is_some() - { - layout_key |= MeshPipelineViewLayoutKey::ENVIRONMENT_MAP; - offsets.push(**view_environment_map_offset); - entries = entries - .extend_with_indices(((18, environment_map_uniform.binding().unwrap()),)); - } - if let Some(view_oit_settings_offset) = view_oit_settings_offset { layout_key |= MeshPipelineViewLayoutKey::OIT_ENABLED; offsets.push(view_oit_settings_offset.offset); entries = entries.extend_with_indices(( - (27, oit_buffers.settings.binding().unwrap()), - (28, oit_buffers.nodes_capacity.binding().unwrap()), - (29, oit_buffers.nodes.binding().unwrap()), - (30, oit_buffers.heads.binding().unwrap()), - (31, oit_buffers.atomic_counter.binding().unwrap()), + (26, oit_buffers.settings.binding().unwrap()), + (27, oit_buffers.nodes_capacity.binding().unwrap()), + (28, oit_buffers.nodes.binding().unwrap()), + (29, oit_buffers.heads.binding().unwrap()), + (30, oit_buffers.atomic_counter.binding().unwrap()), )); } @@ -832,9 +832,9 @@ pub fn prepare_mesh_view_bind_groups( { layout_key |= MeshPipelineViewLayoutKey::ATMOSPHERE; entries = entries.extend_with_indices(( - (32, &atmosphere_textures.transmittance_lut.default_view), - (33, &***atmosphere_sampler), - (34, atmosphere_buffer_binding), + (31, &atmosphere_textures.transmittance_lut.default_view), + (32, &***atmosphere_sampler), + (33, atmosphere_buffer_binding), )); } @@ -844,14 +844,17 @@ pub fn prepare_mesh_view_bind_groups( .get(&blue_noise.texture) .expect("STBN texture is added unconditionally with at least a placeholder") .texture_view; - entries = entries.extend_with_indices(((35, stbn_view),)); + entries = entries.extend_with_indices(((34, stbn_view),)); } if tonemap_in_shader { layout_key |= MeshPipelineViewLayoutKey::TONEMAP_IN_SHADER; let lut_bindings = get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image); - entries = entries.extend_with_indices(((19, lut_bindings.0), (20, lut_bindings.1))); + entries = entries.extend_with_indices(( + (TONEMAPPING_LUT_TEXTURE_BINDING_INDEX, lut_bindings.0), + (TONEMAPPING_LUT_SAMPLER_BINDING_INDEX, lut_bindings.1), + )); } if let Some(ssao_resources) = ssao_resources { @@ -871,7 +874,7 @@ pub fn prepare_mesh_view_bind_groups( .unwrap_or(&fallback_image_zero.sampler); entries = - entries.extend_with_indices(((25, transmission_view), (26, transmission_sampler))); + entries.extend_with_indices(((24, transmission_view), (25, transmission_sampler))); // When using WebGL, we can't have a multisampled texture with `TEXTURE_BINDING` // See https://github.com/gfx-rs/wgpu/issues/5263 @@ -881,7 +884,7 @@ pub fn prepare_mesh_view_bind_groups( for (binding, index) in prepass_bindings .iter() .map(Option::as_ref) - .zip([21, 22, 23, 24]) + .zip([20, 21, 22, 23]) .flat_map(|(b, i)| b.map(|b| (b, i))) { entries = entries.extend_with_indices(((index, binding),)); @@ -897,7 +900,7 @@ pub fn prepare_mesh_view_bind_groups( &fallback_image.d2_array.texture_view, &fallback_image.d2_array.sampler, )); - entries = entries.extend_with_indices(((36, ltc_view), (37, ltc_sampler))); + entries = entries.extend_with_indices(((35, ltc_view), (36, ltc_sampler))); } // DFG LUT @@ -906,11 +909,13 @@ pub fn prepare_mesh_view_bind_groups( .get(&dfg_lut.texture) .map(|img| (&img.texture_view, &img.sampler)) .unwrap_or((&fallback_image.d2.texture_view, &fallback_image.d2.sampler)); - entries = entries.extend_with_indices(((38, dfg_view), (39, dfg_sampler))); + entries = entries.extend_with_indices(((37, dfg_view), (38, dfg_sampler))); } let environment_map_bind_group_entries = render_view_environment_maps.map(|render_view_environment_maps| { + layout_key |= MeshPipelineViewLayoutKey::ENVIRONMENT_MAP; + RenderViewEnvironmentMapBindGroupEntries::get( Some(render_view_environment_maps), &images, diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl index 6903b6a75c56c..4d3f16fb0ca13 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl @@ -64,74 +64,70 @@ const VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE: u32 = 64u; @group(0) @binding(17) var screen_space_ambient_occlusion_texture: texture_2d; #endif -#ifdef ENVIRONMENT_MAP -@group(0) @binding(18) var environment_map_uniform: types::EnvironmentMapUniform; -#endif - #ifdef TONEMAP_IN_SHADER // NB: If you change these, make sure to update `tonemapping_shared.wgsl` too. -@group(0) @binding(19) var dt_lut_texture: texture_3d; -@group(0) @binding(20) var dt_lut_sampler: sampler; +@group(0) @binding(18) var dt_lut_texture: texture_3d; +@group(0) @binding(19) var dt_lut_sampler: sampler; #endif #ifdef MULTISAMPLED #ifdef DEPTH_PREPASS -@group(0) @binding(21) var depth_prepass_texture: texture_depth_multisampled_2d; +@group(0) @binding(20) var depth_prepass_texture: texture_depth_multisampled_2d; #endif // DEPTH_PREPASS #ifdef NORMAL_PREPASS -@group(0) @binding(22) var normal_prepass_texture: texture_multisampled_2d; +@group(0) @binding(21) var normal_prepass_texture: texture_multisampled_2d; #endif // NORMAL_PREPASS #ifdef MOTION_VECTOR_PREPASS -@group(0) @binding(23) var motion_vector_prepass_texture: texture_multisampled_2d; +@group(0) @binding(22) var motion_vector_prepass_texture: texture_multisampled_2d; #endif // MOTION_VECTOR_PREPASS #else // MULTISAMPLED #ifdef DEPTH_PREPASS -@group(0) @binding(21) var depth_prepass_texture: texture_depth_2d; +@group(0) @binding(20) var depth_prepass_texture: texture_depth_2d; #endif // DEPTH_PREPASS #ifdef NORMAL_PREPASS -@group(0) @binding(22) var normal_prepass_texture: texture_2d; +@group(0) @binding(21) var normal_prepass_texture: texture_2d; #endif // NORMAL_PREPASS #ifdef MOTION_VECTOR_PREPASS -@group(0) @binding(23) var motion_vector_prepass_texture: texture_2d; +@group(0) @binding(22) var motion_vector_prepass_texture: texture_2d; #endif // MOTION_VECTOR_PREPASS #endif // MULTISAMPLED #ifdef DEFERRED_PREPASS -@group(0) @binding(24) var deferred_prepass_texture: texture_2d; +@group(0) @binding(23) var deferred_prepass_texture: texture_2d; #endif // DEFERRED_PREPASS -@group(0) @binding(25) var view_transmission_texture: texture_2d; -@group(0) @binding(26) var view_transmission_sampler: sampler; +@group(0) @binding(24) var view_transmission_texture: texture_2d; +@group(0) @binding(25) var view_transmission_sampler: sampler; #ifdef OIT_ENABLED -@group(0) @binding(27) var oit_settings: types::OrderIndependentTransparencySettings; -@group(0) @binding(28) var oit_nodes_capacity: u32; -@group(0) @binding(29) var oit_nodes: array; -@group(0) @binding(30) var oit_heads: array>; -@group(0) @binding(31) var oit_atomic_counter: atomic; +@group(0) @binding(26) var oit_settings: types::OrderIndependentTransparencySettings; +@group(0) @binding(27) var oit_nodes_capacity: u32; +@group(0) @binding(28) var oit_nodes: array; +@group(0) @binding(29) var oit_heads: array>; +@group(0) @binding(30) var oit_atomic_counter: atomic; #endif // OIT_ENABLED #ifdef ATMOSPHERE -@group(0) @binding(32) var atmosphere_transmittance_texture: texture_2d; -@group(0) @binding(33) var atmosphere_transmittance_sampler: sampler; -@group(0) @binding(34) var atmosphere: atmosphere_types::Atmosphere; +@group(0) @binding(31) var atmosphere_transmittance_texture: texture_2d; +@group(0) @binding(32) var atmosphere_transmittance_sampler: sampler; +@group(0) @binding(33) var atmosphere: atmosphere_types::Atmosphere; #endif // ATMOSPHERE #ifdef BLUE_NOISE_TEXTURE -@group(0) @binding(35) var blue_noise_texture: texture_2d_array; +@group(0) @binding(34) var blue_noise_texture: texture_2d_array; #endif // BLUE_NOISE_TEXTURE #ifdef AREA_LIGHT_LUTS -@group(0) @binding(36) var area_light_luts: texture_2d_array; -@group(0) @binding(37) var area_light_luts_sampler: sampler; +@group(0) @binding(35) var area_light_luts: texture_2d_array; +@group(0) @binding(36) var area_light_luts_sampler: sampler; #endif #ifdef DFG_LUT -@group(0) @binding(38) var dfg_lut: texture_2d; -@group(0) @binding(39) var dfg_lut_sampler: sampler; +@group(0) @binding(37) var dfg_lut: texture_2d; +@group(0) @binding(38) var dfg_lut_sampler: sampler; #endif // DFG_LUT #ifdef ENVIRONMENT_MAP diff --git a/crates/bevy_pbr/src/render/mesh_view_types.wgsl b/crates/bevy_pbr/src/render/mesh_view_types.wgsl index da0e6e5a81964..9e5a7f132f975 100644 --- a/crates/bevy_pbr/src/render/mesh_view_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_types.wgsl @@ -160,7 +160,7 @@ struct LightProbe { }; struct LightProbes { - // This must match `MAX_VIEW_REFLECTION_PROBES` on the Rust side. + // This must match `MAX_VIEW_LIGHT_PROBES` on the Rust side. reflection_probes: array, irradiance_volumes: array, reflection_probe_count: i32, @@ -171,6 +171,7 @@ struct LightProbes { // The smallest valid mipmap level for the specular environment cubemap // associated with the view. smallest_specular_mip_level_for_view: u32, + view_rotation: vec4, // The intensity of the environment map associated with the view. intensity_for_view: f32, // Whether the environment map attached to the view affects the diffuse