1111//! This module provides realtime filtering via [`bevy_light::GeneratedEnvironmentMapLight`].
1212//! For prefiltered environment maps, see [`bevy_light::EnvironmentMapLight`].
1313//! These components are intended to be added to a camera.
14- use bevy_asset:: { load_embedded_asset, AssetServer , Assets } ;
14+ use bevy_app:: { App , Plugin , Update } ;
15+ use bevy_asset:: { embedded_asset, load_embedded_asset, AssetServer , Assets } ;
16+ use bevy_core_pipeline:: core_3d:: graph:: { Core3d , Node3d } ;
1517use bevy_ecs:: {
1618 component:: Component ,
1719 entity:: Entity ,
1820 query:: { QueryState , With , Without } ,
1921 resource:: Resource ,
22+ schedule:: IntoScheduleConfigs ,
2023 system:: { lifetimeless:: Read , Commands , Query , Res , ResMut } ,
2124 world:: { FromWorld , World } ,
2225} ;
@@ -25,21 +28,22 @@ use bevy_math::{Quat, UVec2, Vec2};
2528use bevy_render:: {
2629 diagnostic:: RecordDiagnostics ,
2730 render_asset:: { RenderAssetUsages , RenderAssets } ,
28- render_graph:: { Node , NodeRunError , RenderGraphContext , RenderLabel } ,
31+ render_graph:: { Node , NodeRunError , RenderGraphContext , RenderGraphExt , RenderLabel } ,
2932 render_resource:: {
3033 binding_types:: * , AddressMode , BindGroup , BindGroupEntries , BindGroupLayout ,
3134 BindGroupLayoutEntries , CachedComputePipelineId , ComputePassDescriptor ,
32- ComputePipelineDescriptor , Extent3d , FilterMode , PipelineCache , Sampler ,
35+ ComputePipelineDescriptor , DownlevelFlags , Extent3d , FilterMode , PipelineCache , Sampler ,
3336 SamplerBindingType , SamplerDescriptor , ShaderDefVal , ShaderStages , ShaderType ,
3437 StorageTextureAccess , Texture , TextureAspect , TextureDescriptor , TextureDimension ,
3538 TextureFormat , TextureFormatFeatureFlags , TextureSampleType , TextureUsages , TextureView ,
3639 TextureViewDescriptor , TextureViewDimension , UniformBuffer ,
3740 } ,
3841 renderer:: { RenderAdapter , RenderContext , RenderDevice , RenderQueue } ,
3942 settings:: WgpuFeatures ,
43+ sync_component:: SyncComponentPlugin ,
4044 sync_world:: RenderEntity ,
4145 texture:: { CachedTexture , GpuImage , TextureCache } ,
42- Extract ,
46+ Extract , ExtractSchedule , Render , RenderApp , RenderStartup , RenderSystems ,
4347} ;
4448
4549// Implementation: generate diffuse and specular cubemaps required by PBR
@@ -58,6 +62,7 @@ use bevy_render::{
5862
5963use bevy_light:: { EnvironmentMapLight , GeneratedEnvironmentMapLight } ;
6064use core:: cmp:: min;
65+ use tracing:: info;
6166
6267use crate :: Bluenoise ;
6368
@@ -101,6 +106,77 @@ pub struct DownsamplingConfig {
101106 pub combine_bind_group : bool ,
102107}
103108
109+ pub struct EnvironmentMapGenerationPlugin ;
110+
111+ impl Plugin for EnvironmentMapGenerationPlugin {
112+ fn build ( & self , _: & mut App ) { }
113+ fn finish ( & self , app : & mut App ) {
114+ if let Some ( render_app) = app. get_sub_app_mut ( RenderApp ) {
115+ let adapter = render_app. world ( ) . resource :: < RenderAdapter > ( ) ;
116+ let device = render_app. world ( ) . resource :: < RenderDevice > ( ) ;
117+
118+ // Cubemap SPD requires at least 6 storage textures
119+ let limit_support = device. limits ( ) . max_storage_textures_per_shader_stage >= 6
120+ && device. limits ( ) . max_compute_workgroup_storage_size != 0
121+ && device. limits ( ) . max_compute_workgroup_size_x != 0 ;
122+
123+ let downlevel_support = adapter
124+ . get_downlevel_capabilities ( )
125+ . flags
126+ . contains ( DownlevelFlags :: COMPUTE_SHADERS ) ;
127+
128+ if !limit_support || !downlevel_support {
129+ info ! ( "Disabling EnvironmentMapGenerationPlugin because compute is not supported on this platform. This is safe to ignore if you are not using EnvironmentMapGenerationPlugin." ) ;
130+ return ;
131+ }
132+ } else {
133+ return ;
134+ }
135+
136+ embedded_asset ! ( app, "environment_filter.wgsl" ) ;
137+ embedded_asset ! ( app, "downsample.wgsl" ) ;
138+ embedded_asset ! ( app, "copy.wgsl" ) ;
139+
140+ app. add_plugins ( SyncComponentPlugin :: < GeneratedEnvironmentMapLight > :: default ( ) )
141+ . add_systems ( Update , generate_environment_map_light) ;
142+
143+ let Some ( render_app) = app. get_sub_app_mut ( RenderApp ) else {
144+ return ;
145+ } ;
146+
147+ render_app
148+ . add_render_graph_node :: < DownsamplingNode > ( Core3d , GeneratorNode :: Downsampling )
149+ . add_render_graph_node :: < FilteringNode > ( Core3d , GeneratorNode :: Filtering )
150+ . add_render_graph_edges (
151+ Core3d ,
152+ (
153+ Node3d :: EndPrepasses ,
154+ GeneratorNode :: Downsampling ,
155+ GeneratorNode :: Filtering ,
156+ Node3d :: StartMainPass ,
157+ ) ,
158+ )
159+ . add_systems (
160+ ExtractSchedule ,
161+ extract_generated_environment_map_entities. after ( generate_environment_map_light) ,
162+ )
163+ . add_systems (
164+ Render ,
165+ prepare_generated_environment_map_bind_groups
166+ . in_set ( RenderSystems :: PrepareBindGroups ) ,
167+ )
168+ . add_systems (
169+ Render ,
170+ prepare_generated_environment_map_intermediate_textures
171+ . in_set ( RenderSystems :: PrepareResources ) ,
172+ )
173+ . add_systems (
174+ RenderStartup ,
175+ initialize_generated_environment_map_resources,
176+ ) ;
177+ }
178+ }
179+
104180// The number of storage textures required to combine the bind group
105181const REQUIRED_STORAGE_TEXTURES : u32 = 12 ;
106182
@@ -289,6 +365,10 @@ pub fn initialize_generated_environment_map_resources(
289365 if combine_bind_group {
290366 shader_defs. push ( ShaderDefVal :: Int ( "COMBINE_BIND_GROUP" . into ( ) , 1 ) ) ;
291367 }
368+ #[ cfg( feature = "bluenoise_texture" ) ]
369+ {
370+ shader_defs. push ( ShaderDefVal :: Int ( "HAS_BLUE_NOISE" . into ( ) , 1 ) ) ;
371+ }
292372
293373 let downsampling_shader = load_embedded_asset ! ( asset_server. as_ref( ) , "downsample.wgsl" ) ;
294374 let env_filter_shader = load_embedded_asset ! ( asset_server. as_ref( ) , "environment_filter.wgsl" ) ;
@@ -333,7 +413,7 @@ pub fn initialize_generated_environment_map_resources(
333413 layout : vec ! [ layouts. radiance. clone( ) ] ,
334414 push_constant_ranges : vec ! [ ] ,
335415 shader : env_filter_shader. clone ( ) ,
336- shader_defs : vec ! [ ] ,
416+ shader_defs : shader_defs . clone ( ) ,
337417 entry_point : Some ( "generate_radiance_map" . into ( ) ) ,
338418 zero_initialize_workgroup_memory : false ,
339419 } ) ;
@@ -344,7 +424,7 @@ pub fn initialize_generated_environment_map_resources(
344424 layout : vec ! [ layouts. irradiance. clone( ) ] ,
345425 push_constant_ranges : vec ! [ ] ,
346426 shader : env_filter_shader,
347- shader_defs : vec ! [ ] ,
427+ shader_defs : shader_defs . clone ( ) ,
348428 entry_point : Some ( "generate_irradiance_map" . into ( ) ) ,
349429 zero_initialize_workgroup_memory : false ,
350430 } ) ;
@@ -639,6 +719,15 @@ pub fn prepare_generated_environment_map_bind_groups(
639719 ( first, second)
640720 } ;
641721
722+ // create a 2d array view of the bluenoise texture
723+ let stbn_texture_view = stbn_texture
724+ . texture
725+ . clone ( )
726+ . create_view ( & TextureViewDescriptor {
727+ dimension : Some ( TextureViewDimension :: D2Array ) ,
728+ ..Default :: default ( )
729+ } ) ;
730+
642731 // Create radiance map bind groups for each mip level
643732 let num_mips = mip_count as usize ;
644733 let mut radiance_bind_groups = Vec :: with_capacity ( num_mips) ;
@@ -672,7 +761,7 @@ pub fn prepare_generated_environment_map_bind_groups(
672761 & samplers. linear ,
673762 & mip_storage_view,
674763 & radiance_constants_buffer,
675- & stbn_texture . texture_view ,
764+ & stbn_texture_view ,
676765 ) ) ,
677766 ) ;
678767
@@ -709,7 +798,7 @@ pub fn prepare_generated_environment_map_bind_groups(
709798 & samplers. linear ,
710799 & irradiance_map,
711800 & irradiance_constants_buffer,
712- & stbn_texture . texture_view ,
801+ & stbn_texture_view ,
713802 ) ) ,
714803 ) ;
715804
0 commit comments