1mod environment;
37mod node;
38pub mod resources;
39
40use bevy_app::{App, Plugin, Update};
41use bevy_asset::embedded_asset;
42use bevy_camera::Camera3d;
43use bevy_core_pipeline::core_3d::graph::Node3d;
44use bevy_ecs::{
45 component::Component,
46 query::{Changed, QueryItem, With},
47 schedule::IntoScheduleConfigs,
48 system::{lifetimeless::Read, Query},
49};
50use bevy_math::{UVec2, UVec3, Vec3};
51use bevy_reflect::{std_traits::ReflectDefault, Reflect};
52use bevy_render::{
53 extract_component::UniformComponentPlugin,
54 render_resource::{DownlevelFlags, ShaderType, SpecializedRenderPipelines},
55 view::Hdr,
56 RenderStartup,
57};
58use bevy_render::{
59 extract_component::{ExtractComponent, ExtractComponentPlugin},
60 render_graph::{RenderGraphExt, ViewNodeRunner},
61 render_resource::{TextureFormat, TextureUsages},
62 renderer::RenderAdapter,
63 Render, RenderApp, RenderSystems,
64};
65
66use bevy_core_pipeline::core_3d::graph::Core3d;
67use bevy_shader::load_shader_library;
68use environment::{
69 init_atmosphere_probe_layout, init_atmosphere_probe_pipeline,
70 prepare_atmosphere_probe_bind_groups, prepare_atmosphere_probe_components,
71 prepare_probe_textures, AtmosphereEnvironmentMap, EnvironmentNode,
72};
73use resources::{
74 prepare_atmosphere_transforms, queue_render_sky_pipelines, AtmosphereTransforms,
75 RenderSkyBindGroupLayouts,
76};
77use tracing::warn;
78
79use self::{
80 node::{AtmosphereLutsNode, AtmosphereNode, RenderSkyNode},
81 resources::{
82 prepare_atmosphere_bind_groups, prepare_atmosphere_textures, AtmosphereBindGroupLayouts,
83 AtmosphereLutPipelines, AtmosphereSamplers,
84 },
85};
86
87#[doc(hidden)]
88pub struct AtmospherePlugin;
89
90impl Plugin for AtmospherePlugin {
91 fn build(&self, app: &mut App) {
92 load_shader_library!(app, "types.wgsl");
93 load_shader_library!(app, "functions.wgsl");
94 load_shader_library!(app, "bruneton_functions.wgsl");
95 load_shader_library!(app, "bindings.wgsl");
96
97 embedded_asset!(app, "transmittance_lut.wgsl");
98 embedded_asset!(app, "multiscattering_lut.wgsl");
99 embedded_asset!(app, "sky_view_lut.wgsl");
100 embedded_asset!(app, "aerial_view_lut.wgsl");
101 embedded_asset!(app, "render_sky.wgsl");
102 embedded_asset!(app, "environment.wgsl");
103
104 app.add_plugins((
105 ExtractComponentPlugin::<Atmosphere>::default(),
106 ExtractComponentPlugin::<GpuAtmosphereSettings>::default(),
107 ExtractComponentPlugin::<AtmosphereEnvironmentMap>::default(),
108 UniformComponentPlugin::<Atmosphere>::default(),
109 UniformComponentPlugin::<GpuAtmosphereSettings>::default(),
110 ))
111 .add_systems(Update, prepare_atmosphere_probe_components);
112 }
113
114 fn finish(&self, app: &mut App) {
115 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
116 return;
117 };
118
119 let render_adapter = render_app.world().resource::<RenderAdapter>();
120
121 if !render_adapter
122 .get_downlevel_capabilities()
123 .flags
124 .contains(DownlevelFlags::COMPUTE_SHADERS)
125 {
126 warn!("AtmospherePlugin not loaded. GPU lacks support for compute shaders.");
127 return;
128 }
129
130 if !render_adapter
131 .get_texture_format_features(TextureFormat::Rgba16Float)
132 .allowed_usages
133 .contains(TextureUsages::STORAGE_BINDING)
134 {
135 warn!("AtmospherePlugin not loaded. GPU lacks support: TextureFormat::Rgba16Float does not support TextureUsages::STORAGE_BINDING.");
136 return;
137 }
138
139 render_app
140 .init_resource::<AtmosphereBindGroupLayouts>()
141 .init_resource::<RenderSkyBindGroupLayouts>()
142 .init_resource::<AtmosphereSamplers>()
143 .init_resource::<AtmosphereLutPipelines>()
144 .init_resource::<AtmosphereTransforms>()
145 .init_resource::<SpecializedRenderPipelines<RenderSkyBindGroupLayouts>>()
146 .add_systems(
147 RenderStartup,
148 (init_atmosphere_probe_layout, init_atmosphere_probe_pipeline).chain(),
149 )
150 .add_systems(
151 Render,
152 (
153 configure_camera_depth_usages.in_set(RenderSystems::ManageViews),
154 queue_render_sky_pipelines.in_set(RenderSystems::Queue),
155 prepare_atmosphere_textures.in_set(RenderSystems::PrepareResources),
156 prepare_probe_textures
157 .in_set(RenderSystems::PrepareResources)
158 .after(prepare_atmosphere_textures),
159 prepare_atmosphere_probe_bind_groups.in_set(RenderSystems::PrepareBindGroups),
160 prepare_atmosphere_transforms.in_set(RenderSystems::PrepareResources),
161 prepare_atmosphere_bind_groups.in_set(RenderSystems::PrepareBindGroups),
162 ),
163 )
164 .add_render_graph_node::<ViewNodeRunner<AtmosphereLutsNode>>(
165 Core3d,
166 AtmosphereNode::RenderLuts,
167 )
168 .add_render_graph_edges(
169 Core3d,
170 (
171 Node3d::EndPrepasses,
173 AtmosphereNode::RenderLuts,
174 Node3d::StartMainPass,
175 ),
176 )
177 .add_render_graph_node::<ViewNodeRunner<RenderSkyNode>>(
178 Core3d,
179 AtmosphereNode::RenderSky,
180 )
181 .add_render_graph_node::<EnvironmentNode>(Core3d, AtmosphereNode::Environment)
182 .add_render_graph_edges(
183 Core3d,
184 (
185 Node3d::MainOpaquePass,
186 AtmosphereNode::RenderSky,
187 Node3d::MainTransparentPass,
188 ),
189 );
190 }
191}
192
193#[derive(Clone, Component, Reflect, ShaderType)]
214#[require(AtmosphereSettings, Hdr)]
215#[reflect(Clone, Default)]
216pub struct Atmosphere {
217 pub bottom_radius: f32,
221
222 pub top_radius: f32,
227
228 pub ground_albedo: Vec3,
233
234 pub rayleigh_density_exp_scale: f32,
241
242 pub rayleigh_scattering: Vec3,
247
248 pub mie_density_exp_scale: f32,
255
256 pub mie_scattering: f32,
261
262 pub mie_absorption: f32,
267
268 pub mie_asymmetry: f32, pub ozone_layer_altitude: f32,
279
280 pub ozone_layer_width: f32,
284
285 pub ozone_absorption: Vec3,
290}
291
292impl Atmosphere {
293 pub const EARTH: Atmosphere = Atmosphere {
294 bottom_radius: 6_360_000.0,
295 top_radius: 6_460_000.0,
296 ground_albedo: Vec3::splat(0.3),
297 rayleigh_density_exp_scale: 1.0 / 8_000.0,
298 rayleigh_scattering: Vec3::new(5.802e-6, 13.558e-6, 33.100e-6),
299 mie_density_exp_scale: 1.0 / 1_200.0,
300 mie_scattering: 3.996e-6,
301 mie_absorption: 0.444e-6,
302 mie_asymmetry: 0.8,
303 ozone_layer_altitude: 25_000.0,
304 ozone_layer_width: 30_000.0,
305 ozone_absorption: Vec3::new(0.650e-6, 1.881e-6, 0.085e-6),
306 };
307
308 pub fn with_density_multiplier(mut self, mult: f32) -> Self {
309 self.rayleigh_scattering *= mult;
310 self.mie_scattering *= mult;
311 self.mie_absorption *= mult;
312 self.ozone_absorption *= mult;
313 self
314 }
315}
316
317impl Default for Atmosphere {
318 fn default() -> Self {
319 Self::EARTH
320 }
321}
322
323impl ExtractComponent for Atmosphere {
324 type QueryData = Read<Atmosphere>;
325
326 type QueryFilter = With<Camera3d>;
327
328 type Out = Atmosphere;
329
330 fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
331 Some(item.clone())
332 }
333}
334
335#[derive(Clone, Component, Reflect)]
354#[reflect(Clone, Default)]
355pub struct AtmosphereSettings {
356 pub transmittance_lut_size: UVec2,
358
359 pub multiscattering_lut_size: UVec2,
361
362 pub sky_view_lut_size: UVec2,
364
365 pub aerial_view_lut_size: UVec3,
367
368 pub transmittance_lut_samples: u32,
371
372 pub multiscattering_lut_dirs: u32,
375
376 pub multiscattering_lut_samples: u32,
379
380 pub sky_view_lut_samples: u32,
383
384 pub aerial_view_lut_samples: u32,
387
388 pub aerial_view_lut_max_distance: f32,
395
396 pub scene_units_to_m: f32,
399
400 pub sky_max_samples: u32,
403
404 pub rendering_method: AtmosphereMode,
406}
407
408impl Default for AtmosphereSettings {
409 fn default() -> Self {
410 Self {
411 transmittance_lut_size: UVec2::new(256, 128),
412 transmittance_lut_samples: 40,
413 multiscattering_lut_size: UVec2::new(32, 32),
414 multiscattering_lut_dirs: 64,
415 multiscattering_lut_samples: 20,
416 sky_view_lut_size: UVec2::new(400, 200),
417 sky_view_lut_samples: 16,
418 aerial_view_lut_size: UVec3::new(32, 32, 32),
419 aerial_view_lut_samples: 10,
420 aerial_view_lut_max_distance: 3.2e4,
421 scene_units_to_m: 1.0,
422 sky_max_samples: 16,
423 rendering_method: AtmosphereMode::LookupTexture,
424 }
425 }
426}
427
428#[derive(Clone, Component, Reflect, ShaderType)]
429#[reflect(Default)]
430pub struct GpuAtmosphereSettings {
431 pub transmittance_lut_size: UVec2,
432 pub multiscattering_lut_size: UVec2,
433 pub sky_view_lut_size: UVec2,
434 pub aerial_view_lut_size: UVec3,
435 pub transmittance_lut_samples: u32,
436 pub multiscattering_lut_dirs: u32,
437 pub multiscattering_lut_samples: u32,
438 pub sky_view_lut_samples: u32,
439 pub aerial_view_lut_samples: u32,
440 pub aerial_view_lut_max_distance: f32,
441 pub scene_units_to_m: f32,
442 pub sky_max_samples: u32,
443 pub rendering_method: u32,
444}
445
446impl Default for GpuAtmosphereSettings {
447 fn default() -> Self {
448 AtmosphereSettings::default().into()
449 }
450}
451
452impl From<AtmosphereSettings> for GpuAtmosphereSettings {
453 fn from(s: AtmosphereSettings) -> Self {
454 Self {
455 transmittance_lut_size: s.transmittance_lut_size,
456 multiscattering_lut_size: s.multiscattering_lut_size,
457 sky_view_lut_size: s.sky_view_lut_size,
458 aerial_view_lut_size: s.aerial_view_lut_size,
459 transmittance_lut_samples: s.transmittance_lut_samples,
460 multiscattering_lut_dirs: s.multiscattering_lut_dirs,
461 multiscattering_lut_samples: s.multiscattering_lut_samples,
462 sky_view_lut_samples: s.sky_view_lut_samples,
463 aerial_view_lut_samples: s.aerial_view_lut_samples,
464 aerial_view_lut_max_distance: s.aerial_view_lut_max_distance,
465 scene_units_to_m: s.scene_units_to_m,
466 sky_max_samples: s.sky_max_samples,
467 rendering_method: s.rendering_method as u32,
468 }
469 }
470}
471
472impl ExtractComponent for GpuAtmosphereSettings {
473 type QueryData = Read<AtmosphereSettings>;
474
475 type QueryFilter = (With<Camera3d>, With<Atmosphere>);
476
477 type Out = GpuAtmosphereSettings;
478
479 fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
480 Some(item.clone().into())
481 }
482}
483
484fn configure_camera_depth_usages(
485 mut cameras: Query<&mut Camera3d, (Changed<Camera3d>, With<Atmosphere>)>,
486) {
487 for mut camera in &mut cameras {
488 camera.depth_texture_usages.0 |= TextureUsages::TEXTURE_BINDING.bits();
489 }
490}
491
492#[repr(u32)]
495#[derive(Clone, Default, Reflect, Copy)]
496pub enum AtmosphereMode {
497 #[default]
503 LookupTexture = 0,
504 Raymarched = 1,
510}