1mod environment;
37mod node;
38pub mod resources;
39
40use bevy_app::{App, Plugin, Update};
41use bevy_asset::{embedded_asset, AssetId, Handle};
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, prepare_atmosphere_uniforms, queue_render_sky_pipelines,
75 AtmosphereTransforms, GpuAtmosphere, RenderSkyBindGroupLayouts,
76};
77use tracing::warn;
78
79use crate::{
80 medium::ScatteringMedium,
81 resources::{init_atmosphere_buffer, write_atmosphere_buffer},
82};
83
84use self::{
85 node::{AtmosphereLutsNode, AtmosphereNode, RenderSkyNode},
86 resources::{
87 prepare_atmosphere_bind_groups, prepare_atmosphere_textures, AtmosphereBindGroupLayouts,
88 AtmosphereLutPipelines, AtmosphereSampler,
89 },
90};
91
92#[doc(hidden)]
93pub struct AtmospherePlugin;
94
95impl Plugin for AtmospherePlugin {
96 fn build(&self, app: &mut App) {
97 load_shader_library!(app, "types.wgsl");
98 load_shader_library!(app, "functions.wgsl");
99 load_shader_library!(app, "bruneton_functions.wgsl");
100 load_shader_library!(app, "bindings.wgsl");
101
102 embedded_asset!(app, "transmittance_lut.wgsl");
103 embedded_asset!(app, "multiscattering_lut.wgsl");
104 embedded_asset!(app, "sky_view_lut.wgsl");
105 embedded_asset!(app, "aerial_view_lut.wgsl");
106 embedded_asset!(app, "render_sky.wgsl");
107 embedded_asset!(app, "environment.wgsl");
108
109 app.add_plugins((
110 ExtractComponentPlugin::<Atmosphere>::default(),
111 ExtractComponentPlugin::<GpuAtmosphereSettings>::default(),
112 ExtractComponentPlugin::<AtmosphereEnvironmentMap>::default(),
113 UniformComponentPlugin::<GpuAtmosphere>::default(),
114 UniformComponentPlugin::<GpuAtmosphereSettings>::default(),
115 ))
116 .add_systems(Update, prepare_atmosphere_probe_components);
117 }
118
119 fn finish(&self, app: &mut App) {
120 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
121 return;
122 };
123
124 let render_adapter = render_app.world().resource::<RenderAdapter>();
125
126 if !render_adapter
127 .get_downlevel_capabilities()
128 .flags
129 .contains(DownlevelFlags::COMPUTE_SHADERS)
130 {
131 warn!("AtmospherePlugin not loaded. GPU lacks support for compute shaders.");
132 return;
133 }
134
135 if !render_adapter
136 .get_texture_format_features(TextureFormat::Rgba16Float)
137 .allowed_usages
138 .contains(TextureUsages::STORAGE_BINDING)
139 {
140 warn!("AtmospherePlugin not loaded. GPU lacks support: TextureFormat::Rgba16Float does not support TextureUsages::STORAGE_BINDING.");
141 return;
142 }
143
144 render_app
145 .insert_resource(AtmosphereBindGroupLayouts::new())
146 .init_resource::<RenderSkyBindGroupLayouts>()
147 .init_resource::<AtmosphereSampler>()
148 .init_resource::<AtmosphereLutPipelines>()
149 .init_resource::<AtmosphereTransforms>()
150 .init_resource::<SpecializedRenderPipelines<RenderSkyBindGroupLayouts>>()
151 .add_systems(
152 RenderStartup,
153 (
154 init_atmosphere_probe_layout,
155 init_atmosphere_probe_pipeline,
156 init_atmosphere_buffer,
157 )
158 .chain(),
159 )
160 .add_systems(
161 Render,
162 (
163 configure_camera_depth_usages.in_set(RenderSystems::ManageViews),
164 queue_render_sky_pipelines.in_set(RenderSystems::Queue),
165 prepare_atmosphere_textures.in_set(RenderSystems::PrepareResources),
166 prepare_probe_textures
167 .in_set(RenderSystems::PrepareResources)
168 .after(prepare_atmosphere_textures),
169 prepare_atmosphere_uniforms
170 .before(RenderSystems::PrepareResources)
171 .after(RenderSystems::PrepareAssets),
172 prepare_atmosphere_probe_bind_groups.in_set(RenderSystems::PrepareBindGroups),
173 prepare_atmosphere_transforms.in_set(RenderSystems::PrepareResources),
174 prepare_atmosphere_bind_groups.in_set(RenderSystems::PrepareBindGroups),
175 write_atmosphere_buffer.in_set(RenderSystems::PrepareResources),
176 ),
177 )
178 .add_render_graph_node::<ViewNodeRunner<AtmosphereLutsNode>>(
179 Core3d,
180 AtmosphereNode::RenderLuts,
181 )
182 .add_render_graph_edges(
183 Core3d,
184 (
185 Node3d::EndPrepasses,
187 AtmosphereNode::RenderLuts,
188 Node3d::StartMainPass,
189 ),
190 )
191 .add_render_graph_node::<ViewNodeRunner<RenderSkyNode>>(
192 Core3d,
193 AtmosphereNode::RenderSky,
194 )
195 .add_render_graph_node::<EnvironmentNode>(Core3d, AtmosphereNode::Environment)
196 .add_render_graph_edges(
197 Core3d,
198 (
199 Node3d::MainOpaquePass,
200 AtmosphereNode::RenderSky,
201 Node3d::MainTransparentPass,
202 ),
203 );
204 }
205}
206
207#[derive(Clone, Component)]
209#[require(AtmosphereSettings, Hdr)]
210pub struct Atmosphere {
211 pub bottom_radius: f32,
215
216 pub top_radius: f32,
221
222 pub ground_albedo: Vec3,
227
228 pub medium: Handle<ScatteringMedium>,
231}
232
233impl Atmosphere {
234 pub fn earthlike(medium: Handle<ScatteringMedium>) -> Self {
235 const EARTH_BOTTOM_RADIUS: f32 = 6_360_000.0;
236 const EARTH_TOP_RADIUS: f32 = 6_460_000.0;
237 const EARTH_ALBEDO: Vec3 = Vec3::splat(0.3);
238 Self {
239 bottom_radius: EARTH_BOTTOM_RADIUS,
240 top_radius: EARTH_TOP_RADIUS,
241 ground_albedo: EARTH_ALBEDO,
242 medium,
243 }
244 }
245}
246
247impl ExtractComponent for Atmosphere {
248 type QueryData = Read<Atmosphere>;
249
250 type QueryFilter = With<Camera3d>;
251
252 type Out = ExtractedAtmosphere;
253
254 fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
255 Some(ExtractedAtmosphere {
256 bottom_radius: item.bottom_radius,
257 top_radius: item.top_radius,
258 ground_albedo: item.ground_albedo,
259 medium: item.medium.id(),
260 })
261 }
262}
263
264#[derive(Clone, Component)]
267pub struct ExtractedAtmosphere {
268 pub bottom_radius: f32,
269 pub top_radius: f32,
270 pub ground_albedo: Vec3,
271 pub medium: AssetId<ScatteringMedium>,
272}
273
274#[derive(Clone, Component, Reflect)]
293#[reflect(Clone, Default)]
294pub struct AtmosphereSettings {
295 pub transmittance_lut_size: UVec2,
297
298 pub multiscattering_lut_size: UVec2,
300
301 pub sky_view_lut_size: UVec2,
303
304 pub aerial_view_lut_size: UVec3,
306
307 pub transmittance_lut_samples: u32,
310
311 pub multiscattering_lut_dirs: u32,
314
315 pub multiscattering_lut_samples: u32,
318
319 pub sky_view_lut_samples: u32,
322
323 pub aerial_view_lut_samples: u32,
326
327 pub aerial_view_lut_max_distance: f32,
334
335 pub scene_units_to_m: f32,
338
339 pub sky_max_samples: u32,
342
343 pub rendering_method: AtmosphereMode,
345}
346
347impl Default for AtmosphereSettings {
348 fn default() -> Self {
349 Self {
350 transmittance_lut_size: UVec2::new(256, 128),
351 transmittance_lut_samples: 40,
352 multiscattering_lut_size: UVec2::new(32, 32),
353 multiscattering_lut_dirs: 64,
354 multiscattering_lut_samples: 20,
355 sky_view_lut_size: UVec2::new(400, 200),
356 sky_view_lut_samples: 16,
357 aerial_view_lut_size: UVec3::new(32, 32, 32),
358 aerial_view_lut_samples: 10,
359 aerial_view_lut_max_distance: 3.2e4,
360 scene_units_to_m: 1.0,
361 sky_max_samples: 16,
362 rendering_method: AtmosphereMode::LookupTexture,
363 }
364 }
365}
366
367#[derive(Clone, Component, Reflect, ShaderType)]
368#[reflect(Default)]
369pub struct GpuAtmosphereSettings {
370 pub transmittance_lut_size: UVec2,
371 pub multiscattering_lut_size: UVec2,
372 pub sky_view_lut_size: UVec2,
373 pub aerial_view_lut_size: UVec3,
374 pub transmittance_lut_samples: u32,
375 pub multiscattering_lut_dirs: u32,
376 pub multiscattering_lut_samples: u32,
377 pub sky_view_lut_samples: u32,
378 pub aerial_view_lut_samples: u32,
379 pub aerial_view_lut_max_distance: f32,
380 pub scene_units_to_m: f32,
381 pub sky_max_samples: u32,
382 pub rendering_method: u32,
383}
384
385impl Default for GpuAtmosphereSettings {
386 fn default() -> Self {
387 AtmosphereSettings::default().into()
388 }
389}
390
391impl From<AtmosphereSettings> for GpuAtmosphereSettings {
392 fn from(s: AtmosphereSettings) -> Self {
393 Self {
394 transmittance_lut_size: s.transmittance_lut_size,
395 multiscattering_lut_size: s.multiscattering_lut_size,
396 sky_view_lut_size: s.sky_view_lut_size,
397 aerial_view_lut_size: s.aerial_view_lut_size,
398 transmittance_lut_samples: s.transmittance_lut_samples,
399 multiscattering_lut_dirs: s.multiscattering_lut_dirs,
400 multiscattering_lut_samples: s.multiscattering_lut_samples,
401 sky_view_lut_samples: s.sky_view_lut_samples,
402 aerial_view_lut_samples: s.aerial_view_lut_samples,
403 aerial_view_lut_max_distance: s.aerial_view_lut_max_distance,
404 scene_units_to_m: s.scene_units_to_m,
405 sky_max_samples: s.sky_max_samples,
406 rendering_method: s.rendering_method as u32,
407 }
408 }
409}
410
411impl ExtractComponent for GpuAtmosphereSettings {
412 type QueryData = Read<AtmosphereSettings>;
413
414 type QueryFilter = (With<Camera3d>, With<Atmosphere>);
415
416 type Out = GpuAtmosphereSettings;
417
418 fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
419 Some(item.clone().into())
420 }
421}
422
423fn configure_camera_depth_usages(
424 mut cameras: Query<&mut Camera3d, (Changed<Camera3d>, With<ExtractedAtmosphere>)>,
425) {
426 for mut camera in &mut cameras {
427 camera.depth_texture_usages.0 |= TextureUsages::TEXTURE_BINDING.bits();
428 }
429}
430
431#[repr(u32)]
434#[derive(Clone, Default, Reflect, Copy)]
435pub enum AtmosphereMode {
436 #[default]
442 LookupTexture = 0,
443 Raymarched = 1,
449}