# Objective - Fix working on macOS, iOS, Android on main - Fixes #11281 - Fixes #11282 - Fixes #11283 - Fixes #11299 ## Solution - Revert #10057
This commit is contained in:
		
							parent
							
								
									64a15f1b10
								
							
						
					
					
						commit
						3d996639a0
					
				
							
								
								
									
										11
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Cargo.toml
									
									
									
									
									
								
							@ -2456,17 +2456,6 @@ name = "fallback_image"
 | 
				
			|||||||
path = "examples/shader/fallback_image.rs"
 | 
					path = "examples/shader/fallback_image.rs"
 | 
				
			||||||
doc-scrape-examples = true
 | 
					doc-scrape-examples = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[example]]
 | 
					 | 
				
			||||||
name = "reflection_probes"
 | 
					 | 
				
			||||||
path = "examples/3d/reflection_probes.rs"
 | 
					 | 
				
			||||||
doc-scrape-examples = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.metadata.example.reflection_probes]
 | 
					 | 
				
			||||||
name = "Reflection Probes"
 | 
					 | 
				
			||||||
description = "Demonstrates reflection probes"
 | 
					 | 
				
			||||||
category = "3D Rendering"
 | 
					 | 
				
			||||||
wasm = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.metadata.example.fallback_image]
 | 
					[package.metadata.example.fallback_image]
 | 
				
			||||||
hidden = true
 | 
					hidden = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@ -7,8 +7,8 @@
 | 
				
			|||||||
    @group(0) @binding(3) var dt_lut_texture: texture_3d<f32>;
 | 
					    @group(0) @binding(3) var dt_lut_texture: texture_3d<f32>;
 | 
				
			||||||
    @group(0) @binding(4) var dt_lut_sampler: sampler;
 | 
					    @group(0) @binding(4) var dt_lut_sampler: sampler;
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    @group(0) @binding(16) var dt_lut_texture: texture_3d<f32>;
 | 
					    @group(0) @binding(15) var dt_lut_texture: texture_3d<f32>;
 | 
				
			||||||
    @group(0) @binding(17) var dt_lut_sampler: sampler;
 | 
					    @group(0) @binding(16) var dt_lut_sampler: sampler;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn sample_current_lut(p: vec3<f32>) -> vec3<f32> {
 | 
					fn sample_current_lut(p: vec3<f32>) -> vec3<f32> {
 | 
				
			||||||
 | 
				
			|||||||
@ -58,10 +58,7 @@ symphonia-vorbis = ["bevy_audio/symphonia-vorbis"]
 | 
				
			|||||||
symphonia-wav = ["bevy_audio/symphonia-wav"]
 | 
					symphonia-wav = ["bevy_audio/symphonia-wav"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Shader formats
 | 
					# Shader formats
 | 
				
			||||||
shader_format_glsl = [
 | 
					shader_format_glsl = ["bevy_render/shader_format_glsl"]
 | 
				
			||||||
  "bevy_render/shader_format_glsl",
 | 
					 | 
				
			||||||
  "bevy_pbr?/shader_format_glsl",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
shader_format_spirv = ["bevy_render/shader_format_spirv"]
 | 
					shader_format_spirv = ["bevy_render/shader_format_spirv"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
serialize = [
 | 
					serialize = [
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,6 @@ keywords = ["bevy"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[features]
 | 
					[features]
 | 
				
			||||||
webgl = []
 | 
					webgl = []
 | 
				
			||||||
shader_format_glsl = ["naga_oil/glsl"]
 | 
					 | 
				
			||||||
pbr_transmission_textures = []
 | 
					pbr_transmission_textures = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
@ -35,17 +34,8 @@ fixedbitset = "0.4"
 | 
				
			|||||||
# direct dependency required for derive macro
 | 
					# direct dependency required for derive macro
 | 
				
			||||||
bytemuck = { version = "1", features = ["derive"] }
 | 
					bytemuck = { version = "1", features = ["derive"] }
 | 
				
			||||||
radsort = "0.1"
 | 
					radsort = "0.1"
 | 
				
			||||||
smallvec = "1.6"
 | 
					naga_oil = "0.11"
 | 
				
			||||||
thread_local = "1.0"
 | 
					thread_local = "1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
 | 
					 | 
				
			||||||
naga_oil = { version = "0.11" }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 | 
					 | 
				
			||||||
# Omit the `glsl` feature in non-WebAssembly by default.
 | 
					 | 
				
			||||||
naga_oil = { version = "0.11", default-features = false, features = [
 | 
					 | 
				
			||||||
  "test_shader",
 | 
					 | 
				
			||||||
] }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[lints]
 | 
					[lints]
 | 
				
			||||||
workspace = true
 | 
					workspace = true
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,4 @@
 | 
				
			|||||||
use crate::{
 | 
					use crate::{MeshPipeline, MeshViewBindGroup, ScreenSpaceAmbientOcclusionSettings};
 | 
				
			||||||
    environment_map::RenderViewEnvironmentMaps, MeshPipeline, MeshViewBindGroup,
 | 
					 | 
				
			||||||
    ScreenSpaceAmbientOcclusionSettings, ViewLightProbesUniformOffset,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use bevy_app::prelude::*;
 | 
					use bevy_app::prelude::*;
 | 
				
			||||||
use bevy_asset::{load_internal_asset, Handle};
 | 
					use bevy_asset::{load_internal_asset, Handle};
 | 
				
			||||||
use bevy_core_pipeline::{
 | 
					use bevy_core_pipeline::{
 | 
				
			||||||
@ -17,17 +14,25 @@ use bevy_render::{
 | 
				
			|||||||
    extract_component::{
 | 
					    extract_component::{
 | 
				
			||||||
        ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
 | 
					        ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
 | 
					    render_asset::RenderAssets,
 | 
				
			||||||
    render_resource::binding_types::uniform_buffer,
 | 
					    render_graph::{NodeRunError, RenderGraphContext, ViewNode, ViewNodeRunner},
 | 
				
			||||||
    render_resource::*,
 | 
					    render_resource::{
 | 
				
			||||||
 | 
					        binding_types::uniform_buffer, Operations, PipelineCache, RenderPassDescriptor,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    renderer::{RenderContext, RenderDevice},
 | 
					    renderer::{RenderContext, RenderDevice},
 | 
				
			||||||
    texture::BevyDefault,
 | 
					    texture::Image,
 | 
				
			||||||
    view::{ExtractedView, ViewTarget, ViewUniformOffset},
 | 
					    view::{ViewTarget, ViewUniformOffset},
 | 
				
			||||||
    Render, RenderApp, RenderSet,
 | 
					    Render, RenderSet,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use bevy_render::{
 | 
				
			||||||
 | 
					    render_graph::RenderGraphApp, render_resource::*, texture::BevyDefault, view::ExtractedView,
 | 
				
			||||||
 | 
					    RenderApp,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    MeshPipelineKey, ShadowFilteringMethod, ViewFogUniformOffset, ViewLightsUniformOffset,
 | 
					    EnvironmentMapLight, MeshPipelineKey, ShadowFilteringMethod, ViewFogUniformOffset,
 | 
				
			||||||
 | 
					    ViewLightsUniformOffset,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct DeferredPbrLightingPlugin;
 | 
					pub struct DeferredPbrLightingPlugin;
 | 
				
			||||||
@ -146,7 +151,6 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
 | 
				
			|||||||
        &'static ViewUniformOffset,
 | 
					        &'static ViewUniformOffset,
 | 
				
			||||||
        &'static ViewLightsUniformOffset,
 | 
					        &'static ViewLightsUniformOffset,
 | 
				
			||||||
        &'static ViewFogUniformOffset,
 | 
					        &'static ViewFogUniformOffset,
 | 
				
			||||||
        &'static ViewLightProbesUniformOffset,
 | 
					 | 
				
			||||||
        &'static MeshViewBindGroup,
 | 
					        &'static MeshViewBindGroup,
 | 
				
			||||||
        &'static ViewTarget,
 | 
					        &'static ViewTarget,
 | 
				
			||||||
        &'static DeferredLightingIdDepthTexture,
 | 
					        &'static DeferredLightingIdDepthTexture,
 | 
				
			||||||
@ -161,7 +165,6 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
 | 
				
			|||||||
            view_uniform_offset,
 | 
					            view_uniform_offset,
 | 
				
			||||||
            view_lights_offset,
 | 
					            view_lights_offset,
 | 
				
			||||||
            view_fog_offset,
 | 
					            view_fog_offset,
 | 
				
			||||||
            view_light_probes_offset,
 | 
					 | 
				
			||||||
            mesh_view_bind_group,
 | 
					            mesh_view_bind_group,
 | 
				
			||||||
            target,
 | 
					            target,
 | 
				
			||||||
            deferred_lighting_id_depth_texture,
 | 
					            deferred_lighting_id_depth_texture,
 | 
				
			||||||
@ -215,7 +218,6 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
 | 
				
			|||||||
                view_uniform_offset.offset,
 | 
					                view_uniform_offset.offset,
 | 
				
			||||||
                view_lights_offset.offset,
 | 
					                view_lights_offset.offset,
 | 
				
			||||||
                view_fog_offset.offset,
 | 
					                view_fog_offset.offset,
 | 
				
			||||||
                **view_light_probes_offset,
 | 
					 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        render_pass.set_bind_group(1, &bind_group_1, &[]);
 | 
					        render_pass.set_bind_group(1, &bind_group_1, &[]);
 | 
				
			||||||
@ -401,6 +403,7 @@ pub fn prepare_deferred_lighting_pipelines(
 | 
				
			|||||||
            &ExtractedView,
 | 
					            &ExtractedView,
 | 
				
			||||||
            Option<&Tonemapping>,
 | 
					            Option<&Tonemapping>,
 | 
				
			||||||
            Option<&DebandDither>,
 | 
					            Option<&DebandDither>,
 | 
				
			||||||
 | 
					            Option<&EnvironmentMapLight>,
 | 
				
			||||||
            Option<&ShadowFilteringMethod>,
 | 
					            Option<&ShadowFilteringMethod>,
 | 
				
			||||||
            Has<ScreenSpaceAmbientOcclusionSettings>,
 | 
					            Has<ScreenSpaceAmbientOcclusionSettings>,
 | 
				
			||||||
            (
 | 
					            (
 | 
				
			||||||
@ -408,20 +411,20 @@ pub fn prepare_deferred_lighting_pipelines(
 | 
				
			|||||||
                Has<DepthPrepass>,
 | 
					                Has<DepthPrepass>,
 | 
				
			||||||
                Has<MotionVectorPrepass>,
 | 
					                Has<MotionVectorPrepass>,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            Has<RenderViewEnvironmentMaps>,
 | 
					 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        With<DeferredPrepass>,
 | 
					        With<DeferredPrepass>,
 | 
				
			||||||
    >,
 | 
					    >,
 | 
				
			||||||
 | 
					    images: Res<RenderAssets<Image>>,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    for (
 | 
					    for (
 | 
				
			||||||
        entity,
 | 
					        entity,
 | 
				
			||||||
        view,
 | 
					        view,
 | 
				
			||||||
        tonemapping,
 | 
					        tonemapping,
 | 
				
			||||||
        dither,
 | 
					        dither,
 | 
				
			||||||
 | 
					        environment_map,
 | 
				
			||||||
        shadow_filter_method,
 | 
					        shadow_filter_method,
 | 
				
			||||||
        ssao,
 | 
					        ssao,
 | 
				
			||||||
        (normal_prepass, depth_prepass, motion_vector_prepass),
 | 
					        (normal_prepass, depth_prepass, motion_vector_prepass),
 | 
				
			||||||
        has_environment_maps,
 | 
					 | 
				
			||||||
    ) in &views
 | 
					    ) in &views
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        let mut view_key = MeshPipelineKey::from_hdr(view.hdr);
 | 
					        let mut view_key = MeshPipelineKey::from_hdr(view.hdr);
 | 
				
			||||||
@ -468,10 +471,11 @@ pub fn prepare_deferred_lighting_pipelines(
 | 
				
			|||||||
            view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
 | 
					            view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // We don't need to check to see whether the environment map is loaded
 | 
					        let environment_map_loaded = match environment_map {
 | 
				
			||||||
        // because [`gather_light_probes`] already checked that for us before
 | 
					            Some(environment_map) => environment_map.is_loaded(&images),
 | 
				
			||||||
        // adding the [`RenderViewEnvironmentMaps`] component.
 | 
					            None => false,
 | 
				
			||||||
        if has_environment_maps {
 | 
					        };
 | 
				
			||||||
 | 
					        if environment_map_loaded {
 | 
				
			||||||
            view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
 | 
					            view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										50
									
								
								crates/bevy_pbr/src/environment_map/environment_map.wgsl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								crates/bevy_pbr/src/environment_map/environment_map.wgsl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					#define_import_path bevy_pbr::environment_map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#import bevy_pbr::mesh_view_bindings as bindings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct EnvironmentMapLight {
 | 
				
			||||||
 | 
					    diffuse: vec3<f32>,
 | 
				
			||||||
 | 
					    specular: vec3<f32>,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn environment_map_light(
 | 
				
			||||||
 | 
					    perceptual_roughness: f32,
 | 
				
			||||||
 | 
					    roughness: f32,
 | 
				
			||||||
 | 
					    diffuse_color: vec3<f32>,
 | 
				
			||||||
 | 
					    NdotV: f32,
 | 
				
			||||||
 | 
					    f_ab: vec2<f32>,
 | 
				
			||||||
 | 
					    N: vec3<f32>,
 | 
				
			||||||
 | 
					    R: vec3<f32>,
 | 
				
			||||||
 | 
					    F0: vec3<f32>,
 | 
				
			||||||
 | 
					) -> EnvironmentMapLight {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
 | 
				
			||||||
 | 
					    // Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform
 | 
				
			||||||
 | 
					    // because textureNumLevels() does not work on WebGL2
 | 
				
			||||||
 | 
					    let radiance_level = perceptual_roughness * f32(bindings::lights.environment_map_smallest_specular_mip_level);
 | 
				
			||||||
 | 
					    let irradiance = textureSampleLevel(bindings::environment_map_diffuse, bindings::environment_map_sampler, vec3(N.xy, -N.z), 0.0).rgb;
 | 
				
			||||||
 | 
					    let radiance = textureSampleLevel(bindings::environment_map_specular, bindings::environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // No real world material has specular values under 0.02, so we use this range as a
 | 
				
			||||||
 | 
					    // "pre-baked specular occlusion" that extinguishes the fresnel term, for artistic control.
 | 
				
			||||||
 | 
					    // See: https://google.github.io/filament/Filament.html#specularocclusion
 | 
				
			||||||
 | 
					    let specular_occlusion = saturate(dot(F0, vec3(50.0 * 0.33)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Multiscattering approximation: https://www.jcgt.org/published/0008/01/03/paper.pdf
 | 
				
			||||||
 | 
					    // Useful reference: https://bruop.github.io/ibl
 | 
				
			||||||
 | 
					    let Fr = max(vec3(1.0 - roughness), F0) - F0;
 | 
				
			||||||
 | 
					    let kS = F0 + Fr * pow(1.0 - NdotV, 5.0);
 | 
				
			||||||
 | 
					    let Ess = f_ab.x + f_ab.y;
 | 
				
			||||||
 | 
					    let FssEss = kS * Ess * specular_occlusion;
 | 
				
			||||||
 | 
					    let Ems = 1.0 - Ess;
 | 
				
			||||||
 | 
					    let Favg = F0 + (1.0 - F0) / 21.0;
 | 
				
			||||||
 | 
					    let Fms = FssEss * Favg / (1.0 - Ems * Favg);
 | 
				
			||||||
 | 
					    let FmsEms = Fms * Ems;
 | 
				
			||||||
 | 
					    let Edss = 1.0 - (FssEss + FmsEms);
 | 
				
			||||||
 | 
					    let kD = diffuse_color * Edss;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var out: EnvironmentMapLight;
 | 
				
			||||||
 | 
					    out.diffuse = (FmsEms + kD) * irradiance;
 | 
				
			||||||
 | 
					    out.specular = FssEss * radiance;
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										91
									
								
								crates/bevy_pbr/src/environment_map/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								crates/bevy_pbr/src/environment_map/mod.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					use bevy_app::{App, Plugin};
 | 
				
			||||||
 | 
					use bevy_asset::{load_internal_asset, Handle};
 | 
				
			||||||
 | 
					use bevy_core_pipeline::prelude::Camera3d;
 | 
				
			||||||
 | 
					use bevy_ecs::{prelude::Component, query::With};
 | 
				
			||||||
 | 
					use bevy_reflect::Reflect;
 | 
				
			||||||
 | 
					use bevy_render::{
 | 
				
			||||||
 | 
					    extract_component::{ExtractComponent, ExtractComponentPlugin},
 | 
				
			||||||
 | 
					    render_asset::RenderAssets,
 | 
				
			||||||
 | 
					    render_resource::{
 | 
				
			||||||
 | 
					        binding_types::{sampler, texture_cube},
 | 
				
			||||||
 | 
					        *,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    texture::{FallbackImageCubemap, Image},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub const ENVIRONMENT_MAP_SHADER_HANDLE: Handle<Shader> =
 | 
				
			||||||
 | 
					    Handle::weak_from_u128(154476556247605696);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct EnvironmentMapPlugin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Plugin for EnvironmentMapPlugin {
 | 
				
			||||||
 | 
					    fn build(&self, app: &mut App) {
 | 
				
			||||||
 | 
					        load_internal_asset!(
 | 
				
			||||||
 | 
					            app,
 | 
				
			||||||
 | 
					            ENVIRONMENT_MAP_SHADER_HANDLE,
 | 
				
			||||||
 | 
					            "environment_map.wgsl",
 | 
				
			||||||
 | 
					            Shader::from_wgsl
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.register_type::<EnvironmentMapLight>()
 | 
				
			||||||
 | 
					            .add_plugins(ExtractComponentPlugin::<EnvironmentMapLight>::default());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Environment map based ambient lighting representing light from distant scenery.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// When added to a 3D camera, this component adds indirect light
 | 
				
			||||||
 | 
					/// to every point of the scene (including inside, enclosed areas) based on
 | 
				
			||||||
 | 
					/// an environment cubemap texture. This is similar to [`crate::AmbientLight`], but
 | 
				
			||||||
 | 
					/// higher quality, and is intended for outdoor scenes.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The environment map must be prefiltered into a diffuse and specular cubemap based on the
 | 
				
			||||||
 | 
					/// [split-sum approximation](https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf).
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// To prefilter your environment map, you can use `KhronosGroup`'s [glTF-IBL-Sampler](https://github.com/KhronosGroup/glTF-IBL-Sampler).
 | 
				
			||||||
 | 
					/// The diffuse map uses the Lambertian distribution, and the specular map uses the GGX distribution.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// `KhronosGroup` also has several prefiltered environment maps that can be found [here](https://github.com/KhronosGroup/glTF-Sample-Environments).
 | 
				
			||||||
 | 
					#[derive(Component, Reflect, Clone, ExtractComponent)]
 | 
				
			||||||
 | 
					#[extract_component_filter(With<Camera3d>)]
 | 
				
			||||||
 | 
					pub struct EnvironmentMapLight {
 | 
				
			||||||
 | 
					    pub diffuse_map: Handle<Image>,
 | 
				
			||||||
 | 
					    pub specular_map: Handle<Image>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl EnvironmentMapLight {
 | 
				
			||||||
 | 
					    /// Whether or not all textures necessary to use the environment map
 | 
				
			||||||
 | 
					    /// have been loaded by the asset server.
 | 
				
			||||||
 | 
					    pub fn is_loaded(&self, images: &RenderAssets<Image>) -> bool {
 | 
				
			||||||
 | 
					        images.get(&self.diffuse_map).is_some() && images.get(&self.specular_map).is_some()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn get_bindings<'a>(
 | 
				
			||||||
 | 
					    environment_map_light: Option<&EnvironmentMapLight>,
 | 
				
			||||||
 | 
					    images: &'a RenderAssets<Image>,
 | 
				
			||||||
 | 
					    fallback_image_cubemap: &'a FallbackImageCubemap,
 | 
				
			||||||
 | 
					) -> (&'a TextureView, &'a TextureView, &'a Sampler) {
 | 
				
			||||||
 | 
					    let (diffuse_map, specular_map) = match (
 | 
				
			||||||
 | 
					        environment_map_light.and_then(|env_map| images.get(&env_map.diffuse_map)),
 | 
				
			||||||
 | 
					        environment_map_light.and_then(|env_map| images.get(&env_map.specular_map)),
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        (Some(diffuse_map), Some(specular_map)) => {
 | 
				
			||||||
 | 
					            (&diffuse_map.texture_view, &specular_map.texture_view)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        _ => (
 | 
				
			||||||
 | 
					            &fallback_image_cubemap.texture_view,
 | 
				
			||||||
 | 
					            &fallback_image_cubemap.texture_view,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (diffuse_map, specular_map, &fallback_image_cubemap.sampler)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn get_bind_group_layout_entries() -> [BindGroupLayoutEntryBuilder; 3] {
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					        texture_cube(TextureSampleType::Float { filterable: true }),
 | 
				
			||||||
 | 
					        texture_cube(TextureSampleType::Float { filterable: true }),
 | 
				
			||||||
 | 
					        sampler(SamplerBindingType::Filtering),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -3,10 +3,10 @@ pub mod wireframe;
 | 
				
			|||||||
mod alpha;
 | 
					mod alpha;
 | 
				
			||||||
mod bundle;
 | 
					mod bundle;
 | 
				
			||||||
pub mod deferred;
 | 
					pub mod deferred;
 | 
				
			||||||
 | 
					mod environment_map;
 | 
				
			||||||
mod extended_material;
 | 
					mod extended_material;
 | 
				
			||||||
mod fog;
 | 
					mod fog;
 | 
				
			||||||
mod light;
 | 
					mod light;
 | 
				
			||||||
mod light_probe;
 | 
					 | 
				
			||||||
mod lightmap;
 | 
					mod lightmap;
 | 
				
			||||||
mod material;
 | 
					mod material;
 | 
				
			||||||
mod parallax;
 | 
					mod parallax;
 | 
				
			||||||
@ -17,10 +17,10 @@ mod ssao;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
pub use alpha::*;
 | 
					pub use alpha::*;
 | 
				
			||||||
pub use bundle::*;
 | 
					pub use bundle::*;
 | 
				
			||||||
 | 
					pub use environment_map::EnvironmentMapLight;
 | 
				
			||||||
pub use extended_material::*;
 | 
					pub use extended_material::*;
 | 
				
			||||||
pub use fog::*;
 | 
					pub use fog::*;
 | 
				
			||||||
pub use light::*;
 | 
					pub use light::*;
 | 
				
			||||||
pub use light_probe::*;
 | 
					 | 
				
			||||||
pub use lightmap::*;
 | 
					pub use lightmap::*;
 | 
				
			||||||
pub use material::*;
 | 
					pub use material::*;
 | 
				
			||||||
pub use parallax::*;
 | 
					pub use parallax::*;
 | 
				
			||||||
@ -37,12 +37,9 @@ pub mod prelude {
 | 
				
			|||||||
            DirectionalLightBundle, MaterialMeshBundle, PbrBundle, PointLightBundle,
 | 
					            DirectionalLightBundle, MaterialMeshBundle, PbrBundle, PointLightBundle,
 | 
				
			||||||
            SpotLightBundle,
 | 
					            SpotLightBundle,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        environment_map::EnvironmentMapLight,
 | 
				
			||||||
        fog::{FogFalloff, FogSettings},
 | 
					        fog::{FogFalloff, FogSettings},
 | 
				
			||||||
        light::{AmbientLight, DirectionalLight, PointLight, SpotLight},
 | 
					        light::{AmbientLight, DirectionalLight, PointLight, SpotLight},
 | 
				
			||||||
        light_probe::{
 | 
					 | 
				
			||||||
            environment_map::{EnvironmentMapLight, ReflectionProbeBundle},
 | 
					 | 
				
			||||||
            LightProbe,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        material::{Material, MaterialPlugin},
 | 
					        material::{Material, MaterialPlugin},
 | 
				
			||||||
        parallax::ParallaxMappingMethod,
 | 
					        parallax::ParallaxMappingMethod,
 | 
				
			||||||
        pbr_material::StandardMaterial,
 | 
					        pbr_material::StandardMaterial,
 | 
				
			||||||
@ -74,6 +71,7 @@ use bevy_render::{
 | 
				
			|||||||
    ExtractSchedule, Render, RenderApp, RenderSet,
 | 
					    ExtractSchedule, Render, RenderApp, RenderSet,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use bevy_transform::TransformSystem;
 | 
					use bevy_transform::TransformSystem;
 | 
				
			||||||
 | 
					use environment_map::EnvironmentMapPlugin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::deferred::DeferredPbrLightingPlugin;
 | 
					use crate::deferred::DeferredPbrLightingPlugin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -257,12 +255,12 @@ impl Plugin for PbrPlugin {
 | 
				
			|||||||
                    ..Default::default()
 | 
					                    ..Default::default()
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                ScreenSpaceAmbientOcclusionPlugin,
 | 
					                ScreenSpaceAmbientOcclusionPlugin,
 | 
				
			||||||
 | 
					                EnvironmentMapPlugin,
 | 
				
			||||||
                ExtractResourcePlugin::<AmbientLight>::default(),
 | 
					                ExtractResourcePlugin::<AmbientLight>::default(),
 | 
				
			||||||
                FogPlugin,
 | 
					                FogPlugin,
 | 
				
			||||||
                ExtractResourcePlugin::<DefaultOpaqueRendererMethod>::default(),
 | 
					                ExtractResourcePlugin::<DefaultOpaqueRendererMethod>::default(),
 | 
				
			||||||
                ExtractComponentPlugin::<ShadowFilteringMethod>::default(),
 | 
					                ExtractComponentPlugin::<ShadowFilteringMethod>::default(),
 | 
				
			||||||
                LightmapPlugin,
 | 
					                LightmapPlugin,
 | 
				
			||||||
                LightProbePlugin,
 | 
					 | 
				
			||||||
            ))
 | 
					            ))
 | 
				
			||||||
            .configure_sets(
 | 
					            .configure_sets(
 | 
				
			||||||
                PostUpdate,
 | 
					                PostUpdate,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,405 +0,0 @@
 | 
				
			|||||||
//! Environment maps and reflection probes.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! An *environment map* consists of a pair of diffuse and specular cubemaps
 | 
					 | 
				
			||||||
//! that together reflect the static surrounding area of a region in space. When
 | 
					 | 
				
			||||||
//! available, the PBR shader uses these to apply diffuse light and calculate
 | 
					 | 
				
			||||||
//! specular reflections.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! Environment maps come in two flavors, depending on what other components the
 | 
					 | 
				
			||||||
//! entities they're attached to have:
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! 1. If attached to a view, they represent the objects located a very far
 | 
					 | 
				
			||||||
//!    distance from the view, in a similar manner to a skybox. Essentially, these
 | 
					 | 
				
			||||||
//!    *view environment maps* represent a higher-quality replacement for
 | 
					 | 
				
			||||||
//!    [`crate::AmbientLight`] for outdoor scenes. The indirect light from such
 | 
					 | 
				
			||||||
//!    environment maps are added to every point of the scene, including
 | 
					 | 
				
			||||||
//!    interior enclosed areas.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! 2. If attached to a [`LightProbe`], environment maps represent the immediate
 | 
					 | 
				
			||||||
//!    surroundings of a specific location in the scene. These types of
 | 
					 | 
				
			||||||
//!    environment maps are known as *reflection probes*.
 | 
					 | 
				
			||||||
//!    [`ReflectionProbeBundle`] is available as a mechanism to conveniently add
 | 
					 | 
				
			||||||
//!    these to a scene.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! Typically, environment maps are static (i.e. "baked", calculated ahead of
 | 
					 | 
				
			||||||
//! time) and so only reflect fixed static geometry. The environment maps must
 | 
					 | 
				
			||||||
//! be pre-filtered into a pair of cubemaps, one for the diffuse component and
 | 
					 | 
				
			||||||
//! one for the specular component, according to the [split-sum approximation].
 | 
					 | 
				
			||||||
//! To pre-filter your environment map, you can use the [glTF IBL Sampler] or
 | 
					 | 
				
			||||||
//! its [artist-friendly UI]. The diffuse map uses the Lambertian distribution,
 | 
					 | 
				
			||||||
//! while the specular map uses the GGX distribution.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! The Khronos Group has [several pre-filtered environment maps] available for
 | 
					 | 
				
			||||||
//! you to use.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! Currently, reflection probes (i.e. environment maps attached to light
 | 
					 | 
				
			||||||
//! probes) use binding arrays (also known as bindless textures) and
 | 
					 | 
				
			||||||
//! consequently aren't supported on WebGL2 or WebGPU. Reflection probes are
 | 
					 | 
				
			||||||
//! also unsupported if GLSL is in use, due to `naga` limitations. Environment
 | 
					 | 
				
			||||||
//! maps attached to views are, however, supported on all platforms.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! [split-sum approximation]: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! [glTF IBL Sampler]: https://github.com/KhronosGroup/glTF-IBL-Sampler
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! [artist-friendly UI]: https://github.com/pcwalton/gltf-ibl-sampler-egui
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! [several pre-filtered environment maps]: https://github.com/KhronosGroup/glTF-Sample-Environments
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use bevy_asset::{AssetId, Handle};
 | 
					 | 
				
			||||||
use bevy_ecs::{
 | 
					 | 
				
			||||||
    bundle::Bundle, component::Component, query::QueryItem, system::lifetimeless::Read,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use bevy_reflect::Reflect;
 | 
					 | 
				
			||||||
use bevy_render::{
 | 
					 | 
				
			||||||
    extract_instances::ExtractInstance,
 | 
					 | 
				
			||||||
    prelude::SpatialBundle,
 | 
					 | 
				
			||||||
    render_asset::RenderAssets,
 | 
					 | 
				
			||||||
    render_resource::{
 | 
					 | 
				
			||||||
        binding_types, BindGroupLayoutEntryBuilder, Sampler, SamplerBindingType, Shader,
 | 
					 | 
				
			||||||
        TextureSampleType, TextureView,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    texture::{FallbackImage, Image},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
use bevy_utils::HashMap;
 | 
					 | 
				
			||||||
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
use std::num::NonZeroU32;
 | 
					 | 
				
			||||||
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
use std::ops::Deref;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use crate::LightProbe;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A handle to the environment map helper shader.
 | 
					 | 
				
			||||||
pub const ENVIRONMENT_MAP_SHADER_HANDLE: Handle<Shader> =
 | 
					 | 
				
			||||||
    Handle::weak_from_u128(154476556247605696);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A pair of cubemap textures that represent the surroundings of a specific
 | 
					 | 
				
			||||||
/// area in space.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// See [`crate::environment_map`] for detailed information.
 | 
					 | 
				
			||||||
#[derive(Clone, Component, Reflect)]
 | 
					 | 
				
			||||||
pub struct EnvironmentMapLight {
 | 
					 | 
				
			||||||
    /// The blurry image that represents diffuse radiance surrounding a region.
 | 
					 | 
				
			||||||
    pub diffuse_map: Handle<Image>,
 | 
					 | 
				
			||||||
    /// The typically-sharper, mipmapped image that represents specular radiance
 | 
					 | 
				
			||||||
    /// surrounding a region.
 | 
					 | 
				
			||||||
    pub specular_map: Handle<Image>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Like [`EnvironmentMapLight`], but contains asset IDs instead of handles.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// This is for use in the render app.
 | 
					 | 
				
			||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 | 
					 | 
				
			||||||
pub(crate) struct EnvironmentMapIds {
 | 
					 | 
				
			||||||
    /// The blurry image that represents diffuse radiance surrounding a region.
 | 
					 | 
				
			||||||
    pub(crate) diffuse: AssetId<Image>,
 | 
					 | 
				
			||||||
    /// The typically-sharper, mipmapped image that represents specular radiance
 | 
					 | 
				
			||||||
    /// surrounding a region.
 | 
					 | 
				
			||||||
    pub(crate) specular: AssetId<Image>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A bundle that contains everything needed to make an entity a reflection
 | 
					 | 
				
			||||||
/// probe.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// A reflection probe is a type of environment map that specifies the light
 | 
					 | 
				
			||||||
/// surrounding a region in space. For more information, see
 | 
					 | 
				
			||||||
/// [`crate::environment_map`].
 | 
					 | 
				
			||||||
#[derive(Bundle)]
 | 
					 | 
				
			||||||
pub struct ReflectionProbeBundle {
 | 
					 | 
				
			||||||
    /// Contains a transform that specifies the position of this reflection probe in space.
 | 
					 | 
				
			||||||
    pub spatial: SpatialBundle,
 | 
					 | 
				
			||||||
    /// Marks this environment map as a light probe.
 | 
					 | 
				
			||||||
    pub light_probe: LightProbe,
 | 
					 | 
				
			||||||
    /// The cubemaps that make up this environment map.
 | 
					 | 
				
			||||||
    pub environment_map: EnvironmentMapLight,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A component, part of the render world, that stores the mapping from
 | 
					 | 
				
			||||||
/// environment map ID to texture index in the diffuse and specular binding
 | 
					 | 
				
			||||||
/// arrays.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// Cubemap textures belonging to environment maps are collected into binding
 | 
					 | 
				
			||||||
/// arrays, and the index of each texture is presented to the shader for runtime
 | 
					 | 
				
			||||||
/// lookup.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// This component is attached to each view in the render world, because each
 | 
					 | 
				
			||||||
/// view may have a different set of cubemaps that it considers and therefore
 | 
					 | 
				
			||||||
/// cubemap indices are per-view.
 | 
					 | 
				
			||||||
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
#[derive(Component, Default)]
 | 
					 | 
				
			||||||
pub struct RenderViewEnvironmentMaps {
 | 
					 | 
				
			||||||
    /// The list of environment maps presented to the shader, in order.
 | 
					 | 
				
			||||||
    binding_index_to_cubemap: Vec<EnvironmentMapIds>,
 | 
					 | 
				
			||||||
    /// The reverse of `binding_index_to_cubemap`: a map from the environment
 | 
					 | 
				
			||||||
    /// map IDs to the index in `binding_index_to_cubemap`.
 | 
					 | 
				
			||||||
    cubemap_to_binding_index: HashMap<EnvironmentMapIds, u32>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A component, part of the render world, that stores the ID of the environment
 | 
					 | 
				
			||||||
/// map attached to each view.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// This is a simplified version of the structure used when binding arrays are
 | 
					 | 
				
			||||||
/// not available on the current platform.
 | 
					 | 
				
			||||||
#[cfg(any(feature = "shader_format_glsl", target_arch = "wasm32"))]
 | 
					 | 
				
			||||||
#[derive(Component, Default)]
 | 
					 | 
				
			||||||
pub struct RenderViewEnvironmentMaps {
 | 
					 | 
				
			||||||
    /// The environment map attached to the view, if any.
 | 
					 | 
				
			||||||
    cubemap: Option<EnvironmentMapIds>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// All the bind group entries necessary for PBR shaders to access the
 | 
					 | 
				
			||||||
/// environment maps exposed to a view.
 | 
					 | 
				
			||||||
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
pub(crate) struct RenderViewBindGroupEntries<'a> {
 | 
					 | 
				
			||||||
    /// A texture view of each diffuse cubemap, in the same order that they are
 | 
					 | 
				
			||||||
    /// supplied to the view (i.e. in the same order as
 | 
					 | 
				
			||||||
    /// `binding_index_to_cubemap` in [`RenderViewEnvironmentMaps`]).
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This is a vector of `wgpu::TextureView`s. But we don't want to import
 | 
					 | 
				
			||||||
    /// `wgpu` in this crate, so we refer to it indirectly like this.
 | 
					 | 
				
			||||||
    diffuse_texture_views: Vec<&'a <TextureView as Deref>::Target>,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// As above, but for specular cubemaps.
 | 
					 | 
				
			||||||
    specular_texture_views: Vec<&'a <TextureView as Deref>::Target>,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// The sampler used to sample elements of both `diffuse_texture_views` and
 | 
					 | 
				
			||||||
    /// `specular_texture_views`.
 | 
					 | 
				
			||||||
    pub(crate) sampler: &'a Sampler,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// All the bind group entries necessary for PBR shaders to access the
 | 
					 | 
				
			||||||
/// environment maps exposed to a view.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// This is the version used when binding arrays are not available on the
 | 
					 | 
				
			||||||
/// current platform.
 | 
					 | 
				
			||||||
#[cfg(any(feature = "shader_format_glsl", target_arch = "wasm32"))]
 | 
					 | 
				
			||||||
pub(crate) struct RenderViewBindGroupEntries<'a> {
 | 
					 | 
				
			||||||
    /// The texture view of the view's diffuse cubemap.
 | 
					 | 
				
			||||||
    diffuse_texture_view: &'a TextureView,
 | 
					 | 
				
			||||||
    /// The texture view of the view's specular cubemap.
 | 
					 | 
				
			||||||
    specular_texture_view: &'a TextureView,
 | 
					 | 
				
			||||||
    /// The sampler used to sample elements of both `diffuse_texture_view` and
 | 
					 | 
				
			||||||
    /// `specular_texture_view`.
 | 
					 | 
				
			||||||
    pub(crate) sampler: &'a Sampler,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl ExtractInstance for EnvironmentMapIds {
 | 
					 | 
				
			||||||
    type Data = Read<EnvironmentMapLight>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    type Filter = ();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn extract(item: QueryItem<'_, Self::Data>) -> Option<Self> {
 | 
					 | 
				
			||||||
        Some(EnvironmentMapIds {
 | 
					 | 
				
			||||||
            diffuse: item.diffuse_map.id(),
 | 
					 | 
				
			||||||
            specular: item.specular_map.id(),
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl RenderViewEnvironmentMaps {
 | 
					 | 
				
			||||||
    pub(crate) fn new() -> Self {
 | 
					 | 
				
			||||||
        Self::default()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
impl RenderViewEnvironmentMaps {
 | 
					 | 
				
			||||||
    /// Whether there are no environment maps associated with the view.
 | 
					 | 
				
			||||||
    pub(crate) fn is_empty(&self) -> bool {
 | 
					 | 
				
			||||||
        self.binding_index_to_cubemap.is_empty()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Adds a cubemap to the list of bindings, if it wasn't there already, and
 | 
					 | 
				
			||||||
    /// returns its index within that list.
 | 
					 | 
				
			||||||
    pub(crate) fn get_or_insert_cubemap(&mut self, cubemap_id: &EnvironmentMapIds) -> u32 {
 | 
					 | 
				
			||||||
        *self
 | 
					 | 
				
			||||||
            .cubemap_to_binding_index
 | 
					 | 
				
			||||||
            .entry(*cubemap_id)
 | 
					 | 
				
			||||||
            .or_insert_with(|| {
 | 
					 | 
				
			||||||
                let index = self.binding_index_to_cubemap.len() as u32;
 | 
					 | 
				
			||||||
                self.binding_index_to_cubemap.push(*cubemap_id);
 | 
					 | 
				
			||||||
                index
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(any(feature = "shader_format_glsl", target_arch = "wasm32"))]
 | 
					 | 
				
			||||||
impl RenderViewEnvironmentMaps {
 | 
					 | 
				
			||||||
    /// Returns true if there is no environment map for this view or false if
 | 
					 | 
				
			||||||
    /// there is such an environment map.
 | 
					 | 
				
			||||||
    pub(crate) fn is_empty(&self) -> bool {
 | 
					 | 
				
			||||||
        self.cubemap.is_none()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Sets the environment map attached to this view, replacing the previous
 | 
					 | 
				
			||||||
    /// one if any.
 | 
					 | 
				
			||||||
    pub(crate) fn get_or_insert_cubemap(&mut self, cubemap_id: &EnvironmentMapIds) -> u32 {
 | 
					 | 
				
			||||||
        self.cubemap = Some(*cubemap_id);
 | 
					 | 
				
			||||||
        0
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Returns the bind group layout entries for the environment map diffuse and
 | 
					 | 
				
			||||||
/// specular binding arrays respectively, in addition to the sampler.
 | 
					 | 
				
			||||||
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
pub(crate) fn get_bind_group_layout_entries() -> [BindGroupLayoutEntryBuilder; 3] {
 | 
					 | 
				
			||||||
    use crate::MAX_VIEW_REFLECTION_PROBES;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [
 | 
					 | 
				
			||||||
        binding_types::texture_cube(TextureSampleType::Float { filterable: true })
 | 
					 | 
				
			||||||
            .count(NonZeroU32::new(MAX_VIEW_REFLECTION_PROBES as _).unwrap()),
 | 
					 | 
				
			||||||
        binding_types::texture_cube(TextureSampleType::Float { filterable: true })
 | 
					 | 
				
			||||||
            .count(NonZeroU32::new(MAX_VIEW_REFLECTION_PROBES as _).unwrap()),
 | 
					 | 
				
			||||||
        binding_types::sampler(SamplerBindingType::Filtering),
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Returns the bind group layout entries for the environment map diffuse and
 | 
					 | 
				
			||||||
/// specular textures respectively, in addition to the sampler.
 | 
					 | 
				
			||||||
#[cfg(any(feature = "shader_format_glsl", target_arch = "wasm32"))]
 | 
					 | 
				
			||||||
pub(crate) fn get_bind_group_layout_entries() -> [BindGroupLayoutEntryBuilder; 3] {
 | 
					 | 
				
			||||||
    [
 | 
					 | 
				
			||||||
        binding_types::texture_cube(TextureSampleType::Float { filterable: true }),
 | 
					 | 
				
			||||||
        binding_types::texture_cube(TextureSampleType::Float { filterable: true }),
 | 
					 | 
				
			||||||
        binding_types::sampler(SamplerBindingType::Filtering),
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'a> RenderViewBindGroupEntries<'a> {
 | 
					 | 
				
			||||||
    /// Looks up and returns the bindings for the environment map diffuse and
 | 
					 | 
				
			||||||
    /// specular binding arrays respectively, as well as the sampler.
 | 
					 | 
				
			||||||
    #[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
    pub(crate) fn get(
 | 
					 | 
				
			||||||
        render_view_environment_maps: Option<&RenderViewEnvironmentMaps>,
 | 
					 | 
				
			||||||
        images: &'a RenderAssets<Image>,
 | 
					 | 
				
			||||||
        fallback_image: &'a FallbackImage,
 | 
					 | 
				
			||||||
    ) -> RenderViewBindGroupEntries<'a> {
 | 
					 | 
				
			||||||
        use crate::MAX_VIEW_REFLECTION_PROBES;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut diffuse_texture_views = vec![];
 | 
					 | 
				
			||||||
        let mut specular_texture_views = vec![];
 | 
					 | 
				
			||||||
        let mut sampler = None;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if let Some(environment_maps) = render_view_environment_maps {
 | 
					 | 
				
			||||||
            for &cubemap_id in &environment_maps.binding_index_to_cubemap {
 | 
					 | 
				
			||||||
                add_texture_view(
 | 
					 | 
				
			||||||
                    &mut diffuse_texture_views,
 | 
					 | 
				
			||||||
                    &mut sampler,
 | 
					 | 
				
			||||||
                    cubemap_id.diffuse,
 | 
					 | 
				
			||||||
                    images,
 | 
					 | 
				
			||||||
                    fallback_image,
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                add_texture_view(
 | 
					 | 
				
			||||||
                    &mut specular_texture_views,
 | 
					 | 
				
			||||||
                    &mut sampler,
 | 
					 | 
				
			||||||
                    cubemap_id.specular,
 | 
					 | 
				
			||||||
                    images,
 | 
					 | 
				
			||||||
                    fallback_image,
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Pad out the bindings to the size of the binding array using fallback
 | 
					 | 
				
			||||||
        // textures. This is necessary on D3D12.
 | 
					 | 
				
			||||||
        diffuse_texture_views.resize(
 | 
					 | 
				
			||||||
            MAX_VIEW_REFLECTION_PROBES,
 | 
					 | 
				
			||||||
            &*fallback_image.cube.texture_view,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        specular_texture_views.resize(
 | 
					 | 
				
			||||||
            MAX_VIEW_REFLECTION_PROBES,
 | 
					 | 
				
			||||||
            &*fallback_image.cube.texture_view,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        RenderViewBindGroupEntries {
 | 
					 | 
				
			||||||
            diffuse_texture_views,
 | 
					 | 
				
			||||||
            specular_texture_views,
 | 
					 | 
				
			||||||
            sampler: sampler.unwrap_or(&fallback_image.cube.sampler),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Looks up and returns the bindings for the environment map diffuse and
 | 
					 | 
				
			||||||
    /// specular bindings respectively, as well as the sampler.
 | 
					 | 
				
			||||||
    #[cfg(any(feature = "shader_format_glsl", target_arch = "wasm32"))]
 | 
					 | 
				
			||||||
    pub(crate) fn get(
 | 
					 | 
				
			||||||
        render_view_environment_maps: Option<&RenderViewEnvironmentMaps>,
 | 
					 | 
				
			||||||
        images: &'a RenderAssets<Image>,
 | 
					 | 
				
			||||||
        fallback_image: &'a FallbackImage,
 | 
					 | 
				
			||||||
    ) -> RenderViewBindGroupEntries<'a> {
 | 
					 | 
				
			||||||
        if let Some(&RenderViewEnvironmentMaps {
 | 
					 | 
				
			||||||
            cubemap: Some(ref cubemap),
 | 
					 | 
				
			||||||
        }) = render_view_environment_maps
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if let (Some(diffuse_image), Some(specular_image)) =
 | 
					 | 
				
			||||||
                (images.get(cubemap.diffuse), images.get(cubemap.specular))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return RenderViewBindGroupEntries {
 | 
					 | 
				
			||||||
                    diffuse_texture_view: &diffuse_image.texture_view,
 | 
					 | 
				
			||||||
                    specular_texture_view: &specular_image.texture_view,
 | 
					 | 
				
			||||||
                    sampler: &diffuse_image.sampler,
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        RenderViewBindGroupEntries {
 | 
					 | 
				
			||||||
            diffuse_texture_view: &fallback_image.cube.texture_view,
 | 
					 | 
				
			||||||
            specular_texture_view: &fallback_image.cube.texture_view,
 | 
					 | 
				
			||||||
            sampler: &fallback_image.cube.sampler,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Adds a diffuse or specular texture view to the `texture_views` list, and
 | 
					 | 
				
			||||||
/// populates `sampler` if this is the first such view.
 | 
					 | 
				
			||||||
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
fn add_texture_view<'a>(
 | 
					 | 
				
			||||||
    texture_views: &mut Vec<&'a <TextureView as Deref>::Target>,
 | 
					 | 
				
			||||||
    sampler: &mut Option<&'a Sampler>,
 | 
					 | 
				
			||||||
    image_id: AssetId<Image>,
 | 
					 | 
				
			||||||
    images: &'a RenderAssets<Image>,
 | 
					 | 
				
			||||||
    fallback_image: &'a FallbackImage,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    match images.get(image_id) {
 | 
					 | 
				
			||||||
        None => {
 | 
					 | 
				
			||||||
            // Use the fallback image if the cubemap isn't loaded yet.
 | 
					 | 
				
			||||||
            texture_views.push(&*fallback_image.cube.texture_view);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Some(image) => {
 | 
					 | 
				
			||||||
            // If this is the first texture view, populate `sampler`.
 | 
					 | 
				
			||||||
            if sampler.is_none() {
 | 
					 | 
				
			||||||
                *sampler = Some(&image.sampler);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            texture_views.push(&*image.texture_view);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
impl<'a> RenderViewBindGroupEntries<'a> {
 | 
					 | 
				
			||||||
    /// Returns a list of texture views of each diffuse cubemap, in binding
 | 
					 | 
				
			||||||
    /// order.
 | 
					 | 
				
			||||||
    pub(crate) fn diffuse_texture_views(&'a self) -> &'a [&'a <TextureView as Deref>::Target] {
 | 
					 | 
				
			||||||
        self.diffuse_texture_views.as_slice()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns a list of texture views of each specular cubemap, in binding
 | 
					 | 
				
			||||||
    /// order.
 | 
					 | 
				
			||||||
    pub(crate) fn specular_texture_views(&'a self) -> &'a [&'a <TextureView as Deref>::Target] {
 | 
					 | 
				
			||||||
        self.specular_texture_views.as_slice()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(any(feature = "shader_format_glsl", target_arch = "wasm32"))]
 | 
					 | 
				
			||||||
impl<'a> RenderViewBindGroupEntries<'a> {
 | 
					 | 
				
			||||||
    /// Returns the texture view corresponding to the view's diffuse cubemap.
 | 
					 | 
				
			||||||
    pub(crate) fn diffuse_texture_views(&self) -> &'a TextureView {
 | 
					 | 
				
			||||||
        self.diffuse_texture_view
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns the texture view corresponding to the view's specular cubemap.
 | 
					 | 
				
			||||||
    pub(crate) fn specular_texture_views(&self) -> &'a TextureView {
 | 
					 | 
				
			||||||
        self.specular_texture_view
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,171 +0,0 @@
 | 
				
			|||||||
#define_import_path bevy_pbr::environment_map
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#import bevy_pbr::mesh_view_bindings as bindings
 | 
					 | 
				
			||||||
#import bevy_pbr::mesh_view_bindings::light_probes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct EnvironmentMapLight {
 | 
					 | 
				
			||||||
    diffuse: vec3<f32>,
 | 
					 | 
				
			||||||
    specular: vec3<f32>,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct EnvironmentMapRadiances {
 | 
					 | 
				
			||||||
    irradiance: vec3<f32>,
 | 
					 | 
				
			||||||
    radiance: vec3<f32>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Define two versions of this function, one for the case in which there are
 | 
					 | 
				
			||||||
// multiple light probes and one for the case in which only the view light probe
 | 
					 | 
				
			||||||
// is present.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn compute_radiances(
 | 
					 | 
				
			||||||
    perceptual_roughness: f32,
 | 
					 | 
				
			||||||
    N: vec3<f32>,
 | 
					 | 
				
			||||||
    R: vec3<f32>,
 | 
					 | 
				
			||||||
    world_position: vec3<f32>,
 | 
					 | 
				
			||||||
) -> EnvironmentMapRadiances {
 | 
					 | 
				
			||||||
    var radiances: EnvironmentMapRadiances;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Search for a reflection probe that contains the fragment.
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    // TODO: Interpolate between multiple reflection probes.
 | 
					 | 
				
			||||||
    var cubemap_index: i32 = -1;
 | 
					 | 
				
			||||||
    for (var reflection_probe_index: i32 = 0;
 | 
					 | 
				
			||||||
            reflection_probe_index < light_probes.reflection_probe_count;
 | 
					 | 
				
			||||||
            reflection_probe_index += 1) {
 | 
					 | 
				
			||||||
        let reflection_probe = light_probes.reflection_probes[reflection_probe_index];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Unpack the inverse transform.
 | 
					 | 
				
			||||||
        let inverse_transpose_transform = mat4x4<f32>(
 | 
					 | 
				
			||||||
            reflection_probe.inverse_transpose_transform[0],
 | 
					 | 
				
			||||||
            reflection_probe.inverse_transpose_transform[1],
 | 
					 | 
				
			||||||
            reflection_probe.inverse_transpose_transform[2],
 | 
					 | 
				
			||||||
            vec4<f32>(0.0, 0.0, 0.0, 1.0));
 | 
					 | 
				
			||||||
        let inverse_transform = transpose(inverse_transpose_transform);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Check to see if the transformed point is inside the unit cube
 | 
					 | 
				
			||||||
        // centered at the origin.
 | 
					 | 
				
			||||||
        let probe_space_pos = (inverse_transform * vec4<f32>(world_position, 1.0)).xyz;
 | 
					 | 
				
			||||||
        if (all(abs(probe_space_pos) <= vec3(0.5))) {
 | 
					 | 
				
			||||||
            cubemap_index = reflection_probe.cubemap_index;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // If we didn't find a reflection probe, use the view environment map if applicable.
 | 
					 | 
				
			||||||
    if (cubemap_index < 0) {
 | 
					 | 
				
			||||||
        cubemap_index = light_probes.view_cubemap_index;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // If there's no cubemap, bail out.
 | 
					 | 
				
			||||||
    if (cubemap_index < 0) {
 | 
					 | 
				
			||||||
        radiances.irradiance = vec3(0.0);
 | 
					 | 
				
			||||||
        radiances.radiance = vec3(0.0);
 | 
					 | 
				
			||||||
        return radiances;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
 | 
					 | 
				
			||||||
    let radiance_level = perceptual_roughness * f32(textureNumLevels(bindings::specular_environment_maps[cubemap_index]) - 1u);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef LIGHTMAP
 | 
					 | 
				
			||||||
    radiances.irradiance = textureSampleLevel(
 | 
					 | 
				
			||||||
        bindings::diffuse_environment_maps[cubemap_index],
 | 
					 | 
				
			||||||
        bindings::environment_map_sampler,
 | 
					 | 
				
			||||||
        vec3(N.xy, -N.z),
 | 
					 | 
				
			||||||
        0.0).rgb;
 | 
					 | 
				
			||||||
#endif  // LIGHTMAP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    radiances.radiance = textureSampleLevel(
 | 
					 | 
				
			||||||
        bindings::specular_environment_maps[cubemap_index],
 | 
					 | 
				
			||||||
        bindings::environment_map_sampler,
 | 
					 | 
				
			||||||
        vec3(R.xy, -R.z),
 | 
					 | 
				
			||||||
        radiance_level).rgb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return radiances;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#else   // MULTIPLE_LIGHT_PROBES_IN_ARRAY
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn compute_radiances(
 | 
					 | 
				
			||||||
    perceptual_roughness: f32,
 | 
					 | 
				
			||||||
    N: vec3<f32>,
 | 
					 | 
				
			||||||
    R: vec3<f32>,
 | 
					 | 
				
			||||||
    world_position: vec3<f32>,
 | 
					 | 
				
			||||||
) -> EnvironmentMapRadiances {
 | 
					 | 
				
			||||||
    var radiances: EnvironmentMapRadiances;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (light_probes.view_cubemap_index < 0) {
 | 
					 | 
				
			||||||
        radiances.irradiance = vec3(0.0);
 | 
					 | 
				
			||||||
        radiances.radiance = vec3(0.0);
 | 
					 | 
				
			||||||
        return radiances;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
 | 
					 | 
				
			||||||
    // Technically we could use textureNumLevels(specular_environment_map) - 1 here, but we use a uniform
 | 
					 | 
				
			||||||
    // because textureNumLevels() does not work on WebGL2
 | 
					 | 
				
			||||||
    let radiance_level = perceptual_roughness * f32(light_probes.smallest_specular_mip_level_for_view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef LIGHTMAP
 | 
					 | 
				
			||||||
    radiances.irradiance = textureSampleLevel(
 | 
					 | 
				
			||||||
        bindings::diffuse_environment_map,
 | 
					 | 
				
			||||||
        bindings::environment_map_sampler,
 | 
					 | 
				
			||||||
        vec3(N.xy, -N.z),
 | 
					 | 
				
			||||||
        0.0).rgb;
 | 
					 | 
				
			||||||
#endif  // LIGHTMAP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    radiances.radiance = textureSampleLevel(
 | 
					 | 
				
			||||||
        bindings::specular_environment_map,
 | 
					 | 
				
			||||||
        bindings::environment_map_sampler,
 | 
					 | 
				
			||||||
        vec3(R.xy, -R.z),
 | 
					 | 
				
			||||||
        radiance_level).rgb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return radiances;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif  // MULTIPLE_LIGHT_PROBES_IN_ARRAY
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn environment_map_light(
 | 
					 | 
				
			||||||
    perceptual_roughness: f32,
 | 
					 | 
				
			||||||
    roughness: f32,
 | 
					 | 
				
			||||||
    diffuse_color: vec3<f32>,
 | 
					 | 
				
			||||||
    NdotV: f32,
 | 
					 | 
				
			||||||
    f_ab: vec2<f32>,
 | 
					 | 
				
			||||||
    N: vec3<f32>,
 | 
					 | 
				
			||||||
    R: vec3<f32>,
 | 
					 | 
				
			||||||
    F0: vec3<f32>,
 | 
					 | 
				
			||||||
    world_position: vec3<f32>,
 | 
					 | 
				
			||||||
) -> EnvironmentMapLight {
 | 
					 | 
				
			||||||
    let radiances = compute_radiances(perceptual_roughness, N, R, world_position);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // No real world material has specular values under 0.02, so we use this range as a
 | 
					 | 
				
			||||||
    // "pre-baked specular occlusion" that extinguishes the fresnel term, for artistic control.
 | 
					 | 
				
			||||||
    // See: https://google.github.io/filament/Filament.html#specularocclusion
 | 
					 | 
				
			||||||
    let specular_occlusion = saturate(dot(F0, vec3(50.0 * 0.33)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Multiscattering approximation: https://www.jcgt.org/published/0008/01/03/paper.pdf
 | 
					 | 
				
			||||||
    // Useful reference: https://bruop.github.io/ibl
 | 
					 | 
				
			||||||
    let Fr = max(vec3(1.0 - roughness), F0) - F0;
 | 
					 | 
				
			||||||
    let kS = F0 + Fr * pow(1.0 - NdotV, 5.0);
 | 
					 | 
				
			||||||
    let Ess = f_ab.x + f_ab.y;
 | 
					 | 
				
			||||||
    let FssEss = kS * Ess * specular_occlusion;
 | 
					 | 
				
			||||||
    let Ems = 1.0 - Ess;
 | 
					 | 
				
			||||||
    let Favg = F0 + (1.0 - F0) / 21.0;
 | 
					 | 
				
			||||||
    let Fms = FssEss * Favg / (1.0 - Ems * Favg);
 | 
					 | 
				
			||||||
    let FmsEms = Fms * Ems;
 | 
					 | 
				
			||||||
    let Edss = 1.0 - (FssEss + FmsEms);
 | 
					 | 
				
			||||||
    let kD = diffuse_color * Edss;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var out: EnvironmentMapLight;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // If there's a lightmap, ignore the diffuse component of the reflection
 | 
					 | 
				
			||||||
    // probe, so we don't double-count light.
 | 
					 | 
				
			||||||
#ifdef LIGHTMAP
 | 
					 | 
				
			||||||
    out.diffuse = vec3(0.0);
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
    out.diffuse = (FmsEms + kD) * radiances.irradiance;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    out.specular = FssEss * radiances.radiance;
 | 
					 | 
				
			||||||
    return out;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,405 +0,0 @@
 | 
				
			|||||||
//! Light probes for baked global illumination.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use bevy_app::{App, Plugin};
 | 
					 | 
				
			||||||
use bevy_asset::load_internal_asset;
 | 
					 | 
				
			||||||
use bevy_core_pipeline::core_3d::Camera3d;
 | 
					 | 
				
			||||||
use bevy_derive::{Deref, DerefMut};
 | 
					 | 
				
			||||||
use bevy_ecs::{
 | 
					 | 
				
			||||||
    component::Component,
 | 
					 | 
				
			||||||
    entity::Entity,
 | 
					 | 
				
			||||||
    query::With,
 | 
					 | 
				
			||||||
    reflect::ReflectComponent,
 | 
					 | 
				
			||||||
    schedule::IntoSystemConfigs,
 | 
					 | 
				
			||||||
    system::{Commands, Local, Query, Res, ResMut, Resource},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use bevy_math::{Affine3A, Mat4, Vec3A, Vec4};
 | 
					 | 
				
			||||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
 | 
					 | 
				
			||||||
use bevy_render::{
 | 
					 | 
				
			||||||
    extract_instances::ExtractInstancesPlugin,
 | 
					 | 
				
			||||||
    primitives::{Aabb, Frustum},
 | 
					 | 
				
			||||||
    render_asset::RenderAssets,
 | 
					 | 
				
			||||||
    render_resource::{DynamicUniformBuffer, Shader, ShaderType},
 | 
					 | 
				
			||||||
    renderer::{RenderDevice, RenderQueue},
 | 
					 | 
				
			||||||
    texture::Image,
 | 
					 | 
				
			||||||
    Extract, ExtractSchedule, Render, RenderApp, RenderSet,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use bevy_transform::prelude::GlobalTransform;
 | 
					 | 
				
			||||||
use bevy_utils::{EntityHashMap, FloatOrd};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use crate::light_probe::environment_map::{
 | 
					 | 
				
			||||||
    EnvironmentMapIds, EnvironmentMapLight, RenderViewEnvironmentMaps,
 | 
					 | 
				
			||||||
    ENVIRONMENT_MAP_SHADER_HANDLE,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub mod environment_map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// The maximum number of reflection probes that each view will consider.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// Because the fragment shader does a linear search through the list for each
 | 
					 | 
				
			||||||
/// fragment, this number needs to be relatively small.
 | 
					 | 
				
			||||||
pub const MAX_VIEW_REFLECTION_PROBES: usize = 8;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Adds support for light probes: cuboid bounding regions that apply global
 | 
					 | 
				
			||||||
/// illumination to objects within them.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// This also adds support for view environment maps: diffuse and specular
 | 
					 | 
				
			||||||
/// cubemaps applied to all objects that a view renders.
 | 
					 | 
				
			||||||
pub struct LightProbePlugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A marker component for a light probe, which is a cuboid region that provides
 | 
					 | 
				
			||||||
/// global illumination to all fragments inside it.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// The light probe range is conceptually a unit cube (1×1×1) centered on the
 | 
					 | 
				
			||||||
/// origin.  The [`bevy_transform::prelude::Transform`] applied to this entity
 | 
					 | 
				
			||||||
/// can scale, rotate, or translate that cube so that it contains all fragments
 | 
					 | 
				
			||||||
/// that should take this light probe into account.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// Note that a light probe will have no effect unless the entity contains some
 | 
					 | 
				
			||||||
/// kind of illumination. At present, the only supported type of illumination is
 | 
					 | 
				
			||||||
/// the [`EnvironmentMapLight`].
 | 
					 | 
				
			||||||
#[derive(Component, Debug, Clone, Copy, Default, Reflect)]
 | 
					 | 
				
			||||||
#[reflect(Component, Default)]
 | 
					 | 
				
			||||||
pub struct LightProbe;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A GPU type that stores information about a reflection probe.
 | 
					 | 
				
			||||||
#[derive(Clone, Copy, ShaderType, Default)]
 | 
					 | 
				
			||||||
struct RenderReflectionProbe {
 | 
					 | 
				
			||||||
    /// The transform from the world space to the model space. This is used to
 | 
					 | 
				
			||||||
    /// efficiently check for bounding box intersection.
 | 
					 | 
				
			||||||
    inverse_transpose_transform: [Vec4; 3],
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// The index of the environment map in the diffuse and specular cubemap
 | 
					 | 
				
			||||||
    /// binding arrays.
 | 
					 | 
				
			||||||
    cubemap_index: i32,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A per-view shader uniform that specifies all the light probes that the view
 | 
					 | 
				
			||||||
/// takes into account.
 | 
					 | 
				
			||||||
#[derive(ShaderType)]
 | 
					 | 
				
			||||||
pub struct LightProbesUniform {
 | 
					 | 
				
			||||||
    /// The list of applicable reflection probes, sorted from nearest to the
 | 
					 | 
				
			||||||
    /// camera to the farthest away from the camera.
 | 
					 | 
				
			||||||
    reflection_probes: [RenderReflectionProbe; MAX_VIEW_REFLECTION_PROBES],
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// The number of reflection probes in the list.
 | 
					 | 
				
			||||||
    reflection_probe_count: i32,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// The index of the diffuse and specular environment maps associated with
 | 
					 | 
				
			||||||
    /// the view itself. This is used as a fallback if no reflection probe in
 | 
					 | 
				
			||||||
    /// the list contains the fragment.
 | 
					 | 
				
			||||||
    view_cubemap_index: i32,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// The smallest valid mipmap level for the specular environment cubemap
 | 
					 | 
				
			||||||
    /// associated with the view.
 | 
					 | 
				
			||||||
    smallest_specular_mip_level_for_view: u32,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A map from each camera to the light probe uniform associated with it.
 | 
					 | 
				
			||||||
#[derive(Resource, Default, Deref, DerefMut)]
 | 
					 | 
				
			||||||
struct RenderLightProbes(EntityHashMap<Entity, LightProbesUniform>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A GPU buffer that stores information about all light probes.
 | 
					 | 
				
			||||||
#[derive(Resource, Default, Deref, DerefMut)]
 | 
					 | 
				
			||||||
pub struct LightProbesBuffer(DynamicUniformBuffer<LightProbesUniform>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A component attached to each camera in the render world that stores the
 | 
					 | 
				
			||||||
/// index of the [`LightProbesUniform`] in the [`LightProbesBuffer`].
 | 
					 | 
				
			||||||
#[derive(Component, Default, Deref, DerefMut)]
 | 
					 | 
				
			||||||
pub struct ViewLightProbesUniformOffset(u32);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Information that [`gather_light_probes`] keeps about each light probe.
 | 
					 | 
				
			||||||
#[derive(Clone, Copy)]
 | 
					 | 
				
			||||||
#[allow(dead_code)]
 | 
					 | 
				
			||||||
struct LightProbeInfo {
 | 
					 | 
				
			||||||
    // The transform from world space to light probe space.
 | 
					 | 
				
			||||||
    inverse_transform: Mat4,
 | 
					 | 
				
			||||||
    // The transform from light probe space to world space.
 | 
					 | 
				
			||||||
    affine_transform: Affine3A,
 | 
					 | 
				
			||||||
    // The diffuse and specular environment maps associated with this light
 | 
					 | 
				
			||||||
    // probe.
 | 
					 | 
				
			||||||
    environment_maps: EnvironmentMapIds,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl LightProbe {
 | 
					 | 
				
			||||||
    /// Creates a new light probe component.
 | 
					 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub fn new() -> Self {
 | 
					 | 
				
			||||||
        Self
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Plugin for LightProbePlugin {
 | 
					 | 
				
			||||||
    fn build(&self, app: &mut App) {
 | 
					 | 
				
			||||||
        load_internal_asset!(
 | 
					 | 
				
			||||||
            app,
 | 
					 | 
				
			||||||
            ENVIRONMENT_MAP_SHADER_HANDLE,
 | 
					 | 
				
			||||||
            "environment_map.wgsl",
 | 
					 | 
				
			||||||
            Shader::from_wgsl
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        app.register_type::<LightProbe>()
 | 
					 | 
				
			||||||
            .register_type::<EnvironmentMapLight>();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn finish(&self, app: &mut App) {
 | 
					 | 
				
			||||||
        let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        render_app
 | 
					 | 
				
			||||||
            .add_plugins(ExtractInstancesPlugin::<EnvironmentMapIds>::new())
 | 
					 | 
				
			||||||
            .init_resource::<LightProbesBuffer>()
 | 
					 | 
				
			||||||
            .init_resource::<RenderLightProbes>()
 | 
					 | 
				
			||||||
            .add_systems(ExtractSchedule, gather_light_probes)
 | 
					 | 
				
			||||||
            .add_systems(
 | 
					 | 
				
			||||||
                Render,
 | 
					 | 
				
			||||||
                upload_light_probes.in_set(RenderSet::PrepareResources),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Gathers up all light probes in the scene and assigns them to views,
 | 
					 | 
				
			||||||
/// performing frustum culling and distance sorting in the process.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// This populates the [`RenderLightProbes`] resource.
 | 
					 | 
				
			||||||
fn gather_light_probes(
 | 
					 | 
				
			||||||
    mut render_light_probes: ResMut<RenderLightProbes>,
 | 
					 | 
				
			||||||
    image_assets: Res<RenderAssets<Image>>,
 | 
					 | 
				
			||||||
    light_probe_query: Extract<Query<(&GlobalTransform, &EnvironmentMapLight), With<LightProbe>>>,
 | 
					 | 
				
			||||||
    view_query: Extract<
 | 
					 | 
				
			||||||
        Query<
 | 
					 | 
				
			||||||
            (
 | 
					 | 
				
			||||||
                Entity,
 | 
					 | 
				
			||||||
                &GlobalTransform,
 | 
					 | 
				
			||||||
                &Frustum,
 | 
					 | 
				
			||||||
                Option<&EnvironmentMapLight>,
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            With<Camera3d>,
 | 
					 | 
				
			||||||
        >,
 | 
					 | 
				
			||||||
    >,
 | 
					 | 
				
			||||||
    mut light_probes: Local<Vec<LightProbeInfo>>,
 | 
					 | 
				
			||||||
    mut view_light_probes: Local<Vec<LightProbeInfo>>,
 | 
					 | 
				
			||||||
    mut commands: Commands,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    // Create [`LightProbeInfo`] for every light probe in the scene.
 | 
					 | 
				
			||||||
    light_probes.clear();
 | 
					 | 
				
			||||||
    light_probes.extend(
 | 
					 | 
				
			||||||
        light_probe_query
 | 
					 | 
				
			||||||
            .iter()
 | 
					 | 
				
			||||||
            .filter_map(|query_row| LightProbeInfo::new(query_row, &image_assets)),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Build up the light probes uniform and the key table.
 | 
					 | 
				
			||||||
    render_light_probes.clear();
 | 
					 | 
				
			||||||
    for (view_entity, view_transform, view_frustum, view_environment_maps) in view_query.iter() {
 | 
					 | 
				
			||||||
        // Cull light probes outside the view frustum.
 | 
					 | 
				
			||||||
        view_light_probes.clear();
 | 
					 | 
				
			||||||
        view_light_probes.extend(
 | 
					 | 
				
			||||||
            light_probes
 | 
					 | 
				
			||||||
                .iter()
 | 
					 | 
				
			||||||
                .filter(|light_probe_info| light_probe_info.frustum_cull(view_frustum))
 | 
					 | 
				
			||||||
                .cloned(),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Sort by distance to camera.
 | 
					 | 
				
			||||||
        view_light_probes.sort_by_cached_key(|light_probe_info| {
 | 
					 | 
				
			||||||
            light_probe_info.camera_distance_sort_key(view_transform)
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Create the light probes uniform.
 | 
					 | 
				
			||||||
        let (light_probes_uniform, render_view_environment_maps) =
 | 
					 | 
				
			||||||
            LightProbesUniform::build(view_environment_maps, &view_light_probes, &image_assets);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Record the uniforms.
 | 
					 | 
				
			||||||
        render_light_probes.insert(view_entity, light_probes_uniform);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Record the per-view environment maps.
 | 
					 | 
				
			||||||
        let mut commands = commands.get_or_spawn(view_entity);
 | 
					 | 
				
			||||||
        if render_view_environment_maps.is_empty() {
 | 
					 | 
				
			||||||
            commands.remove::<RenderViewEnvironmentMaps>();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            commands.insert(render_view_environment_maps);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Uploads the result of [`gather_light_probes`] to the GPU.
 | 
					 | 
				
			||||||
fn upload_light_probes(
 | 
					 | 
				
			||||||
    mut commands: Commands,
 | 
					 | 
				
			||||||
    light_probes_uniforms: Res<RenderLightProbes>,
 | 
					 | 
				
			||||||
    mut light_probes_buffer: ResMut<LightProbesBuffer>,
 | 
					 | 
				
			||||||
    render_device: Res<RenderDevice>,
 | 
					 | 
				
			||||||
    render_queue: Res<RenderQueue>,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    // Get the uniform buffer writer.
 | 
					 | 
				
			||||||
    let Some(mut writer) =
 | 
					 | 
				
			||||||
        light_probes_buffer.get_writer(light_probes_uniforms.len(), &render_device, &render_queue)
 | 
					 | 
				
			||||||
    else {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Send each view's uniforms to the GPU.
 | 
					 | 
				
			||||||
    for (&view_entity, light_probes_uniform) in light_probes_uniforms.iter() {
 | 
					 | 
				
			||||||
        commands
 | 
					 | 
				
			||||||
            .entity(view_entity)
 | 
					 | 
				
			||||||
            .insert(ViewLightProbesUniformOffset(
 | 
					 | 
				
			||||||
                writer.write(light_probes_uniform),
 | 
					 | 
				
			||||||
            ));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Default for LightProbesUniform {
 | 
					 | 
				
			||||||
    fn default() -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            reflection_probes: [RenderReflectionProbe::default(); MAX_VIEW_REFLECTION_PROBES],
 | 
					 | 
				
			||||||
            reflection_probe_count: 0,
 | 
					 | 
				
			||||||
            view_cubemap_index: -1,
 | 
					 | 
				
			||||||
            smallest_specular_mip_level_for_view: 0,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl LightProbesUniform {
 | 
					 | 
				
			||||||
    /// Constructs a [`LightProbesUniform`] containing all the environment maps
 | 
					 | 
				
			||||||
    /// that fragments rendered by a single view need to consider.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// The `view_environment_maps` parameter describes the environment maps
 | 
					 | 
				
			||||||
    /// attached to the view. The `light_probes` parameter is expected to be the
 | 
					 | 
				
			||||||
    /// list of light probes in the scene, sorted by increasing view distance
 | 
					 | 
				
			||||||
    /// from the camera.
 | 
					 | 
				
			||||||
    fn build(
 | 
					 | 
				
			||||||
        view_environment_maps: Option<&EnvironmentMapLight>,
 | 
					 | 
				
			||||||
        light_probes: &[LightProbeInfo],
 | 
					 | 
				
			||||||
        image_assets: &RenderAssets<Image>,
 | 
					 | 
				
			||||||
    ) -> (LightProbesUniform, RenderViewEnvironmentMaps) {
 | 
					 | 
				
			||||||
        let mut render_view_environment_maps = RenderViewEnvironmentMaps::new();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Find the index of the cubemap associated with the view, and determine
 | 
					 | 
				
			||||||
        // its smallest mip level.
 | 
					 | 
				
			||||||
        let (mut view_cubemap_index, mut smallest_specular_mip_level_for_view) = (-1, 0);
 | 
					 | 
				
			||||||
        if let Some(EnvironmentMapLight {
 | 
					 | 
				
			||||||
            diffuse_map: diffuse_map_handle,
 | 
					 | 
				
			||||||
            specular_map: specular_map_handle,
 | 
					 | 
				
			||||||
        }) = view_environment_maps
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if let (Some(_), Some(specular_map)) = (
 | 
					 | 
				
			||||||
                image_assets.get(diffuse_map_handle),
 | 
					 | 
				
			||||||
                image_assets.get(specular_map_handle),
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                view_cubemap_index =
 | 
					 | 
				
			||||||
                    render_view_environment_maps.get_or_insert_cubemap(&EnvironmentMapIds {
 | 
					 | 
				
			||||||
                        diffuse: diffuse_map_handle.id(),
 | 
					 | 
				
			||||||
                        specular: specular_map_handle.id(),
 | 
					 | 
				
			||||||
                    }) as i32;
 | 
					 | 
				
			||||||
                smallest_specular_mip_level_for_view = specular_map.mip_level_count - 1;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Initialize the uniform to only contain the view environment map, if
 | 
					 | 
				
			||||||
        // applicable.
 | 
					 | 
				
			||||||
        let mut uniform = LightProbesUniform {
 | 
					 | 
				
			||||||
            reflection_probes: [RenderReflectionProbe::default(); MAX_VIEW_REFLECTION_PROBES],
 | 
					 | 
				
			||||||
            reflection_probe_count: light_probes.len().min(MAX_VIEW_REFLECTION_PROBES) as i32,
 | 
					 | 
				
			||||||
            view_cubemap_index,
 | 
					 | 
				
			||||||
            smallest_specular_mip_level_for_view,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Add reflection probes from the scene, if supported by the current
 | 
					 | 
				
			||||||
        // platform.
 | 
					 | 
				
			||||||
        uniform.maybe_gather_reflection_probes(&mut render_view_environment_maps, light_probes);
 | 
					 | 
				
			||||||
        (uniform, render_view_environment_maps)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Gathers up all reflection probes in the scene and writes them into this
 | 
					 | 
				
			||||||
    /// uniform and `render_view_environment_maps`.
 | 
					 | 
				
			||||||
    #[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
    fn maybe_gather_reflection_probes(
 | 
					 | 
				
			||||||
        &mut self,
 | 
					 | 
				
			||||||
        render_view_environment_maps: &mut RenderViewEnvironmentMaps,
 | 
					 | 
				
			||||||
        light_probes: &[LightProbeInfo],
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        for (reflection_probe, light_probe) in self
 | 
					 | 
				
			||||||
            .reflection_probes
 | 
					 | 
				
			||||||
            .iter_mut()
 | 
					 | 
				
			||||||
            .zip(light_probes.iter().take(MAX_VIEW_REFLECTION_PROBES))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // Determine the index of the cubemap in the binding array.
 | 
					 | 
				
			||||||
            let cubemap_index = render_view_environment_maps
 | 
					 | 
				
			||||||
                .get_or_insert_cubemap(&light_probe.environment_maps)
 | 
					 | 
				
			||||||
                as i32;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Transpose the inverse transform to compress the structure on the
 | 
					 | 
				
			||||||
            // GPU (from 4 `Vec4`s to 3 `Vec4`s). The shader will transpose it
 | 
					 | 
				
			||||||
            // to recover the original inverse transform.
 | 
					 | 
				
			||||||
            let inverse_transpose_transform = light_probe.inverse_transform.transpose();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Write in the reflection probe data.
 | 
					 | 
				
			||||||
            *reflection_probe = RenderReflectionProbe {
 | 
					 | 
				
			||||||
                inverse_transpose_transform: [
 | 
					 | 
				
			||||||
                    inverse_transpose_transform.x_axis,
 | 
					 | 
				
			||||||
                    inverse_transpose_transform.y_axis,
 | 
					 | 
				
			||||||
                    inverse_transpose_transform.z_axis,
 | 
					 | 
				
			||||||
                ],
 | 
					 | 
				
			||||||
                cubemap_index,
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// This is the version of `maybe_gather_reflection_probes` used on
 | 
					 | 
				
			||||||
    /// platforms in which binding arrays aren't available. It's simply a no-op.
 | 
					 | 
				
			||||||
    #[cfg(any(feature = "shader_format_glsl", target_arch = "wasm32"))]
 | 
					 | 
				
			||||||
    fn maybe_gather_reflection_probes(
 | 
					 | 
				
			||||||
        &mut self,
 | 
					 | 
				
			||||||
        _: &mut RenderViewEnvironmentMaps,
 | 
					 | 
				
			||||||
        _: &[LightProbeInfo],
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl LightProbeInfo {
 | 
					 | 
				
			||||||
    /// Given the set of light probe components, constructs and returns
 | 
					 | 
				
			||||||
    /// [`LightProbeInfo`]. This is done for every light probe in the scene
 | 
					 | 
				
			||||||
    /// every frame.
 | 
					 | 
				
			||||||
    fn new(
 | 
					 | 
				
			||||||
        (light_probe_transform, environment_map): (&GlobalTransform, &EnvironmentMapLight),
 | 
					 | 
				
			||||||
        image_assets: &RenderAssets<Image>,
 | 
					 | 
				
			||||||
    ) -> Option<LightProbeInfo> {
 | 
					 | 
				
			||||||
        if image_assets.get(&environment_map.diffuse_map).is_none()
 | 
					 | 
				
			||||||
            || image_assets.get(&environment_map.specular_map).is_none()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return None;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Some(LightProbeInfo {
 | 
					 | 
				
			||||||
            affine_transform: light_probe_transform.affine(),
 | 
					 | 
				
			||||||
            inverse_transform: light_probe_transform.compute_matrix().inverse(),
 | 
					 | 
				
			||||||
            environment_maps: EnvironmentMapIds {
 | 
					 | 
				
			||||||
                diffuse: environment_map.diffuse_map.id(),
 | 
					 | 
				
			||||||
                specular: environment_map.specular_map.id(),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns true if this light probe is in the viewing frustum of the camera
 | 
					 | 
				
			||||||
    /// or false if it isn't.
 | 
					 | 
				
			||||||
    fn frustum_cull(&self, view_frustum: &Frustum) -> bool {
 | 
					 | 
				
			||||||
        view_frustum.intersects_obb(
 | 
					 | 
				
			||||||
            &Aabb {
 | 
					 | 
				
			||||||
                center: Vec3A::default(),
 | 
					 | 
				
			||||||
                half_extents: Vec3A::splat(0.5),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            &self.affine_transform,
 | 
					 | 
				
			||||||
            true,
 | 
					 | 
				
			||||||
            false,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns the squared distance from this light probe to the camera,
 | 
					 | 
				
			||||||
    /// suitable for distance sorting.
 | 
					 | 
				
			||||||
    fn camera_distance_sort_key(&self, view_transform: &GlobalTransform) -> FloatOrd {
 | 
					 | 
				
			||||||
        FloatOrd(
 | 
					 | 
				
			||||||
            (self.affine_transform.translation - view_transform.translation_vec3a())
 | 
					 | 
				
			||||||
                .length_squared(),
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
use crate::{environment_map::RenderViewEnvironmentMaps, *};
 | 
					use crate::*;
 | 
				
			||||||
use bevy_app::{App, Plugin};
 | 
					use bevy_app::{App, Plugin};
 | 
				
			||||||
use bevy_asset::{Asset, AssetApp, AssetEvent, AssetId, AssetServer, Assets, Handle};
 | 
					use bevy_asset::{Asset, AssetApp, AssetEvent, AssetId, AssetServer, Assets, Handle};
 | 
				
			||||||
use bevy_core_pipeline::{
 | 
					use bevy_core_pipeline::{
 | 
				
			||||||
@ -466,12 +466,14 @@ pub fn queue_material_meshes<M: Material>(
 | 
				
			|||||||
    render_materials: Res<RenderMaterials<M>>,
 | 
					    render_materials: Res<RenderMaterials<M>>,
 | 
				
			||||||
    mut render_mesh_instances: ResMut<RenderMeshInstances>,
 | 
					    mut render_mesh_instances: ResMut<RenderMeshInstances>,
 | 
				
			||||||
    render_material_instances: Res<RenderMaterialInstances<M>>,
 | 
					    render_material_instances: Res<RenderMaterialInstances<M>>,
 | 
				
			||||||
 | 
					    images: Res<RenderAssets<Image>>,
 | 
				
			||||||
    render_lightmaps: Res<RenderLightmaps>,
 | 
					    render_lightmaps: Res<RenderLightmaps>,
 | 
				
			||||||
    mut views: Query<(
 | 
					    mut views: Query<(
 | 
				
			||||||
        &ExtractedView,
 | 
					        &ExtractedView,
 | 
				
			||||||
        &VisibleEntities,
 | 
					        &VisibleEntities,
 | 
				
			||||||
        Option<&Tonemapping>,
 | 
					        Option<&Tonemapping>,
 | 
				
			||||||
        Option<&DebandDither>,
 | 
					        Option<&DebandDither>,
 | 
				
			||||||
 | 
					        Option<&EnvironmentMapLight>,
 | 
				
			||||||
        Option<&ShadowFilteringMethod>,
 | 
					        Option<&ShadowFilteringMethod>,
 | 
				
			||||||
        Has<ScreenSpaceAmbientOcclusionSettings>,
 | 
					        Has<ScreenSpaceAmbientOcclusionSettings>,
 | 
				
			||||||
        (
 | 
					        (
 | 
				
			||||||
@ -487,7 +489,6 @@ pub fn queue_material_meshes<M: Material>(
 | 
				
			|||||||
        &mut RenderPhase<AlphaMask3d>,
 | 
					        &mut RenderPhase<AlphaMask3d>,
 | 
				
			||||||
        &mut RenderPhase<Transmissive3d>,
 | 
					        &mut RenderPhase<Transmissive3d>,
 | 
				
			||||||
        &mut RenderPhase<Transparent3d>,
 | 
					        &mut RenderPhase<Transparent3d>,
 | 
				
			||||||
        Has<RenderViewEnvironmentMaps>,
 | 
					 | 
				
			||||||
    )>,
 | 
					    )>,
 | 
				
			||||||
) where
 | 
					) where
 | 
				
			||||||
    M::Data: PartialEq + Eq + Hash + Clone,
 | 
					    M::Data: PartialEq + Eq + Hash + Clone,
 | 
				
			||||||
@ -497,6 +498,7 @@ pub fn queue_material_meshes<M: Material>(
 | 
				
			|||||||
        visible_entities,
 | 
					        visible_entities,
 | 
				
			||||||
        tonemapping,
 | 
					        tonemapping,
 | 
				
			||||||
        dither,
 | 
					        dither,
 | 
				
			||||||
 | 
					        environment_map,
 | 
				
			||||||
        shadow_filter_method,
 | 
					        shadow_filter_method,
 | 
				
			||||||
        ssao,
 | 
					        ssao,
 | 
				
			||||||
        (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
 | 
					        (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
 | 
				
			||||||
@ -507,7 +509,6 @@ pub fn queue_material_meshes<M: Material>(
 | 
				
			|||||||
        mut alpha_mask_phase,
 | 
					        mut alpha_mask_phase,
 | 
				
			||||||
        mut transmissive_phase,
 | 
					        mut transmissive_phase,
 | 
				
			||||||
        mut transparent_phase,
 | 
					        mut transparent_phase,
 | 
				
			||||||
        has_environment_maps,
 | 
					 | 
				
			||||||
    ) in &mut views
 | 
					    ) in &mut views
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial<M>>();
 | 
					        let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial<M>>();
 | 
				
			||||||
@ -538,7 +539,9 @@ pub fn queue_material_meshes<M: Material>(
 | 
				
			|||||||
            view_key |= MeshPipelineKey::TEMPORAL_JITTER;
 | 
					            view_key |= MeshPipelineKey::TEMPORAL_JITTER;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if has_environment_maps {
 | 
					        let environment_map_loaded = environment_map.is_some_and(|map| map.is_loaded(&images));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if environment_map_loaded {
 | 
				
			||||||
            view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
 | 
					            view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -196,6 +196,7 @@ pub struct GpuLights {
 | 
				
			|||||||
    n_directional_lights: u32,
 | 
					    n_directional_lights: u32,
 | 
				
			||||||
    // offset from spot light's light index to spot light's shadow map index
 | 
					    // offset from spot light's light index to spot light's shadow map index
 | 
				
			||||||
    spot_light_shadowmap_offset: i32,
 | 
					    spot_light_shadowmap_offset: i32,
 | 
				
			||||||
 | 
					    environment_map_smallest_specular_mip_level: u32,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NOTE: this must be kept in sync with the same constants in pbr.frag
 | 
					// NOTE: this must be kept in sync with the same constants in pbr.frag
 | 
				
			||||||
@ -643,12 +644,18 @@ pub(crate) fn spot_light_projection_matrix(angle: f32) -> Mat4 {
 | 
				
			|||||||
pub fn prepare_lights(
 | 
					pub fn prepare_lights(
 | 
				
			||||||
    mut commands: Commands,
 | 
					    mut commands: Commands,
 | 
				
			||||||
    mut texture_cache: ResMut<TextureCache>,
 | 
					    mut texture_cache: ResMut<TextureCache>,
 | 
				
			||||||
 | 
					    images: Res<RenderAssets<Image>>,
 | 
				
			||||||
    render_device: Res<RenderDevice>,
 | 
					    render_device: Res<RenderDevice>,
 | 
				
			||||||
    render_queue: Res<RenderQueue>,
 | 
					    render_queue: Res<RenderQueue>,
 | 
				
			||||||
    mut global_light_meta: ResMut<GlobalLightMeta>,
 | 
					    mut global_light_meta: ResMut<GlobalLightMeta>,
 | 
				
			||||||
    mut light_meta: ResMut<LightMeta>,
 | 
					    mut light_meta: ResMut<LightMeta>,
 | 
				
			||||||
    views: Query<
 | 
					    views: Query<
 | 
				
			||||||
        (Entity, &ExtractedView, &ExtractedClusterConfig),
 | 
					        (
 | 
				
			||||||
 | 
					            Entity,
 | 
				
			||||||
 | 
					            &ExtractedView,
 | 
				
			||||||
 | 
					            &ExtractedClusterConfig,
 | 
				
			||||||
 | 
					            Option<&EnvironmentMapLight>,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
        With<RenderPhase<Transparent3d>>,
 | 
					        With<RenderPhase<Transparent3d>>,
 | 
				
			||||||
    >,
 | 
					    >,
 | 
				
			||||||
    ambient_light: Res<AmbientLight>,
 | 
					    ambient_light: Res<AmbientLight>,
 | 
				
			||||||
@ -894,7 +901,7 @@ pub fn prepare_lights(
 | 
				
			|||||||
        .write_buffer(&render_device, &render_queue);
 | 
					        .write_buffer(&render_device, &render_queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // set up light data for each view
 | 
					    // set up light data for each view
 | 
				
			||||||
    for (entity, extracted_view, clusters) in &views {
 | 
					    for (entity, extracted_view, clusters, environment_map) in &views {
 | 
				
			||||||
        let point_light_depth_texture = texture_cache.get(
 | 
					        let point_light_depth_texture = texture_cache.get(
 | 
				
			||||||
            &render_device,
 | 
					            &render_device,
 | 
				
			||||||
            TextureDescriptor {
 | 
					            TextureDescriptor {
 | 
				
			||||||
@ -961,6 +968,10 @@ pub fn prepare_lights(
 | 
				
			|||||||
            // index to shadow map index, we need to subtract point light count and add directional shadowmap count.
 | 
					            // 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
 | 
					            spot_light_shadowmap_offset: num_directional_cascades_enabled as i32
 | 
				
			||||||
                - point_light_count as i32,
 | 
					                - point_light_count as i32,
 | 
				
			||||||
 | 
					            environment_map_smallest_specular_mip_level: environment_map
 | 
				
			||||||
 | 
					                .and_then(|env_map| images.get(&env_map.specular_map))
 | 
				
			||||||
 | 
					                .map(|specular_map| specular_map.mip_level_count - 1)
 | 
				
			||||||
 | 
					                .unwrap_or(0),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
 | 
					        // TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,3 @@
 | 
				
			|||||||
use crate::{
 | 
					 | 
				
			||||||
    MaterialBindGroupId, NotShadowCaster, NotShadowReceiver, PreviousGlobalTransform, Shadow,
 | 
					 | 
				
			||||||
    ViewFogUniformOffset, ViewLightProbesUniformOffset, ViewLightsUniformOffset,
 | 
					 | 
				
			||||||
    CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use bevy_app::{Plugin, PostUpdate};
 | 
					use bevy_app::{Plugin, PostUpdate};
 | 
				
			||||||
use bevy_asset::{load_internal_asset, AssetId, Handle};
 | 
					use bevy_asset::{load_internal_asset, AssetId, Handle};
 | 
				
			||||||
use bevy_core_pipeline::{
 | 
					use bevy_core_pipeline::{
 | 
				
			||||||
@ -26,9 +21,7 @@ use bevy_render::{
 | 
				
			|||||||
    render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
 | 
					    render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
 | 
				
			||||||
    render_resource::*,
 | 
					    render_resource::*,
 | 
				
			||||||
    renderer::{RenderDevice, RenderQueue},
 | 
					    renderer::{RenderDevice, RenderQueue},
 | 
				
			||||||
    texture::{
 | 
					    texture::*,
 | 
				
			||||||
        BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    view::{ViewTarget, ViewUniformOffset, ViewVisibility},
 | 
					    view::{ViewTarget, ViewUniformOffset, ViewVisibility},
 | 
				
			||||||
    Extract, ExtractSchedule, Render, RenderApp, RenderSet,
 | 
					    Extract, ExtractSchedule, Render, RenderApp, RenderSet,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -874,9 +867,6 @@ impl SpecializedMeshPipeline for MeshPipeline {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        ));
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
        shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let format = if key.contains(MeshPipelineKey::HDR) {
 | 
					        let format = if key.contains(MeshPipelineKey::HDR) {
 | 
				
			||||||
            ViewTarget::TEXTURE_FORMAT_HDR
 | 
					            ViewTarget::TEXTURE_FORMAT_HDR
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@ -1005,7 +995,6 @@ pub fn prepare_mesh_bind_group(
 | 
				
			|||||||
    let Some(model) = mesh_uniforms.binding() else {
 | 
					    let Some(model) = mesh_uniforms.binding() else {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					 | 
				
			||||||
    groups.model_only = Some(layouts.model_only(&render_device, &model));
 | 
					    groups.model_only = Some(layouts.model_only(&render_device, &model));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let skin = skins_uniform.buffer.buffer();
 | 
					    let skin = skins_uniform.buffer.buffer();
 | 
				
			||||||
@ -1043,7 +1032,6 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
 | 
				
			|||||||
        Read<ViewUniformOffset>,
 | 
					        Read<ViewUniformOffset>,
 | 
				
			||||||
        Read<ViewLightsUniformOffset>,
 | 
					        Read<ViewLightsUniformOffset>,
 | 
				
			||||||
        Read<ViewFogUniformOffset>,
 | 
					        Read<ViewFogUniformOffset>,
 | 
				
			||||||
        Read<ViewLightProbesUniformOffset>,
 | 
					 | 
				
			||||||
        Read<MeshViewBindGroup>,
 | 
					        Read<MeshViewBindGroup>,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    type ItemData = ();
 | 
					    type ItemData = ();
 | 
				
			||||||
@ -1051,7 +1039,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
 | 
				
			|||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    fn render<'w>(
 | 
					    fn render<'w>(
 | 
				
			||||||
        _item: &P,
 | 
					        _item: &P,
 | 
				
			||||||
        (view_uniform, view_lights, view_fog, view_light_probes, mesh_view_bind_group): ROQueryItem<
 | 
					        (view_uniform, view_lights, view_fog, mesh_view_bind_group): ROQueryItem<
 | 
				
			||||||
            'w,
 | 
					            'w,
 | 
				
			||||||
            Self::ViewData,
 | 
					            Self::ViewData,
 | 
				
			||||||
        >,
 | 
					        >,
 | 
				
			||||||
@ -1062,12 +1050,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
 | 
				
			|||||||
        pass.set_bind_group(
 | 
					        pass.set_bind_group(
 | 
				
			||||||
            I,
 | 
					            I,
 | 
				
			||||||
            &mesh_view_bind_group.value,
 | 
					            &mesh_view_bind_group.value,
 | 
				
			||||||
            &[
 | 
					            &[view_uniform.offset, view_lights.offset, view_fog.offset],
 | 
				
			||||||
                view_uniform.offset,
 | 
					 | 
				
			||||||
                view_lights.offset,
 | 
					 | 
				
			||||||
                view_fog.offset,
 | 
					 | 
				
			||||||
                **view_light_probes,
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        RenderCommandResult::Success
 | 
					        RenderCommandResult::Success
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ use bevy_render::{
 | 
				
			|||||||
    render_asset::RenderAssets,
 | 
					    render_asset::RenderAssets,
 | 
				
			||||||
    render_resource::{binding_types::*, *},
 | 
					    render_resource::{binding_types::*, *},
 | 
				
			||||||
    renderer::RenderDevice,
 | 
					    renderer::RenderDevice,
 | 
				
			||||||
    texture::{BevyDefault, FallbackImage, FallbackImageMsaa, FallbackImageZero, Image},
 | 
					    texture::{BevyDefault, FallbackImageCubemap, FallbackImageMsaa, FallbackImageZero, Image},
 | 
				
			||||||
    view::{Msaa, ViewUniform, ViewUniforms},
 | 
					    view::{Msaa, ViewUniform, ViewUniforms},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -27,10 +27,9 @@ use bevy_render::render_resource::binding_types::texture_cube;
 | 
				
			|||||||
use bevy_render::render_resource::binding_types::{texture_2d_array, texture_cube_array};
 | 
					use bevy_render::render_resource::binding_types::{texture_2d_array, texture_cube_array};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    environment_map::{self, RenderViewBindGroupEntries, RenderViewEnvironmentMaps},
 | 
					    environment_map, prepass, EnvironmentMapLight, FogMeta, GlobalLightMeta, GpuFog, GpuLights,
 | 
				
			||||||
    prepass, FogMeta, GlobalLightMeta, GpuFog, GpuLights, GpuPointLights, LightMeta,
 | 
					    GpuPointLights, LightMeta, MeshPipeline, MeshPipelineKey, ScreenSpaceAmbientOcclusionTextures,
 | 
				
			||||||
    LightProbesBuffer, LightProbesUniform, MeshPipeline, MeshPipelineKey,
 | 
					    ShadowSamplers, ViewClusterBindings, ViewShadowBindings,
 | 
				
			||||||
    ScreenSpaceAmbientOcclusionTextures, ShadowSamplers, ViewClusterBindings, ViewShadowBindings,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
@ -235,11 +234,9 @@ fn layout_entries(
 | 
				
			|||||||
            (9, uniform_buffer::<GlobalsUniform>(false)),
 | 
					            (9, uniform_buffer::<GlobalsUniform>(false)),
 | 
				
			||||||
            // Fog
 | 
					            // Fog
 | 
				
			||||||
            (10, uniform_buffer::<GpuFog>(true)),
 | 
					            (10, uniform_buffer::<GpuFog>(true)),
 | 
				
			||||||
            // Light probes
 | 
					 | 
				
			||||||
            (11, uniform_buffer::<LightProbesUniform>(true)),
 | 
					 | 
				
			||||||
            // Screen space ambient occlusion texture
 | 
					            // Screen space ambient occlusion texture
 | 
				
			||||||
            (
 | 
					            (
 | 
				
			||||||
                12,
 | 
					                11,
 | 
				
			||||||
                texture_2d(TextureSampleType::Float { filterable: false }),
 | 
					                texture_2d(TextureSampleType::Float { filterable: false }),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
@ -248,16 +245,16 @@ fn layout_entries(
 | 
				
			|||||||
    // EnvironmentMapLight
 | 
					    // EnvironmentMapLight
 | 
				
			||||||
    let environment_map_entries = environment_map::get_bind_group_layout_entries();
 | 
					    let environment_map_entries = environment_map::get_bind_group_layout_entries();
 | 
				
			||||||
    entries = entries.extend_with_indices((
 | 
					    entries = entries.extend_with_indices((
 | 
				
			||||||
        (13, environment_map_entries[0]),
 | 
					        (12, environment_map_entries[0]),
 | 
				
			||||||
        (14, environment_map_entries[1]),
 | 
					        (13, environment_map_entries[1]),
 | 
				
			||||||
        (15, environment_map_entries[2]),
 | 
					        (14, environment_map_entries[2]),
 | 
				
			||||||
    ));
 | 
					    ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Tonemapping
 | 
					    // Tonemapping
 | 
				
			||||||
    let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
 | 
					    let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
 | 
				
			||||||
    entries = entries.extend_with_indices((
 | 
					    entries = entries.extend_with_indices((
 | 
				
			||||||
        (16, tonemapping_lut_entries[0]),
 | 
					        (15, tonemapping_lut_entries[0]),
 | 
				
			||||||
        (17, tonemapping_lut_entries[1]),
 | 
					        (16, tonemapping_lut_entries[1]),
 | 
				
			||||||
    ));
 | 
					    ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Prepass
 | 
					    // Prepass
 | 
				
			||||||
@ -267,7 +264,7 @@ fn layout_entries(
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key)
 | 
					        for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key)
 | 
				
			||||||
            .iter()
 | 
					            .iter()
 | 
				
			||||||
            .zip([18, 19, 20, 21])
 | 
					            .zip([17, 18, 19, 20])
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if let Some(entry) = entry {
 | 
					            if let Some(entry) = entry {
 | 
				
			||||||
                entries = entries.extend_with_indices(((binding as u32, *entry),));
 | 
					                entries = entries.extend_with_indices(((binding as u32, *entry),));
 | 
				
			||||||
@ -278,10 +275,10 @@ fn layout_entries(
 | 
				
			|||||||
    // View Transmission Texture
 | 
					    // View Transmission Texture
 | 
				
			||||||
    entries = entries.extend_with_indices((
 | 
					    entries = entries.extend_with_indices((
 | 
				
			||||||
        (
 | 
					        (
 | 
				
			||||||
            22,
 | 
					            21,
 | 
				
			||||||
            texture_2d(TextureSampleType::Float { filterable: true }),
 | 
					            texture_2d(TextureSampleType::Float { filterable: true }),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        (23, sampler(SamplerBindingType::Filtering)),
 | 
					        (22, sampler(SamplerBindingType::Filtering)),
 | 
				
			||||||
    ));
 | 
					    ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    entries.to_vec()
 | 
					    entries.to_vec()
 | 
				
			||||||
@ -334,19 +331,18 @@ pub fn prepare_mesh_view_bind_groups(
 | 
				
			|||||||
        Option<&ScreenSpaceAmbientOcclusionTextures>,
 | 
					        Option<&ScreenSpaceAmbientOcclusionTextures>,
 | 
				
			||||||
        Option<&ViewPrepassTextures>,
 | 
					        Option<&ViewPrepassTextures>,
 | 
				
			||||||
        Option<&ViewTransmissionTexture>,
 | 
					        Option<&ViewTransmissionTexture>,
 | 
				
			||||||
 | 
					        Option<&EnvironmentMapLight>,
 | 
				
			||||||
        &Tonemapping,
 | 
					        &Tonemapping,
 | 
				
			||||||
        Option<&RenderViewEnvironmentMaps>,
 | 
					 | 
				
			||||||
    )>,
 | 
					    )>,
 | 
				
			||||||
    (images, mut fallback_images, fallback_image, fallback_image_zero): (
 | 
					    (images, mut fallback_images, fallback_cubemap, fallback_image_zero): (
 | 
				
			||||||
        Res<RenderAssets<Image>>,
 | 
					        Res<RenderAssets<Image>>,
 | 
				
			||||||
        FallbackImageMsaa,
 | 
					        FallbackImageMsaa,
 | 
				
			||||||
        Res<FallbackImage>,
 | 
					        Res<FallbackImageCubemap>,
 | 
				
			||||||
        Res<FallbackImageZero>,
 | 
					        Res<FallbackImageZero>,
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    msaa: Res<Msaa>,
 | 
					    msaa: Res<Msaa>,
 | 
				
			||||||
    globals_buffer: Res<GlobalsBuffer>,
 | 
					    globals_buffer: Res<GlobalsBuffer>,
 | 
				
			||||||
    tonemapping_luts: Res<TonemappingLuts>,
 | 
					    tonemapping_luts: Res<TonemappingLuts>,
 | 
				
			||||||
    light_probes_buffer: Res<LightProbesBuffer>,
 | 
					 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    if let (
 | 
					    if let (
 | 
				
			||||||
        Some(view_binding),
 | 
					        Some(view_binding),
 | 
				
			||||||
@ -354,14 +350,12 @@ pub fn prepare_mesh_view_bind_groups(
 | 
				
			|||||||
        Some(point_light_binding),
 | 
					        Some(point_light_binding),
 | 
				
			||||||
        Some(globals),
 | 
					        Some(globals),
 | 
				
			||||||
        Some(fog_binding),
 | 
					        Some(fog_binding),
 | 
				
			||||||
        Some(light_probes_binding),
 | 
					 | 
				
			||||||
    ) = (
 | 
					    ) = (
 | 
				
			||||||
        view_uniforms.uniforms.binding(),
 | 
					        view_uniforms.uniforms.binding(),
 | 
				
			||||||
        light_meta.view_gpu_lights.binding(),
 | 
					        light_meta.view_gpu_lights.binding(),
 | 
				
			||||||
        global_light_meta.gpu_point_lights.binding(),
 | 
					        global_light_meta.gpu_point_lights.binding(),
 | 
				
			||||||
        globals_buffer.buffer.binding(),
 | 
					        globals_buffer.buffer.binding(),
 | 
				
			||||||
        fog_meta.gpu_fogs.binding(),
 | 
					        fog_meta.gpu_fogs.binding(),
 | 
				
			||||||
        light_probes_buffer.binding(),
 | 
					 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        for (
 | 
					        for (
 | 
				
			||||||
            entity,
 | 
					            entity,
 | 
				
			||||||
@ -370,8 +364,8 @@ pub fn prepare_mesh_view_bind_groups(
 | 
				
			|||||||
            ssao_textures,
 | 
					            ssao_textures,
 | 
				
			||||||
            prepass_textures,
 | 
					            prepass_textures,
 | 
				
			||||||
            transmission_texture,
 | 
					            transmission_texture,
 | 
				
			||||||
 | 
					            environment_map,
 | 
				
			||||||
            tonemapping,
 | 
					            tonemapping,
 | 
				
			||||||
            render_view_environment_maps,
 | 
					 | 
				
			||||||
        ) in &views
 | 
					        ) in &views
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            let fallback_ssao = fallback_images
 | 
					            let fallback_ssao = fallback_images
 | 
				
			||||||
@ -399,23 +393,19 @@ pub fn prepare_mesh_view_bind_groups(
 | 
				
			|||||||
                (8, cluster_bindings.offsets_and_counts_binding().unwrap()),
 | 
					                (8, cluster_bindings.offsets_and_counts_binding().unwrap()),
 | 
				
			||||||
                (9, globals.clone()),
 | 
					                (9, globals.clone()),
 | 
				
			||||||
                (10, fog_binding.clone()),
 | 
					                (10, fog_binding.clone()),
 | 
				
			||||||
                (11, light_probes_binding.clone()),
 | 
					                (11, ssao_view),
 | 
				
			||||||
                (12, ssao_view),
 | 
					 | 
				
			||||||
            ));
 | 
					            ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let bind_group_entries = RenderViewBindGroupEntries::get(
 | 
					            let env_map_bindings =
 | 
				
			||||||
                render_view_environment_maps,
 | 
					                environment_map::get_bindings(environment_map, &images, &fallback_cubemap);
 | 
				
			||||||
                &images,
 | 
					 | 
				
			||||||
                &fallback_image,
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            entries = entries.extend_with_indices((
 | 
					            entries = entries.extend_with_indices((
 | 
				
			||||||
                (13, bind_group_entries.diffuse_texture_views()),
 | 
					                (12, env_map_bindings.0),
 | 
				
			||||||
                (14, bind_group_entries.specular_texture_views()),
 | 
					                (13, env_map_bindings.1),
 | 
				
			||||||
                (15, bind_group_entries.sampler),
 | 
					                (14, env_map_bindings.2),
 | 
				
			||||||
            ));
 | 
					            ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let lut_bindings = get_lut_bindings(&images, &tonemapping_luts, tonemapping);
 | 
					            let lut_bindings = get_lut_bindings(&images, &tonemapping_luts, tonemapping);
 | 
				
			||||||
            entries = entries.extend_with_indices(((16, lut_bindings.0), (17, lut_bindings.1)));
 | 
					            entries = entries.extend_with_indices(((15, lut_bindings.0), (16, lut_bindings.1)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // When using WebGL, we can't have a depth texture with multisampling
 | 
					            // When using WebGL, we can't have a depth texture with multisampling
 | 
				
			||||||
            let prepass_bindings;
 | 
					            let prepass_bindings;
 | 
				
			||||||
@ -425,7 +415,7 @@ pub fn prepare_mesh_view_bind_groups(
 | 
				
			|||||||
                for (binding, index) in prepass_bindings
 | 
					                for (binding, index) in prepass_bindings
 | 
				
			||||||
                    .iter()
 | 
					                    .iter()
 | 
				
			||||||
                    .map(Option::as_ref)
 | 
					                    .map(Option::as_ref)
 | 
				
			||||||
                    .zip([18, 19, 20, 21])
 | 
					                    .zip([17, 18, 19, 20])
 | 
				
			||||||
                    .flat_map(|(b, i)| b.map(|b| (b, i)))
 | 
					                    .flat_map(|(b, i)| b.map(|b| (b, i)))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    entries = entries.extend_with_indices(((index, binding),));
 | 
					                    entries = entries.extend_with_indices(((index, binding),));
 | 
				
			||||||
@ -441,7 +431,7 @@ pub fn prepare_mesh_view_bind_groups(
 | 
				
			|||||||
                .unwrap_or(&fallback_image_zero.sampler);
 | 
					                .unwrap_or(&fallback_image_zero.sampler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            entries =
 | 
					            entries =
 | 
				
			||||||
                entries.extend_with_indices(((22, transmission_view), (23, transmission_sampler)));
 | 
					                entries.extend_with_indices(((21, transmission_view), (22, transmission_sampler)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            commands.entity(entity).insert(MeshViewBindGroup {
 | 
					            commands.entity(entity).insert(MeshViewBindGroup {
 | 
				
			||||||
                value: render_device.create_bind_group("mesh_view_bind_group", layout, &entries),
 | 
					                value: render_device.create_bind_group("mesh_view_bind_group", layout, &entries),
 | 
				
			||||||
 | 
				
			|||||||
@ -33,50 +33,44 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@group(0) @binding(9) var<uniform> globals: Globals;
 | 
					@group(0) @binding(9) var<uniform> globals: Globals;
 | 
				
			||||||
@group(0) @binding(10) var<uniform> fog: types::Fog;
 | 
					@group(0) @binding(10) var<uniform> fog: types::Fog;
 | 
				
			||||||
@group(0) @binding(11) var<uniform> light_probes: types::LightProbes;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@group(0) @binding(12) var screen_space_ambient_occlusion_texture: texture_2d<f32>;
 | 
					@group(0) @binding(11) var screen_space_ambient_occlusion_texture: texture_2d<f32>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY
 | 
					@group(0) @binding(12) var environment_map_diffuse: texture_cube<f32>;
 | 
				
			||||||
@group(0) @binding(13) var diffuse_environment_maps: binding_array<texture_cube<f32>>;
 | 
					@group(0) @binding(13) var environment_map_specular: texture_cube<f32>;
 | 
				
			||||||
@group(0) @binding(14) var specular_environment_maps: binding_array<texture_cube<f32>>;
 | 
					@group(0) @binding(14) var environment_map_sampler: sampler;
 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
@group(0) @binding(13) var diffuse_environment_map: texture_cube<f32>;
 | 
					 | 
				
			||||||
@group(0) @binding(14) var specular_environment_map: texture_cube<f32>;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@group(0) @binding(15) var environment_map_sampler: sampler;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@group(0) @binding(16) var dt_lut_texture: texture_3d<f32>;
 | 
					@group(0) @binding(15) var dt_lut_texture: texture_3d<f32>;
 | 
				
			||||||
@group(0) @binding(17) var dt_lut_sampler: sampler;
 | 
					@group(0) @binding(16) var dt_lut_sampler: sampler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef MULTISAMPLED
 | 
					#ifdef MULTISAMPLED
 | 
				
			||||||
#ifdef DEPTH_PREPASS
 | 
					#ifdef DEPTH_PREPASS
 | 
				
			||||||
@group(0) @binding(18) var depth_prepass_texture: texture_depth_multisampled_2d;
 | 
					@group(0) @binding(17) var depth_prepass_texture: texture_depth_multisampled_2d;
 | 
				
			||||||
#endif // DEPTH_PREPASS
 | 
					#endif // DEPTH_PREPASS
 | 
				
			||||||
#ifdef NORMAL_PREPASS
 | 
					#ifdef NORMAL_PREPASS
 | 
				
			||||||
@group(0) @binding(19) var normal_prepass_texture: texture_multisampled_2d<f32>;
 | 
					@group(0) @binding(18) var normal_prepass_texture: texture_multisampled_2d<f32>;
 | 
				
			||||||
#endif // NORMAL_PREPASS
 | 
					#endif // NORMAL_PREPASS
 | 
				
			||||||
#ifdef MOTION_VECTOR_PREPASS
 | 
					#ifdef MOTION_VECTOR_PREPASS
 | 
				
			||||||
@group(0) @binding(20) var motion_vector_prepass_texture: texture_multisampled_2d<f32>;
 | 
					@group(0) @binding(19) var motion_vector_prepass_texture: texture_multisampled_2d<f32>;
 | 
				
			||||||
#endif // MOTION_VECTOR_PREPASS
 | 
					#endif // MOTION_VECTOR_PREPASS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#else // MULTISAMPLED
 | 
					#else // MULTISAMPLED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef DEPTH_PREPASS
 | 
					#ifdef DEPTH_PREPASS
 | 
				
			||||||
@group(0) @binding(18) var depth_prepass_texture: texture_depth_2d;
 | 
					@group(0) @binding(17) var depth_prepass_texture: texture_depth_2d;
 | 
				
			||||||
#endif // DEPTH_PREPASS
 | 
					#endif // DEPTH_PREPASS
 | 
				
			||||||
#ifdef NORMAL_PREPASS
 | 
					#ifdef NORMAL_PREPASS
 | 
				
			||||||
@group(0) @binding(19) var normal_prepass_texture: texture_2d<f32>;
 | 
					@group(0) @binding(18) var normal_prepass_texture: texture_2d<f32>;
 | 
				
			||||||
#endif // NORMAL_PREPASS
 | 
					#endif // NORMAL_PREPASS
 | 
				
			||||||
#ifdef MOTION_VECTOR_PREPASS
 | 
					#ifdef MOTION_VECTOR_PREPASS
 | 
				
			||||||
@group(0) @binding(20) var motion_vector_prepass_texture: texture_2d<f32>;
 | 
					@group(0) @binding(19) var motion_vector_prepass_texture: texture_2d<f32>;
 | 
				
			||||||
#endif // MOTION_VECTOR_PREPASS
 | 
					#endif // MOTION_VECTOR_PREPASS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // MULTISAMPLED
 | 
					#endif // MULTISAMPLED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef DEFERRED_PREPASS
 | 
					#ifdef DEFERRED_PREPASS
 | 
				
			||||||
@group(0) @binding(21) var deferred_prepass_texture: texture_2d<u32>;
 | 
					@group(0) @binding(20) var deferred_prepass_texture: texture_2d<u32>;
 | 
				
			||||||
#endif // DEFERRED_PREPASS
 | 
					#endif // DEFERRED_PREPASS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@group(0) @binding(22) var view_transmission_texture: texture_2d<f32>;
 | 
					@group(0) @binding(21) var view_transmission_texture: texture_2d<f32>;
 | 
				
			||||||
@group(0) @binding(23) var view_transmission_sampler: sampler;
 | 
					@group(0) @binding(22) var view_transmission_sampler: sampler;
 | 
				
			||||||
 | 
				
			|||||||
@ -109,22 +109,3 @@ struct ClusterOffsetsAndCounts {
 | 
				
			|||||||
    data: array<vec4<u32>, 1024u>,
 | 
					    data: array<vec4<u32>, 1024u>,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					 | 
				
			||||||
struct ReflectionProbe {
 | 
					 | 
				
			||||||
    // This is stored as the transpose in order to save space in this structure.
 | 
					 | 
				
			||||||
    // It'll be transposed in the `environment_map_light` function.
 | 
					 | 
				
			||||||
    inverse_transpose_transform: mat3x4<f32>,
 | 
					 | 
				
			||||||
    cubemap_index: i32,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct LightProbes {
 | 
					 | 
				
			||||||
    // This must match `MAX_VIEW_REFLECTION_PROBES` on the Rust side.
 | 
					 | 
				
			||||||
    reflection_probes: array<ReflectionProbe, 8u>,
 | 
					 | 
				
			||||||
    reflection_probe_count: i32,
 | 
					 | 
				
			||||||
    // The index of the view environment map cubemap binding, or -1 if there's
 | 
					 | 
				
			||||||
    // no such cubemap.
 | 
					 | 
				
			||||||
    view_cubemap_index: i32,
 | 
					 | 
				
			||||||
    // The smallest valid mipmap level for the specular environment cubemap
 | 
					 | 
				
			||||||
    // associated with the view.
 | 
					 | 
				
			||||||
    smallest_specular_mip_level_for_view: u32,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -322,16 +322,7 @@ fn apply_pbr_lighting(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Environment map light (indirect)
 | 
					    // Environment map light (indirect)
 | 
				
			||||||
#ifdef ENVIRONMENT_MAP
 | 
					#ifdef ENVIRONMENT_MAP
 | 
				
			||||||
    let environment_light = environment_map::environment_map_light(
 | 
					    let environment_light = environment_map::environment_map_light(perceptual_roughness, roughness, diffuse_color, NdotV, f_ab, in.N, R, F0);
 | 
				
			||||||
        perceptual_roughness,
 | 
					 | 
				
			||||||
        roughness,
 | 
					 | 
				
			||||||
        diffuse_color,
 | 
					 | 
				
			||||||
        NdotV,
 | 
					 | 
				
			||||||
        f_ab,
 | 
					 | 
				
			||||||
        in.N,
 | 
					 | 
				
			||||||
        R,
 | 
					 | 
				
			||||||
        F0,
 | 
					 | 
				
			||||||
        in.world_position.xyz);
 | 
					 | 
				
			||||||
    indirect_light += (environment_light.diffuse * occlusion) + environment_light.specular;
 | 
					    indirect_light += (environment_light.diffuse * occlusion) + environment_light.specular;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // we'll use the specular component of the transmitted environment
 | 
					    // we'll use the specular component of the transmitted environment
 | 
				
			||||||
@ -357,16 +348,7 @@ fn apply_pbr_lighting(
 | 
				
			|||||||
            refract(in.V, -in.N, 1.0 / ior) * thickness // add refracted vector scaled by thickness, towards exit point
 | 
					            refract(in.V, -in.N, 1.0 / ior) * thickness // add refracted vector scaled by thickness, towards exit point
 | 
				
			||||||
        ); // normalize to find exit point view vector
 | 
					        ); // normalize to find exit point view vector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let transmitted_environment_light = bevy_pbr::environment_map::environment_map_light(
 | 
					        let transmitted_environment_light = bevy_pbr::environment_map::environment_map_light(perceptual_roughness, roughness, vec3<f32>(1.0), 1.0, f_ab, -in.N, T, vec3<f32>(1.0));
 | 
				
			||||||
            perceptual_roughness,
 | 
					 | 
				
			||||||
            roughness,
 | 
					 | 
				
			||||||
            vec3<f32>(1.0),
 | 
					 | 
				
			||||||
            1.0,
 | 
					 | 
				
			||||||
            f_ab,
 | 
					 | 
				
			||||||
            -in.N,
 | 
					 | 
				
			||||||
            T,
 | 
					 | 
				
			||||||
            vec3<f32>(1.0),
 | 
					 | 
				
			||||||
            in.world_position.xyz);
 | 
					 | 
				
			||||||
        transmitted_light += transmitted_environment_light.diffuse * diffuse_transmissive_color;
 | 
					        transmitted_light += transmitted_environment_light.diffuse * diffuse_transmissive_color;
 | 
				
			||||||
        specular_transmitted_environment_light = transmitted_environment_light.specular * specular_transmissive_color;
 | 
					        specular_transmitted_environment_light = transmitted_environment_light.specular * specular_transmissive_color;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@ dds = ["ddsfile"]
 | 
				
			|||||||
pnm = ["image/pnm"]
 | 
					pnm = ["image/pnm"]
 | 
				
			||||||
bevy_ci_testing = ["bevy_app/bevy_ci_testing"]
 | 
					bevy_ci_testing = ["bevy_app/bevy_ci_testing"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
shader_format_glsl = ["naga/glsl-in", "naga/wgsl-out", "naga_oil/glsl"]
 | 
					shader_format_glsl = ["naga/glsl-in", "naga/wgsl-out"]
 | 
				
			||||||
shader_format_spirv = ["wgpu/spirv", "naga/spv-in", "naga/spv-out"]
 | 
					shader_format_spirv = ["wgpu/spirv", "naga/spv-in", "naga/spv-out"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# For ktx2 supercompression
 | 
					# For ktx2 supercompression
 | 
				
			||||||
@ -67,9 +67,7 @@ wgpu = { version = "0.18", features = [
 | 
				
			|||||||
  "fragile-send-sync-non-atomic-wasm",
 | 
					  "fragile-send-sync-non-atomic-wasm",
 | 
				
			||||||
] }
 | 
					] }
 | 
				
			||||||
naga = { version = "0.14.2", features = ["wgsl-in"] }
 | 
					naga = { version = "0.14.2", features = ["wgsl-in"] }
 | 
				
			||||||
naga_oil = { version = "0.11", default-features = false, features = [
 | 
					naga_oil = "0.11"
 | 
				
			||||||
  "test_shader",
 | 
					 | 
				
			||||||
] }
 | 
					 | 
				
			||||||
serde = { version = "1", features = ["derive"] }
 | 
					serde = { version = "1", features = ["derive"] }
 | 
				
			||||||
bitflags = "2.3"
 | 
					bitflags = "2.3"
 | 
				
			||||||
bytemuck = { version = "1.5", features = ["derive"] }
 | 
					bytemuck = { version = "1.5", features = ["derive"] }
 | 
				
			||||||
 | 
				
			|||||||
@ -213,12 +213,7 @@ impl From<&Source> for naga_oil::compose::ShaderLanguage {
 | 
				
			|||||||
    fn from(value: &Source) -> Self {
 | 
					    fn from(value: &Source) -> Self {
 | 
				
			||||||
        match value {
 | 
					        match value {
 | 
				
			||||||
            Source::Wgsl(_) => naga_oil::compose::ShaderLanguage::Wgsl,
 | 
					            Source::Wgsl(_) => naga_oil::compose::ShaderLanguage::Wgsl,
 | 
				
			||||||
            #[cfg(any(feature = "shader_format_glsl", target_arch = "wasm32"))]
 | 
					 | 
				
			||||||
            Source::Glsl(_, _) => naga_oil::compose::ShaderLanguage::Glsl,
 | 
					            Source::Glsl(_, _) => naga_oil::compose::ShaderLanguage::Glsl,
 | 
				
			||||||
            #[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					 | 
				
			||||||
            Source::Glsl(_, _) => panic!(
 | 
					 | 
				
			||||||
                "GLSL is not supported in this configuration; use the feature `shader_format_glsl`"
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            Source::SpirV(_) => panic!("spirv not yet implemented"),
 | 
					            Source::SpirV(_) => panic!("spirv not yet implemented"),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -228,16 +223,13 @@ impl From<&Source> for naga_oil::compose::ShaderType {
 | 
				
			|||||||
    fn from(value: &Source) -> Self {
 | 
					    fn from(value: &Source) -> Self {
 | 
				
			||||||
        match value {
 | 
					        match value {
 | 
				
			||||||
            Source::Wgsl(_) => naga_oil::compose::ShaderType::Wgsl,
 | 
					            Source::Wgsl(_) => naga_oil::compose::ShaderType::Wgsl,
 | 
				
			||||||
            #[cfg(any(feature = "shader_format_glsl", target_arch = "wasm32"))]
 | 
					            Source::Glsl(_, naga::ShaderStage::Vertex) => naga_oil::compose::ShaderType::GlslVertex,
 | 
				
			||||||
            Source::Glsl(_, shader_stage) => match shader_stage {
 | 
					            Source::Glsl(_, naga::ShaderStage::Fragment) => {
 | 
				
			||||||
                naga::ShaderStage::Vertex => naga_oil::compose::ShaderType::GlslVertex,
 | 
					                naga_oil::compose::ShaderType::GlslFragment
 | 
				
			||||||
                naga::ShaderStage::Fragment => naga_oil::compose::ShaderType::GlslFragment,
 | 
					            }
 | 
				
			||||||
                naga::ShaderStage::Compute => panic!("glsl compute not yet implemented"),
 | 
					            Source::Glsl(_, naga::ShaderStage::Compute) => {
 | 
				
			||||||
            },
 | 
					                panic!("glsl compute not yet implemented")
 | 
				
			||||||
            #[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
 | 
					            }
 | 
				
			||||||
            Source::Glsl(_, _) => panic!(
 | 
					 | 
				
			||||||
                "GLSL is not supported in this configuration; use the feature `shader_format_glsl`"
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            Source::SpirV(_) => panic!("spirv not yet implemented"),
 | 
					            Source::SpirV(_) => panic!("spirv not yet implemented"),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,345 +0,0 @@
 | 
				
			|||||||
//! This example shows how to place reflection probes in the scene.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! Press Space to switch between no reflections, environment map reflections
 | 
					 | 
				
			||||||
//! (i.e. the skybox only, not the cubes), and a full reflection probe that
 | 
					 | 
				
			||||||
//! reflects the skybox and the cubes. Press Enter to pause rotation.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! Reflection probes don't work on WebGL 2 or WebGPU.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use bevy::core_pipeline::Skybox;
 | 
					 | 
				
			||||||
use bevy::prelude::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use std::fmt::{Display, Formatter, Result as FmtResult};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Rotation speed in radians per frame.
 | 
					 | 
				
			||||||
const ROTATION_SPEED: f32 = 0.005;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static STOP_ROTATION_HELP_TEXT: &str = "Press Enter to stop rotation";
 | 
					 | 
				
			||||||
static START_ROTATION_HELP_TEXT: &str = "Press Enter to start rotation";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static REFLECTION_MODE_HELP_TEXT: &str = "Press Space to switch reflection mode";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// The mode the application is in.
 | 
					 | 
				
			||||||
#[derive(Resource)]
 | 
					 | 
				
			||||||
struct AppStatus {
 | 
					 | 
				
			||||||
    // Which environment maps the user has requested to display.
 | 
					 | 
				
			||||||
    reflection_mode: ReflectionMode,
 | 
					 | 
				
			||||||
    // Whether the user has requested the scene to rotate.
 | 
					 | 
				
			||||||
    rotating: bool,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Which environment maps the user has requested to display.
 | 
					 | 
				
			||||||
#[derive(Clone, Copy)]
 | 
					 | 
				
			||||||
enum ReflectionMode {
 | 
					 | 
				
			||||||
    // No environment maps are shown.
 | 
					 | 
				
			||||||
    None = 0,
 | 
					 | 
				
			||||||
    // Only a world environment map is shown.
 | 
					 | 
				
			||||||
    EnvironmentMap = 1,
 | 
					 | 
				
			||||||
    // Both a world environment map and a reflection probe are present. The
 | 
					 | 
				
			||||||
    // reflection probe is shown in the sphere.
 | 
					 | 
				
			||||||
    ReflectionProbe = 2,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// The various reflection maps.
 | 
					 | 
				
			||||||
#[derive(Resource)]
 | 
					 | 
				
			||||||
struct Cubemaps {
 | 
					 | 
				
			||||||
    // The blurry diffuse cubemap. This is used for both the world environment
 | 
					 | 
				
			||||||
    // map and the reflection probe. (In reality you wouldn't do this, but this
 | 
					 | 
				
			||||||
    // reduces complexity of this example a bit.)
 | 
					 | 
				
			||||||
    diffuse: Handle<Image>,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The specular cubemap that reflects the world, but not the cubes.
 | 
					 | 
				
			||||||
    specular_environment_map: Handle<Image>,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The specular cubemap that reflects both the world and the cubes.
 | 
					 | 
				
			||||||
    specular_reflection_probe: Handle<Image>,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The skybox cubemap image. This is almost the same as
 | 
					 | 
				
			||||||
    // `specular_environment_map`.
 | 
					 | 
				
			||||||
    skybox: Handle<Image>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn main() {
 | 
					 | 
				
			||||||
    // Create the app.
 | 
					 | 
				
			||||||
    App::new()
 | 
					 | 
				
			||||||
        .add_plugins(DefaultPlugins)
 | 
					 | 
				
			||||||
        .init_resource::<AppStatus>()
 | 
					 | 
				
			||||||
        .init_resource::<Cubemaps>()
 | 
					 | 
				
			||||||
        .add_systems(Startup, setup)
 | 
					 | 
				
			||||||
        .add_systems(PreUpdate, add_environment_map_to_camera)
 | 
					 | 
				
			||||||
        .add_systems(Update, change_reflection_type)
 | 
					 | 
				
			||||||
        .add_systems(Update, toggle_rotation)
 | 
					 | 
				
			||||||
        .add_systems(
 | 
					 | 
				
			||||||
            Update,
 | 
					 | 
				
			||||||
            rotate_camera
 | 
					 | 
				
			||||||
                .after(toggle_rotation)
 | 
					 | 
				
			||||||
                .after(change_reflection_type),
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .add_systems(Update, update_text.after(rotate_camera))
 | 
					 | 
				
			||||||
        .run();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Spawns all the scene objects.
 | 
					 | 
				
			||||||
fn setup(
 | 
					 | 
				
			||||||
    mut commands: Commands,
 | 
					 | 
				
			||||||
    mut meshes: ResMut<Assets<Mesh>>,
 | 
					 | 
				
			||||||
    mut materials: ResMut<Assets<StandardMaterial>>,
 | 
					 | 
				
			||||||
    asset_server: Res<AssetServer>,
 | 
					 | 
				
			||||||
    app_status: Res<AppStatus>,
 | 
					 | 
				
			||||||
    cubemaps: Res<Cubemaps>,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    spawn_scene(&mut commands, &asset_server);
 | 
					 | 
				
			||||||
    spawn_sphere(&mut commands, &mut meshes, &mut materials);
 | 
					 | 
				
			||||||
    spawn_reflection_probe(&mut commands, &cubemaps);
 | 
					 | 
				
			||||||
    spawn_text(&mut commands, &asset_server, &app_status);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Spawns the cubes, light, and camera.
 | 
					 | 
				
			||||||
fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
 | 
					 | 
				
			||||||
    commands.spawn(SceneBundle {
 | 
					 | 
				
			||||||
        scene: asset_server.load("models/cubes/Cubes.glb#Scene0"),
 | 
					 | 
				
			||||||
        ..SceneBundle::default()
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Creates the sphere mesh and spawns it.
 | 
					 | 
				
			||||||
fn spawn_sphere(
 | 
					 | 
				
			||||||
    commands: &mut Commands,
 | 
					 | 
				
			||||||
    meshes: &mut Assets<Mesh>,
 | 
					 | 
				
			||||||
    materials: &mut Assets<StandardMaterial>,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    // Create a sphere mesh.
 | 
					 | 
				
			||||||
    let sphere_mesh = meshes.add(
 | 
					 | 
				
			||||||
        Mesh::try_from(shape::Icosphere {
 | 
					 | 
				
			||||||
            radius: 1.0,
 | 
					 | 
				
			||||||
            subdivisions: 7,
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .unwrap(),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Create a sphere.
 | 
					 | 
				
			||||||
    commands.spawn(PbrBundle {
 | 
					 | 
				
			||||||
        mesh: sphere_mesh.clone(),
 | 
					 | 
				
			||||||
        material: materials.add(StandardMaterial {
 | 
					 | 
				
			||||||
            base_color: Color::hex("#ffd891").unwrap(),
 | 
					 | 
				
			||||||
            metallic: 1.0,
 | 
					 | 
				
			||||||
            perceptual_roughness: 0.0,
 | 
					 | 
				
			||||||
            ..StandardMaterial::default()
 | 
					 | 
				
			||||||
        }),
 | 
					 | 
				
			||||||
        transform: Transform::default(),
 | 
					 | 
				
			||||||
        ..PbrBundle::default()
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Spawns the reflection probe.
 | 
					 | 
				
			||||||
fn spawn_reflection_probe(commands: &mut Commands, cubemaps: &Cubemaps) {
 | 
					 | 
				
			||||||
    commands.spawn(ReflectionProbeBundle {
 | 
					 | 
				
			||||||
        spatial: SpatialBundle {
 | 
					 | 
				
			||||||
            // 2.0 because the sphere's radius is 1.0 and we want to fully enclose it.
 | 
					 | 
				
			||||||
            transform: Transform::from_scale(Vec3::splat(2.0)),
 | 
					 | 
				
			||||||
            ..SpatialBundle::default()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        light_probe: LightProbe,
 | 
					 | 
				
			||||||
        environment_map: EnvironmentMapLight {
 | 
					 | 
				
			||||||
            diffuse_map: cubemaps.diffuse.clone(),
 | 
					 | 
				
			||||||
            specular_map: cubemaps.specular_reflection_probe.clone(),
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Spawns the help text.
 | 
					 | 
				
			||||||
fn spawn_text(commands: &mut Commands, asset_server: &AssetServer, app_status: &AppStatus) {
 | 
					 | 
				
			||||||
    // Create the text.
 | 
					 | 
				
			||||||
    commands.spawn(
 | 
					 | 
				
			||||||
        TextBundle {
 | 
					 | 
				
			||||||
            text: app_status.create_text(asset_server),
 | 
					 | 
				
			||||||
            ..TextBundle::default()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        .with_style(Style {
 | 
					 | 
				
			||||||
            position_type: PositionType::Absolute,
 | 
					 | 
				
			||||||
            bottom: Val::Px(10.0),
 | 
					 | 
				
			||||||
            left: Val::Px(10.0),
 | 
					 | 
				
			||||||
            ..default()
 | 
					 | 
				
			||||||
        }),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Adds a world environment map to the camera. This separate system is needed because the camera is
 | 
					 | 
				
			||||||
// managed by the scene spawner, as it's part of the glTF file with the cubes, so we have to add
 | 
					 | 
				
			||||||
// the environment map after the fact.
 | 
					 | 
				
			||||||
fn add_environment_map_to_camera(
 | 
					 | 
				
			||||||
    mut commands: Commands,
 | 
					 | 
				
			||||||
    query: Query<Entity, Added<Camera3d>>,
 | 
					 | 
				
			||||||
    cubemaps: Res<Cubemaps>,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    for camera_entity in query.iter() {
 | 
					 | 
				
			||||||
        commands
 | 
					 | 
				
			||||||
            .entity(camera_entity)
 | 
					 | 
				
			||||||
            .insert(create_camera_environment_map_light(&cubemaps))
 | 
					 | 
				
			||||||
            .insert(Skybox(cubemaps.skybox.clone()));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// A system that handles switching between different reflection modes.
 | 
					 | 
				
			||||||
fn change_reflection_type(
 | 
					 | 
				
			||||||
    mut commands: Commands,
 | 
					 | 
				
			||||||
    light_probe_query: Query<Entity, With<LightProbe>>,
 | 
					 | 
				
			||||||
    camera_query: Query<Entity, With<Camera3d>>,
 | 
					 | 
				
			||||||
    keyboard: Res<ButtonInput<KeyCode>>,
 | 
					 | 
				
			||||||
    mut app_status: ResMut<AppStatus>,
 | 
					 | 
				
			||||||
    cubemaps: Res<Cubemaps>,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    // Only do anything if space was pressed.
 | 
					 | 
				
			||||||
    if !keyboard.just_pressed(KeyCode::Space) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Switch reflection mode.
 | 
					 | 
				
			||||||
    app_status.reflection_mode =
 | 
					 | 
				
			||||||
        ReflectionMode::try_from((app_status.reflection_mode as u32 + 1) % 3).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Add or remove the light probe.
 | 
					 | 
				
			||||||
    for light_probe in light_probe_query.iter() {
 | 
					 | 
				
			||||||
        commands.entity(light_probe).despawn();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    match app_status.reflection_mode {
 | 
					 | 
				
			||||||
        ReflectionMode::None | ReflectionMode::EnvironmentMap => {}
 | 
					 | 
				
			||||||
        ReflectionMode::ReflectionProbe => spawn_reflection_probe(&mut commands, &cubemaps),
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Add or remove the environment map from the camera.
 | 
					 | 
				
			||||||
    for camera in camera_query.iter() {
 | 
					 | 
				
			||||||
        match app_status.reflection_mode {
 | 
					 | 
				
			||||||
            ReflectionMode::None => {
 | 
					 | 
				
			||||||
                commands.entity(camera).remove::<EnvironmentMapLight>();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            ReflectionMode::EnvironmentMap | ReflectionMode::ReflectionProbe => {
 | 
					 | 
				
			||||||
                commands
 | 
					 | 
				
			||||||
                    .entity(camera)
 | 
					 | 
				
			||||||
                    .insert(create_camera_environment_map_light(&cubemaps));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// A system that handles enabling and disabling rotation.
 | 
					 | 
				
			||||||
fn toggle_rotation(keyboard: Res<ButtonInput<KeyCode>>, mut app_status: ResMut<AppStatus>) {
 | 
					 | 
				
			||||||
    if keyboard.just_pressed(KeyCode::Enter) {
 | 
					 | 
				
			||||||
        app_status.rotating = !app_status.rotating;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// A system that updates the help text.
 | 
					 | 
				
			||||||
fn update_text(
 | 
					 | 
				
			||||||
    mut text_query: Query<&mut Text>,
 | 
					 | 
				
			||||||
    app_status: Res<AppStatus>,
 | 
					 | 
				
			||||||
    asset_server: Res<AssetServer>,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    for mut text in text_query.iter_mut() {
 | 
					 | 
				
			||||||
        *text = app_status.create_text(&asset_server);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl TryFrom<u32> for ReflectionMode {
 | 
					 | 
				
			||||||
    type Error = ();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(value: u32) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        match value {
 | 
					 | 
				
			||||||
            0 => Ok(ReflectionMode::None),
 | 
					 | 
				
			||||||
            1 => Ok(ReflectionMode::EnvironmentMap),
 | 
					 | 
				
			||||||
            2 => Ok(ReflectionMode::ReflectionProbe),
 | 
					 | 
				
			||||||
            _ => Err(()),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Display for ReflectionMode {
 | 
					 | 
				
			||||||
    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
 | 
					 | 
				
			||||||
        let text = match *self {
 | 
					 | 
				
			||||||
            ReflectionMode::None => "No reflections",
 | 
					 | 
				
			||||||
            ReflectionMode::EnvironmentMap => "Environment map",
 | 
					 | 
				
			||||||
            ReflectionMode::ReflectionProbe => "Reflection probe",
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        formatter.write_str(text)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl AppStatus {
 | 
					 | 
				
			||||||
    // Constructs the help text at the bottom of the screen based on the
 | 
					 | 
				
			||||||
    // application status.
 | 
					 | 
				
			||||||
    fn create_text(&self, asset_server: &AssetServer) -> Text {
 | 
					 | 
				
			||||||
        let rotation_help_text = if self.rotating {
 | 
					 | 
				
			||||||
            STOP_ROTATION_HELP_TEXT
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            START_ROTATION_HELP_TEXT
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Text::from_section(
 | 
					 | 
				
			||||||
            format!(
 | 
					 | 
				
			||||||
                "{}\n{}\n{}",
 | 
					 | 
				
			||||||
                self.reflection_mode, rotation_help_text, REFLECTION_MODE_HELP_TEXT
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            TextStyle {
 | 
					 | 
				
			||||||
                font: asset_server.load("fonts/FiraMono-Medium.ttf"),
 | 
					 | 
				
			||||||
                font_size: 24.0,
 | 
					 | 
				
			||||||
                color: Color::ANTIQUE_WHITE,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Creates the world environment map light, used as a fallback if no reflection
 | 
					 | 
				
			||||||
// probe is applicable to a mesh.
 | 
					 | 
				
			||||||
fn create_camera_environment_map_light(cubemaps: &Cubemaps) -> EnvironmentMapLight {
 | 
					 | 
				
			||||||
    EnvironmentMapLight {
 | 
					 | 
				
			||||||
        diffuse_map: cubemaps.diffuse.clone(),
 | 
					 | 
				
			||||||
        specular_map: cubemaps.specular_environment_map.clone(),
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Rotates the camera a bit every frame.
 | 
					 | 
				
			||||||
fn rotate_camera(
 | 
					 | 
				
			||||||
    mut camera_query: Query<&mut Transform, With<Camera3d>>,
 | 
					 | 
				
			||||||
    app_status: Res<AppStatus>,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    if !app_status.rotating {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for mut transform in camera_query.iter_mut() {
 | 
					 | 
				
			||||||
        transform.translation = Vec2::from_angle(ROTATION_SPEED)
 | 
					 | 
				
			||||||
            .rotate(transform.translation.xz())
 | 
					 | 
				
			||||||
            .extend(transform.translation.y)
 | 
					 | 
				
			||||||
            .xzy();
 | 
					 | 
				
			||||||
        transform.look_at(Vec3::ZERO, Vec3::Y);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Loads the cubemaps from the assets directory.
 | 
					 | 
				
			||||||
impl FromWorld for Cubemaps {
 | 
					 | 
				
			||||||
    fn from_world(world: &mut World) -> Self {
 | 
					 | 
				
			||||||
        let asset_server = world.resource::<AssetServer>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Just use the specular map for the skybox since it's not too blurry.
 | 
					 | 
				
			||||||
        // In reality you wouldn't do this--you'd use a real skybox texture--but
 | 
					 | 
				
			||||||
        // reusing the textures like this saves space in the Bevy repository.
 | 
					 | 
				
			||||||
        let specular_map = asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Cubemaps {
 | 
					 | 
				
			||||||
            diffuse: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
 | 
					 | 
				
			||||||
            specular_reflection_probe: asset_server
 | 
					 | 
				
			||||||
                .load("environment_maps/cubes_reflection_probe_specular_rgb9e5_zstd.ktx2"),
 | 
					 | 
				
			||||||
            specular_environment_map: specular_map.clone(),
 | 
					 | 
				
			||||||
            skybox: specular_map,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Default for AppStatus {
 | 
					 | 
				
			||||||
    fn default() -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            reflection_mode: ReflectionMode::ReflectionProbe,
 | 
					 | 
				
			||||||
            rotating: true,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -134,7 +134,6 @@ Example | Description
 | 
				
			|||||||
[Parallax Mapping](../examples/3d/parallax_mapping.rs) | Demonstrates use of a normal map and depth map for parallax mapping
 | 
					[Parallax Mapping](../examples/3d/parallax_mapping.rs) | Demonstrates use of a normal map and depth map for parallax mapping
 | 
				
			||||||
[Parenting](../examples/3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations
 | 
					[Parenting](../examples/3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations
 | 
				
			||||||
[Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
 | 
					[Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
 | 
				
			||||||
[Reflection Probes](../examples/3d/reflection_probes.rs) | Demonstrates reflection probes
 | 
					 | 
				
			||||||
[Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images
 | 
					[Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images
 | 
				
			||||||
[Screen Space Ambient Occlusion](../examples/3d/ssao.rs) | A scene showcasing screen space ambient occlusion
 | 
					[Screen Space Ambient Occlusion](../examples/3d/ssao.rs) | A scene showcasing screen space ambient occlusion
 | 
				
			||||||
[Shadow Biases](../examples/3d/shadow_biases.rs) | Demonstrates how shadow biases affect shadows in a 3d scene
 | 
					[Shadow Biases](../examples/3d/shadow_biases.rs) | Demonstrates how shadow biases affect shadows in a 3d scene
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user