
# Objective Add web support to atmosphere by gating dual source blending and using a macro to determine the target platform. The main objective of this PR is to ensure that users of Bevy's atmosphere feature can also run it in a web-based context where WebGPU support is enabled. ## Solution - Make use of the `#[cfg(not(target_arch = "wasm32"))]` macro to gate the dual source blending, as this is not (yet) supported in web browsers. - Rename the function `sample_sun_illuminance` to `sample_sun_radiance` and move calls out of conditionals to ensure the shader compiles and runs in both native and web-based contexts. - Moved the multiplication of the transmittance out when calculating the sun color, because calling the `sample_sun_illuminance` function was causing issues in web. Overall this results in cleaner code and more readable. ## Testing - Tested by building a wasm target and loading it in a web page with Vite dev server using `mate-h/bevy-webgpu` repo template. - Tested the native build with `cargo run --example atmosphere` to ensure it still works with dual source blending. --- ## Showcase Screenshots show the atmosphere example running in two different contexts: <img width="1281" alt="atmosphere-web-showcase" src="https://github.com/user-attachments/assets/40b1ee91-89ae-41a6-8189-89630d1ca1a6" /> --------- Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
733 lines
26 KiB
Rust
733 lines
26 KiB
Rust
use bevy_core_pipeline::{
|
|
core_3d::Camera3d, fullscreen_vertex_shader::fullscreen_shader_vertex_state,
|
|
};
|
|
use bevy_ecs::{
|
|
component::Component,
|
|
entity::Entity,
|
|
query::With,
|
|
resource::Resource,
|
|
system::{Commands, Query, Res, ResMut},
|
|
world::{FromWorld, World},
|
|
};
|
|
use bevy_math::{Mat4, Vec3};
|
|
use bevy_render::{
|
|
camera::Camera,
|
|
extract_component::ComponentUniforms,
|
|
render_resource::{binding_types::*, *},
|
|
renderer::{RenderDevice, RenderQueue},
|
|
texture::{CachedTexture, TextureCache},
|
|
view::{ExtractedView, Msaa, ViewDepthTexture, ViewUniform, ViewUniforms},
|
|
};
|
|
|
|
use crate::{GpuLights, LightMeta};
|
|
|
|
use super::{shaders, Atmosphere, AtmosphereSettings};
|
|
|
|
#[derive(Resource)]
|
|
pub(crate) struct AtmosphereBindGroupLayouts {
|
|
pub transmittance_lut: BindGroupLayout,
|
|
pub multiscattering_lut: BindGroupLayout,
|
|
pub sky_view_lut: BindGroupLayout,
|
|
pub aerial_view_lut: BindGroupLayout,
|
|
}
|
|
|
|
#[derive(Resource)]
|
|
pub(crate) struct RenderSkyBindGroupLayouts {
|
|
pub render_sky: BindGroupLayout,
|
|
pub render_sky_msaa: BindGroupLayout,
|
|
}
|
|
|
|
impl FromWorld for AtmosphereBindGroupLayouts {
|
|
fn from_world(world: &mut World) -> Self {
|
|
let render_device = world.resource::<RenderDevice>();
|
|
let transmittance_lut = render_device.create_bind_group_layout(
|
|
"transmittance_lut_bind_group_layout",
|
|
&BindGroupLayoutEntries::with_indices(
|
|
ShaderStages::COMPUTE,
|
|
(
|
|
(0, uniform_buffer::<Atmosphere>(true)),
|
|
(1, uniform_buffer::<AtmosphereSettings>(true)),
|
|
(
|
|
// transmittance lut storage texture
|
|
13,
|
|
texture_storage_2d(
|
|
TextureFormat::Rgba16Float,
|
|
StorageTextureAccess::WriteOnly,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
let multiscattering_lut = render_device.create_bind_group_layout(
|
|
"multiscattering_lut_bind_group_layout",
|
|
&BindGroupLayoutEntries::with_indices(
|
|
ShaderStages::COMPUTE,
|
|
(
|
|
(0, uniform_buffer::<Atmosphere>(true)),
|
|
(1, uniform_buffer::<AtmosphereSettings>(true)),
|
|
(5, texture_2d(TextureSampleType::Float { filterable: true })), //transmittance lut and sampler
|
|
(6, sampler(SamplerBindingType::Filtering)),
|
|
(
|
|
//multiscattering lut storage texture
|
|
13,
|
|
texture_storage_2d(
|
|
TextureFormat::Rgba16Float,
|
|
StorageTextureAccess::WriteOnly,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
let sky_view_lut = render_device.create_bind_group_layout(
|
|
"sky_view_lut_bind_group_layout",
|
|
&BindGroupLayoutEntries::with_indices(
|
|
ShaderStages::COMPUTE,
|
|
(
|
|
(0, uniform_buffer::<Atmosphere>(true)),
|
|
(1, uniform_buffer::<AtmosphereSettings>(true)),
|
|
(2, uniform_buffer::<AtmosphereTransform>(true)),
|
|
(3, uniform_buffer::<ViewUniform>(true)),
|
|
(4, uniform_buffer::<GpuLights>(true)),
|
|
(5, texture_2d(TextureSampleType::Float { filterable: true })), //transmittance lut and sampler
|
|
(6, sampler(SamplerBindingType::Filtering)),
|
|
(7, texture_2d(TextureSampleType::Float { filterable: true })), //multiscattering lut and sampler
|
|
(8, sampler(SamplerBindingType::Filtering)),
|
|
(
|
|
13,
|
|
texture_storage_2d(
|
|
TextureFormat::Rgba16Float,
|
|
StorageTextureAccess::WriteOnly,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
let aerial_view_lut = render_device.create_bind_group_layout(
|
|
"aerial_view_lut_bind_group_layout",
|
|
&BindGroupLayoutEntries::with_indices(
|
|
ShaderStages::COMPUTE,
|
|
(
|
|
(0, uniform_buffer::<Atmosphere>(true)),
|
|
(1, uniform_buffer::<AtmosphereSettings>(true)),
|
|
(3, uniform_buffer::<ViewUniform>(true)),
|
|
(4, uniform_buffer::<GpuLights>(true)),
|
|
(5, texture_2d(TextureSampleType::Float { filterable: true })), //transmittance lut and sampler
|
|
(6, sampler(SamplerBindingType::Filtering)),
|
|
(7, texture_2d(TextureSampleType::Float { filterable: true })), //multiscattering lut and sampler
|
|
(8, sampler(SamplerBindingType::Filtering)),
|
|
(
|
|
//Aerial view lut storage texture
|
|
13,
|
|
texture_storage_3d(
|
|
TextureFormat::Rgba16Float,
|
|
StorageTextureAccess::WriteOnly,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
Self {
|
|
transmittance_lut,
|
|
multiscattering_lut,
|
|
sky_view_lut,
|
|
aerial_view_lut,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromWorld for RenderSkyBindGroupLayouts {
|
|
fn from_world(world: &mut World) -> Self {
|
|
let render_device = world.resource::<RenderDevice>();
|
|
let render_sky = render_device.create_bind_group_layout(
|
|
"render_sky_bind_group_layout",
|
|
&BindGroupLayoutEntries::with_indices(
|
|
ShaderStages::FRAGMENT,
|
|
(
|
|
(0, uniform_buffer::<Atmosphere>(true)),
|
|
(1, uniform_buffer::<AtmosphereSettings>(true)),
|
|
(2, uniform_buffer::<AtmosphereTransform>(true)),
|
|
(3, uniform_buffer::<ViewUniform>(true)),
|
|
(4, uniform_buffer::<GpuLights>(true)),
|
|
(5, texture_2d(TextureSampleType::Float { filterable: true })), //transmittance lut and sampler
|
|
(6, sampler(SamplerBindingType::Filtering)),
|
|
(9, texture_2d(TextureSampleType::Float { filterable: true })), //sky view lut and sampler
|
|
(10, sampler(SamplerBindingType::Filtering)),
|
|
(
|
|
// aerial view lut and sampler
|
|
11,
|
|
texture_3d(TextureSampleType::Float { filterable: true }),
|
|
),
|
|
(12, sampler(SamplerBindingType::Filtering)),
|
|
(
|
|
//view depth texture
|
|
13,
|
|
texture_2d(TextureSampleType::Depth),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
let render_sky_msaa = render_device.create_bind_group_layout(
|
|
"render_sky_msaa_bind_group_layout",
|
|
&BindGroupLayoutEntries::with_indices(
|
|
ShaderStages::FRAGMENT,
|
|
(
|
|
(0, uniform_buffer::<Atmosphere>(true)),
|
|
(1, uniform_buffer::<AtmosphereSettings>(true)),
|
|
(2, uniform_buffer::<AtmosphereTransform>(true)),
|
|
(3, uniform_buffer::<ViewUniform>(true)),
|
|
(4, uniform_buffer::<GpuLights>(true)),
|
|
(5, texture_2d(TextureSampleType::Float { filterable: true })), //transmittance lut and sampler
|
|
(6, sampler(SamplerBindingType::Filtering)),
|
|
(9, texture_2d(TextureSampleType::Float { filterable: true })), //sky view lut and sampler
|
|
(10, sampler(SamplerBindingType::Filtering)),
|
|
(
|
|
// aerial view lut and sampler
|
|
11,
|
|
texture_3d(TextureSampleType::Float { filterable: true }),
|
|
),
|
|
(12, sampler(SamplerBindingType::Filtering)),
|
|
(
|
|
//view depth texture
|
|
13,
|
|
texture_2d_multisampled(TextureSampleType::Depth),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
Self {
|
|
render_sky,
|
|
render_sky_msaa,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Resource)]
|
|
pub struct AtmosphereSamplers {
|
|
pub transmittance_lut: Sampler,
|
|
pub multiscattering_lut: Sampler,
|
|
pub sky_view_lut: Sampler,
|
|
pub aerial_view_lut: Sampler,
|
|
}
|
|
|
|
impl FromWorld for AtmosphereSamplers {
|
|
fn from_world(world: &mut World) -> Self {
|
|
let render_device = world.resource::<RenderDevice>();
|
|
|
|
let base_sampler = SamplerDescriptor {
|
|
mag_filter: FilterMode::Linear,
|
|
min_filter: FilterMode::Linear,
|
|
mipmap_filter: FilterMode::Nearest,
|
|
..Default::default()
|
|
};
|
|
|
|
let transmittance_lut = render_device.create_sampler(&SamplerDescriptor {
|
|
label: Some("transmittance_lut_sampler"),
|
|
..base_sampler
|
|
});
|
|
|
|
let multiscattering_lut = render_device.create_sampler(&SamplerDescriptor {
|
|
label: Some("multiscattering_lut_sampler"),
|
|
..base_sampler
|
|
});
|
|
|
|
let sky_view_lut = render_device.create_sampler(&SamplerDescriptor {
|
|
label: Some("sky_view_lut_sampler"),
|
|
address_mode_u: AddressMode::Repeat,
|
|
..base_sampler
|
|
});
|
|
|
|
let aerial_view_lut = render_device.create_sampler(&SamplerDescriptor {
|
|
label: Some("aerial_view_lut_sampler"),
|
|
..base_sampler
|
|
});
|
|
|
|
Self {
|
|
transmittance_lut,
|
|
multiscattering_lut,
|
|
sky_view_lut,
|
|
aerial_view_lut,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Resource)]
|
|
pub(crate) struct AtmosphereLutPipelines {
|
|
pub transmittance_lut: CachedComputePipelineId,
|
|
pub multiscattering_lut: CachedComputePipelineId,
|
|
pub sky_view_lut: CachedComputePipelineId,
|
|
pub aerial_view_lut: CachedComputePipelineId,
|
|
}
|
|
|
|
impl FromWorld for AtmosphereLutPipelines {
|
|
fn from_world(world: &mut World) -> Self {
|
|
let pipeline_cache = world.resource::<PipelineCache>();
|
|
let layouts = world.resource::<AtmosphereBindGroupLayouts>();
|
|
|
|
let transmittance_lut = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
|
|
label: Some("transmittance_lut_pipeline".into()),
|
|
layout: vec![layouts.transmittance_lut.clone()],
|
|
push_constant_ranges: vec![],
|
|
shader: shaders::TRANSMITTANCE_LUT,
|
|
shader_defs: vec![],
|
|
entry_point: "main".into(),
|
|
zero_initialize_workgroup_memory: false,
|
|
});
|
|
|
|
let multiscattering_lut =
|
|
pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
|
|
label: Some("multi_scattering_lut_pipeline".into()),
|
|
layout: vec![layouts.multiscattering_lut.clone()],
|
|
push_constant_ranges: vec![],
|
|
shader: shaders::MULTISCATTERING_LUT,
|
|
shader_defs: vec![],
|
|
entry_point: "main".into(),
|
|
zero_initialize_workgroup_memory: false,
|
|
});
|
|
|
|
let sky_view_lut = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
|
|
label: Some("sky_view_lut_pipeline".into()),
|
|
layout: vec![layouts.sky_view_lut.clone()],
|
|
push_constant_ranges: vec![],
|
|
shader: shaders::SKY_VIEW_LUT,
|
|
shader_defs: vec![],
|
|
entry_point: "main".into(),
|
|
zero_initialize_workgroup_memory: false,
|
|
});
|
|
|
|
let aerial_view_lut = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
|
|
label: Some("aerial_view_lut_pipeline".into()),
|
|
layout: vec![layouts.aerial_view_lut.clone()],
|
|
push_constant_ranges: vec![],
|
|
shader: shaders::AERIAL_VIEW_LUT,
|
|
shader_defs: vec![],
|
|
entry_point: "main".into(),
|
|
zero_initialize_workgroup_memory: false,
|
|
});
|
|
|
|
Self {
|
|
transmittance_lut,
|
|
multiscattering_lut,
|
|
sky_view_lut,
|
|
aerial_view_lut,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Component)]
|
|
pub(crate) struct RenderSkyPipelineId(pub CachedRenderPipelineId);
|
|
|
|
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
|
pub(crate) struct RenderSkyPipelineKey {
|
|
pub msaa_samples: u32,
|
|
pub hdr: bool,
|
|
pub dual_source_blending: bool,
|
|
}
|
|
|
|
impl SpecializedRenderPipeline for RenderSkyBindGroupLayouts {
|
|
type Key = RenderSkyPipelineKey;
|
|
|
|
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
|
let mut shader_defs = Vec::new();
|
|
|
|
if key.msaa_samples > 1 {
|
|
shader_defs.push("MULTISAMPLED".into());
|
|
}
|
|
if key.hdr {
|
|
shader_defs.push("TONEMAP_IN_SHADER".into());
|
|
}
|
|
if key.dual_source_blending {
|
|
shader_defs.push("DUAL_SOURCE_BLENDING".into());
|
|
}
|
|
|
|
let dst_factor = if key.dual_source_blending {
|
|
BlendFactor::Src1
|
|
} else {
|
|
BlendFactor::SrcAlpha
|
|
};
|
|
|
|
RenderPipelineDescriptor {
|
|
label: Some(format!("render_sky_pipeline_{}", key.msaa_samples).into()),
|
|
layout: vec![if key.msaa_samples == 1 {
|
|
self.render_sky.clone()
|
|
} else {
|
|
self.render_sky_msaa.clone()
|
|
}],
|
|
push_constant_ranges: vec![],
|
|
vertex: fullscreen_shader_vertex_state(),
|
|
primitive: PrimitiveState::default(),
|
|
depth_stencil: None,
|
|
multisample: MultisampleState {
|
|
count: key.msaa_samples,
|
|
mask: !0,
|
|
alpha_to_coverage_enabled: false,
|
|
},
|
|
zero_initialize_workgroup_memory: false,
|
|
fragment: Some(FragmentState {
|
|
shader: shaders::RENDER_SKY.clone(),
|
|
shader_defs,
|
|
entry_point: "main".into(),
|
|
targets: vec![Some(ColorTargetState {
|
|
format: TextureFormat::Rgba16Float,
|
|
blend: Some(BlendState {
|
|
color: BlendComponent {
|
|
src_factor: BlendFactor::One,
|
|
dst_factor,
|
|
operation: BlendOperation::Add,
|
|
},
|
|
alpha: BlendComponent {
|
|
src_factor: BlendFactor::Zero,
|
|
dst_factor: BlendFactor::One,
|
|
operation: BlendOperation::Add,
|
|
},
|
|
}),
|
|
write_mask: ColorWrites::ALL,
|
|
})],
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(super) fn queue_render_sky_pipelines(
|
|
views: Query<(Entity, &Camera, &Msaa), With<Atmosphere>>,
|
|
pipeline_cache: Res<PipelineCache>,
|
|
layouts: Res<RenderSkyBindGroupLayouts>,
|
|
mut specializer: ResMut<SpecializedRenderPipelines<RenderSkyBindGroupLayouts>>,
|
|
render_device: Res<RenderDevice>,
|
|
mut commands: Commands,
|
|
) {
|
|
for (entity, camera, msaa) in &views {
|
|
let id = specializer.specialize(
|
|
&pipeline_cache,
|
|
&layouts,
|
|
RenderSkyPipelineKey {
|
|
msaa_samples: msaa.samples(),
|
|
hdr: camera.hdr,
|
|
dual_source_blending: render_device
|
|
.features()
|
|
.contains(WgpuFeatures::DUAL_SOURCE_BLENDING),
|
|
},
|
|
);
|
|
commands.entity(entity).insert(RenderSkyPipelineId(id));
|
|
}
|
|
}
|
|
|
|
#[derive(Component)]
|
|
pub struct AtmosphereTextures {
|
|
pub transmittance_lut: CachedTexture,
|
|
pub multiscattering_lut: CachedTexture,
|
|
pub sky_view_lut: CachedTexture,
|
|
pub aerial_view_lut: CachedTexture,
|
|
}
|
|
|
|
pub(super) fn prepare_atmosphere_textures(
|
|
views: Query<(Entity, &AtmosphereSettings), With<Atmosphere>>,
|
|
render_device: Res<RenderDevice>,
|
|
mut texture_cache: ResMut<TextureCache>,
|
|
mut commands: Commands,
|
|
) {
|
|
for (entity, lut_settings) in &views {
|
|
let transmittance_lut = texture_cache.get(
|
|
&render_device,
|
|
TextureDescriptor {
|
|
label: Some("transmittance_lut"),
|
|
size: Extent3d {
|
|
width: lut_settings.transmittance_lut_size.x,
|
|
height: lut_settings.transmittance_lut_size.y,
|
|
depth_or_array_layers: 1,
|
|
},
|
|
mip_level_count: 1,
|
|
sample_count: 1,
|
|
dimension: TextureDimension::D2,
|
|
format: TextureFormat::Rgba16Float,
|
|
usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
|
|
view_formats: &[],
|
|
},
|
|
);
|
|
|
|
let multiscattering_lut = texture_cache.get(
|
|
&render_device,
|
|
TextureDescriptor {
|
|
label: Some("multiscattering_lut"),
|
|
size: Extent3d {
|
|
width: lut_settings.multiscattering_lut_size.x,
|
|
height: lut_settings.multiscattering_lut_size.y,
|
|
depth_or_array_layers: 1,
|
|
},
|
|
mip_level_count: 1,
|
|
sample_count: 1,
|
|
dimension: TextureDimension::D2,
|
|
format: TextureFormat::Rgba16Float,
|
|
usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
|
|
view_formats: &[],
|
|
},
|
|
);
|
|
|
|
let sky_view_lut = texture_cache.get(
|
|
&render_device,
|
|
TextureDescriptor {
|
|
label: Some("sky_view_lut"),
|
|
size: Extent3d {
|
|
width: lut_settings.sky_view_lut_size.x,
|
|
height: lut_settings.sky_view_lut_size.y,
|
|
depth_or_array_layers: 1,
|
|
},
|
|
mip_level_count: 1,
|
|
sample_count: 1,
|
|
dimension: TextureDimension::D2,
|
|
format: TextureFormat::Rgba16Float,
|
|
usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
|
|
view_formats: &[],
|
|
},
|
|
);
|
|
|
|
let aerial_view_lut = texture_cache.get(
|
|
&render_device,
|
|
TextureDescriptor {
|
|
label: Some("aerial_view_lut"),
|
|
size: Extent3d {
|
|
width: lut_settings.aerial_view_lut_size.x,
|
|
height: lut_settings.aerial_view_lut_size.y,
|
|
depth_or_array_layers: lut_settings.aerial_view_lut_size.z,
|
|
},
|
|
mip_level_count: 1,
|
|
sample_count: 1,
|
|
dimension: TextureDimension::D3,
|
|
format: TextureFormat::Rgba16Float,
|
|
usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
|
|
view_formats: &[],
|
|
},
|
|
);
|
|
|
|
commands.entity(entity).insert({
|
|
AtmosphereTextures {
|
|
transmittance_lut,
|
|
multiscattering_lut,
|
|
sky_view_lut,
|
|
aerial_view_lut,
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
#[derive(Resource, Default)]
|
|
pub struct AtmosphereTransforms {
|
|
uniforms: DynamicUniformBuffer<AtmosphereTransform>,
|
|
}
|
|
|
|
impl AtmosphereTransforms {
|
|
#[inline]
|
|
pub fn uniforms(&self) -> &DynamicUniformBuffer<AtmosphereTransform> {
|
|
&self.uniforms
|
|
}
|
|
}
|
|
|
|
#[derive(ShaderType)]
|
|
pub struct AtmosphereTransform {
|
|
world_from_atmosphere: Mat4,
|
|
atmosphere_from_world: Mat4,
|
|
}
|
|
|
|
#[derive(Component)]
|
|
pub struct AtmosphereTransformsOffset {
|
|
index: u32,
|
|
}
|
|
|
|
impl AtmosphereTransformsOffset {
|
|
#[inline]
|
|
pub fn index(&self) -> u32 {
|
|
self.index
|
|
}
|
|
}
|
|
|
|
pub(super) fn prepare_atmosphere_transforms(
|
|
views: Query<(Entity, &ExtractedView), (With<Atmosphere>, With<Camera3d>)>,
|
|
render_device: Res<RenderDevice>,
|
|
render_queue: Res<RenderQueue>,
|
|
mut atmo_uniforms: ResMut<AtmosphereTransforms>,
|
|
mut commands: Commands,
|
|
) {
|
|
let atmo_count = views.iter().len();
|
|
let Some(mut writer) =
|
|
atmo_uniforms
|
|
.uniforms
|
|
.get_writer(atmo_count, &render_device, &render_queue)
|
|
else {
|
|
return;
|
|
};
|
|
|
|
for (entity, view) in &views {
|
|
let world_from_view = view.world_from_view.compute_matrix();
|
|
let camera_z = world_from_view.z_axis.truncate();
|
|
let camera_y = world_from_view.y_axis.truncate();
|
|
let atmo_z = camera_z
|
|
.with_y(0.0)
|
|
.try_normalize()
|
|
.unwrap_or_else(|| camera_y.with_y(0.0).normalize());
|
|
let atmo_y = Vec3::Y;
|
|
let atmo_x = atmo_y.cross(atmo_z).normalize();
|
|
let world_from_atmosphere = Mat4::from_cols(
|
|
atmo_x.extend(0.0),
|
|
atmo_y.extend(0.0),
|
|
atmo_z.extend(0.0),
|
|
world_from_view.w_axis,
|
|
);
|
|
|
|
let atmosphere_from_world = world_from_atmosphere.inverse();
|
|
|
|
commands.entity(entity).insert(AtmosphereTransformsOffset {
|
|
index: writer.write(&AtmosphereTransform {
|
|
world_from_atmosphere,
|
|
atmosphere_from_world,
|
|
}),
|
|
});
|
|
}
|
|
}
|
|
|
|
#[derive(Component)]
|
|
pub(crate) struct AtmosphereBindGroups {
|
|
pub transmittance_lut: BindGroup,
|
|
pub multiscattering_lut: BindGroup,
|
|
pub sky_view_lut: BindGroup,
|
|
pub aerial_view_lut: BindGroup,
|
|
pub render_sky: BindGroup,
|
|
}
|
|
|
|
pub(super) fn prepare_atmosphere_bind_groups(
|
|
views: Query<
|
|
(Entity, &AtmosphereTextures, &ViewDepthTexture, &Msaa),
|
|
(With<Camera3d>, With<Atmosphere>),
|
|
>,
|
|
render_device: Res<RenderDevice>,
|
|
layouts: Res<AtmosphereBindGroupLayouts>,
|
|
render_sky_layouts: Res<RenderSkyBindGroupLayouts>,
|
|
samplers: Res<AtmosphereSamplers>,
|
|
view_uniforms: Res<ViewUniforms>,
|
|
lights_uniforms: Res<LightMeta>,
|
|
atmosphere_transforms: Res<AtmosphereTransforms>,
|
|
atmosphere_uniforms: Res<ComponentUniforms<Atmosphere>>,
|
|
settings_uniforms: Res<ComponentUniforms<AtmosphereSettings>>,
|
|
|
|
mut commands: Commands,
|
|
) {
|
|
if views.iter().len() == 0 {
|
|
return;
|
|
}
|
|
|
|
let atmosphere_binding = atmosphere_uniforms
|
|
.binding()
|
|
.expect("Failed to prepare atmosphere bind groups. Atmosphere uniform buffer missing");
|
|
|
|
let transforms_binding = atmosphere_transforms
|
|
.uniforms()
|
|
.binding()
|
|
.expect("Failed to prepare atmosphere bind groups. Atmosphere transforms buffer missing");
|
|
|
|
let settings_binding = settings_uniforms.binding().expect(
|
|
"Failed to prepare atmosphere bind groups. AtmosphereSettings uniform buffer missing",
|
|
);
|
|
|
|
let view_binding = view_uniforms
|
|
.uniforms
|
|
.binding()
|
|
.expect("Failed to prepare atmosphere bind groups. View uniform buffer missing");
|
|
|
|
let lights_binding = lights_uniforms
|
|
.view_gpu_lights
|
|
.binding()
|
|
.expect("Failed to prepare atmosphere bind groups. Lights uniform buffer missing");
|
|
|
|
for (entity, textures, view_depth_texture, msaa) in &views {
|
|
let transmittance_lut = render_device.create_bind_group(
|
|
"transmittance_lut_bind_group",
|
|
&layouts.transmittance_lut,
|
|
&BindGroupEntries::with_indices((
|
|
(0, atmosphere_binding.clone()),
|
|
(1, settings_binding.clone()),
|
|
(13, &textures.transmittance_lut.default_view),
|
|
)),
|
|
);
|
|
|
|
let multiscattering_lut = render_device.create_bind_group(
|
|
"multiscattering_lut_bind_group",
|
|
&layouts.multiscattering_lut,
|
|
&BindGroupEntries::with_indices((
|
|
(0, atmosphere_binding.clone()),
|
|
(1, settings_binding.clone()),
|
|
(5, &textures.transmittance_lut.default_view),
|
|
(6, &samplers.transmittance_lut),
|
|
(13, &textures.multiscattering_lut.default_view),
|
|
)),
|
|
);
|
|
|
|
let sky_view_lut = render_device.create_bind_group(
|
|
"sky_view_lut_bind_group",
|
|
&layouts.sky_view_lut,
|
|
&BindGroupEntries::with_indices((
|
|
(0, atmosphere_binding.clone()),
|
|
(1, settings_binding.clone()),
|
|
(2, transforms_binding.clone()),
|
|
(3, view_binding.clone()),
|
|
(4, lights_binding.clone()),
|
|
(5, &textures.transmittance_lut.default_view),
|
|
(6, &samplers.transmittance_lut),
|
|
(7, &textures.multiscattering_lut.default_view),
|
|
(8, &samplers.multiscattering_lut),
|
|
(13, &textures.sky_view_lut.default_view),
|
|
)),
|
|
);
|
|
|
|
let aerial_view_lut = render_device.create_bind_group(
|
|
"sky_view_lut_bind_group",
|
|
&layouts.aerial_view_lut,
|
|
&BindGroupEntries::with_indices((
|
|
(0, atmosphere_binding.clone()),
|
|
(1, settings_binding.clone()),
|
|
(3, view_binding.clone()),
|
|
(4, lights_binding.clone()),
|
|
(5, &textures.transmittance_lut.default_view),
|
|
(6, &samplers.transmittance_lut),
|
|
(7, &textures.multiscattering_lut.default_view),
|
|
(8, &samplers.multiscattering_lut),
|
|
(13, &textures.aerial_view_lut.default_view),
|
|
)),
|
|
);
|
|
|
|
let render_sky = render_device.create_bind_group(
|
|
"render_sky_bind_group",
|
|
if *msaa == Msaa::Off {
|
|
&render_sky_layouts.render_sky
|
|
} else {
|
|
&render_sky_layouts.render_sky_msaa
|
|
},
|
|
&BindGroupEntries::with_indices((
|
|
(0, atmosphere_binding.clone()),
|
|
(1, settings_binding.clone()),
|
|
(2, transforms_binding.clone()),
|
|
(3, view_binding.clone()),
|
|
(4, lights_binding.clone()),
|
|
(5, &textures.transmittance_lut.default_view),
|
|
(6, &samplers.transmittance_lut),
|
|
(9, &textures.sky_view_lut.default_view),
|
|
(10, &samplers.sky_view_lut),
|
|
(11, &textures.aerial_view_lut.default_view),
|
|
(12, &samplers.aerial_view_lut),
|
|
(13, view_depth_texture.view()),
|
|
)),
|
|
);
|
|
|
|
commands.entity(entity).insert(AtmosphereBindGroups {
|
|
transmittance_lut,
|
|
multiscattering_lut,
|
|
sky_view_lut,
|
|
aerial_view_lut,
|
|
render_sky,
|
|
});
|
|
}
|
|
}
|