11
11
//! This module provides realtime filtering via [`bevy_light::GeneratedEnvironmentMapLight`].
12
12
//! For prefiltered environment maps, see [`bevy_light::EnvironmentMapLight`].
13
13
//! 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 } ;
15
17
use bevy_ecs:: {
16
18
component:: Component ,
17
19
entity:: Entity ,
18
20
query:: { QueryState , With , Without } ,
19
21
resource:: Resource ,
22
+ schedule:: IntoScheduleConfigs ,
20
23
system:: { lifetimeless:: Read , Commands , Query , Res , ResMut } ,
21
24
world:: { FromWorld , World } ,
22
25
} ;
@@ -25,21 +28,22 @@ use bevy_math::{Quat, UVec2, Vec2};
25
28
use bevy_render:: {
26
29
diagnostic:: RecordDiagnostics ,
27
30
render_asset:: { RenderAssetUsages , RenderAssets } ,
28
- render_graph:: { Node , NodeRunError , RenderGraphContext , RenderLabel } ,
31
+ render_graph:: { Node , NodeRunError , RenderGraphContext , RenderGraphExt , RenderLabel } ,
29
32
render_resource:: {
30
33
binding_types:: * , AddressMode , BindGroup , BindGroupEntries , BindGroupLayout ,
31
34
BindGroupLayoutEntries , CachedComputePipelineId , ComputePassDescriptor ,
32
- ComputePipelineDescriptor , Extent3d , FilterMode , PipelineCache , Sampler ,
35
+ ComputePipelineDescriptor , DownlevelFlags , Extent3d , FilterMode , PipelineCache , Sampler ,
33
36
SamplerBindingType , SamplerDescriptor , ShaderDefVal , ShaderStages , ShaderType ,
34
37
StorageTextureAccess , Texture , TextureAspect , TextureDescriptor , TextureDimension ,
35
38
TextureFormat , TextureFormatFeatureFlags , TextureSampleType , TextureUsages , TextureView ,
36
39
TextureViewDescriptor , TextureViewDimension , UniformBuffer ,
37
40
} ,
38
41
renderer:: { RenderAdapter , RenderContext , RenderDevice , RenderQueue } ,
39
42
settings:: WgpuFeatures ,
43
+ sync_component:: SyncComponentPlugin ,
40
44
sync_world:: RenderEntity ,
41
45
texture:: { CachedTexture , GpuImage , TextureCache } ,
42
- Extract ,
46
+ Extract , ExtractSchedule , Render , RenderApp , RenderStartup , RenderSystems ,
43
47
} ;
44
48
45
49
// Implementation: generate diffuse and specular cubemaps required by PBR
@@ -58,6 +62,7 @@ use bevy_render::{
58
62
59
63
use bevy_light:: { EnvironmentMapLight , GeneratedEnvironmentMapLight } ;
60
64
use core:: cmp:: min;
65
+ use tracing:: info;
61
66
62
67
use crate :: Bluenoise ;
63
68
@@ -101,6 +106,77 @@ pub struct DownsamplingConfig {
101
106
pub combine_bind_group : bool ,
102
107
}
103
108
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
+
104
180
// The number of storage textures required to combine the bind group
105
181
const REQUIRED_STORAGE_TEXTURES : u32 = 12 ;
106
182
@@ -289,6 +365,10 @@ pub fn initialize_generated_environment_map_resources(
289
365
if combine_bind_group {
290
366
shader_defs. push ( ShaderDefVal :: Int ( "COMBINE_BIND_GROUP" . into ( ) , 1 ) ) ;
291
367
}
368
+ #[ cfg( feature = "bluenoise_texture" ) ]
369
+ {
370
+ shader_defs. push ( ShaderDefVal :: Int ( "HAS_BLUE_NOISE" . into ( ) , 1 ) ) ;
371
+ }
292
372
293
373
let downsampling_shader = load_embedded_asset ! ( asset_server. as_ref( ) , "downsample.wgsl" ) ;
294
374
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(
333
413
layout : vec ! [ layouts. radiance. clone( ) ] ,
334
414
push_constant_ranges : vec ! [ ] ,
335
415
shader : env_filter_shader. clone ( ) ,
336
- shader_defs : vec ! [ ] ,
416
+ shader_defs : shader_defs . clone ( ) ,
337
417
entry_point : Some ( "generate_radiance_map" . into ( ) ) ,
338
418
zero_initialize_workgroup_memory : false ,
339
419
} ) ;
@@ -344,7 +424,7 @@ pub fn initialize_generated_environment_map_resources(
344
424
layout : vec ! [ layouts. irradiance. clone( ) ] ,
345
425
push_constant_ranges : vec ! [ ] ,
346
426
shader : env_filter_shader,
347
- shader_defs : vec ! [ ] ,
427
+ shader_defs : shader_defs . clone ( ) ,
348
428
entry_point : Some ( "generate_irradiance_map" . into ( ) ) ,
349
429
zero_initialize_workgroup_memory : false ,
350
430
} ) ;
@@ -639,6 +719,15 @@ pub fn prepare_generated_environment_map_bind_groups(
639
719
( first, second)
640
720
} ;
641
721
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
+
642
731
// Create radiance map bind groups for each mip level
643
732
let num_mips = mip_count as usize ;
644
733
let mut radiance_bind_groups = Vec :: with_capacity ( num_mips) ;
@@ -672,7 +761,7 @@ pub fn prepare_generated_environment_map_bind_groups(
672
761
& samplers. linear ,
673
762
& mip_storage_view,
674
763
& radiance_constants_buffer,
675
- & stbn_texture . texture_view ,
764
+ & stbn_texture_view ,
676
765
) ) ,
677
766
) ;
678
767
@@ -709,7 +798,7 @@ pub fn prepare_generated_environment_map_bind_groups(
709
798
& samplers. linear ,
710
799
& irradiance_map,
711
800
& irradiance_constants_buffer,
712
- & stbn_texture . texture_view ,
801
+ & stbn_texture_view ,
713
802
) ) ,
714
803
) ;
715
804
0 commit comments