bevy_pbr/atmosphere/
resources.rs

1use crate::{
2    ExtractedAtmosphere, GpuLights, GpuScatteringMedium, LightMeta, ScatteringMedium,
3    ScatteringMediumSampler,
4};
5use bevy_asset::{load_embedded_asset, AssetId, Handle};
6use bevy_camera::{Camera, Camera3d};
7use bevy_core_pipeline::FullscreenShader;
8use bevy_derive::Deref;
9use bevy_ecs::{
10    component::Component,
11    entity::Entity,
12    error::BevyError,
13    query::With,
14    resource::Resource,
15    system::{Commands, Query, Res, ResMut},
16    world::{FromWorld, World},
17};
18use bevy_image::ToExtents;
19use bevy_math::{Affine3A, Mat4, Vec3, Vec3A};
20use bevy_render::{
21    extract_component::ComponentUniforms,
22    render_asset::RenderAssets,
23    render_resource::{binding_types::*, *},
24    renderer::{RenderDevice, RenderQueue},
25    texture::{CachedTexture, TextureCache},
26    view::{ExtractedView, Msaa, ViewDepthTexture, ViewUniform, ViewUniforms},
27};
28use bevy_shader::Shader;
29use bevy_utils::default;
30
31use super::GpuAtmosphereSettings;
32
33#[derive(Resource)]
34pub(crate) struct AtmosphereBindGroupLayouts {
35    pub transmittance_lut: BindGroupLayoutDescriptor,
36    pub multiscattering_lut: BindGroupLayoutDescriptor,
37    pub sky_view_lut: BindGroupLayoutDescriptor,
38    pub aerial_view_lut: BindGroupLayoutDescriptor,
39}
40
41#[derive(Resource)]
42pub(crate) struct RenderSkyBindGroupLayouts {
43    pub render_sky: BindGroupLayoutDescriptor,
44    pub render_sky_msaa: BindGroupLayoutDescriptor,
45    pub fullscreen_shader: FullscreenShader,
46    pub fragment_shader: Handle<Shader>,
47}
48
49impl AtmosphereBindGroupLayouts {
50    pub fn new() -> Self {
51        let transmittance_lut = BindGroupLayoutDescriptor::new(
52            "transmittance_lut_bind_group_layout",
53            &BindGroupLayoutEntries::with_indices(
54                ShaderStages::COMPUTE,
55                (
56                    (0, uniform_buffer::<GpuAtmosphere>(true)),
57                    (1, uniform_buffer::<GpuAtmosphereSettings>(true)),
58                    // scattering medium luts and sampler
59                    (5, texture_2d(TextureSampleType::default())),
60                    (6, texture_2d(TextureSampleType::default())),
61                    (7, sampler(SamplerBindingType::Filtering)),
62                    // transmittance lut storage texture
63                    (
64                        13,
65                        texture_storage_2d(
66                            TextureFormat::Rgba16Float,
67                            StorageTextureAccess::WriteOnly,
68                        ),
69                    ),
70                ),
71            ),
72        );
73
74        let multiscattering_lut = BindGroupLayoutDescriptor::new(
75            "multiscattering_lut_bind_group_layout",
76            &BindGroupLayoutEntries::with_indices(
77                ShaderStages::COMPUTE,
78                (
79                    (0, uniform_buffer::<GpuAtmosphere>(true)),
80                    (1, uniform_buffer::<GpuAtmosphereSettings>(true)),
81                    // scattering medium luts and sampler
82                    (5, texture_2d(TextureSampleType::default())),
83                    (6, texture_2d(TextureSampleType::default())),
84                    (7, sampler(SamplerBindingType::Filtering)),
85                    // atmosphere luts and sampler
86                    (8, texture_2d(TextureSampleType::default())), // transmittance
87                    (12, sampler(SamplerBindingType::Filtering)),
88                    // multiscattering lut storage texture
89                    (
90                        13,
91                        texture_storage_2d(
92                            TextureFormat::Rgba16Float,
93                            StorageTextureAccess::WriteOnly,
94                        ),
95                    ),
96                ),
97            ),
98        );
99
100        let sky_view_lut = BindGroupLayoutDescriptor::new(
101            "sky_view_lut_bind_group_layout",
102            &BindGroupLayoutEntries::with_indices(
103                ShaderStages::COMPUTE,
104                (
105                    (0, uniform_buffer::<GpuAtmosphere>(true)),
106                    (1, uniform_buffer::<GpuAtmosphereSettings>(true)),
107                    (2, uniform_buffer::<AtmosphereTransform>(true)),
108                    (3, uniform_buffer::<ViewUniform>(true)),
109                    (4, uniform_buffer::<GpuLights>(true)),
110                    // scattering medium luts and sampler
111                    (5, texture_2d(TextureSampleType::default())),
112                    (6, texture_2d(TextureSampleType::default())),
113                    (7, sampler(SamplerBindingType::Filtering)),
114                    // atmosphere luts and sampler
115                    (8, texture_2d(TextureSampleType::default())), // transmittance
116                    (9, texture_2d(TextureSampleType::default())), // multiscattering
117                    (12, sampler(SamplerBindingType::Filtering)),
118                    // sky view lut storage texture
119                    (
120                        13,
121                        texture_storage_2d(
122                            TextureFormat::Rgba16Float,
123                            StorageTextureAccess::WriteOnly,
124                        ),
125                    ),
126                ),
127            ),
128        );
129
130        let aerial_view_lut = BindGroupLayoutDescriptor::new(
131            "aerial_view_lut_bind_group_layout",
132            &BindGroupLayoutEntries::with_indices(
133                ShaderStages::COMPUTE,
134                (
135                    (0, uniform_buffer::<GpuAtmosphere>(true)),
136                    (1, uniform_buffer::<GpuAtmosphereSettings>(true)),
137                    (3, uniform_buffer::<ViewUniform>(true)),
138                    (4, uniform_buffer::<GpuLights>(true)),
139                    // scattering medium luts and sampler
140                    (5, texture_2d(TextureSampleType::default())),
141                    (6, texture_2d(TextureSampleType::default())),
142                    (7, sampler(SamplerBindingType::Filtering)),
143                    // atmosphere luts and sampler
144                    (8, texture_2d(TextureSampleType::default())), // transmittance
145                    (9, texture_2d(TextureSampleType::default())), // multiscattering
146                    (12, sampler(SamplerBindingType::Filtering)),
147                    // eerial view lut storage texture
148                    (
149                        13,
150                        texture_storage_3d(
151                            TextureFormat::Rgba16Float,
152                            StorageTextureAccess::WriteOnly,
153                        ),
154                    ),
155                ),
156            ),
157        );
158
159        Self {
160            transmittance_lut,
161            multiscattering_lut,
162            sky_view_lut,
163            aerial_view_lut,
164        }
165    }
166}
167
168impl FromWorld for RenderSkyBindGroupLayouts {
169    fn from_world(world: &mut World) -> Self {
170        let render_sky = BindGroupLayoutDescriptor::new(
171            "render_sky_bind_group_layout",
172            &BindGroupLayoutEntries::with_indices(
173                ShaderStages::FRAGMENT,
174                (
175                    (0, uniform_buffer::<GpuAtmosphere>(true)),
176                    (1, uniform_buffer::<GpuAtmosphereSettings>(true)),
177                    (2, uniform_buffer::<AtmosphereTransform>(true)),
178                    (3, uniform_buffer::<ViewUniform>(true)),
179                    (4, uniform_buffer::<GpuLights>(true)),
180                    // scattering medium luts and sampler
181                    (5, texture_2d(TextureSampleType::default())),
182                    (6, texture_2d(TextureSampleType::default())),
183                    (7, sampler(SamplerBindingType::Filtering)),
184                    // atmosphere luts and sampler
185                    (8, texture_2d(TextureSampleType::default())), // transmittance
186                    (9, texture_2d(TextureSampleType::default())), // multiscattering
187                    (10, texture_2d(TextureSampleType::default())), // sky view
188                    (11, texture_3d(TextureSampleType::default())), // aerial view
189                    (12, sampler(SamplerBindingType::Filtering)),
190                    // view depth texture
191                    (13, texture_2d(TextureSampleType::Depth)),
192                ),
193            ),
194        );
195
196        let render_sky_msaa = BindGroupLayoutDescriptor::new(
197            "render_sky_msaa_bind_group_layout",
198            &BindGroupLayoutEntries::with_indices(
199                ShaderStages::FRAGMENT,
200                (
201                    (0, uniform_buffer::<GpuAtmosphere>(true)),
202                    (1, uniform_buffer::<GpuAtmosphereSettings>(true)),
203                    (2, uniform_buffer::<AtmosphereTransform>(true)),
204                    (3, uniform_buffer::<ViewUniform>(true)),
205                    (4, uniform_buffer::<GpuLights>(true)),
206                    // scattering medium luts and sampler
207                    (5, texture_2d(TextureSampleType::default())),
208                    (6, texture_2d(TextureSampleType::default())),
209                    (7, sampler(SamplerBindingType::Filtering)),
210                    // atmosphere luts and sampler
211                    (8, texture_2d(TextureSampleType::default())), // transmittance
212                    (9, texture_2d(TextureSampleType::default())), // multiscattering
213                    (10, texture_2d(TextureSampleType::default())), // sky view
214                    (11, texture_3d(TextureSampleType::default())), // aerial view
215                    (12, sampler(SamplerBindingType::Filtering)),
216                    // view depth texture
217                    (13, texture_2d_multisampled(TextureSampleType::Depth)),
218                ),
219            ),
220        );
221
222        Self {
223            render_sky,
224            render_sky_msaa,
225            fullscreen_shader: world.resource::<FullscreenShader>().clone(),
226            fragment_shader: load_embedded_asset!(world, "render_sky.wgsl"),
227        }
228    }
229}
230
231#[derive(Resource, Deref)]
232pub struct AtmosphereSampler(Sampler);
233
234impl FromWorld for AtmosphereSampler {
235    fn from_world(world: &mut World) -> Self {
236        let render_device = world.resource::<RenderDevice>();
237
238        let sampler = render_device.create_sampler(&SamplerDescriptor {
239            mag_filter: FilterMode::Linear,
240            min_filter: FilterMode::Linear,
241            mipmap_filter: FilterMode::Nearest,
242            ..Default::default()
243        });
244
245        Self(sampler)
246    }
247}
248
249#[derive(Resource)]
250pub(crate) struct AtmosphereLutPipelines {
251    pub transmittance_lut: CachedComputePipelineId,
252    pub multiscattering_lut: CachedComputePipelineId,
253    pub sky_view_lut: CachedComputePipelineId,
254    pub aerial_view_lut: CachedComputePipelineId,
255}
256
257impl FromWorld for AtmosphereLutPipelines {
258    fn from_world(world: &mut World) -> Self {
259        let pipeline_cache = world.resource::<PipelineCache>();
260        let layouts = world.resource::<AtmosphereBindGroupLayouts>();
261
262        let transmittance_lut = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
263            label: Some("transmittance_lut_pipeline".into()),
264            layout: vec![layouts.transmittance_lut.clone()],
265            shader: load_embedded_asset!(world, "transmittance_lut.wgsl"),
266            ..default()
267        });
268
269        let multiscattering_lut =
270            pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
271                label: Some("multi_scattering_lut_pipeline".into()),
272                layout: vec![layouts.multiscattering_lut.clone()],
273                shader: load_embedded_asset!(world, "multiscattering_lut.wgsl"),
274                ..default()
275            });
276
277        let sky_view_lut = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
278            label: Some("sky_view_lut_pipeline".into()),
279            layout: vec![layouts.sky_view_lut.clone()],
280            shader: load_embedded_asset!(world, "sky_view_lut.wgsl"),
281            ..default()
282        });
283
284        let aerial_view_lut = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
285            label: Some("aerial_view_lut_pipeline".into()),
286            layout: vec![layouts.aerial_view_lut.clone()],
287            shader: load_embedded_asset!(world, "aerial_view_lut.wgsl"),
288            ..default()
289        });
290
291        Self {
292            transmittance_lut,
293            multiscattering_lut,
294            sky_view_lut,
295            aerial_view_lut,
296        }
297    }
298}
299
300#[derive(Component)]
301pub(crate) struct RenderSkyPipelineId(pub CachedRenderPipelineId);
302
303#[derive(Copy, Clone, Hash, PartialEq, Eq)]
304pub(crate) struct RenderSkyPipelineKey {
305    pub msaa_samples: u32,
306    pub dual_source_blending: bool,
307}
308
309impl SpecializedRenderPipeline for RenderSkyBindGroupLayouts {
310    type Key = RenderSkyPipelineKey;
311
312    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
313        let mut shader_defs = Vec::new();
314
315        if key.msaa_samples > 1 {
316            shader_defs.push("MULTISAMPLED".into());
317        }
318        if key.dual_source_blending {
319            shader_defs.push("DUAL_SOURCE_BLENDING".into());
320        }
321
322        let dst_factor = if key.dual_source_blending {
323            BlendFactor::Src1
324        } else {
325            BlendFactor::SrcAlpha
326        };
327
328        RenderPipelineDescriptor {
329            label: Some(format!("render_sky_pipeline_{}", key.msaa_samples).into()),
330            layout: vec![if key.msaa_samples == 1 {
331                self.render_sky.clone()
332            } else {
333                self.render_sky_msaa.clone()
334            }],
335            vertex: self.fullscreen_shader.to_vertex_state(),
336            fragment: Some(FragmentState {
337                shader: self.fragment_shader.clone(),
338                shader_defs,
339                targets: vec![Some(ColorTargetState {
340                    format: TextureFormat::Rgba16Float,
341                    blend: Some(BlendState {
342                        color: BlendComponent {
343                            src_factor: BlendFactor::One,
344                            dst_factor,
345                            operation: BlendOperation::Add,
346                        },
347                        alpha: BlendComponent {
348                            src_factor: BlendFactor::Zero,
349                            dst_factor: BlendFactor::One,
350                            operation: BlendOperation::Add,
351                        },
352                    }),
353                    write_mask: ColorWrites::ALL,
354                })],
355                ..default()
356            }),
357            multisample: MultisampleState {
358                count: key.msaa_samples,
359                ..default()
360            },
361            ..default()
362        }
363    }
364}
365
366pub(super) fn queue_render_sky_pipelines(
367    views: Query<(Entity, &Msaa), (With<Camera>, With<ExtractedAtmosphere>)>,
368    pipeline_cache: Res<PipelineCache>,
369    layouts: Res<RenderSkyBindGroupLayouts>,
370    mut specializer: ResMut<SpecializedRenderPipelines<RenderSkyBindGroupLayouts>>,
371    render_device: Res<RenderDevice>,
372    mut commands: Commands,
373) {
374    for (entity, msaa) in &views {
375        let id = specializer.specialize(
376            &pipeline_cache,
377            &layouts,
378            RenderSkyPipelineKey {
379                msaa_samples: msaa.samples(),
380                dual_source_blending: render_device
381                    .features()
382                    .contains(WgpuFeatures::DUAL_SOURCE_BLENDING),
383            },
384        );
385        commands.entity(entity).insert(RenderSkyPipelineId(id));
386    }
387}
388
389#[derive(Component)]
390pub struct AtmosphereTextures {
391    pub transmittance_lut: CachedTexture,
392    pub multiscattering_lut: CachedTexture,
393    pub sky_view_lut: CachedTexture,
394    pub aerial_view_lut: CachedTexture,
395}
396
397pub(super) fn prepare_atmosphere_textures(
398    views: Query<(Entity, &GpuAtmosphereSettings), With<ExtractedAtmosphere>>,
399    render_device: Res<RenderDevice>,
400    mut texture_cache: ResMut<TextureCache>,
401    mut commands: Commands,
402) {
403    for (entity, lut_settings) in &views {
404        let transmittance_lut = texture_cache.get(
405            &render_device,
406            TextureDescriptor {
407                label: Some("transmittance_lut"),
408                size: lut_settings.transmittance_lut_size.to_extents(),
409                mip_level_count: 1,
410                sample_count: 1,
411                dimension: TextureDimension::D2,
412                format: TextureFormat::Rgba16Float,
413                usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
414                view_formats: &[],
415            },
416        );
417
418        let multiscattering_lut = texture_cache.get(
419            &render_device,
420            TextureDescriptor {
421                label: Some("multiscattering_lut"),
422                size: lut_settings.multiscattering_lut_size.to_extents(),
423                mip_level_count: 1,
424                sample_count: 1,
425                dimension: TextureDimension::D2,
426                format: TextureFormat::Rgba16Float,
427                usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
428                view_formats: &[],
429            },
430        );
431
432        let sky_view_lut = texture_cache.get(
433            &render_device,
434            TextureDescriptor {
435                label: Some("sky_view_lut"),
436                size: lut_settings.sky_view_lut_size.to_extents(),
437                mip_level_count: 1,
438                sample_count: 1,
439                dimension: TextureDimension::D2,
440                format: TextureFormat::Rgba16Float,
441                usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
442                view_formats: &[],
443            },
444        );
445
446        let aerial_view_lut = texture_cache.get(
447            &render_device,
448            TextureDescriptor {
449                label: Some("aerial_view_lut"),
450                size: lut_settings.aerial_view_lut_size.to_extents(),
451                mip_level_count: 1,
452                sample_count: 1,
453                dimension: TextureDimension::D3,
454                format: TextureFormat::Rgba16Float,
455                usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
456                view_formats: &[],
457            },
458        );
459
460        commands.entity(entity).insert({
461            AtmosphereTextures {
462                transmittance_lut,
463                multiscattering_lut,
464                sky_view_lut,
465                aerial_view_lut,
466            }
467        });
468    }
469}
470
471#[derive(Copy, Clone, Debug, thiserror::Error)]
472#[error("ScatteringMedium missing with id {0:?}: make sure the asset was not removed.")]
473struct ScatteringMediumMissingError(AssetId<ScatteringMedium>);
474
475/// The shader-uniform representation of an Atmosphere.
476#[derive(Clone, Component, ShaderType)]
477pub struct GpuAtmosphere {
478    //TODO: rename to Planet later?
479    pub ground_albedo: Vec3,
480    pub bottom_radius: f32,
481    pub top_radius: f32,
482}
483
484pub fn prepare_atmosphere_uniforms(
485    mut commands: Commands,
486    atmospheres: Query<(Entity, &ExtractedAtmosphere)>,
487) -> Result<(), BevyError> {
488    for (entity, atmosphere) in atmospheres {
489        commands.entity(entity).insert(GpuAtmosphere {
490            ground_albedo: atmosphere.ground_albedo,
491            bottom_radius: atmosphere.bottom_radius,
492            top_radius: atmosphere.top_radius,
493        });
494    }
495    Ok(())
496}
497
498#[derive(Resource, Default)]
499pub struct AtmosphereTransforms {
500    uniforms: DynamicUniformBuffer<AtmosphereTransform>,
501}
502
503impl AtmosphereTransforms {
504    #[inline]
505    pub fn uniforms(&self) -> &DynamicUniformBuffer<AtmosphereTransform> {
506        &self.uniforms
507    }
508}
509
510#[derive(ShaderType)]
511pub struct AtmosphereTransform {
512    world_from_atmosphere: Mat4,
513}
514
515#[derive(Component)]
516pub struct AtmosphereTransformsOffset {
517    index: u32,
518}
519
520impl AtmosphereTransformsOffset {
521    #[inline]
522    pub fn index(&self) -> u32 {
523        self.index
524    }
525}
526
527pub(super) fn prepare_atmosphere_transforms(
528    views: Query<(Entity, &ExtractedView), (With<ExtractedAtmosphere>, With<Camera3d>)>,
529    render_device: Res<RenderDevice>,
530    render_queue: Res<RenderQueue>,
531    mut atmo_uniforms: ResMut<AtmosphereTransforms>,
532    mut commands: Commands,
533) {
534    let atmo_count = views.iter().len();
535    let Some(mut writer) =
536        atmo_uniforms
537            .uniforms
538            .get_writer(atmo_count, &render_device, &render_queue)
539    else {
540        return;
541    };
542
543    for (entity, view) in &views {
544        let world_from_view = view.world_from_view.affine();
545        let camera_z = world_from_view.matrix3.z_axis;
546        let camera_y = world_from_view.matrix3.y_axis;
547        let atmo_z = camera_z
548            .with_y(0.0)
549            .try_normalize()
550            .unwrap_or_else(|| camera_y.with_y(0.0).normalize());
551        let atmo_y = Vec3A::Y;
552        let atmo_x = atmo_y.cross(atmo_z).normalize();
553        let world_from_atmosphere =
554            Affine3A::from_cols(atmo_x, atmo_y, atmo_z, world_from_view.translation);
555
556        let world_from_atmosphere = Mat4::from(world_from_atmosphere);
557
558        commands.entity(entity).insert(AtmosphereTransformsOffset {
559            index: writer.write(&AtmosphereTransform {
560                world_from_atmosphere,
561            }),
562        });
563    }
564}
565
566#[derive(Component)]
567pub(crate) struct AtmosphereBindGroups {
568    pub transmittance_lut: BindGroup,
569    pub multiscattering_lut: BindGroup,
570    pub sky_view_lut: BindGroup,
571    pub aerial_view_lut: BindGroup,
572    pub render_sky: BindGroup,
573}
574
575#[derive(Copy, Clone, Debug, thiserror::Error)]
576enum AtmosphereBindGroupError {
577    #[error("Failed to prepare atmosphere bind groups. Atmosphere uniform buffer missing")]
578    Atmosphere,
579    #[error(
580        "Failed to prepare atmosphere bind groups. AtmosphereTransforms uniform buffer missing"
581    )]
582    Transforms,
583    #[error("Failed to prepare atmosphere bind groups. AtmosphereSettings uniform buffer missing")]
584    Settings,
585    #[error("Failed to prepare atmosphere bind groups. View uniform buffer missing")]
586    ViewUniforms,
587    #[error("Failed to prepare atmosphere bind groups. Light uniform buffer missing")]
588    LightUniforms,
589}
590
591pub(super) fn prepare_atmosphere_bind_groups(
592    views: Query<
593        (
594            Entity,
595            &ExtractedAtmosphere,
596            &AtmosphereTextures,
597            &ViewDepthTexture,
598            &Msaa,
599        ),
600        (With<Camera3d>, With<ExtractedAtmosphere>),
601    >,
602    render_device: Res<RenderDevice>,
603    layouts: Res<AtmosphereBindGroupLayouts>,
604    render_sky_layouts: Res<RenderSkyBindGroupLayouts>,
605    atmosphere_sampler: Res<AtmosphereSampler>,
606    view_uniforms: Res<ViewUniforms>,
607    lights_uniforms: Res<LightMeta>,
608    atmosphere_transforms: Res<AtmosphereTransforms>,
609    atmosphere_uniforms: Res<ComponentUniforms<GpuAtmosphere>>,
610    settings_uniforms: Res<ComponentUniforms<GpuAtmosphereSettings>>,
611    gpu_media: Res<RenderAssets<GpuScatteringMedium>>,
612    medium_sampler: Res<ScatteringMediumSampler>,
613    pipeline_cache: Res<PipelineCache>,
614    mut commands: Commands,
615) -> Result<(), BevyError> {
616    if views.iter().len() == 0 {
617        return Ok(());
618    }
619
620    let atmosphere_binding = atmosphere_uniforms
621        .binding()
622        .ok_or(AtmosphereBindGroupError::Atmosphere)?;
623
624    let transforms_binding = atmosphere_transforms
625        .uniforms()
626        .binding()
627        .ok_or(AtmosphereBindGroupError::Transforms)?;
628
629    let settings_binding = settings_uniforms
630        .binding()
631        .ok_or(AtmosphereBindGroupError::Settings)?;
632
633    let view_binding = view_uniforms
634        .uniforms
635        .binding()
636        .ok_or(AtmosphereBindGroupError::ViewUniforms)?;
637
638    let lights_binding = lights_uniforms
639        .view_gpu_lights
640        .binding()
641        .ok_or(AtmosphereBindGroupError::LightUniforms)?;
642
643    for (entity, atmosphere, textures, view_depth_texture, msaa) in &views {
644        let gpu_medium = gpu_media
645            .get(atmosphere.medium)
646            .ok_or(ScatteringMediumMissingError(atmosphere.medium))?;
647
648        let transmittance_lut = render_device.create_bind_group(
649            "transmittance_lut_bind_group",
650            &pipeline_cache.get_bind_group_layout(&layouts.transmittance_lut),
651            &BindGroupEntries::with_indices((
652                // uniforms
653                (0, atmosphere_binding.clone()),
654                (1, settings_binding.clone()),
655                // scattering medium luts and sampler
656                (5, &gpu_medium.density_lut_view),
657                (6, &gpu_medium.scattering_lut_view),
658                (7, medium_sampler.sampler()),
659                // transmittance lut storage texture
660                (13, &textures.transmittance_lut.default_view),
661            )),
662        );
663
664        let multiscattering_lut = render_device.create_bind_group(
665            "multiscattering_lut_bind_group",
666            &pipeline_cache.get_bind_group_layout(&layouts.multiscattering_lut),
667            &BindGroupEntries::with_indices((
668                // uniforms
669                (0, atmosphere_binding.clone()),
670                (1, settings_binding.clone()),
671                // scattering medium luts and sampler
672                (5, &gpu_medium.density_lut_view),
673                (6, &gpu_medium.scattering_lut_view),
674                (7, medium_sampler.sampler()),
675                // atmosphere luts and sampler
676                (8, &textures.transmittance_lut.default_view),
677                (12, &**atmosphere_sampler),
678                // multiscattering lut storage texture
679                (13, &textures.multiscattering_lut.default_view),
680            )),
681        );
682
683        let sky_view_lut = render_device.create_bind_group(
684            "sky_view_lut_bind_group",
685            &pipeline_cache.get_bind_group_layout(&layouts.sky_view_lut),
686            &BindGroupEntries::with_indices((
687                // uniforms
688                (0, atmosphere_binding.clone()),
689                (1, settings_binding.clone()),
690                (2, transforms_binding.clone()),
691                (3, view_binding.clone()),
692                (4, lights_binding.clone()),
693                // scattering medium luts and sampler
694                (5, &gpu_medium.density_lut_view),
695                (6, &gpu_medium.scattering_lut_view),
696                (7, medium_sampler.sampler()),
697                // atmosphere luts and sampler
698                (8, &textures.transmittance_lut.default_view),
699                (9, &textures.multiscattering_lut.default_view),
700                (12, &**atmosphere_sampler),
701                // sky view lut storage texture
702                (13, &textures.sky_view_lut.default_view),
703            )),
704        );
705
706        let aerial_view_lut = render_device.create_bind_group(
707            "sky_view_lut_bind_group",
708            &pipeline_cache.get_bind_group_layout(&layouts.aerial_view_lut),
709            &BindGroupEntries::with_indices((
710                // uniforms
711                (0, atmosphere_binding.clone()),
712                (1, settings_binding.clone()),
713                (3, view_binding.clone()),
714                (4, lights_binding.clone()),
715                // scattering medium luts and sampler
716                (5, &gpu_medium.density_lut_view),
717                (6, &gpu_medium.scattering_lut_view),
718                (7, medium_sampler.sampler()),
719                // atmosphere luts and sampler
720                (8, &textures.transmittance_lut.default_view),
721                (9, &textures.multiscattering_lut.default_view),
722                (12, &**atmosphere_sampler),
723                // aerial view lut storage texture
724                (13, &textures.aerial_view_lut.default_view),
725            )),
726        );
727
728        let render_sky = render_device.create_bind_group(
729            "render_sky_bind_group",
730            &pipeline_cache.get_bind_group_layout(if *msaa == Msaa::Off {
731                &render_sky_layouts.render_sky
732            } else {
733                &render_sky_layouts.render_sky_msaa
734            }),
735            &BindGroupEntries::with_indices((
736                // uniforms
737                (0, atmosphere_binding.clone()),
738                (1, settings_binding.clone()),
739                (2, transforms_binding.clone()),
740                (3, view_binding.clone()),
741                (4, lights_binding.clone()),
742                // scattering medium luts and sampler
743                (5, &gpu_medium.density_lut_view),
744                (6, &gpu_medium.scattering_lut_view),
745                (7, medium_sampler.sampler()),
746                // atmosphere luts and sampler
747                (8, &textures.transmittance_lut.default_view),
748                (9, &textures.multiscattering_lut.default_view),
749                (10, &textures.sky_view_lut.default_view),
750                (11, &textures.aerial_view_lut.default_view),
751                (12, &**atmosphere_sampler),
752                // view depth texture
753                (13, view_depth_texture.view()),
754            )),
755        );
756
757        commands.entity(entity).insert(AtmosphereBindGroups {
758            transmittance_lut,
759            multiscattering_lut,
760            sky_view_lut,
761            aerial_view_lut,
762            render_sky,
763        });
764    }
765
766    Ok(())
767}
768
769#[derive(ShaderType)]
770#[repr(C)]
771pub(crate) struct AtmosphereData {
772    pub atmosphere: GpuAtmosphere,
773    pub settings: GpuAtmosphereSettings,
774}
775
776pub fn init_atmosphere_buffer(mut commands: Commands) {
777    commands.insert_resource(AtmosphereBuffer {
778        buffer: StorageBuffer::from(AtmosphereData {
779            atmosphere: GpuAtmosphere {
780                ground_albedo: Vec3::ZERO,
781                bottom_radius: 0.0,
782                top_radius: 0.0,
783            },
784            settings: GpuAtmosphereSettings::default(),
785        }),
786    });
787}
788
789#[derive(Resource)]
790pub struct AtmosphereBuffer {
791    pub(crate) buffer: StorageBuffer<AtmosphereData>,
792}
793
794pub(crate) fn write_atmosphere_buffer(
795    device: Res<RenderDevice>,
796    queue: Res<RenderQueue>,
797    atmosphere_entity: Query<(&GpuAtmosphere, &GpuAtmosphereSettings), With<Camera3d>>,
798    mut atmosphere_buffer: ResMut<AtmosphereBuffer>,
799) {
800    let Ok((atmosphere, settings)) = atmosphere_entity.single() else {
801        return;
802    };
803
804    atmosphere_buffer.buffer.set(AtmosphereData {
805        atmosphere: atmosphere.clone(),
806        settings: settings.clone(),
807    });
808    atmosphere_buffer.buffer.write_buffer(&device, &queue);
809}