From 9d948abf47aa807f0062b723032ca0530f26f872 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Thu, 29 Aug 2024 09:58:19 -0700 Subject: [PATCH 01/43] wip --- crates/bevy_pbr/src/lib.rs | 1 + crates/bevy_pbr/src/sky/mod.rs | 476 ++++++++++++++++++ crates/bevy_pbr/src/sky/sky_atmosphere.wgsl | 62 +++ crates/bevy_pbr/src/sky/sky_common.wgsl | 81 +++ .../src/sky/sky_transmittance_lut.wgsl | 92 ++++ 5 files changed, 712 insertions(+) create mode 100644 crates/bevy_pbr/src/sky/mod.rs create mode 100644 crates/bevy_pbr/src/sky/sky_atmosphere.wgsl create mode 100644 crates/bevy_pbr/src/sky/sky_common.wgsl create mode 100644 crates/bevy_pbr/src/sky/sky_transmittance_lut.wgsl diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index ea84c49f18709..8ea88c77f886d 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -9,6 +9,7 @@ #[cfg(feature = "meshlet")] mod meshlet; +pub mod sky; pub mod wireframe; /// Experimental features that are not yet finished. Please report any issues you encounter! diff --git a/crates/bevy_pbr/src/sky/mod.rs b/crates/bevy_pbr/src/sky/mod.rs new file mode 100644 index 0000000000000..5ac982670ed91 --- /dev/null +++ b/crates/bevy_pbr/src/sky/mod.rs @@ -0,0 +1,476 @@ +use bevy_app::{App, Plugin}; +use bevy_asset::{load_internal_asset, AssetServer, Handle}; +use bevy_core_pipeline::core_3d::graph::Node3d; +use bevy_ecs::{ + component::Component, + entity::Entity, + query::{QueryItem, With}, + schedule::IntoSystemConfigs, + system::{Commands, Query, Res, ResMut, Resource}, + world::{FromWorld, World}, +}; +use bevy_reflect::Reflect; +use bevy_render::render_resource::binding_types::uniform_buffer; +use bevy_render::render_resource::{ + BindGroupLayoutEntries, CachedComputePipelineId, ComputePipelineDescriptor, +}; +use bevy_render::{ + camera::{Camera, ExtractedCamera}, + globals::GlobalsBuffer, + render_graph::{RenderGraphApp, ViewNode, ViewNodeRunner}, + render_resource::{ + BindGroup, BindGroupEntries, BindGroupLayout, CachedRenderPipelineId, ColorTargetState, + ColorWrites, Extent3d, FragmentState, MultisampleState, Operations, PipelineCache, + PrimitiveState, RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, + Shader, ShaderStages, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, + }, + renderer::{RenderAdapter, RenderDevice}, + texture::{CachedTexture, TextureCache}, + view::{ViewUniform, ViewUniformOffset, ViewUniforms}, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, +}; +use bevy_render::{ + render_graph::{NodeRunError, RenderGraphContext, RenderLabel}, + renderer::RenderContext, +}; +use bevy_utils::{prelude::default, tracing::warn}; + +use bevy_core_pipeline::{ + core_3d::{graph::Core3d, Camera3d}, + fullscreen_vertex_shader::fullscreen_shader_vertex_state, +}; + +#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] +pub struct SkyLabel; + +const TRANSMITTANCE_LUT_SHADER_HANDLE: Handle = + Handle::weak_from_u128(165991536981278682488488481343101998581); +const COMMON_SHADER_HANDLE: Handle = + Handle::weak_from_u128(165991536981278682488488481343101998582); +const ATMOSPHERE_SHADER_HANDLE: Handle = + Handle::weak_from_u128(165991536981278682488488481343101998583); + +pub struct SkyPlugin; + +impl Plugin for SkyPlugin { + fn build(&self, app: &mut App) { + load_internal_asset!( + app, + TRANSMITTANCE_LUT_SHADER_HANDLE, + "sky_transmittance_lut.wgsl", + Shader::from_wgsl + ); + + load_internal_asset!( + app, + COMMON_SHADER_HANDLE, + "sky_common.wgsl", + Shader::from_wgsl + ); + + load_internal_asset!( + app, + ATMOSPHERE_SHADER_HANDLE, + "sky_atmosphere.wgsl", + Shader::from_wgsl + ); + + app.register_type::(); + } + + fn finish(&self, app: &mut App) { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { + return; + }; + + if !render_app + .world() + .resource::() + .get_texture_format_features(TextureFormat::Rgba16Float) + .allowed_usages + .contains(TextureUsages::STORAGE_BINDING) + { + warn!("SkyPlugin not loaded. GPU lacks support: TextureFormat::Rgba16Float does not support TextureUsages::STORAGE_BINDING."); + return; + } + + render_app + .init_resource::() + // .init_resource::>() + .add_systems(ExtractSchedule, extract_sky_settings) + .add_systems( + Render, + ( + prepare_sky_textures.in_set(RenderSet::PrepareResources), + prepare_sky_bind_groups.in_set(RenderSet::PrepareBindGroups), + ), + ) + .add_render_graph_node::>(Core3d, SkyLabel) + .add_render_graph_edges( + Core3d, + ( + // END_PRE_PASSES -> PREPARE_SKY -> MAIN_PASS + Node3d::EndPrepasses, + SkyLabel, + Node3d::StartMainPass, + ), + ); + } +} + +#[derive(Clone, Component, Default, Reflect)] +pub struct Sky {} + +fn extract_sky_settings( + mut commands: Commands, + cameras: Extract>>, +) { + for (entity, camera, sky_settings) in &cameras { + if camera.is_active { + commands.get_or_spawn(entity).insert(sky_settings.clone()); + } + } +} + +#[derive(Resource)] +struct SkyPipelines { + transmittance_lut_pipeline: CachedRenderPipelineId, + // sky_view_lut: CachedRenderPipelineId, + // aerial_view_lut: CachedRenderPipelineId, + // multiscattering_lut: CachedComputePipelineId, + + // common_bind_group_layout: BindGroupLayout, + transmittance_lut_bind_group_layout: BindGroupLayout, + // sky_view_lut_bind_group_layout: BindGroupLayout, + // aerial_view_lut_bind_group_layout: BindGroupLayout, + // multiscattering_lut_bind_group_layout: BindGroupLayout, +} + +impl FromWorld for SkyPipelines { + fn from_world(world: &mut World) -> Self { + let render_device = world.resource::(); + let pipeline_cache = world.resource::(); + + let transmittance_lut_bind_group_layout = + render_device.create_bind_group_layout("sky_transmittance_lut_bind_group_layout", &[]); + + let transmittance_lut_pipeline = + pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor { + label: Some("sky_transmittance_lut_pipeline".into()), + layout: vec![transmittance_lut_bind_group_layout.clone()], + push_constant_ranges: vec![], + vertex: fullscreen_shader_vertex_state(), + primitive: PrimitiveState::default(), + depth_stencil: None, + multisample: MultisampleState::default(), + fragment: Some(FragmentState { + shader: TRANSMITTANCE_LUT_SHADER_HANDLE.clone(), + shader_defs: vec![], + entry_point: "main".into(), + targets: vec![Some(ColorTargetState { + format: TextureFormat::Rgba16Float, + blend: None, + write_mask: ColorWrites::ALL, + })], + }), + }); + + // let multi_scattering_lut_bind_group_layout = render_device.create_bind_group_layout( + // "sky_multi_scattering_lut_bind_group_layout", + // &BindGroupLayoutEntries::sequential( + // ShaderStages::COMPUTE, + // (uniform_buffer::(true),), + // ), + // ); + + // let multi_scattering_lut_pipeline = + // pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { + // label: Some("sky_multi_scattering_lut_pipeline".into()), + // layout: vec![multi_scattering_lut_bind_group_layout.clone()], + // push_constant_ranges: vec![], + // shader: world + // .resource::() + // .load("shaders/sky/multi_scattering_lut.wgsl"), + // shader_defs: vec![], + // entry_point: "main".into(), + // }); + + Self { + transmittance_lut_pipeline, + // sky_view_lut: todo!(), + // aerial_view_lut: todo!(), + // multiscattering_lut: todo!(), + // common_bind_group_layout: todo!(), + transmittance_lut_bind_group_layout, + // sky_view_lut_bind_group_layout: todo!(), + // aerial_view_lut_bind_group_layout: todo!(), + // multiscattering_lut_bind_group_layout: todo!(), + } + } +} + +// #[derive(Component)] +// struct TransmittanceLutPipelineId(CachedRenderPipelineId); + +// #[derive(Component)] +// struct SkyViewLutPipelineId(CachedRenderPipelineId); + +// #[derive(Component)] +// struct AerialPerspectivePipelineId(CachedRenderPipelineId); + +// #[derive(Component)] +// struct MultiScatteringLutPipelineId(CachedComputePipelineId); + +// fn prepare_ssao_pipelines( +// mut commands: Commands, +// pipeline_cache: Res, +// mut render_pipelines: ResMut>, +// mut compute_pipelines: ResMut>, +// pipeline: Res, +// views: Query<( +// Entity, +// // &ScreenSpaceAmbientOcclusionSettings, +// // Option<&TemporalJitter>, +// )>, +// ) { +// for (entity, ssao_settings, temporal_jitter) in &views { +// let pipeline_id = compute_pipelines.specialize( +// &pipeline_cache, +// &pipeline, +// SsaoPipelineKey { +// ssao_settings: ssao_settings.clone(), +// temporal_noise: temporal_jitter.is_some(), +// }, +// ); + +// commands.entity(entity).insert(SsaoPipelineId(pipeline_id)); +// } +// } + +#[derive(Component)] +struct SkyTextures { + transmittance_lut: CachedTexture, + sky_view_lut: CachedTexture, + aerial_perspective_lut: CachedTexture, + multi_scattering_lut: CachedTexture, +} + +fn prepare_sky_textures( + mut commands: Commands, + mut texture_cache: ResMut, + render_device: Res, + views: Query<(Entity, &ExtractedCamera), With>, +) { + for (entity, camera) in &views { + let transmittance_lut = texture_cache.get( + &render_device, + TextureDescriptor { + label: Some("transmittance_lut"), + size: Extent3d { + width: 256, + height: 64, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Rgba16Float, + usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }, + ); + + let sky_view_lut = texture_cache.get( + &render_device, + TextureDescriptor { + label: Some("sky_view_lut"), + size: Extent3d { + width: 192, + height: 108, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Rg11b10Float, + usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }, + ); + + let aerial_perspective_lut = texture_cache.get( + &render_device, + TextureDescriptor { + label: Some("aerial_perspective"), + size: Extent3d { + width: 32, + height: 32, + depth_or_array_layers: 32, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D3, + format: TextureFormat::Rgba16Float, + usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }, + ); + + let multi_scattering_lut = texture_cache.get( + &render_device, + TextureDescriptor { + label: Some("multi_scattering"), + size: Extent3d { + width: 32, + height: 32, + 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: &[], + }, + ); + + commands.entity(entity).insert({ + SkyTextures { + transmittance_lut, + sky_view_lut, + aerial_perspective_lut, + multi_scattering_lut, + } + }); + } +} + +#[derive(Component)] +struct SkyBindGroups { + transmittance_lut_bind_group: BindGroup, +} + +// Separate prepare needed, because Resources like ViewUniforms are not available in ViewNode::run() +fn prepare_sky_bind_groups( + mut commands: Commands, + render_device: Res, + pipelines: Res, + view_uniforms: Res, + global_uniforms: Res, + views: Query<(Entity, &SkyTextures)>, +) { + // bevy_log::debug!("prepare_sky_bindgroups"); + + let (Some(view_uniforms), Some(globals_uniforms)) = ( + view_uniforms.uniforms.binding(), + global_uniforms.buffer.binding(), + ) else { + return; + }; + + for (entity, sky_textures) in &views { + // bevy_log::debug!("{:?}", entity); + let transmittance_lut_bind_group = render_device.create_bind_group( + "transmittance_lut_bind_group", + &pipelines.transmittance_lut_bind_group_layout, + &[], + ); + + // let multi_scattering_lut_bind_group = render_device.create_bind_group( + // "multi_scattering_lut_bind_group", + // &pipelines.transmittance_lut_bind_group_layout, + // &BindGroupEntries::sequential((view_uniforms.clone(),)), + // ); + + commands.entity(entity).insert(SkyBindGroups { + transmittance_lut_bind_group, + }); + } +} + +#[derive(Default)] +struct SkyNode {} + +impl ViewNode for SkyNode { + type ViewQuery = ( + &'static ExtractedCamera, + &'static SkyTextures, + // &'static SsaoPipelineId, + &'static SkyBindGroups, + &'static ViewUniformOffset, + ); + + fn run( + &self, + graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + (camera, textures, bind_groups, view_uniform_offset): QueryItem, + world: &World, + ) -> Result<(), NodeRunError> { + let pipelines = world.resource::(); + let pipeline_cache = world.resource::(); + let ( + // Some(camera_size), + Some(transmittance_lut_pipeline), + // Some(spatial_denoise_pipeline), + // Some(gtao_pipeline), + ) = ( + // camera.physical_viewport_size, + pipeline_cache.get_render_pipeline(pipelines.transmittance_lut_pipeline), + // pipeline_cache.get_compute_pipeline(pipelines.spatial_denoise_pipeline), + // pipeline_cache.get_compute_pipeline(pipeline_id.0), + ) + else { + return Ok(()); + }; + + render_context.command_encoder().push_debug_group("sky"); + + { + let mut transmittance_lut_pass = + render_context + .command_encoder() + .begin_render_pass(&RenderPassDescriptor { + label: Some("transmittance_lut_pass"), + color_attachments: &[Some(RenderPassColorAttachment { + view: &textures.transmittance_lut.default_view, + resolve_target: None, + ops: Operations::default(), + })], + depth_stencil_attachment: None, + ..default() + }); + transmittance_lut_pass.set_pipeline(transmittance_lut_pipeline); + transmittance_lut_pass.set_bind_group( + 0, + &bind_groups.transmittance_lut_bind_group, + &[], + ); + transmittance_lut_pass.draw(0..3, 0..1); + } + + // { + // let mut preprocess_depth_pass = + // render_context + // .command_encoder() + // .begin_compute_pass(&ComputePassDescriptor { + // label: Some("ssao_preprocess_depth_pass"), + // }); + // preprocess_depth_pass.set_pipeline(preprocess_depth_pipeline); + // preprocess_depth_pass.set_bind_group(0, &bind_groups.preprocess_depth_bind_group, &[]); + // preprocess_depth_pass.set_bind_group( + // 1, + // &bind_groups.common_bind_group, + // &[view_uniform_offset.offset], + // ); + // preprocess_depth_pass.dispatch_workgroups( + // div_ceil(camera_size.x, 16), + // div_ceil(camera_size.y, 16), + // 1, + // ); + // } + + render_context.command_encoder().pop_debug_group(); + Ok(()) + } +} diff --git a/crates/bevy_pbr/src/sky/sky_atmosphere.wgsl b/crates/bevy_pbr/src/sky/sky_atmosphere.wgsl new file mode 100644 index 0000000000000..2677345867604 --- /dev/null +++ b/crates/bevy_pbr/src/sky/sky_atmosphere.wgsl @@ -0,0 +1,62 @@ +#define_import_path bevy_pbr::sky_atmosphere + +struct AtmosphereParameters { + // Radius of the planet + bottom_radius: f32, + + // Radius at which we consider the atmosphere to 'end' for out calculations (from center of planet) + top_radius: f32, + + rayleigh_density_exp_scale: f32, + rayleigh_scattering: vec3, + + mie_density_exp_scale: f32, + mie_scattering: vec3, + mie_extinction: vec3, + mie_absorption: vec3, + mie_phase_function_g: f32, + + ozone_density_layer_0_width: f32, + ozone_density_layer_0_constant_term: f32, + ozone_density_layer_0_linear_term: f32, + ozone_density_layer_1_constant_term: f32, + ozone_density_layer_1_linear_term: f32, + + absorption_extinction: vec3, + + ground_albedo: vec3, +}; + +fn get_atmosphere_parameters() -> AtmosphereParameters { + var atmosphere: AtmosphereParameters; + atmosphere.bottom_radius = 6360.0; + atmosphere.top_radius = atmosphere.bottom_radius + 100.0; + + // Rayleigh scattering + let earth_rayleigh_scale_height = 8.0; + + atmosphere.rayleigh_density_exp_scale = -1.0f / earth_rayleigh_scale_height; + atmosphere.rayleigh_scattering = vec3(0.005802, 0.013558, 0.033100); + + // Mie scattering + let earth_mie_scale_height = 1.2; + + atmosphere.mie_density_exp_scale = -1.0f / earth_mie_scale_height; + atmosphere.mie_scattering = vec3(0.003996, 0.003996, 0.003996); + atmosphere.mie_extinction = vec3(0.004440, 0.004440, 0.004440); + atmosphere.mie_absorption = max(vec3(0.0), atmosphere.mie_extinction - atmosphere.mie_scattering); + atmosphere.mie_phase_function_g = 0.8; + + // Ozone absorption + atmosphere.ozone_density_layer_0_width = 25.0; + atmosphere.ozone_density_layer_0_constant_term = -2.0 / 3.0; + atmosphere.ozone_density_layer_0_linear_term = 1.0 / 15.0; + atmosphere.ozone_density_layer_1_constant_term = 8.0 / 3.0; + atmosphere.ozone_density_layer_1_linear_term = -1.0 / 15.0; + + atmosphere.absorption_extinction = vec3(0.000650, 0.001881, 0.000085); + + atmosphere.ground_albedo = vec3(0.3f); + + return atmosphere; +} diff --git a/crates/bevy_pbr/src/sky/sky_common.wgsl b/crates/bevy_pbr/src/sky/sky_common.wgsl new file mode 100644 index 0000000000000..b5af76b848170 --- /dev/null +++ b/crates/bevy_pbr/src/sky/sky_common.wgsl @@ -0,0 +1,81 @@ +#define_import_path bevy_pbr::sky_common + +#import bevy_pbr::{ + sky_atmosphere::AtmosphereParameters, +} + +// Mapping from view height (r) and zenith cos angle (mu) to UV coordinates in the transmittance LUT +// Assuming r between ground and top atmosphere boundary, and mu = cos(zenith_angle) +// Chosen to increase precision near the ground and to work around a discontinuity at the horizon +// See Bruneton and Neyret 2008, "Precomputed Atmospheric Scattering" section 4 +fn r_mu_to_uv(atmosphere: AtmosphereParameters, r: f32, mu: f32) -> vec2 { + // Distance along a horizontal ray from the ground to the top atmosphere boundary + let H = sqrt(atmosphere.top_radius * atmosphere.top_radius - + atmosphere.bottom_radius * atmosphere.bottom_radius); + + // Distance from a point at height r to the horizon + // ignore the case where r <= atmosphere.bottom_radius + let rho = sqrt(max(r * r - atmosphere.bottom_radius * atmosphere.bottom_radius, 0.0)); + + // Distance from a point at height r to the top atmosphere boundary at zenith angle mu + let d = distance_to_top_atmosphere_boundary(atmosphere, r, mu); + + // Minimum and maximum distance to the top atmosphere boundary from a point at height r + let d_min = atmosphere.top_radius - r; // length of the ray straight up to the top atmosphere boundary + let d_max = rho + H; // length of the ray to the top atmosphere boundary and grazing the horizon + + let u = (d - d_min) / (d_max - d_min); + let v = rho / H; + return vec2(u, v); +} + +// Inverse of the mapping above, mapping from UV coordinates in the transmittance LUT to view height (r) and zenith cos angle (mu) +fn uv_to_r_mu(atmosphere: AtmosphereParameters, uv: vec2) -> vec2 { + // Distance to top atmosphere boundary for a horizontal ray at ground level + let H = sqrt(atmosphere.top_radius * atmosphere.top_radius - atmosphere.bottom_radius * atmosphere.bottom_radius); + + // Distance to the horizon, from which we can compute r: + let rho = H * uv.y; + let r = sqrt(rho * rho + atmosphere.bottom_radius * atmosphere.bottom_radius); + + // Distance to the top atmosphere boundary for the ray (r,mu), and its minimum + // and maximum values over all mu - obtained for (r,1) and (r,mu_horizon) - + // from which we can recover mu: + let d_min = atmosphere.top_radius - r; + let d_max = rho + H; + let d = d_min + uv.x * (d_max - d_min); + + var mu: f32; + if (d == 0.0) { + mu = 1.0; + } else { + mu = (H * H - rho * rho - d * d) / (2.0 * r * d); + } + + mu = clamp(mu, -1.0, 1.0); + + return vec2(r, mu); +} + +/// Simplified ray-sphere intersection +/// where: +/// Ray origin, o = [0,0,r] with r <= atmosphere.top_radius +/// mu is the cosine of spherical coordinate theta (-1.0 <= mu <= 1.0) +/// so ray direction in spherical coordinates is [1,acos(mu),0] which needs to be converted to cartesian +/// Direction of ray, u = [0,sqrt(1-mu*mu),mu] +/// Center of sphere, c = [0,0,0] +/// Radius of sphere, r = atmosphere.top_radius +/// This function solves the quadratic equation for line-sphere intersection simplified under these assumptions +fn distance_to_top_atmosphere_boundary(atmosphere: AtmosphereParameters, r: f32, mu: f32) -> f32 { + // ignore the case where r > atmosphere.top_radius + let positive_discriminant = max(r * r * (mu * mu - 1.0) + atmosphere.top_radius * atmosphere.top_radius, 0.0); + return max(-r * mu + sqrt(positive_discriminant), 0.0); +} + +/// Simplified ray-sphere intersection +/// as above for intersections with the ground +fn distance_to_bottom_atmosphere_boundary(atmosphere: AtmosphereParameters, r: f32, mu: f32) -> f32 { + let positive_discriminant = max(r * r * (mu * mu - 1.0) + atmosphere.bottom_radius * atmosphere.bottom_radius, 0.0); + return max(-r * mu - sqrt(positive_discriminant), 0.0); +} + diff --git a/crates/bevy_pbr/src/sky/sky_transmittance_lut.wgsl b/crates/bevy_pbr/src/sky/sky_transmittance_lut.wgsl new file mode 100644 index 0000000000000..35289ba604b61 --- /dev/null +++ b/crates/bevy_pbr/src/sky/sky_transmittance_lut.wgsl @@ -0,0 +1,92 @@ +#import bevy_pbr::{ + sky_atmosphere::{AtmosphereParameters, get_atmosphere_parameters}, + sky_common::{uv_to_r_mu, distance_to_top_atmosphere_boundary, distance_to_bottom_atmosphere_boundary} +} + +#import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput + +@fragment +fn main(in: FullscreenVertexOutput) -> @location(0) vec4 { + let atmosphere = get_atmosphere_parameters(); + + // map UV coordinates to view height (r) and zenith cos angle (mu) + let r_mu = uv_to_r_mu(atmosphere, in.uv); + + // compute the optical depth from view height r to the top atmosphere boundary + let optical_depth = compute_optical_depth_to_top_atmosphere_boundary(atmosphere, r_mu.x, r_mu.y); + + let transmittance = exp(-optical_depth); + + return vec4(transmittance, 1.0); +} + +/// Compute the optical depth of the atmosphere from the ground to the top atmosphere boundary +/// at a given view height (r) and zenith cos angle (mu) +fn compute_optical_depth_to_top_atmosphere_boundary(atmosphere: AtmosphereParameters, r: f32, mu:f32) -> vec3 { + let t_bottom = distance_to_bottom_atmosphere_boundary(atmosphere, r, mu); + let t_top = distance_to_top_atmosphere_boundary(atmosphere, r, mu); + let t_max = max(t_bottom, t_top); + + let sample_count = 40u; + + var optical_depth = vec3(0.0f); + var prev_t = 0.0f; + + for (var i = 0u; i < sample_count; i++) { + // SebH uses this for multiple scattering. It might not be needed here, but I've kept it to get results that are as close as possible to the original + let t_i = (t_max * f32(i) + 0.3f) / f32(sample_count); + let dt = t_i - prev_t; + prev_t = t_i; + + // distance r from current sample point to planet center + let r_i = sqrt(t_i * t_i + 2.0 * r * mu * t_i + r * r); + let view_height = r_i - atmosphere.bottom_radius; + + let atmosphere_sample = sample_atmosphere(atmosphere, view_height); + let sample_optical_depth = atmosphere_sample.extinction * dt; + + optical_depth += sample_optical_depth; + } + + return optical_depth; +} + +struct AtmosphereSample { + scattering: vec3, + absorption: vec3, + extinction: vec3 +}; + +fn sample_atmosphere(atmosphere: AtmosphereParameters, view_height: f32) -> AtmosphereSample { + var result: AtmosphereSample; + + // atmosphere values at view_height + let mie_density = exp(atmosphere.mie_density_exp_scale * view_height); + let rayleigh_density = exp(atmosphere.rayleigh_density_exp_scale * view_height); + var ozone_density: f32; + if (view_height < atmosphere.ozone_density_layer_0_width) { + ozone_density = atmosphere.ozone_density_layer_0_linear_term * view_height + atmosphere.ozone_density_layer_0_constant_term; + } else { + ozone_density = atmosphere.ozone_density_layer_1_linear_term * view_height + atmosphere.ozone_density_layer_1_constant_term; + } + ozone_density = saturate(ozone_density); + + let mie_scattering = mie_density * atmosphere.mie_scattering; + let mie_absorption = mie_density * atmosphere.mie_absorption; + let mie_extinction = mie_density * atmosphere.mie_extinction; + + let rayleigh_scattering = rayleigh_density * atmosphere.rayleigh_scattering; + // no rayleigh absorption + // rayleigh extinction is the sum of scattering and absorption + + // ozone doesn't contribute to scattering + let ozone_absorption = ozone_density * atmosphere.absorption_extinction; + // ozone extinction is the sum of scattering and absorption + + result.scattering = mie_scattering + rayleigh_scattering; + result.absorption = mie_absorption + ozone_absorption; + result.extinction = mie_extinction + rayleigh_scattering + ozone_absorption; + + return result; +} + From 515dfdebf4074ddc1bcd95b448833a8309b379a8 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:44:07 -0700 Subject: [PATCH 02/43] WIP --- .../src/atmosphere/aerial_view_lut.wgsl | 0 crates/bevy_pbr/src/atmosphere/bindings.wgsl | 5 + .../common.wgsl} | 64 +++++---- .../bevy_pbr/src/{sky => atmosphere}/mod.rs | 122 +++++++++++------- .../src/atmosphere/multi_scattering_lut.wgsl | 0 .../bevy_pbr/src/atmosphere/sky_view_lut.wgsl | 0 .../src/atmosphere/transmittance_lut.wgsl | 94 ++++++++++++++ .../types.wgsl} | 45 +++---- crates/bevy_pbr/src/lib.rs | 2 +- .../src/sky/sky_transmittance_lut.wgsl | 92 ------------- 10 files changed, 228 insertions(+), 196 deletions(-) create mode 100644 crates/bevy_pbr/src/atmosphere/aerial_view_lut.wgsl create mode 100644 crates/bevy_pbr/src/atmosphere/bindings.wgsl rename crates/bevy_pbr/src/{sky/sky_common.wgsl => atmosphere/common.wgsl} (56%) rename crates/bevy_pbr/src/{sky => atmosphere}/mod.rs (81%) create mode 100644 crates/bevy_pbr/src/atmosphere/multi_scattering_lut.wgsl create mode 100644 crates/bevy_pbr/src/atmosphere/sky_view_lut.wgsl create mode 100644 crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl rename crates/bevy_pbr/src/{sky/sky_atmosphere.wgsl => atmosphere/types.wgsl} (53%) delete mode 100644 crates/bevy_pbr/src/sky/sky_transmittance_lut.wgsl diff --git a/crates/bevy_pbr/src/atmosphere/aerial_view_lut.wgsl b/crates/bevy_pbr/src/atmosphere/aerial_view_lut.wgsl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/bevy_pbr/src/atmosphere/bindings.wgsl b/crates/bevy_pbr/src/atmosphere/bindings.wgsl new file mode 100644 index 0000000000000..0225b22924afc --- /dev/null +++ b/crates/bevy_pbr/src/atmosphere/bindings.wgsl @@ -0,0 +1,5 @@ +#define_import_path bevy_pbr::atmosphere::bindings + +@group(0) @binding(0) var atmosphere: Atmosphere; +@group(0) @binding(1) var view_uniforms: ViewUniforms; //TODO: import view uniform type +@group(0) @binding(2) var diff --git a/crates/bevy_pbr/src/sky/sky_common.wgsl b/crates/bevy_pbr/src/atmosphere/common.wgsl similarity index 56% rename from crates/bevy_pbr/src/sky/sky_common.wgsl rename to crates/bevy_pbr/src/atmosphere/common.wgsl index b5af76b848170..ebc242766fd6b 100644 --- a/crates/bevy_pbr/src/sky/sky_common.wgsl +++ b/crates/bevy_pbr/src/atmosphere/common.wgsl @@ -1,60 +1,58 @@ -#define_import_path bevy_pbr::sky_common +#define_import_path bevy_pbr::atmosphere::common + +#import bevy_pbr::atmosphere::types::Atmosphere, -#import bevy_pbr::{ - sky_atmosphere::AtmosphereParameters, -} // Mapping from view height (r) and zenith cos angle (mu) to UV coordinates in the transmittance LUT // Assuming r between ground and top atmosphere boundary, and mu = cos(zenith_angle) // Chosen to increase precision near the ground and to work around a discontinuity at the horizon // See Bruneton and Neyret 2008, "Precomputed Atmospheric Scattering" section 4 -fn r_mu_to_uv(atmosphere: AtmosphereParameters, r: f32, mu: f32) -> vec2 { +fn r_mu_to_uv(atmosphere: Atmosphere, r: f32, mu: f32) -> vec2 { // Distance along a horizontal ray from the ground to the top atmosphere boundary - let H = sqrt(atmosphere.top_radius * atmosphere.top_radius - - atmosphere.bottom_radius * atmosphere.bottom_radius); + let H = sqrt(atmosphere.top_radius * atmosphere.top_radius - atmosphere.bottom_radius * atmosphere.bottom_radius); // Distance from a point at height r to the horizon // ignore the case where r <= atmosphere.bottom_radius - let rho = sqrt(max(r * r - atmosphere.bottom_radius * atmosphere.bottom_radius, 0.0)); + let rho = sqrt(max(r * r - atmosphere.bottom_radius * atmosphere.bottom_radius, 0.0)); // Distance from a point at height r to the top atmosphere boundary at zenith angle mu - let d = distance_to_top_atmosphere_boundary(atmosphere, r, mu); + let d = distance_to_top_atmosphere_boundary(atmosphere, r, mu); // Minimum and maximum distance to the top atmosphere boundary from a point at height r - let d_min = atmosphere.top_radius - r; // length of the ray straight up to the top atmosphere boundary - let d_max = rho + H; // length of the ray to the top atmosphere boundary and grazing the horizon + let d_min = atmosphere.top_radius - r; // length of the ray straight up to the top atmosphere boundary + let d_max = rho + H; // length of the ray to the top atmosphere boundary and grazing the horizon - let u = (d - d_min) / (d_max - d_min); - let v = rho / H; - return vec2(u, v); + let u = (d - d_min) / (d_max - d_min); + let v = rho / H; + return vec2(u, v); } // Inverse of the mapping above, mapping from UV coordinates in the transmittance LUT to view height (r) and zenith cos angle (mu) -fn uv_to_r_mu(atmosphere: AtmosphereParameters, uv: vec2) -> vec2 { +fn uv_to_r_mu(atmosphere: Atmosphere, uv: vec2) -> vec2 { // Distance to top atmosphere boundary for a horizontal ray at ground level - let H = sqrt(atmosphere.top_radius * atmosphere.top_radius - atmosphere.bottom_radius * atmosphere.bottom_radius); + let H = sqrt(atmosphere.top_radius * atmosphere.top_radius - atmosphere.bottom_radius * atmosphere.bottom_radius); // Distance to the horizon, from which we can compute r: - let rho = H * uv.y; - let r = sqrt(rho * rho + atmosphere.bottom_radius * atmosphere.bottom_radius); + let rho = H * uv.y; + let r = sqrt(rho * rho + atmosphere.bottom_radius * atmosphere.bottom_radius); // Distance to the top atmosphere boundary for the ray (r,mu), and its minimum // and maximum values over all mu - obtained for (r,1) and (r,mu_horizon) - // from which we can recover mu: - let d_min = atmosphere.top_radius - r; - let d_max = rho + H; - let d = d_min + uv.x * (d_max - d_min); + let d_min = atmosphere.top_radius - r; + let d_max = rho + H; + let d = d_min + uv.x * (d_max - d_min); - var mu: f32; - if (d == 0.0) { - mu = 1.0; - } else { - mu = (H * H - rho * rho - d * d) / (2.0 * r * d); - } + var mu: f32; + if d == 0.0 { + mu = 1.0; + } else { + mu = (H * H - rho * rho - d * d) / (2.0 * r * d); + } - mu = clamp(mu, -1.0, 1.0); + mu = clamp(mu, -1.0, 1.0); - return vec2(r, mu); + return vec2(r, mu); } /// Simplified ray-sphere intersection @@ -66,15 +64,15 @@ fn uv_to_r_mu(atmosphere: AtmosphereParameters, uv: vec2) -> vec2 { /// Center of sphere, c = [0,0,0] /// Radius of sphere, r = atmosphere.top_radius /// This function solves the quadratic equation for line-sphere intersection simplified under these assumptions -fn distance_to_top_atmosphere_boundary(atmosphere: AtmosphereParameters, r: f32, mu: f32) -> f32 { +fn distance_to_top_atmosphere_boundary(atmosphere: Atmosphere, r: f32, mu: f32) -> f32 { // ignore the case where r > atmosphere.top_radius - let positive_discriminant = max(r * r * (mu * mu - 1.0) + atmosphere.top_radius * atmosphere.top_radius, 0.0); - return max(-r * mu + sqrt(positive_discriminant), 0.0); + let positive_discriminant = max(r * r * (mu * mu - 1.0) + atmosphere.top_radius * atmosphere.top_radius, 0.0); + return max(-r * mu + sqrt(positive_discriminant), 0.0); } /// Simplified ray-sphere intersection /// as above for intersections with the ground -fn distance_to_bottom_atmosphere_boundary(atmosphere: AtmosphereParameters, r: f32, mu: f32) -> f32 { +fn distance_to_bottom_atmosphere_boundary(atmosphere: Atmosphere, r: f32, mu: f32) -> f32 { let positive_discriminant = max(r * r * (mu * mu - 1.0) + atmosphere.bottom_radius * atmosphere.bottom_radius, 0.0); return max(-r * mu - sqrt(positive_discriminant), 0.0); } diff --git a/crates/bevy_pbr/src/sky/mod.rs b/crates/bevy_pbr/src/atmosphere/mod.rs similarity index 81% rename from crates/bevy_pbr/src/sky/mod.rs rename to crates/bevy_pbr/src/atmosphere/mod.rs index 5ac982670ed91..fabb1654e0b99 100644 --- a/crates/bevy_pbr/src/sky/mod.rs +++ b/crates/bevy_pbr/src/atmosphere/mod.rs @@ -1,16 +1,17 @@ use bevy_app::{App, Plugin}; -use bevy_asset::{load_internal_asset, AssetServer, Handle}; +use bevy_asset::{load_internal_asset, load_internal_binary_asset, AssetServer, Handle}; use bevy_core_pipeline::core_3d::graph::Node3d; use bevy_ecs::{ component::Component, entity::Entity, query::{QueryItem, With}, schedule::IntoSystemConfigs, - system::{Commands, Query, Res, ResMut, Resource}, + system::{lifetimeless::Read, Commands, Query, Res, ResMut, Resource}, world::{FromWorld, World}, }; +use bevy_math::Vec3; use bevy_reflect::Reflect; -use bevy_render::render_resource::binding_types::uniform_buffer; +use bevy_render::render_resource::{binding_types::uniform_buffer, ShaderType}; use bevy_render::render_resource::{ BindGroupLayoutEntries, CachedComputePipelineId, ComputePipelineDescriptor, }; @@ -40,42 +41,44 @@ use bevy_core_pipeline::{ fullscreen_vertex_shader::fullscreen_shader_vertex_state, }; -#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] -pub struct SkyLabel; +mod shaders { + use bevy_asset::Handle; + use bevy_render::render_resource::Shader; -const TRANSMITTANCE_LUT_SHADER_HANDLE: Handle = - Handle::weak_from_u128(165991536981278682488488481343101998581); -const COMMON_SHADER_HANDLE: Handle = - Handle::weak_from_u128(165991536981278682488488481343101998582); -const ATMOSPHERE_SHADER_HANDLE: Handle = - Handle::weak_from_u128(165991536981278682488488481343101998583); + pub const TYPES: Handle = Handle::weak_from_u128(0xB4CA686B10FA592B508580CCC2F9558C); + pub const COMMON: Handle = Handle::weak_from_u128(0xD5524FD88BDC153FBF256B7F2C21906F); + pub const BINDINGS: Handle = Handle::weak_from_u128(0x030D981C17C01B09847E3600513D5DFB); + + pub const TRANSMITTANCE_LUT: Handle = + Handle::weak_from_u128(0xEECBDEDFEED7F4EAFBD401BFAA5E0EFB); + // pub const ATMOSPHERE: Handle = Handle::weak_from_u128(0xD8F6A3827A0A9C7511291580B95E4B73); +} pub struct SkyPlugin; impl Plugin for SkyPlugin { fn build(&self, app: &mut App) { - load_internal_asset!( - app, - TRANSMITTANCE_LUT_SHADER_HANDLE, - "sky_transmittance_lut.wgsl", - Shader::from_wgsl - ); + load_internal_asset!(app, shaders::TYPES, "types.wgsl", Shader::from_wgsl); + load_internal_asset!(app, shaders::TYPES, "common.wgsl", Shader::from_wgsl); + load_internal_asset!(app, shaders::BINDINGS, "bindings.wgsl", Shader::from_wgsl); load_internal_asset!( app, - COMMON_SHADER_HANDLE, - "sky_common.wgsl", + shaders::TRANSMITTANCE_LUT, + "transmittance_lut.wgsl", Shader::from_wgsl ); - load_internal_asset!( - app, - ATMOSPHERE_SHADER_HANDLE, - "sky_atmosphere.wgsl", - Shader::from_wgsl - ); + load_internal_asset!(app, shaders::COMMON, "common.wgsl", Shader::from_wgsl); - app.register_type::(); + // load_internal_asset!( + // app, + // shaders::ATMOSPHERE, + // "atmosphere.wgsl", + // Shader::from_wgsl + // ); + + app.register_type::(); } fn finish(&self, app: &mut App) { @@ -118,12 +121,33 @@ impl Plugin for SkyPlugin { } } -#[derive(Clone, Component, Default, Reflect)] -pub struct Sky {} +//TODO: padding/alignment? +#[derive(Clone, Component, Default, Reflect, ShaderType)] +pub struct Atmosphere { + // Radius of the planet + bottom_radius: f32, + + // Radius at which we consider the atmosphere to 'end' for out calculations (from center of planet) + top_radius: f32, + + rayleigh_density_exp_scale: f32, + rayleigh_scattering: Vec3, + + mie_density_exp_scale: f32, + mie_scattering: f32, //units: km^-1 + mie_absorption: f32, //units: km^-1 + mie_phase_function_g: f32, //the "asymmetry" value of the phase function, unitless. Domain: (-1, 1) + + ozone_layer_center_altitude: f32, //units: km + ozone_layer_half_width: f32, //units: km + ozone_absorption: Vec3, //ozone absorption. units: km^-1 + + ground_albedo: Vec3, //note: never used even in the paper? maybe for the multiscattering LUT +} fn extract_sky_settings( mut commands: Commands, - cameras: Extract>>, + cameras: Extract>>, ) { for (entity, camera, sky_settings) in &cameras { if camera.is_active { @@ -151,20 +175,25 @@ impl FromWorld for SkyPipelines { let render_device = world.resource::(); let pipeline_cache = world.resource::(); - let transmittance_lut_bind_group_layout = - render_device.create_bind_group_layout("sky_transmittance_lut_bind_group_layout", &[]); + let bind_group_layout = render_device.create_bind_group_layout( + "atmosphere_lut_bind_group_layout", + &BindGroupLayoutEntries::single( + ShaderStages::FRAGMENT, + uniform_buffer::(false), + ), + ); let transmittance_lut_pipeline = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor { label: Some("sky_transmittance_lut_pipeline".into()), - layout: vec![transmittance_lut_bind_group_layout.clone()], + layout: vec![bind_group_layout.clone()], push_constant_ranges: vec![], vertex: fullscreen_shader_vertex_state(), primitive: PrimitiveState::default(), depth_stencil: None, multisample: MultisampleState::default(), fragment: Some(FragmentState { - shader: TRANSMITTANCE_LUT_SHADER_HANDLE.clone(), + shader: shaders::TRANSMITTANCE_LUT.clone(), shader_defs: vec![], entry_point: "main".into(), targets: vec![Some(ColorTargetState { @@ -201,7 +230,7 @@ impl FromWorld for SkyPipelines { // aerial_view_lut: todo!(), // multiscattering_lut: todo!(), // common_bind_group_layout: todo!(), - transmittance_lut_bind_group_layout, + transmittance_lut_bind_group_layout: bind_group_layout, // sky_view_lut_bind_group_layout: todo!(), // aerial_view_lut_bind_group_layout: todo!(), // multiscattering_lut_bind_group_layout: todo!(), @@ -259,7 +288,7 @@ fn prepare_sky_textures( mut commands: Commands, mut texture_cache: ResMut, render_device: Res, - views: Query<(Entity, &ExtractedCamera), With>, + views: Query<(Entity, &ExtractedCamera), With>, ) { for (entity, camera) in &views { let transmittance_lut = texture_cache.get( @@ -285,8 +314,8 @@ fn prepare_sky_textures( TextureDescriptor { label: Some("sky_view_lut"), size: Extent3d { - width: 192, - height: 108, + width: 256, + height: 256, depth_or_array_layers: 1, }, mip_level_count: 1, @@ -301,7 +330,7 @@ fn prepare_sky_textures( let aerial_perspective_lut = texture_cache.get( &render_device, TextureDescriptor { - label: Some("aerial_perspective"), + label: Some("aerial_view_lut"), size: Extent3d { width: 32, height: 32, @@ -346,8 +375,8 @@ fn prepare_sky_textures( } #[derive(Component)] -struct SkyBindGroups { - transmittance_lut_bind_group: BindGroup, +struct SkyBindGroup { + layout: BindGroupLayout, } // Separate prepare needed, because Resources like ViewUniforms are not available in ViewNode::run() @@ -388,16 +417,19 @@ fn prepare_sky_bind_groups( } } +#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash, RenderLabel)] +struct SkyLabel; + #[derive(Default)] struct SkyNode {} impl ViewNode for SkyNode { type ViewQuery = ( - &'static ExtractedCamera, - &'static SkyTextures, - // &'static SsaoPipelineId, - &'static SkyBindGroups, - &'static ViewUniformOffset, + Read, + Read, + // Read, + Read, + Read, ); fn run( diff --git a/crates/bevy_pbr/src/atmosphere/multi_scattering_lut.wgsl b/crates/bevy_pbr/src/atmosphere/multi_scattering_lut.wgsl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/bevy_pbr/src/atmosphere/sky_view_lut.wgsl b/crates/bevy_pbr/src/atmosphere/sky_view_lut.wgsl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl b/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl new file mode 100644 index 0000000000000..9d9dbae7fafe1 --- /dev/null +++ b/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl @@ -0,0 +1,94 @@ +#import bevy_pbr::{ + atmosphere::{ + types::{Atmosphere, get_atmosphere_parameters}, + common::{uv_to_r_mu, distance_to_top_atmosphere_boundary, distance_to_bottom_atmosphere_boundary} + } +} + +#import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput + +@fragment +fn main(in: FullscreenVertexOutput) -> @location(0) vec4 { + let atmosphere = get_atmosphere_parameters(); + + // map UV coordinates to view height (r) and zenith cos angle (mu) + let r_mu = uv_to_r_mu(atmosphere, in.uv); + + // compute the optical depth from view height r to the top atmosphere boundary + let optical_depth = compute_optical_depth_to_top_atmosphere_boundary(atmosphere, r_mu.x, r_mu.y); + + let transmittance = exp(-optical_depth); + + return vec4(transmittance, 1.0); +} + +/// Compute the optical depth of the atmosphere from the ground to the top atmosphere boundary +/// at a given view height (r) and zenith cos angle (mu) +fn compute_optical_depth_to_top_atmosphere_boundary(atmosphere: Atmosphere, r: f32, mu: f32) -> vec3 { + let t_bottom = distance_to_bottom_atmosphere_boundary(atmosphere, r, mu); + let t_top = distance_to_top_atmosphere_boundary(atmosphere, r, mu); + let t_max = max(t_bottom, t_top); + + let sample_count = 40u; + + var optical_depth = vec3(0.0f); + var prev_t = 0.0f; + + for (var i = 0u; i < sample_count; i++) { + // SebH uses this for multiple scattering. It might not be needed here, but I've kept it to get results that are as close as possible to the original + let t_i = (t_max * f32(i) + 0.3f) / f32(sample_count); + let dt = t_i - prev_t; + prev_t = t_i; + + // distance r from current sample point to planet center + let r_i = sqrt(t_i * t_i + 2.0 * r * mu * t_i + r * r); + let view_height = r_i - atmosphere.bottom_radius; + + let atmosphere_sample = sample_atmosphere(atmosphere, view_height); + let sample_optical_depth = atmosphere_sample.extinction * dt; + + optical_depth += sample_optical_depth; + } + + return optical_depth; +} + +struct AtmosphereSample { + scattering: vec3, + absorption: vec3, + extinction: vec3 +}; + +fn sample_atmosphere(atmosphere: Atmosphere, view_height: f32) -> AtmosphereSample { + var result: AtmosphereSample; + + // atmosphere values at view_height + let mie_density = exp(atmosphere.mie_density_exp_scale * view_height); + let rayleigh_density = exp(atmosphere.rayleigh_density_exp_scale * view_height); + var ozone_density: f32; + if view_height < atmosphere.ozone_density_layer_0_width { + ozone_density = atmosphere.ozone_density_layer_0_linear_term * view_height + atmosphere.ozone_density_layer_0_constant_term; + } else { + ozone_density = atmosphere.ozone_density_layer_1_linear_term * view_height + atmosphere.ozone_density_layer_1_constant_term; + } + ozone_density = saturate(ozone_density); + + let mie_scattering = mie_density * atmosphere.mie_scattering; + let mie_absorption = mie_density * atmosphere.mie_absorption; + let mie_extinction = mie_density * atmosphere.mie_extinction; + + let rayleigh_scattering = rayleigh_density * atmosphere.rayleigh_scattering; + // no rayleigh absorption + // rayleigh extinction is the sum of scattering and absorption + + // ozone doesn't contribute to scattering + let ozone_absorption = ozone_density * atmosphere.absorption_extinction; + // ozone extinction is the sum of scattering and absorption + + result.scattering = mie_scattering + rayleigh_scattering; + result.absorption = mie_absorption + ozone_absorption; + result.extinction = mie_extinction + rayleigh_scattering + ozone_absorption; + + return result; +} + diff --git a/crates/bevy_pbr/src/sky/sky_atmosphere.wgsl b/crates/bevy_pbr/src/atmosphere/types.wgsl similarity index 53% rename from crates/bevy_pbr/src/sky/sky_atmosphere.wgsl rename to crates/bevy_pbr/src/atmosphere/types.wgsl index 2677345867604..9a584d5c41b91 100644 --- a/crates/bevy_pbr/src/sky/sky_atmosphere.wgsl +++ b/crates/bevy_pbr/src/atmosphere/types.wgsl @@ -1,36 +1,31 @@ -#define_import_path bevy_pbr::sky_atmosphere +#define_import_path bevy_pbr::atmosphere::types -struct AtmosphereParameters { - // Radius of the planet - bottom_radius: f32, +struct Atmosphere { + // Radius of the planet + bottom_radius: f32, - // Radius at which we consider the atmosphere to 'end' for out calculations (from center of planet) - top_radius: f32, + // Radius at which we consider the atmosphere to 'end' for out calculations (from center of planet) + top_radius: f32, - rayleigh_density_exp_scale: f32, - rayleigh_scattering: vec3, + rayleigh_density_exp_scale: f32, + rayleigh_scattering: vec3, - mie_density_exp_scale: f32, - mie_scattering: vec3, - mie_extinction: vec3, - mie_absorption: vec3, - mie_phase_function_g: f32, + mie_density_exp_scale: f32, + mie_scattering: f32, //units: km^-1 + mie_absorption: f32, //units: km^-1 + mie_phase_function_g: f32, //the "asymmetry" value of the phase function, unitless. Domain: (-1, 1) - ozone_density_layer_0_width: f32, - ozone_density_layer_0_constant_term: f32, - ozone_density_layer_0_linear_term: f32, - ozone_density_layer_1_constant_term: f32, - ozone_density_layer_1_linear_term: f32, + ozone_layer_center_altitude: f32, //units: km + ozone_layer_half_width: f32, //units: km + ozone_absorption: vec3, //ozone absorption. units: km^-1 - absorption_extinction: vec3, - - ground_albedo: vec3, + ground_albedo: vec3, //note: never used even in the paper? maybe for the multiscattering LUT }; -fn get_atmosphere_parameters() -> AtmosphereParameters { - var atmosphere: AtmosphereParameters; - atmosphere.bottom_radius = 6360.0; - atmosphere.top_radius = atmosphere.bottom_radius + 100.0; +fn get_atmosphere_parameters() -> Atmosphere { + var atmosphere: Atmosphere; + atmosphere.bottom_radius = 6360.0; //units?????? + atmosphere.top_radius = atmosphere.bottom_radius + 100.0; //okay probably km // Rayleigh scattering let earth_rayleigh_scale_height = 8.0; diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 8ea88c77f886d..cc11c5a218c13 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -9,7 +9,6 @@ #[cfg(feature = "meshlet")] mod meshlet; -pub mod sky; pub mod wireframe; /// Experimental features that are not yet finished. Please report any issues you encounter! @@ -24,6 +23,7 @@ pub mod experimental { } } +pub mod atmosphere; mod bundle; mod cluster; pub mod deferred; diff --git a/crates/bevy_pbr/src/sky/sky_transmittance_lut.wgsl b/crates/bevy_pbr/src/sky/sky_transmittance_lut.wgsl deleted file mode 100644 index 35289ba604b61..0000000000000 --- a/crates/bevy_pbr/src/sky/sky_transmittance_lut.wgsl +++ /dev/null @@ -1,92 +0,0 @@ -#import bevy_pbr::{ - sky_atmosphere::{AtmosphereParameters, get_atmosphere_parameters}, - sky_common::{uv_to_r_mu, distance_to_top_atmosphere_boundary, distance_to_bottom_atmosphere_boundary} -} - -#import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput - -@fragment -fn main(in: FullscreenVertexOutput) -> @location(0) vec4 { - let atmosphere = get_atmosphere_parameters(); - - // map UV coordinates to view height (r) and zenith cos angle (mu) - let r_mu = uv_to_r_mu(atmosphere, in.uv); - - // compute the optical depth from view height r to the top atmosphere boundary - let optical_depth = compute_optical_depth_to_top_atmosphere_boundary(atmosphere, r_mu.x, r_mu.y); - - let transmittance = exp(-optical_depth); - - return vec4(transmittance, 1.0); -} - -/// Compute the optical depth of the atmosphere from the ground to the top atmosphere boundary -/// at a given view height (r) and zenith cos angle (mu) -fn compute_optical_depth_to_top_atmosphere_boundary(atmosphere: AtmosphereParameters, r: f32, mu:f32) -> vec3 { - let t_bottom = distance_to_bottom_atmosphere_boundary(atmosphere, r, mu); - let t_top = distance_to_top_atmosphere_boundary(atmosphere, r, mu); - let t_max = max(t_bottom, t_top); - - let sample_count = 40u; - - var optical_depth = vec3(0.0f); - var prev_t = 0.0f; - - for (var i = 0u; i < sample_count; i++) { - // SebH uses this for multiple scattering. It might not be needed here, but I've kept it to get results that are as close as possible to the original - let t_i = (t_max * f32(i) + 0.3f) / f32(sample_count); - let dt = t_i - prev_t; - prev_t = t_i; - - // distance r from current sample point to planet center - let r_i = sqrt(t_i * t_i + 2.0 * r * mu * t_i + r * r); - let view_height = r_i - atmosphere.bottom_radius; - - let atmosphere_sample = sample_atmosphere(atmosphere, view_height); - let sample_optical_depth = atmosphere_sample.extinction * dt; - - optical_depth += sample_optical_depth; - } - - return optical_depth; -} - -struct AtmosphereSample { - scattering: vec3, - absorption: vec3, - extinction: vec3 -}; - -fn sample_atmosphere(atmosphere: AtmosphereParameters, view_height: f32) -> AtmosphereSample { - var result: AtmosphereSample; - - // atmosphere values at view_height - let mie_density = exp(atmosphere.mie_density_exp_scale * view_height); - let rayleigh_density = exp(atmosphere.rayleigh_density_exp_scale * view_height); - var ozone_density: f32; - if (view_height < atmosphere.ozone_density_layer_0_width) { - ozone_density = atmosphere.ozone_density_layer_0_linear_term * view_height + atmosphere.ozone_density_layer_0_constant_term; - } else { - ozone_density = atmosphere.ozone_density_layer_1_linear_term * view_height + atmosphere.ozone_density_layer_1_constant_term; - } - ozone_density = saturate(ozone_density); - - let mie_scattering = mie_density * atmosphere.mie_scattering; - let mie_absorption = mie_density * atmosphere.mie_absorption; - let mie_extinction = mie_density * atmosphere.mie_extinction; - - let rayleigh_scattering = rayleigh_density * atmosphere.rayleigh_scattering; - // no rayleigh absorption - // rayleigh extinction is the sum of scattering and absorption - - // ozone doesn't contribute to scattering - let ozone_absorption = ozone_density * atmosphere.absorption_extinction; - // ozone extinction is the sum of scattering and absorption - - result.scattering = mie_scattering + rayleigh_scattering; - result.absorption = mie_absorption + ozone_absorption; - result.extinction = mie_extinction + rayleigh_scattering + ozone_absorption; - - return result; -} - From 534f505849bdc7f28b32cc5e92cc4173aab54355 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Thu, 5 Sep 2024 12:53:50 -0700 Subject: [PATCH 03/43] WIP --- crates/bevy_pbr/src/atmosphere/bindings.wgsl | 7 +- crates/bevy_pbr/src/atmosphere/mod.rs | 455 +++--------------- ...ring_lut.wgsl => multiscattering_lut.wgsl} | 0 crates/bevy_pbr/src/atmosphere/node.rs | 143 ++++++ crates/bevy_pbr/src/atmosphere/resources.rs | 333 +++++++++++++ crates/bevy_pbr/src/atmosphere/types.wgsl | 6 +- crates/bevy_pbr/src/lib.rs | 3 +- .../bind_group_layout_entries.rs | 12 + 8 files changed, 553 insertions(+), 406 deletions(-) rename crates/bevy_pbr/src/atmosphere/{multi_scattering_lut.wgsl => multiscattering_lut.wgsl} (100%) create mode 100644 crates/bevy_pbr/src/atmosphere/node.rs create mode 100644 crates/bevy_pbr/src/atmosphere/resources.rs diff --git a/crates/bevy_pbr/src/atmosphere/bindings.wgsl b/crates/bevy_pbr/src/atmosphere/bindings.wgsl index 0225b22924afc..f0fcd557182f6 100644 --- a/crates/bevy_pbr/src/atmosphere/bindings.wgsl +++ b/crates/bevy_pbr/src/atmosphere/bindings.wgsl @@ -1,5 +1,8 @@ #define_import_path bevy_pbr::atmosphere::bindings +#import bevy_pbr::mesh_view_types::Lights +#import bevy_render::{view::View, globals::Globals} + + +//TODO: set max directional lights in Lights array, and max cascades per directional light @group(0) @binding(0) var atmosphere: Atmosphere; -@group(0) @binding(1) var view_uniforms: ViewUniforms; //TODO: import view uniform type -@group(0) @binding(2) var diff --git a/crates/bevy_pbr/src/atmosphere/mod.rs b/crates/bevy_pbr/src/atmosphere/mod.rs index fabb1654e0b99..dadabba046e4a 100644 --- a/crates/bevy_pbr/src/atmosphere/mod.rs +++ b/crates/bevy_pbr/src/atmosphere/mod.rs @@ -1,44 +1,36 @@ +mod node; +mod resources; + use bevy_app::{App, Plugin}; -use bevy_asset::{load_internal_asset, load_internal_binary_asset, AssetServer, Handle}; +use bevy_asset::load_internal_asset; use bevy_core_pipeline::core_3d::graph::Node3d; use bevy_ecs::{ component::Component, entity::Entity, - query::{QueryItem, With}, + query::With, schedule::IntoSystemConfigs, - system::{lifetimeless::Read, Commands, Query, Res, ResMut, Resource}, - world::{FromWorld, World}, + system::{Commands, Query}, }; use bevy_math::Vec3; use bevy_reflect::Reflect; -use bevy_render::render_resource::{binding_types::uniform_buffer, ShaderType}; -use bevy_render::render_resource::{ - BindGroupLayoutEntries, CachedComputePipelineId, ComputePipelineDescriptor, -}; use bevy_render::{ - camera::{Camera, ExtractedCamera}, - globals::GlobalsBuffer, - render_graph::{RenderGraphApp, ViewNode, ViewNodeRunner}, - render_resource::{ - BindGroup, BindGroupEntries, BindGroupLayout, CachedRenderPipelineId, ColorTargetState, - ColorWrites, Extent3d, FragmentState, MultisampleState, Operations, PipelineCache, - PrimitiveState, RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, - Shader, ShaderStages, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, - }, - renderer::{RenderAdapter, RenderDevice}, - texture::{CachedTexture, TextureCache}, - view::{ViewUniform, ViewUniformOffset, ViewUniforms}, + camera::Camera, + render_graph::{RenderGraphApp, ViewNodeRunner}, + render_resource::{Shader, TextureFormat, TextureUsages}, + renderer::RenderAdapter, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use bevy_render::{ - render_graph::{NodeRunError, RenderGraphContext, RenderLabel}, - renderer::RenderContext, -}; -use bevy_utils::{prelude::default, tracing::warn}; +use bevy_render::{extract_component::UniformComponentPlugin, render_resource::ShaderType}; +use bevy_utils::tracing::warn; + +use bevy_core_pipeline::core_3d::{graph::Core3d, Camera3d}; -use bevy_core_pipeline::{ - core_3d::{graph::Core3d, Camera3d}, - fullscreen_vertex_shader::fullscreen_shader_vertex_state, +use self::{ + node::{SkyLabel, SkyNode}, + resources::{ + prepare_atmosphere_bind_groups, prepare_atmosphere_textures, AtmosphereBindGroupLayouts, + AtmospherePipelines, + }, }; mod shaders { @@ -47,20 +39,23 @@ mod shaders { pub const TYPES: Handle = Handle::weak_from_u128(0xB4CA686B10FA592B508580CCC2F9558C); pub const COMMON: Handle = Handle::weak_from_u128(0xD5524FD88BDC153FBF256B7F2C21906F); - pub const BINDINGS: Handle = Handle::weak_from_u128(0x030D981C17C01B09847E3600513D5DFB); pub const TRANSMITTANCE_LUT: Handle = Handle::weak_from_u128(0xEECBDEDFEED7F4EAFBD401BFAA5E0EFB); - // pub const ATMOSPHERE: Handle = Handle::weak_from_u128(0xD8F6A3827A0A9C7511291580B95E4B73); + pub const MULTISCATTERING_LUT: Handle = + Handle::weak_from_u128(0x65915B32C44B6287C0CCE1E70AF2936A); + pub const SKY_VIEW_LUT: Handle = + Handle::weak_from_u128(0x54136D7E6FFCD45BE38399A4E5ED7186); + pub const AERIAL_VIEW_LUT: Handle = + Handle::weak_from_u128(0x6FDEC284AD356B78C3A4D8ED4CBA0BC5); } -pub struct SkyPlugin; +pub struct AtmospherePlugin; -impl Plugin for SkyPlugin { +impl Plugin for AtmospherePlugin { fn build(&self, app: &mut App) { load_internal_asset!(app, shaders::TYPES, "types.wgsl", Shader::from_wgsl); - load_internal_asset!(app, shaders::TYPES, "common.wgsl", Shader::from_wgsl); - load_internal_asset!(app, shaders::BINDINGS, "bindings.wgsl", Shader::from_wgsl); + load_internal_asset!(app, shaders::COMMON, "common.wgsl", Shader::from_wgsl); load_internal_asset!( app, @@ -69,14 +64,26 @@ impl Plugin for SkyPlugin { Shader::from_wgsl ); - load_internal_asset!(app, shaders::COMMON, "common.wgsl", Shader::from_wgsl); + load_internal_asset!( + app, + shaders::MULTISCATTERING_LUT, + "multiscattering_lut.wgsl", + Shader::from_wgsl + ); + + load_internal_asset!( + app, + shaders::SKY_VIEW_LUT, + "sky_view_lut.wgsl", + Shader::from_wgsl + ); - // load_internal_asset!( - // app, - // shaders::ATMOSPHERE, - // "atmosphere.wgsl", - // Shader::from_wgsl - // ); + load_internal_asset!( + app, + shaders::AERIAL_VIEW_LUT, + "aerial_view_lut.wgsl", + Shader::from_wgsl + ); app.register_type::(); } @@ -98,14 +105,15 @@ impl Plugin for SkyPlugin { } render_app - .init_resource::() - // .init_resource::>() + .init_resource::() + .init_resource::() .add_systems(ExtractSchedule, extract_sky_settings) + .add_plugins(UniformComponentPlugin::::default()) .add_systems( Render, ( - prepare_sky_textures.in_set(RenderSet::PrepareResources), - prepare_sky_bind_groups.in_set(RenderSet::PrepareBindGroups), + prepare_atmosphere_textures.in_set(RenderSet::PrepareResources), + prepare_atmosphere_bind_groups.in_set(RenderSet::PrepareBindGroups), ), ) .add_render_graph_node::>(Core3d, SkyLabel) @@ -124,7 +132,9 @@ impl Plugin for SkyPlugin { //TODO: padding/alignment? #[derive(Clone, Component, Default, Reflect, ShaderType)] pub struct Atmosphere { - // Radius of the planet + /// Radius of the planet + /// + /// units: km bottom_radius: f32, // Radius at which we consider the atmosphere to 'end' for out calculations (from center of planet) @@ -141,8 +151,6 @@ pub struct Atmosphere { ozone_layer_center_altitude: f32, //units: km ozone_layer_half_width: f32, //units: km ozone_absorption: Vec3, //ozone absorption. units: km^-1 - - ground_albedo: Vec3, //note: never used even in the paper? maybe for the multiscattering LUT } fn extract_sky_settings( @@ -155,354 +163,3 @@ fn extract_sky_settings( } } } - -#[derive(Resource)] -struct SkyPipelines { - transmittance_lut_pipeline: CachedRenderPipelineId, - // sky_view_lut: CachedRenderPipelineId, - // aerial_view_lut: CachedRenderPipelineId, - // multiscattering_lut: CachedComputePipelineId, - - // common_bind_group_layout: BindGroupLayout, - transmittance_lut_bind_group_layout: BindGroupLayout, - // sky_view_lut_bind_group_layout: BindGroupLayout, - // aerial_view_lut_bind_group_layout: BindGroupLayout, - // multiscattering_lut_bind_group_layout: BindGroupLayout, -} - -impl FromWorld for SkyPipelines { - fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); - let pipeline_cache = world.resource::(); - - let bind_group_layout = render_device.create_bind_group_layout( - "atmosphere_lut_bind_group_layout", - &BindGroupLayoutEntries::single( - ShaderStages::FRAGMENT, - uniform_buffer::(false), - ), - ); - - let transmittance_lut_pipeline = - pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor { - label: Some("sky_transmittance_lut_pipeline".into()), - layout: vec![bind_group_layout.clone()], - push_constant_ranges: vec![], - vertex: fullscreen_shader_vertex_state(), - primitive: PrimitiveState::default(), - depth_stencil: None, - multisample: MultisampleState::default(), - fragment: Some(FragmentState { - shader: shaders::TRANSMITTANCE_LUT.clone(), - shader_defs: vec![], - entry_point: "main".into(), - targets: vec![Some(ColorTargetState { - format: TextureFormat::Rgba16Float, - blend: None, - write_mask: ColorWrites::ALL, - })], - }), - }); - - // let multi_scattering_lut_bind_group_layout = render_device.create_bind_group_layout( - // "sky_multi_scattering_lut_bind_group_layout", - // &BindGroupLayoutEntries::sequential( - // ShaderStages::COMPUTE, - // (uniform_buffer::(true),), - // ), - // ); - - // let multi_scattering_lut_pipeline = - // pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { - // label: Some("sky_multi_scattering_lut_pipeline".into()), - // layout: vec![multi_scattering_lut_bind_group_layout.clone()], - // push_constant_ranges: vec![], - // shader: world - // .resource::() - // .load("shaders/sky/multi_scattering_lut.wgsl"), - // shader_defs: vec![], - // entry_point: "main".into(), - // }); - - Self { - transmittance_lut_pipeline, - // sky_view_lut: todo!(), - // aerial_view_lut: todo!(), - // multiscattering_lut: todo!(), - // common_bind_group_layout: todo!(), - transmittance_lut_bind_group_layout: bind_group_layout, - // sky_view_lut_bind_group_layout: todo!(), - // aerial_view_lut_bind_group_layout: todo!(), - // multiscattering_lut_bind_group_layout: todo!(), - } - } -} - -// #[derive(Component)] -// struct TransmittanceLutPipelineId(CachedRenderPipelineId); - -// #[derive(Component)] -// struct SkyViewLutPipelineId(CachedRenderPipelineId); - -// #[derive(Component)] -// struct AerialPerspectivePipelineId(CachedRenderPipelineId); - -// #[derive(Component)] -// struct MultiScatteringLutPipelineId(CachedComputePipelineId); - -// fn prepare_ssao_pipelines( -// mut commands: Commands, -// pipeline_cache: Res, -// mut render_pipelines: ResMut>, -// mut compute_pipelines: ResMut>, -// pipeline: Res, -// views: Query<( -// Entity, -// // &ScreenSpaceAmbientOcclusionSettings, -// // Option<&TemporalJitter>, -// )>, -// ) { -// for (entity, ssao_settings, temporal_jitter) in &views { -// let pipeline_id = compute_pipelines.specialize( -// &pipeline_cache, -// &pipeline, -// SsaoPipelineKey { -// ssao_settings: ssao_settings.clone(), -// temporal_noise: temporal_jitter.is_some(), -// }, -// ); - -// commands.entity(entity).insert(SsaoPipelineId(pipeline_id)); -// } -// } - -#[derive(Component)] -struct SkyTextures { - transmittance_lut: CachedTexture, - sky_view_lut: CachedTexture, - aerial_perspective_lut: CachedTexture, - multi_scattering_lut: CachedTexture, -} - -fn prepare_sky_textures( - mut commands: Commands, - mut texture_cache: ResMut, - render_device: Res, - views: Query<(Entity, &ExtractedCamera), With>, -) { - for (entity, camera) in &views { - let transmittance_lut = texture_cache.get( - &render_device, - TextureDescriptor { - label: Some("transmittance_lut"), - size: Extent3d { - width: 256, - height: 64, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: TextureFormat::Rgba16Float, - usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }, - ); - - let sky_view_lut = texture_cache.get( - &render_device, - TextureDescriptor { - label: Some("sky_view_lut"), - size: Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: TextureFormat::Rg11b10Float, - usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }, - ); - - let aerial_perspective_lut = texture_cache.get( - &render_device, - TextureDescriptor { - label: Some("aerial_view_lut"), - size: Extent3d { - width: 32, - height: 32, - depth_or_array_layers: 32, - }, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D3, - format: TextureFormat::Rgba16Float, - usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }, - ); - - let multi_scattering_lut = texture_cache.get( - &render_device, - TextureDescriptor { - label: Some("multi_scattering"), - size: Extent3d { - width: 32, - height: 32, - 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: &[], - }, - ); - - commands.entity(entity).insert({ - SkyTextures { - transmittance_lut, - sky_view_lut, - aerial_perspective_lut, - multi_scattering_lut, - } - }); - } -} - -#[derive(Component)] -struct SkyBindGroup { - layout: BindGroupLayout, -} - -// Separate prepare needed, because Resources like ViewUniforms are not available in ViewNode::run() -fn prepare_sky_bind_groups( - mut commands: Commands, - render_device: Res, - pipelines: Res, - view_uniforms: Res, - global_uniforms: Res, - views: Query<(Entity, &SkyTextures)>, -) { - // bevy_log::debug!("prepare_sky_bindgroups"); - - let (Some(view_uniforms), Some(globals_uniforms)) = ( - view_uniforms.uniforms.binding(), - global_uniforms.buffer.binding(), - ) else { - return; - }; - - for (entity, sky_textures) in &views { - // bevy_log::debug!("{:?}", entity); - let transmittance_lut_bind_group = render_device.create_bind_group( - "transmittance_lut_bind_group", - &pipelines.transmittance_lut_bind_group_layout, - &[], - ); - - // let multi_scattering_lut_bind_group = render_device.create_bind_group( - // "multi_scattering_lut_bind_group", - // &pipelines.transmittance_lut_bind_group_layout, - // &BindGroupEntries::sequential((view_uniforms.clone(),)), - // ); - - commands.entity(entity).insert(SkyBindGroups { - transmittance_lut_bind_group, - }); - } -} - -#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash, RenderLabel)] -struct SkyLabel; - -#[derive(Default)] -struct SkyNode {} - -impl ViewNode for SkyNode { - type ViewQuery = ( - Read, - Read, - // Read, - Read, - Read, - ); - - fn run( - &self, - graph: &mut RenderGraphContext, - render_context: &mut RenderContext, - (camera, textures, bind_groups, view_uniform_offset): QueryItem, - world: &World, - ) -> Result<(), NodeRunError> { - let pipelines = world.resource::(); - let pipeline_cache = world.resource::(); - let ( - // Some(camera_size), - Some(transmittance_lut_pipeline), - // Some(spatial_denoise_pipeline), - // Some(gtao_pipeline), - ) = ( - // camera.physical_viewport_size, - pipeline_cache.get_render_pipeline(pipelines.transmittance_lut_pipeline), - // pipeline_cache.get_compute_pipeline(pipelines.spatial_denoise_pipeline), - // pipeline_cache.get_compute_pipeline(pipeline_id.0), - ) - else { - return Ok(()); - }; - - render_context.command_encoder().push_debug_group("sky"); - - { - let mut transmittance_lut_pass = - render_context - .command_encoder() - .begin_render_pass(&RenderPassDescriptor { - label: Some("transmittance_lut_pass"), - color_attachments: &[Some(RenderPassColorAttachment { - view: &textures.transmittance_lut.default_view, - resolve_target: None, - ops: Operations::default(), - })], - depth_stencil_attachment: None, - ..default() - }); - transmittance_lut_pass.set_pipeline(transmittance_lut_pipeline); - transmittance_lut_pass.set_bind_group( - 0, - &bind_groups.transmittance_lut_bind_group, - &[], - ); - transmittance_lut_pass.draw(0..3, 0..1); - } - - // { - // let mut preprocess_depth_pass = - // render_context - // .command_encoder() - // .begin_compute_pass(&ComputePassDescriptor { - // label: Some("ssao_preprocess_depth_pass"), - // }); - // preprocess_depth_pass.set_pipeline(preprocess_depth_pipeline); - // preprocess_depth_pass.set_bind_group(0, &bind_groups.preprocess_depth_bind_group, &[]); - // preprocess_depth_pass.set_bind_group( - // 1, - // &bind_groups.common_bind_group, - // &[view_uniform_offset.offset], - // ); - // preprocess_depth_pass.dispatch_workgroups( - // div_ceil(camera_size.x, 16), - // div_ceil(camera_size.y, 16), - // 1, - // ); - // } - - render_context.command_encoder().pop_debug_group(); - Ok(()) - } -} diff --git a/crates/bevy_pbr/src/atmosphere/multi_scattering_lut.wgsl b/crates/bevy_pbr/src/atmosphere/multiscattering_lut.wgsl similarity index 100% rename from crates/bevy_pbr/src/atmosphere/multi_scattering_lut.wgsl rename to crates/bevy_pbr/src/atmosphere/multiscattering_lut.wgsl diff --git a/crates/bevy_pbr/src/atmosphere/node.rs b/crates/bevy_pbr/src/atmosphere/node.rs new file mode 100644 index 0000000000000..9d77f341ba213 --- /dev/null +++ b/crates/bevy_pbr/src/atmosphere/node.rs @@ -0,0 +1,143 @@ +use bevy_ecs::{query::QueryItem, system::lifetimeless::Read, world::World}; +use bevy_render::{ + camera::ExtractedCamera, + extract_component::DynamicUniformIndex, + render_graph::{NodeRunError, RenderGraphContext, RenderLabel, ViewNode}, + render_resource::{ + ComputePassDescriptor, Operations, PipelineCache, RenderPassColorAttachment, + RenderPassDescriptor, + }, + renderer::RenderContext, + view::ViewUniformOffset, +}; + +use crate::MeshViewBindGroup; + +use super::{ + resources::{AtmosphereBindGroups, AtmospherePipelines, AtmosphereTextures}, + Atmosphere, +}; + +#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash, RenderLabel)] +pub(super) struct SkyLabel; + +#[derive(Default)] +pub(super) struct SkyNode {} + +impl ViewNode for SkyNode { + type ViewQuery = ( + Read, + Read, + Read, + Read, + Read>, + ); + + fn run( + &self, + graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + ( + textures, + bind_groups, + view_uniform_offset, + mesh_view_bind_group, + atmosphere_uniform_offset, + ): QueryItem, + world: &World, + ) -> Result<(), NodeRunError> { + let pipelines = world.resource::(); + let pipeline_cache = world.resource::(); + let ( + Some(transmittance_lut_pipeline), + Some(multiscattering_lut_pipeline), + Some(sky_view_lut_pipeline), + Some(aerial_view_lut_pipeline), + ) = ( + pipeline_cache.get_render_pipeline(pipelines.transmittance_lut), + pipeline_cache.get_compute_pipeline(pipelines.multiscattering_lut), + pipeline_cache.get_render_pipeline(pipelines.sky_view_lut), + pipeline_cache.get_compute_pipeline(pipelines.aerial_view_lut), + ) + else { + //TODO: warning + return Ok(()); + }; + + let mut commands = render_context.command_encoder(); + + commands.push_debug_group("sky"); + + { + let mut transmittance_lut_pass = commands.begin_render_pass(&RenderPassDescriptor { + label: Some("transmittance_lut_pass"), + color_attachments: &[Some(RenderPassColorAttachment { + view: &textures.transmittance_lut.default_view, + resolve_target: None, + ops: Operations::default(), + })], + depth_stencil_attachment: None, + ..Default::default() + }); + transmittance_lut_pass.set_pipeline(transmittance_lut_pipeline); //TODO: MESH VIEW BIND GROUP + transmittance_lut_pass.set_bind_group( + 0, + &bind_groups.transmittance_lut, + &[atmosphere_uniform_offset.index()], + ); + transmittance_lut_pass.draw(0..3, 0..1); + } + + { + let mut multiscattering_lut_pass = + commands.begin_compute_pass(&ComputePassDescriptor { + label: Some("multiscatttering_lut_pass"), + timestamp_writes: None, + }); + multiscattering_lut_pass.set_pipeline(multiscattering_lut_pipeline); + multiscattering_lut_pass.set_bind_group( + 0, + &bind_groups.multiscattering_lut, + &[atmosphere_uniform_offset.index()], + ); + multiscattering_lut_pass.dispatch_workgroups(todo!(), todo!(), todo!()); + } + + { + let mut sky_view_lut_pass = commands.begin_render_pass(&RenderPassDescriptor { + label: Some("transmittance_lut_pass"), + color_attachments: &[Some(RenderPassColorAttachment { + view: &textures.transmittance_lut.default_view, + resolve_target: None, + ops: Operations::default(), + })], + depth_stencil_attachment: None, + ..Default::default() + }); + sky_view_lut_pass.set_pipeline(transmittance_lut_pipeline); //TODO: MESH VIEW BIND GROUP + sky_view_lut_pass.set_bind_group( + 0, + &bind_groups.sky_view_lut, + &[atmosphere_uniform_offset.index()], + ); + sky_view_lut_pass.draw(0..3, 0..1); + } + + { + let mut aerial_view_lut_pass = commands.begin_compute_pass(&ComputePassDescriptor { + label: Some("multiscatttering_lut_pass"), + timestamp_writes: None, + }); + aerial_view_lut_pass.set_pipeline(multiscattering_lut_pipeline); + aerial_view_lut_pass.set_bind_group( + 0, + &bind_groups.aerial_view_lut, + &[atmosphere_uniform_offset.index()], + ); + aerial_view_lut_pass.dispatch_workgroups(todo!(), todo!(), todo!()); + } + + render_context.command_encoder().pop_debug_group(); + Ok(()) + } +} diff --git a/crates/bevy_pbr/src/atmosphere/resources.rs b/crates/bevy_pbr/src/atmosphere/resources.rs new file mode 100644 index 0000000000000..beebfbe4bed49 --- /dev/null +++ b/crates/bevy_pbr/src/atmosphere/resources.rs @@ -0,0 +1,333 @@ +use bevy_core_pipeline::{ + core_3d::Camera3d, fullscreen_vertex_shader::fullscreen_shader_vertex_state, +}; +use bevy_ecs::{ + component::Component, + entity::Entity, + query::With, + system::{Commands, Query, Res, ResMut, Resource}, + world::{FromWorld, World}, +}; +use bevy_render::{ + camera::ExtractedCamera, + extract_component::ComponentUniforms, + render_resource::{ + binding_types::{texture_2d, texture_storage_2d, texture_storage_3d, uniform_buffer}, + BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutEntries, + CachedComputePipelineId, CachedRenderPipelineId, ColorTargetState, ColorWrites, + ComputePipelineDescriptor, Extent3d, FragmentState, MultisampleState, PipelineCache, + PrimitiveState, RenderPipelineDescriptor, ShaderStages, StorageTextureAccess, + TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages, + }, + renderer::RenderDevice, + texture::{CachedTexture, TextureCache}, +}; + +use super::{shaders, Atmosphere}; + +#[derive(Resource)] +pub(crate) struct AtmosphereBindGroupLayouts { + pub transmittance_lut: BindGroupLayout, + pub multiscattering_lut: BindGroupLayout, + pub sky_view_lut: BindGroupLayout, + pub aerial_view_lut: BindGroupLayout, +} + +impl FromWorld for AtmosphereBindGroupLayouts { + fn from_world(world: &mut World) -> Self { + let render_device = world.resource::(); + let transmittance_lut = render_device.create_bind_group_layout( + "transmittance_lut_bind_group_layout", + &BindGroupLayoutEntries::single( + ShaderStages::FRAGMENT, + uniform_buffer::(true), + ), + ); + + let multiscattering_lut = render_device.create_bind_group_layout( + "multiscattering_lut_bind_group_layout", + &BindGroupLayoutEntries::sequential( + ShaderStages::COMPUTE, + ( + uniform_buffer::(true), + texture_storage_2d(TextureFormat::Rgba16Float, StorageTextureAccess::WriteOnly), + ), + ), + ); + + let sky_view_lut = render_device.create_bind_group_layout( + "sky_view_lut_bind_group_layout", + &BindGroupLayoutEntries::sequential( + ShaderStages::FRAGMENT, + ( + uniform_buffer::(true), + texture_2d(TextureSampleType::Float { filterable: true }), //transmittance_lut + texture_2d(TextureSampleType::Float { filterable: true }), //multiscattering_lut + ), + ), + ); + + let aerial_view_lut = render_device.create_bind_group_layout( + "aerial_view_lut_bind_group_layout", + &BindGroupLayoutEntries::sequential( + ShaderStages::COMPUTE, + ( + uniform_buffer::(true), + texture_2d(TextureSampleType::Float { filterable: true }), //transmittance_lut + texture_2d(TextureSampleType::Float { filterable: true }), //multiscattering_lut + texture_storage_3d(TextureFormat::Rgba16Float, StorageTextureAccess::WriteOnly), + ), + ), + ); + + Self { + transmittance_lut, + multiscattering_lut, + sky_view_lut, + aerial_view_lut, + } + } +} + +#[derive(Resource)] +pub(crate) struct AtmospherePipelines { + pub transmittance_lut: CachedRenderPipelineId, + pub multiscattering_lut: CachedComputePipelineId, + pub sky_view_lut: CachedRenderPipelineId, + pub aerial_view_lut: CachedComputePipelineId, +} + +impl FromWorld for AtmospherePipelines { + fn from_world(world: &mut World) -> Self { + let pipeline_cache = world.resource::(); + let layouts = world.resource::(); + + let transmittance_lut = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor { + label: Some("sky_transmittance_lut_pipeline".into()), + layout: vec![layouts.transmittance_lut.clone()], + push_constant_ranges: vec![], + vertex: fullscreen_shader_vertex_state(), + primitive: PrimitiveState::default(), + depth_stencil: None, + multisample: MultisampleState::default(), + fragment: Some(FragmentState { + shader: shaders::TRANSMITTANCE_LUT.clone(), + shader_defs: vec![], + entry_point: "main".into(), + targets: vec![Some(ColorTargetState { + format: TextureFormat::Rgba16Float, + blend: None, + write_mask: ColorWrites::ALL, + })], + }), + }); + + let multi_scattering_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(), + }); + + let sky_view_lut = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor { + label: Some("sky_transmittance_lut_pipeline".into()), + layout: vec![layouts.transmittance_lut.clone()], + push_constant_ranges: vec![], + vertex: fullscreen_shader_vertex_state(), + primitive: PrimitiveState::default(), + depth_stencil: None, + multisample: MultisampleState::default(), + fragment: Some(FragmentState { + shader: shaders::SKY_VIEW_LUT.clone(), + shader_defs: vec![], + entry_point: "main".into(), + targets: vec![Some(ColorTargetState { + format: TextureFormat::Rgba16Float, + blend: None, + write_mask: ColorWrites::ALL, + })], + }), + }); + + let aerial_view_lut = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { + label: Some("aerial_view_lut_pipeline".into()), + layout: vec![layouts.multiscattering_lut.clone()], + push_constant_ranges: vec![], + shader: shaders::AERIAL_VIEW_LUT, + shader_defs: vec![], + entry_point: "main".into(), + }); + + Self { + transmittance_lut, + multiscattering_lut: multi_scattering_lut, + sky_view_lut, + aerial_view_lut, + } + } +} + +#[derive(Component)] +pub struct AtmosphereTextures { + pub transmittance_lut: CachedTexture, + pub sky_view_lut: CachedTexture, + pub aerial_view_lut: CachedTexture, + pub multiscattering_lut: CachedTexture, +} + +pub(super) fn prepare_atmosphere_textures( + views: Query<(Entity, &ExtractedCamera), With>, + render_device: Res, + mut texture_cache: ResMut, + mut commands: Commands, +) { + for (entity, camera) in &views { + let transmittance_lut = texture_cache.get( + &render_device, + TextureDescriptor { + label: Some("transmittance_lut"), + size: Extent3d { + width: 256, + height: 64, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Rgba16Float, + usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }, + ); + + let sky_view_lut = texture_cache.get( + &render_device, + TextureDescriptor { + label: Some("sky_view_lut"), + size: Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Rg11b10Float, + usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }, + ); + + let aerial_perspective_lut = texture_cache.get( + &render_device, + TextureDescriptor { + label: Some("aerial_view_lut"), + size: Extent3d { + width: 32, + height: 32, + depth_or_array_layers: 32, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D3, + format: TextureFormat::Rgba16Float, + usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }, + ); + + let multi_scattering_lut = texture_cache.get( + &render_device, + TextureDescriptor { + label: Some("multi_scattering"), + size: Extent3d { + width: 32, + height: 32, + 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: &[], + }, + ); + + commands.entity(entity).insert({ + AtmosphereTextures { + transmittance_lut, + sky_view_lut, + aerial_view_lut: aerial_perspective_lut, + multiscattering_lut: multi_scattering_lut, + } + }); + } +} + +#[derive(Component)] +pub struct AtmosphereBindGroups { + pub transmittance_lut: BindGroup, + pub multiscattering_lut: BindGroup, + pub sky_view_lut: BindGroup, + pub aerial_view_lut: BindGroup, +} + +pub(super) fn prepare_atmosphere_bind_groups( + views: Query<(Entity, &AtmosphereTextures), (With, With)>, + render_device: Res, + layouts: Res, + uniforms: Res>, + mut commands: Commands, +) { + for (entity, textures) in &views { + let uniforms_binding = uniforms + .binding() + .expect("Failed to prepare atmosphere bind groups. Atmosphere uniforms buffer missing"); + + let transmittance_lut = render_device.create_bind_group( + "transmittance_lut_bind_group", + &layouts.transmittance_lut, + &BindGroupEntries::single(uniforms_binding.clone()), + ); + + let multiscattering_lut = render_device.create_bind_group( + "multiscattering_lut_bind_group", + &layouts.multiscattering_lut, + &BindGroupEntries::sequential(( + uniforms_binding.clone(), + &textures.multiscattering_lut.default_view, + )), + ); + + let sky_view_lut = render_device.create_bind_group( + "sky_view_lut_bind_group", + &layouts.sky_view_lut, + &BindGroupEntries::sequential(( + uniforms_binding.clone(), + &textures.transmittance_lut.default_view, + &textures.multiscattering_lut.default_view, + )), + ); + let aerial_view_lut = render_device.create_bind_group( + "sky_view_lut_bind_group", + &layouts.aerial_view_lut, + &BindGroupEntries::sequential(( + uniforms_binding.clone(), + &textures.transmittance_lut.default_view, + &textures.multiscattering_lut.default_view, + &textures.aerial_view_lut.default_view, + )), + ); + + commands.entity(entity).insert(AtmosphereBindGroups { + transmittance_lut, + multiscattering_lut, + sky_view_lut, + aerial_view_lut, + }); + } +} diff --git a/crates/bevy_pbr/src/atmosphere/types.wgsl b/crates/bevy_pbr/src/atmosphere/types.wgsl index 9a584d5c41b91..9be0ab2b9cfd5 100644 --- a/crates/bevy_pbr/src/atmosphere/types.wgsl +++ b/crates/bevy_pbr/src/atmosphere/types.wgsl @@ -2,10 +2,10 @@ struct Atmosphere { // Radius of the planet - bottom_radius: f32, + bottom_radius: f32, //units: km // Radius at which we consider the atmosphere to 'end' for out calculations (from center of planet) - top_radius: f32, + top_radius: f32, //units: km rayleigh_density_exp_scale: f32, rayleigh_scattering: vec3, @@ -18,8 +18,6 @@ struct Atmosphere { ozone_layer_center_altitude: f32, //units: km ozone_layer_half_width: f32, //units: km ozone_absorption: vec3, //ozone absorption. units: km^-1 - - ground_albedo: vec3, //note: never used even in the paper? maybe for the multiscattering LUT }; fn get_atmosphere_parameters() -> Atmosphere { diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index cc11c5a218c13..488e9a68de2a0 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -101,7 +101,7 @@ pub mod graph { } } -use crate::{deferred::DeferredPbrLightingPlugin, graph::NodePbr}; +use crate::{atmosphere::AtmospherePlugin, deferred::DeferredPbrLightingPlugin, graph::NodePbr}; use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, AssetApp, Assets, Handle}; use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d}; @@ -333,6 +333,7 @@ impl Plugin for PbrPlugin { VolumetricFogPlugin, ScreenSpaceReflectionsPlugin, )) + .add_plugins(AtmospherePlugin) .configure_sets( PostUpdate, ( diff --git a/crates/bevy_render/src/render_resource/bind_group_layout_entries.rs b/crates/bevy_render/src/render_resource/bind_group_layout_entries.rs index 58d1d62a19889..cad8f9a66016b 100644 --- a/crates/bevy_render/src/render_resource/bind_group_layout_entries.rs +++ b/crates/bevy_render/src/render_resource/bind_group_layout_entries.rs @@ -548,4 +548,16 @@ pub mod binding_types { } .into_bind_group_layout_entry_builder() } + + pub fn texture_storage_3d( + format: TextureFormat, + access: StorageTextureAccess, + ) -> BindGroupLayoutEntryBuilder { + BindingType::StorageTexture { + access, + format, + view_dimension: TextureViewDimension::D3, + } + .into_bind_group_layout_entry_builder() + } } From 3fa0980a58f249ef7fbb3f1fe4a7ceb7a567ee60 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:18:24 -0700 Subject: [PATCH 04/43] use buffer binding --- crates/bevy_pbr/src/atmosphere/bindings.wgsl | 8 ----- .../src/atmosphere/transmittance_lut.wgsl | 13 ++++--- crates/bevy_pbr/src/atmosphere/types.wgsl | 34 ------------------- 3 files changed, 6 insertions(+), 49 deletions(-) delete mode 100644 crates/bevy_pbr/src/atmosphere/bindings.wgsl diff --git a/crates/bevy_pbr/src/atmosphere/bindings.wgsl b/crates/bevy_pbr/src/atmosphere/bindings.wgsl deleted file mode 100644 index f0fcd557182f6..0000000000000 --- a/crates/bevy_pbr/src/atmosphere/bindings.wgsl +++ /dev/null @@ -1,8 +0,0 @@ -#define_import_path bevy_pbr::atmosphere::bindings - -#import bevy_pbr::mesh_view_types::Lights -#import bevy_render::{view::View, globals::Globals} - - -//TODO: set max directional lights in Lights array, and max cascades per directional light -@group(0) @binding(0) var atmosphere: Atmosphere; diff --git a/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl b/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl index 9d9dbae7fafe1..dc241d2d0f867 100644 --- a/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl +++ b/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl @@ -1,16 +1,15 @@ -#import bevy_pbr::{ - atmosphere::{ - types::{Atmosphere, get_atmosphere_parameters}, - common::{uv_to_r_mu, distance_to_top_atmosphere_boundary, distance_to_bottom_atmosphere_boundary} - } +#import bevy_pbr::atmosphere::{ + types::Atmosphere, + common::{uv_to_r_mu, distance_to_top_atmosphere_boundary, distance_to_bottom_atmosphere_boundary} } + #import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput +@group(0) @binding(0) var atmosphere: Atmosphere; + @fragment fn main(in: FullscreenVertexOutput) -> @location(0) vec4 { - let atmosphere = get_atmosphere_parameters(); - // map UV coordinates to view height (r) and zenith cos angle (mu) let r_mu = uv_to_r_mu(atmosphere, in.uv); diff --git a/crates/bevy_pbr/src/atmosphere/types.wgsl b/crates/bevy_pbr/src/atmosphere/types.wgsl index 9be0ab2b9cfd5..824238af5cf8a 100644 --- a/crates/bevy_pbr/src/atmosphere/types.wgsl +++ b/crates/bevy_pbr/src/atmosphere/types.wgsl @@ -19,37 +19,3 @@ struct Atmosphere { ozone_layer_half_width: f32, //units: km ozone_absorption: vec3, //ozone absorption. units: km^-1 }; - -fn get_atmosphere_parameters() -> Atmosphere { - var atmosphere: Atmosphere; - atmosphere.bottom_radius = 6360.0; //units?????? - atmosphere.top_radius = atmosphere.bottom_radius + 100.0; //okay probably km - - // Rayleigh scattering - let earth_rayleigh_scale_height = 8.0; - - atmosphere.rayleigh_density_exp_scale = -1.0f / earth_rayleigh_scale_height; - atmosphere.rayleigh_scattering = vec3(0.005802, 0.013558, 0.033100); - - // Mie scattering - let earth_mie_scale_height = 1.2; - - atmosphere.mie_density_exp_scale = -1.0f / earth_mie_scale_height; - atmosphere.mie_scattering = vec3(0.003996, 0.003996, 0.003996); - atmosphere.mie_extinction = vec3(0.004440, 0.004440, 0.004440); - atmosphere.mie_absorption = max(vec3(0.0), atmosphere.mie_extinction - atmosphere.mie_scattering); - atmosphere.mie_phase_function_g = 0.8; - - // Ozone absorption - atmosphere.ozone_density_layer_0_width = 25.0; - atmosphere.ozone_density_layer_0_constant_term = -2.0 / 3.0; - atmosphere.ozone_density_layer_0_linear_term = 1.0 / 15.0; - atmosphere.ozone_density_layer_1_constant_term = 8.0 / 3.0; - atmosphere.ozone_density_layer_1_linear_term = -1.0 / 15.0; - - atmosphere.absorption_extinction = vec3(0.000650, 0.001881, 0.000085); - - atmosphere.ground_albedo = vec3(0.3f); - - return atmosphere; -} From d5c58f0d99020e2adbcc5668643b8a8863d71df9 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Mon, 9 Sep 2024 12:10:55 -0700 Subject: [PATCH 05/43] WIP + example --- Cargo.toml | 143 ++++++++++-------- .../src/atmosphere/aerial_view_lut.wgsl | 12 ++ crates/bevy_pbr/src/atmosphere/common.wgsl | 1 + crates/bevy_pbr/src/atmosphere/mod.rs | 105 +++++++++++-- .../src/atmosphere/multiscattering_lut.wgsl | 29 ++++ crates/bevy_pbr/src/atmosphere/node.rs | 62 +++++--- crates/bevy_pbr/src/atmosphere/resources.rs | 66 +++----- .../bevy_pbr/src/atmosphere/sky_view_lut.wgsl | 10 ++ .../src/atmosphere/transmittance_lut.wgsl | 12 +- crates/bevy_pbr/src/lib.rs | 3 +- examples/3d/atmosphere.rs | 107 +++++++++++++ 11 files changed, 400 insertions(+), 150 deletions(-) create mode 100644 examples/3d/atmosphere.rs diff --git a/Cargo.toml b/Cargo.toml index 3708e1d45bd9d..10c45520d5191 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,20 +14,20 @@ rust-version = "1.79.0" [workspace] exclude = [ - "benches", - "crates/bevy_derive/compile_fail", - "crates/bevy_ecs/compile_fail", - "crates/bevy_reflect/compile_fail", - "tools/compile_fail_utils", + "benches", + "crates/bevy_derive/compile_fail", + "crates/bevy_ecs/compile_fail", + "crates/bevy_reflect/compile_fail", + "tools/compile_fail_utils", ] members = [ - "crates/*", - "examples/mobile", - "tools/ci", - "tools/build-templated-pages", - "tools/build-wasm-example", - "tools/example-showcase", - "errors", + "crates/*", + "examples/mobile", + "tools/ci", + "tools/build-templated-pages", + "tools/build-wasm-example", + "tools/example-showcase", + "errors", ] [workspace.lints.clippy] @@ -57,34 +57,34 @@ workspace = true [features] default = [ - "animation", - "bevy_asset", - "bevy_state", - "bevy_audio", - "bevy_color", - "bevy_gilrs", - "bevy_scene", - "bevy_winit", - "bevy_core_pipeline", - "bevy_pbr", - "bevy_picking", - "bevy_gltf", - "bevy_render", - "bevy_sprite", - "bevy_text", - "bevy_ui", - "multi_threaded", - "png", - "hdr", - "vorbis", - "x11", - "bevy_gizmos", - "android_shared_stdcxx", - "tonemapping_luts", - "smaa_luts", - "default_font", - "webgl2", - "sysinfo_plugin", + "animation", + "bevy_asset", + "bevy_state", + "bevy_audio", + "bevy_color", + "bevy_gilrs", + "bevy_scene", + "bevy_winit", + "bevy_core_pipeline", + "bevy_pbr", + "bevy_picking", + "bevy_gltf", + "bevy_render", + "bevy_sprite", + "bevy_text", + "bevy_ui", + "multi_threaded", + "png", + "hdr", + "vorbis", + "x11", + "bevy_gizmos", + "android_shared_stdcxx", + "tonemapping_luts", + "smaa_luts", + "default_font", + "webgl2", + "sysinfo_plugin", ] # Force dynamic linking, which improves iterative compile times @@ -107,9 +107,9 @@ bevy_color = ["bevy_internal/bevy_color"] # Provides cameras and other basic render pipeline features bevy_core_pipeline = [ - "bevy_internal/bevy_core_pipeline", - "bevy_asset", - "bevy_render", + "bevy_internal/bevy_core_pipeline", + "bevy_asset", + "bevy_render", ] # Adds gamepad support @@ -120,10 +120,10 @@ bevy_gltf = ["bevy_internal/bevy_gltf", "bevy_asset", "bevy_scene", "bevy_pbr"] # Adds PBR rendering bevy_pbr = [ - "bevy_internal/bevy_pbr", - "bevy_asset", - "bevy_render", - "bevy_core_pipeline", + "bevy_internal/bevy_pbr", + "bevy_asset", + "bevy_render", + "bevy_core_pipeline", ] # Provides picking functionality @@ -137,10 +137,10 @@ bevy_scene = ["bevy_internal/bevy_scene", "bevy_asset"] # Provides sprite functionality bevy_sprite = [ - "bevy_internal/bevy_sprite", - "bevy_render", - "bevy_core_pipeline", - "bevy_color", + "bevy_internal/bevy_sprite", + "bevy_render", + "bevy_core_pipeline", + "bevy_color", ] # Provides text functionality @@ -148,11 +148,11 @@ bevy_text = ["bevy_internal/bevy_text", "bevy_asset", "bevy_sprite"] # A custom ECS-driven UI framework bevy_ui = [ - "bevy_internal/bevy_ui", - "bevy_core_pipeline", - "bevy_text", - "bevy_sprite", - "bevy_color", + "bevy_internal/bevy_ui", + "bevy_core_pipeline", + "bevy_text", + "bevy_sprite", + "bevy_color", ] # winit window and input backend @@ -172,9 +172,9 @@ trace_tracy = ["trace", "bevy_internal/trace_tracy"] # Tracing support, with memory profiling, exposing a port for Tracy trace_tracy_memory = [ - "trace", - "bevy_internal/trace_tracy", - "bevy_internal/trace_tracy_memory", + "trace", + "bevy_internal/trace_tracy", + "bevy_internal/trace_tracy_memory", ] # Tracing support @@ -308,7 +308,7 @@ pbr_transmission_textures = ["bevy_internal/pbr_transmission_textures"] # Enable support for multi-layer material textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs pbr_multi_layer_material_textures = [ - "bevy_internal/pbr_multi_layer_material_textures", + "bevy_internal/pbr_multi_layer_material_textures", ] # Enable support for anisotropy texture in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs @@ -717,6 +717,17 @@ description = "A scene showcasing the atmospheric fog effect" category = "3D Rendering" wasm = true +[[example]] +name = "atmosphere" +path = "examples/3d/atmosphere.rs" +doc-scrape-examples = true + +[package.metadata.example.atmosphere] +name = "Atmosphere" +description = "A scene showcasing pbr atmospheric scattering" +category = "3D Rendering" +wasm = true + [[example]] name = "fog" path = "examples/3d/fog.rs" @@ -1071,12 +1082,12 @@ description = "Meshlet rendering for dense high-poly scenes (experimental)" category = "3D Rendering" wasm = false setup = [ - [ - "curl", - "-o", - "assets/models/bunny.meshlet_mesh", - "https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/b6c712cfc87c65de419f856845401aba336a7bcd/bunny.meshlet_mesh", - ], + [ + "curl", + "-o", + "assets/models/bunny.meshlet_mesh", + "https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/b6c712cfc87c65de419f856845401aba336a7bcd/bunny.meshlet_mesh", + ], ] [[example]] diff --git a/crates/bevy_pbr/src/atmosphere/aerial_view_lut.wgsl b/crates/bevy_pbr/src/atmosphere/aerial_view_lut.wgsl index e69de29bb2d1d..c5f256935216b 100644 --- a/crates/bevy_pbr/src/atmosphere/aerial_view_lut.wgsl +++ b/crates/bevy_pbr/src/atmosphere/aerial_view_lut.wgsl @@ -0,0 +1,12 @@ +#import bevy_pbr::atmosphere::types::Atmosphere; + +@group(0) @binding(0) var atmosphere: Atmosphere; +@group(0) @binding(1) var transmittance_lut: texture_2d; +@group(0) @binding(2) var multiscattering_lut: texture_2d; +@group(0) @binding(3) var aerial_view_lut: texture_storage_3d; + +@compute +@workgroup_size(4, 4, 4) +fn main(@builtin(global_invocation_id) idx: vec3) { +} + diff --git a/crates/bevy_pbr/src/atmosphere/common.wgsl b/crates/bevy_pbr/src/atmosphere/common.wgsl index ebc242766fd6b..cf9f6b3aa01f6 100644 --- a/crates/bevy_pbr/src/atmosphere/common.wgsl +++ b/crates/bevy_pbr/src/atmosphere/common.wgsl @@ -77,3 +77,4 @@ fn distance_to_bottom_atmosphere_boundary(atmosphere: Atmosphere, r: f32, mu: f3 return max(-r * mu - sqrt(positive_discriminant), 0.0); } + diff --git a/crates/bevy_pbr/src/atmosphere/mod.rs b/crates/bevy_pbr/src/atmosphere/mod.rs index dadabba046e4a..96b0d1bc3893c 100644 --- a/crates/bevy_pbr/src/atmosphere/mod.rs +++ b/crates/bevy_pbr/src/atmosphere/mod.rs @@ -7,16 +7,16 @@ use bevy_core_pipeline::core_3d::graph::Node3d; use bevy_ecs::{ component::Component, entity::Entity, - query::With, + query::{With, Without}, schedule::IntoSystemConfigs, system::{Commands, Query}, }; -use bevy_math::Vec3; +use bevy_math::{Vec2, Vec3}; use bevy_reflect::Reflect; use bevy_render::{ camera::Camera, render_graph::{RenderGraphApp, ViewNodeRunner}, - render_resource::{Shader, TextureFormat, TextureUsages}, + render_resource::{Extent3d, Shader, TextureFormat, TextureUsages}, renderer::RenderAdapter, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; @@ -85,7 +85,8 @@ impl Plugin for AtmospherePlugin { Shader::from_wgsl ); - app.register_type::(); + app.register_type::() + .add_plugins(UniformComponentPlugin::::default()); } fn finish(&self, app: &mut App) { @@ -107,8 +108,7 @@ impl Plugin for AtmospherePlugin { render_app .init_resource::() .init_resource::() - .add_systems(ExtractSchedule, extract_sky_settings) - .add_plugins(UniformComponentPlugin::::default()) + .add_systems(ExtractSchedule, extract_atmosphere) .add_systems( Render, ( @@ -130,7 +130,7 @@ impl Plugin for AtmospherePlugin { } //TODO: padding/alignment? -#[derive(Clone, Component, Default, Reflect, ShaderType)] +#[derive(Clone, Component, Reflect, ShaderType)] pub struct Atmosphere { /// Radius of the planet /// @@ -153,13 +153,96 @@ pub struct Atmosphere { ozone_absorption: Vec3, //ozone absorption. units: km^-1 } -fn extract_sky_settings( +impl Default for Atmosphere { + fn default() -> Self { + Self::EARTH + } +} + +impl Atmosphere { + //TODO: check all these values before merge + pub const EARTH: Atmosphere = Atmosphere { + bottom_radius: 6360.0, + top_radius: 6460.0, + rayleigh_density_exp_scale: -1.0 / 8.0, + rayleigh_scattering: Vec3::new(0.005802, 0.013558, 0.033100), + mie_density_exp_scale: -1.0 / 1.2, + mie_scattering: 0.03996, + mie_absorption: 0.000444, + mie_phase_function_g: 0.8, + ozone_layer_center_altitude: 25.0, + ozone_layer_half_width: 15.0, + ozone_absorption: Vec3::new(0.000650, 0.001881, 0.000085), + }; +} + +fn extract_atmosphere( mut commands: Commands, - cameras: Extract>>, + cameras: Extract< + Query<(Entity, &Camera, &Atmosphere, Option<&AtmosphereLutSettings>), With>, + >, ) { - for (entity, camera, sky_settings) in &cameras { + for (entity, camera, atmosphere, lut_settings) in &cameras { if camera.is_active { - commands.get_or_spawn(entity).insert(sky_settings.clone()); + commands.get_or_spawn(entity).insert(( + atmosphere.clone(), + lut_settings + .cloned() + .unwrap_or_else(|| AtmosphereLutSettings::from_camera(camera)), + )); + } + } +} + +#[derive(Clone, Component)] +pub struct AtmosphereLutSettings { + pub transmittance_lut_size: Extent3d, + pub multiscattering_lut_size: Extent3d, + pub sky_view_lut_size: Extent3d, + pub aerial_view_lut_size: Extent3d, +} + +impl Default for AtmosphereLutSettings { + fn default() -> Self { + Self { + transmittance_lut_size: Extent3d { + width: 256, + height: 128, + depth_or_array_layers: 1, + }, + multiscattering_lut_size: Extent3d { + width: 32, + height: 32, + depth_or_array_layers: 1, + }, + sky_view_lut_size: Extent3d { + width: 192, + height: 108, + depth_or_array_layers: 1, + }, + aerial_view_lut_size: Extent3d { + width: 32, + height: 32, + depth_or_array_layers: 32, + }, + } + } +} + +impl AtmosphereLutSettings { + pub fn from_camera(camera: &Camera) -> Self { + //TODO: correct method? + if let Some(viewport_size) = camera.logical_viewport_size() { + Self { + sky_view_lut_size: Extent3d { + width: viewport_size.x as u32 / 10, + height: viewport_size.y as u32 / 10, + depth_or_array_layers: 1, + }, + ..Self::default() + } + } else { + Self::default() } } } diff --git a/crates/bevy_pbr/src/atmosphere/multiscattering_lut.wgsl b/crates/bevy_pbr/src/atmosphere/multiscattering_lut.wgsl index e69de29bb2d1d..c1ef30a111ee9 100644 --- a/crates/bevy_pbr/src/atmosphere/multiscattering_lut.wgsl +++ b/crates/bevy_pbr/src/atmosphere/multiscattering_lut.wgsl @@ -0,0 +1,29 @@ +#import bevy_pbr::atmosphere::types::Atmosphere; + +@group(0) @binding(0) var atmosphere: Atmosphere; +@group(0) @binding(1) var multiscattering_lut: texture_storage_2d; + +fn s2_sequence(n: u32) -> vec2 { +// const phi_2 = vec2(1.3247179572447460259609088, 1.7548776662466927600495087); +// fract(0.5 + phi_2 * n); + return vec2(0.0, 0.0); +} + +//Lambert equal-area projection. +fn map_to_sphere(uv: vec2) -> vec3 { + return vec3(0.0, 0.0, 0.0); //TODO +} + +const SPHERE_SAMPLES: u32 = 64u; +const STEPS: u32 = 20u; + +@compute +@workgroup_size(16, 16, 1) +fn main(@builtin(global_invocation_id) global_id: vec3) { + //for (let sphere_sample_index: u32 = 0u; sphere_sample_index < SPHERE_SAMPLES; sphere_sample_index++) { + // let dir = map_to_sphere(s2_sequence(sphere_sample_index)); + // + // for (let step_index: u32 = 0u; step_index < STEPS; step_index++) { + // } + //} +} diff --git a/crates/bevy_pbr/src/atmosphere/node.rs b/crates/bevy_pbr/src/atmosphere/node.rs index 9d77f341ba213..d7f4a9c76a04e 100644 --- a/crates/bevy_pbr/src/atmosphere/node.rs +++ b/crates/bevy_pbr/src/atmosphere/node.rs @@ -1,6 +1,5 @@ use bevy_ecs::{query::QueryItem, system::lifetimeless::Read, world::World}; use bevy_render::{ - camera::ExtractedCamera, extract_component::DynamicUniformIndex, render_graph::{NodeRunError, RenderGraphContext, RenderLabel, ViewNode}, render_resource::{ @@ -8,14 +7,11 @@ use bevy_render::{ RenderPassDescriptor, }, renderer::RenderContext, - view::ViewUniformOffset, }; -use crate::MeshViewBindGroup; - use super::{ resources::{AtmosphereBindGroups, AtmospherePipelines, AtmosphereTextures}, - Atmosphere, + Atmosphere, AtmosphereLutSettings, }; #[derive(PartialEq, Eq, Debug, Copy, Clone, Hash, RenderLabel)] @@ -27,23 +23,18 @@ pub(super) struct SkyNode {} impl ViewNode for SkyNode { type ViewQuery = ( Read, + Read, Read, - Read, - Read, Read>, ); fn run( &self, - graph: &mut RenderGraphContext, + _graph: &mut RenderGraphContext, render_context: &mut RenderContext, - ( - textures, - bind_groups, - view_uniform_offset, - mesh_view_bind_group, - atmosphere_uniform_offset, - ): QueryItem, + (textures, lut_settings, bind_groups, atmosphere_uniform_offset): QueryItem< + Self::ViewQuery, + >, world: &World, ) -> Result<(), NodeRunError> { let pipelines = world.resource::(); @@ -64,9 +55,9 @@ impl ViewNode for SkyNode { return Ok(()); }; - let mut commands = render_context.command_encoder(); + let commands = render_context.command_encoder(); - commands.push_debug_group("sky"); + commands.push_debug_group("atmosphere_luts"); { let mut transmittance_lut_pass = commands.begin_render_pass(&RenderPassDescriptor { @@ -88,6 +79,7 @@ impl ViewNode for SkyNode { transmittance_lut_pass.draw(0..3, 0..1); } + //todo: use fragment shader here? maybe shared memory would be nice though { let mut multiscattering_lut_pass = commands.begin_compute_pass(&ComputePassDescriptor { @@ -100,21 +92,32 @@ impl ViewNode for SkyNode { &bind_groups.multiscattering_lut, &[atmosphere_uniform_offset.index()], ); - multiscattering_lut_pass.dispatch_workgroups(todo!(), todo!(), todo!()); + + const MULTISCATTERING_WORKGROUP_SIZE: u32 = 16; + let workgroups_x = lut_settings + .multiscattering_lut_size + .width + .div_ceil(MULTISCATTERING_WORKGROUP_SIZE); + let workgroups_y = lut_settings + .multiscattering_lut_size + .height + .div_ceil(MULTISCATTERING_WORKGROUP_SIZE); + + multiscattering_lut_pass.dispatch_workgroups(workgroups_x, workgroups_y, 1); } { let mut sky_view_lut_pass = commands.begin_render_pass(&RenderPassDescriptor { label: Some("transmittance_lut_pass"), color_attachments: &[Some(RenderPassColorAttachment { - view: &textures.transmittance_lut.default_view, + view: &textures.sky_view_lut.default_view, resolve_target: None, ops: Operations::default(), })], depth_stencil_attachment: None, ..Default::default() }); - sky_view_lut_pass.set_pipeline(transmittance_lut_pipeline); //TODO: MESH VIEW BIND GROUP + sky_view_lut_pass.set_pipeline(sky_view_lut_pipeline); sky_view_lut_pass.set_bind_group( 0, &bind_groups.sky_view_lut, @@ -128,13 +131,28 @@ impl ViewNode for SkyNode { label: Some("multiscatttering_lut_pass"), timestamp_writes: None, }); - aerial_view_lut_pass.set_pipeline(multiscattering_lut_pipeline); + aerial_view_lut_pass.set_pipeline(aerial_view_lut_pipeline); aerial_view_lut_pass.set_bind_group( 0, &bind_groups.aerial_view_lut, &[atmosphere_uniform_offset.index()], ); - aerial_view_lut_pass.dispatch_workgroups(todo!(), todo!(), todo!()); + + const AERIAL_VIEW_WORKGROUP_SIZE: u32 = 4; + let workgroups_x = lut_settings + .aerial_view_lut_size + .width + .div_ceil(AERIAL_VIEW_WORKGROUP_SIZE); + let workgroups_y = lut_settings + .aerial_view_lut_size + .height + .div_ceil(AERIAL_VIEW_WORKGROUP_SIZE); + let workgroups_z = lut_settings + .aerial_view_lut_size + .depth_or_array_layers + .div_ceil(AERIAL_VIEW_WORKGROUP_SIZE); + + aerial_view_lut_pass.dispatch_workgroups(workgroups_x, workgroups_y, workgroups_z); } render_context.command_encoder().pop_debug_group(); diff --git a/crates/bevy_pbr/src/atmosphere/resources.rs b/crates/bevy_pbr/src/atmosphere/resources.rs index beebfbe4bed49..6528f03d4dfa0 100644 --- a/crates/bevy_pbr/src/atmosphere/resources.rs +++ b/crates/bevy_pbr/src/atmosphere/resources.rs @@ -23,7 +23,7 @@ use bevy_render::{ texture::{CachedTexture, TextureCache}, }; -use super::{shaders, Atmosphere}; +use super::{shaders, Atmosphere, AtmosphereLutSettings}; #[derive(Resource)] pub(crate) struct AtmosphereBindGroupLayouts { @@ -134,7 +134,7 @@ impl FromWorld for AtmospherePipelines { let sky_view_lut = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor { label: Some("sky_transmittance_lut_pipeline".into()), - layout: vec![layouts.transmittance_lut.clone()], + layout: vec![layouts.sky_view_lut.clone()], push_constant_ranges: vec![], vertex: fullscreen_shader_vertex_state(), primitive: PrimitiveState::default(), @@ -154,7 +154,7 @@ impl FromWorld for AtmospherePipelines { let aerial_view_lut = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { label: Some("aerial_view_lut_pipeline".into()), - layout: vec![layouts.multiscattering_lut.clone()], + layout: vec![layouts.aerial_view_lut.clone()], push_constant_ranges: vec![], shader: shaders::AERIAL_VIEW_LUT, shader_defs: vec![], @@ -173,27 +173,23 @@ impl FromWorld for AtmospherePipelines { #[derive(Component)] pub struct AtmosphereTextures { pub transmittance_lut: CachedTexture, + pub multiscattering_lut: CachedTexture, pub sky_view_lut: CachedTexture, pub aerial_view_lut: CachedTexture, - pub multiscattering_lut: CachedTexture, } pub(super) fn prepare_atmosphere_textures( - views: Query<(Entity, &ExtractedCamera), With>, + views: Query<(Entity, &AtmosphereLutSettings), With>, render_device: Res, mut texture_cache: ResMut, mut commands: Commands, ) { - for (entity, camera) in &views { + for (entity, lut_settings) in &views { let transmittance_lut = texture_cache.get( &render_device, TextureDescriptor { label: Some("transmittance_lut"), - size: Extent3d { - width: 256, - height: 64, - depth_or_array_layers: 1, - }, + size: lut_settings.transmittance_lut_size, mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, @@ -203,54 +199,42 @@ pub(super) fn prepare_atmosphere_textures( }, ); - let sky_view_lut = texture_cache.get( + let multiscattering_lut = texture_cache.get( &render_device, TextureDescriptor { - label: Some("sky_view_lut"), - size: Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }, + label: Some("multiscattering_lut"), + size: lut_settings.multiscattering_lut_size, mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, - format: TextureFormat::Rg11b10Float, - usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, + format: TextureFormat::Rgba16Float, + usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, view_formats: &[], }, ); - let aerial_perspective_lut = texture_cache.get( + let sky_view_lut = texture_cache.get( &render_device, TextureDescriptor { - label: Some("aerial_view_lut"), - size: Extent3d { - width: 32, - height: 32, - depth_or_array_layers: 32, - }, + label: Some("sky_view_lut"), + size: lut_settings.sky_view_lut_size, mip_level_count: 1, sample_count: 1, - dimension: TextureDimension::D3, - format: TextureFormat::Rgba16Float, - usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, + dimension: TextureDimension::D2, + format: TextureFormat::Rgba16Float, //TODO: check if needs hdr + usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, view_formats: &[], }, ); - let multi_scattering_lut = texture_cache.get( + let aerial_view_lut = texture_cache.get( &render_device, TextureDescriptor { - label: Some("multi_scattering"), - size: Extent3d { - width: 32, - height: 32, - depth_or_array_layers: 1, - }, + label: Some("aerial_view_lut"), + size: lut_settings.aerial_view_lut_size, mip_level_count: 1, sample_count: 1, - dimension: TextureDimension::D2, + dimension: TextureDimension::D3, format: TextureFormat::Rgba16Float, usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, view_formats: &[], @@ -260,16 +244,16 @@ pub(super) fn prepare_atmosphere_textures( commands.entity(entity).insert({ AtmosphereTextures { transmittance_lut, + multiscattering_lut, sky_view_lut, - aerial_view_lut: aerial_perspective_lut, - multiscattering_lut: multi_scattering_lut, + aerial_view_lut, } }); } } #[derive(Component)] -pub struct AtmosphereBindGroups { +pub(crate) struct AtmosphereBindGroups { pub transmittance_lut: BindGroup, pub multiscattering_lut: BindGroup, pub sky_view_lut: BindGroup, diff --git a/crates/bevy_pbr/src/atmosphere/sky_view_lut.wgsl b/crates/bevy_pbr/src/atmosphere/sky_view_lut.wgsl index e69de29bb2d1d..c9a9da8db6412 100644 --- a/crates/bevy_pbr/src/atmosphere/sky_view_lut.wgsl +++ b/crates/bevy_pbr/src/atmosphere/sky_view_lut.wgsl @@ -0,0 +1,10 @@ +#import bevy_pbr::atmosphere::types::Atmosphere; + +@group(0) @binding(0) var atmosphere: Atmosphere; +@group(0) @binding(1) var transmittance_lut: texture_2d; +@group(0) @binding(2) var multiscattering_lut: texture_2d; + +@fragment +fn main() -> @location(0) vec4 { + return vec4(0.0, 0.0, 0.0, 0.0); +} diff --git a/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl b/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl index dc241d2d0f867..6fbd239410b76 100644 --- a/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl +++ b/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl @@ -64,24 +64,18 @@ fn sample_atmosphere(atmosphere: Atmosphere, view_height: f32) -> AtmosphereSamp // atmosphere values at view_height let mie_density = exp(atmosphere.mie_density_exp_scale * view_height); let rayleigh_density = exp(atmosphere.rayleigh_density_exp_scale * view_height); - var ozone_density: f32; - if view_height < atmosphere.ozone_density_layer_0_width { - ozone_density = atmosphere.ozone_density_layer_0_linear_term * view_height + atmosphere.ozone_density_layer_0_constant_term; - } else { - ozone_density = atmosphere.ozone_density_layer_1_linear_term * view_height + atmosphere.ozone_density_layer_1_constant_term; - } - ozone_density = saturate(ozone_density); + var ozone_density: f32 = max(0.0, 1.0 - (abs(view_height - atmosphere.ozone_layer_center_altitude) / atmosphere.ozone_layer_half_width)); let mie_scattering = mie_density * atmosphere.mie_scattering; let mie_absorption = mie_density * atmosphere.mie_absorption; - let mie_extinction = mie_density * atmosphere.mie_extinction; + let mie_extinction = mie_scattering + mie_absorption; let rayleigh_scattering = rayleigh_density * atmosphere.rayleigh_scattering; // no rayleigh absorption // rayleigh extinction is the sum of scattering and absorption // ozone doesn't contribute to scattering - let ozone_absorption = ozone_density * atmosphere.absorption_extinction; + let ozone_absorption = ozone_density * atmosphere.ozone_absorption; // ozone extinction is the sum of scattering and absorption result.scattering = mie_scattering + rayleigh_scattering; diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 488e9a68de2a0..697a0bc23dd72 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -23,7 +23,7 @@ pub mod experimental { } } -pub mod atmosphere; +mod atmosphere; mod bundle; mod cluster; pub mod deferred; @@ -44,6 +44,7 @@ mod volumetric_fog; use bevy_color::{Color, LinearRgba}; use std::marker::PhantomData; +pub use atmosphere::*; pub use bundle::*; pub use cluster::*; pub use extended_material::*; diff --git a/examples/3d/atmosphere.rs b/examples/3d/atmosphere.rs new file mode 100644 index 0000000000000..f62236ce89563 --- /dev/null +++ b/examples/3d/atmosphere.rs @@ -0,0 +1,107 @@ +//! This example showcases pbr atmospheric scattering +//! +//! ## Controls +//! +//! | Key Binding | Action | +//! |:-------------------|:---------------------------------------| +//! | `Spacebar` | Toggle Atmospheric Fog | +//! | `S` | Toggle Directional Light Fog Influence | + +use std::f32::consts::PI; + +use bevy::{ + pbr::{Atmosphere, CascadeShadowConfigBuilder}, + prelude::*, +}; +use bevy_internal::color::palettes; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems( + Startup, + (setup_camera_fog, setup_terrain_scene, setup_instructions), + ) + .add_systems(Update, rotate_sun) + .run(); +} + +fn setup_camera_fog(mut commands: Commands) { + commands.spawn(( + Camera3dBundle { + transform: Transform::from_xyz(-1.2, 0.15, 0.0).looking_at(Vec3::Y * 0.1, Vec3::Y), + ..default() + }, + Atmosphere::EARTH, + )); +} + +fn setup_terrain_scene(mut commands: Commands, asset_server: Res) { + // Configure a properly scaled cascade shadow map for this scene (defaults are too large, mesh units are in km) + let cascade_shadow_config = CascadeShadowConfigBuilder { + first_cascade_far_bound: 0.3, + maximum_distance: 3.0, + ..default() + } + .build(); + + // Sun + commands.spawn(DirectionalLightBundle { + directional_light: DirectionalLight { + color: Color::srgb(0.98, 0.95, 0.82), + shadows_enabled: true, + ..default() + }, + transform: Transform::from_xyz(1.0, -1.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), + cascade_shadow_config, + ..default() + }); + + // Terrain + commands.spawn(SceneBundle { + scene: asset_server + .load(GltfAssetLabel::Scene(0).from_asset("models/terrain/Mountains.gltf")), + ..default() + }); +} + +//TODO: update this +fn setup_instructions(mut commands: Commands) { + commands.spawn( + TextBundle::from_section( + "Press Spacebar to Toggle Atmospheric Fog.\nPress S to Toggle Directional Light Fog Influence.", + TextStyle::default(), + ) + .with_style(Style { + position_type: PositionType::Absolute, + bottom: Val::Px(12.0), + left: Val::Px(12.0), + ..default() + }), + ); +} + +// fn toggle_system(keycode: Res>, mut fog: Query<&mut FogSettings>) { +// let mut fog_settings = fog.single_mut(); +// +// if keycode.just_pressed(KeyCode::Space) { +// let a = fog_settings.color.alpha(); +// fog_settings.color.set_alpha(1.0 - a); +// } +// +// if keycode.just_pressed(KeyCode::KeyS) { +// let a = fog_settings.directional_light_color.alpha(); +// fog_settings.directional_light_color.set_alpha(0.5 - a); +// } +// } + +fn rotate_sun( + mut sun: Query<&mut Transform, With>, + time: Res