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 (5, texture_2d(TextureSampleType::default())),
60 (6, texture_2d(TextureSampleType::default())),
61 (7, sampler(SamplerBindingType::Filtering)),
62 (
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 (5, texture_2d(TextureSampleType::default())),
83 (6, texture_2d(TextureSampleType::default())),
84 (7, sampler(SamplerBindingType::Filtering)),
85 (8, texture_2d(TextureSampleType::default())), (12, sampler(SamplerBindingType::Filtering)),
88 (
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 (5, texture_2d(TextureSampleType::default())),
112 (6, texture_2d(TextureSampleType::default())),
113 (7, sampler(SamplerBindingType::Filtering)),
114 (8, texture_2d(TextureSampleType::default())), (9, texture_2d(TextureSampleType::default())), (12, sampler(SamplerBindingType::Filtering)),
118 (
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 (5, texture_2d(TextureSampleType::default())),
141 (6, texture_2d(TextureSampleType::default())),
142 (7, sampler(SamplerBindingType::Filtering)),
143 (8, texture_2d(TextureSampleType::default())), (9, texture_2d(TextureSampleType::default())), (12, sampler(SamplerBindingType::Filtering)),
147 (
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 (5, texture_2d(TextureSampleType::default())),
182 (6, texture_2d(TextureSampleType::default())),
183 (7, sampler(SamplerBindingType::Filtering)),
184 (8, texture_2d(TextureSampleType::default())), (9, texture_2d(TextureSampleType::default())), (10, texture_2d(TextureSampleType::default())), (11, texture_3d(TextureSampleType::default())), (12, sampler(SamplerBindingType::Filtering)),
190 (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 (5, texture_2d(TextureSampleType::default())),
208 (6, texture_2d(TextureSampleType::default())),
209 (7, sampler(SamplerBindingType::Filtering)),
210 (8, texture_2d(TextureSampleType::default())), (9, texture_2d(TextureSampleType::default())), (10, texture_2d(TextureSampleType::default())), (11, texture_3d(TextureSampleType::default())), (12, sampler(SamplerBindingType::Filtering)),
216 (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#[derive(Clone, Component, ShaderType)]
477pub struct GpuAtmosphere {
478 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 (0, atmosphere_binding.clone()),
654 (1, settings_binding.clone()),
655 (5, &gpu_medium.density_lut_view),
657 (6, &gpu_medium.scattering_lut_view),
658 (7, medium_sampler.sampler()),
659 (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 (0, atmosphere_binding.clone()),
670 (1, settings_binding.clone()),
671 (5, &gpu_medium.density_lut_view),
673 (6, &gpu_medium.scattering_lut_view),
674 (7, medium_sampler.sampler()),
675 (8, &textures.transmittance_lut.default_view),
677 (12, &**atmosphere_sampler),
678 (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 (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 (5, &gpu_medium.density_lut_view),
695 (6, &gpu_medium.scattering_lut_view),
696 (7, medium_sampler.sampler()),
697 (8, &textures.transmittance_lut.default_view),
699 (9, &textures.multiscattering_lut.default_view),
700 (12, &**atmosphere_sampler),
701 (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 (0, atmosphere_binding.clone()),
712 (1, settings_binding.clone()),
713 (3, view_binding.clone()),
714 (4, lights_binding.clone()),
715 (5, &gpu_medium.density_lut_view),
717 (6, &gpu_medium.scattering_lut_view),
718 (7, medium_sampler.sampler()),
719 (8, &textures.transmittance_lut.default_view),
721 (9, &textures.multiscattering_lut.default_view),
722 (12, &**atmosphere_sampler),
723 (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 (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 (5, &gpu_medium.density_lut_view),
744 (6, &gpu_medium.scattering_lut_view),
745 (7, medium_sampler.sampler()),
746 (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 (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}