Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified assets/models/terrain/terrain.glb
Binary file not shown.
16 changes: 8 additions & 8 deletions crates/bevy_pbr/src/atmosphere/bruneton_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,19 @@
// 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 transmittance_lut_r_mu_to_uv(r: f32, mu: f32) -> vec2<f32> {
fn transmittance_lut_r_mu_to_uv(atm: Atmosphere, r: f32, mu: f32) -> vec2<f32> {
// 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(atm.top_radius * atm.top_radius - atm.bottom_radius * atm.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 - atm.bottom_radius * atm.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(r, mu);
let d = distance_to_top_atmosphere_boundary(atm, 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_min = atm.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);
Expand Down Expand Up @@ -121,9 +121,9 @@ fn transmittance_lut_uv_to_r_mu(uv: vec2<f32>) -> vec2<f32> {
/// 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(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);
fn distance_to_top_atmosphere_boundary(atm: Atmosphere, r: f32, mu: f32) -> f32 {
// ignore the case where r > atm.top_radius
let positive_discriminant = max(r * r * (mu * mu - 1.0) + atm.top_radius * atm.top_radius, 0.0);
return max(-r * mu + sqrt(positive_discriminant), 0.0);
}

Expand Down
28 changes: 26 additions & 2 deletions crates/bevy_pbr/src/atmosphere/functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ fn sky_view_lut_uv_to_zenith_azimuth(r: f32, uv: vec2<f32>) -> vec2<f32> {
// LUT SAMPLING

fn sample_transmittance_lut(r: f32, mu: f32) -> vec3<f32> {
let uv = transmittance_lut_r_mu_to_uv(r, mu);
let uv = transmittance_lut_r_mu_to_uv(atmosphere, r, mu);
return textureSampleLevel(transmittance_lut, transmittance_lut_sampler, uv, 0.0).rgb;
}

Expand Down Expand Up @@ -300,10 +300,34 @@ fn sample_sun_radiance(ray_dir_ws: vec3<f32>) -> vec3<f32> {
return sun_radiance;
}

fn calculate_visible_sun_ratio(atmosphere: Atmosphere, r: f32, mu: f32, sun_angular_size: f32) -> f32 {
let bottom_radius = atmosphere.bottom_radius;
// Calculate the angle between horizon and sun center
// Invert the horizon angle calculation to fix shading direction
let horizon_cos = -sqrt(1.0 - (bottom_radius * bottom_radius) / (r * r));
let horizon_angle = acos(horizon_cos);
let sun_zenith_angle = acos(mu);

// If sun is completely above horizon
if sun_zenith_angle + sun_angular_size * 0.5 <= horizon_angle {
return 1.0;
}

// If sun is completely below horizon
if sun_zenith_angle - sun_angular_size * 0.5 >= horizon_angle {
return 0.0;
}

// Calculate partial visibility using circular segment area formula
let d = (horizon_angle - sun_zenith_angle) / (sun_angular_size * 0.5);
let visible_ratio = 0.5 + d * 0.5;
return clamp(visible_ratio, 0.0, 1.0);
}

// TRANSFORM UTILITIES

fn max_atmosphere_distance(r: f32, mu: f32) -> f32 {
let t_top = distance_to_top_atmosphere_boundary(r, mu);
let t_top = distance_to_top_atmosphere_boundary(atmosphere, r, mu);
let t_bottom = distance_to_bottom_atmosphere_boundary(r, mu);
let hits = ray_intersects_ground(r, mu);
return mix(t_top, t_bottom, f32(hits));
Expand Down
15 changes: 12 additions & 3 deletions crates/bevy_pbr/src/atmosphere/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ use tracing::warn;
use self::{
node::{AtmosphereLutsNode, AtmosphereNode, RenderSkyNode},
resources::{
prepare_atmosphere_bind_groups, prepare_atmosphere_textures, AtmosphereBindGroupLayouts,
AtmosphereLutPipelines, AtmosphereSamplers,
init_atmosphere_buffer, prepare_atmosphere_bind_groups, prepare_atmosphere_textures,
write_atmosphere_buffer, AtmosphereBindGroupLayouts, AtmosphereLutPipelines,
AtmosphereSamplers,
},
};

Expand Down Expand Up @@ -145,7 +146,12 @@ impl Plugin for AtmospherePlugin {
.init_resource::<SpecializedRenderPipelines<RenderSkyBindGroupLayouts>>()
.add_systems(
RenderStartup,
(init_atmosphere_probe_layout, init_atmosphere_probe_pipeline).chain(),
(
init_atmosphere_probe_layout,
init_atmosphere_probe_pipeline,
init_atmosphere_buffer,
)
.chain(),
)
.add_systems(
Render,
Expand All @@ -159,6 +165,9 @@ impl Plugin for AtmospherePlugin {
prepare_atmosphere_probe_bind_groups.in_set(RenderSystems::PrepareBindGroups),
prepare_atmosphere_transforms.in_set(RenderSystems::PrepareResources),
prepare_atmosphere_bind_groups.in_set(RenderSystems::PrepareBindGroups),
write_atmosphere_buffer
.in_set(RenderSystems::PrepareResources)
.before(RenderSystems::PrepareBindGroups),
),
)
.add_render_graph_node::<ViewNodeRunner<AtmosphereLutsNode>>(
Expand Down
38 changes: 38 additions & 0 deletions crates/bevy_pbr/src/atmosphere/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,3 +698,41 @@ pub(super) fn prepare_atmosphere_bind_groups(
});
}
}

#[derive(ShaderType)]
#[repr(C)]
pub(crate) struct AtmosphereData {
pub atmosphere: Atmosphere,
pub settings: GpuAtmosphereSettings,
}

pub fn init_atmosphere_buffer(mut commands: Commands) {
commands.insert_resource(AtmosphereBuffer {
buffer: StorageBuffer::from(AtmosphereData {
atmosphere: Atmosphere::default(),
settings: GpuAtmosphereSettings::default(),
}),
});
}

#[derive(Resource)]
pub struct AtmosphereBuffer {
pub(crate) buffer: StorageBuffer<AtmosphereData>,
}

pub(crate) fn write_atmosphere_buffer(
device: Res<RenderDevice>,
queue: Res<RenderQueue>,
atmosphere_entity: Query<(&Atmosphere, &GpuAtmosphereSettings), With<Camera3d>>,
mut atmosphere_buffer: ResMut<AtmosphereBuffer>,
) {
let Ok((atmosphere, settings)) = atmosphere_entity.single() else {
return;
};

atmosphere_buffer.buffer.set(AtmosphereData {
atmosphere: atmosphere.clone(),
settings: settings.clone(),
});
atmosphere_buffer.buffer.write_buffer(&device, &queue);
}
5 changes: 5 additions & 0 deletions crates/bevy_pbr/src/atmosphere/types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,8 @@ struct AtmosphereSettings {
struct AtmosphereTransforms {
world_from_atmosphere: mat4x4<f32>,
}

struct AtmosphereData {
atmosphere: Atmosphere,
settings: AtmosphereSettings,
}
13 changes: 12 additions & 1 deletion crates/bevy_pbr/src/deferred/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use crate::{
ViewLightProbesUniformOffset, ViewScreenSpaceReflectionsUniformOffset,
TONEMAPPING_LUT_SAMPLER_BINDING_INDEX, TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
};
use crate::{DistanceFog, MeshPipelineKey, ViewFogUniformOffset, ViewLightsUniformOffset};
use crate::{
Atmosphere, DistanceFog, MeshPipelineKey, ViewFogUniformOffset, ViewLightsUniformOffset,
};
use bevy_app::prelude::*;
use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
use bevy_core_pipeline::{
Expand Down Expand Up @@ -325,6 +327,9 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
if key.contains(MeshPipelineKey::DISTANCE_FOG) {
shader_defs.push("DISTANCE_FOG".into());
}
if key.contains(MeshPipelineKey::ATMOSPHERE) {
shader_defs.push("ATMOSPHERE".into());
}

// Always true, since we're in the deferred lighting pipeline
shader_defs.push("DEFERRED_PREPASS".into());
Expand Down Expand Up @@ -453,6 +458,7 @@ pub fn prepare_deferred_lighting_pipelines(
Has<RenderViewLightProbes<EnvironmentMapLight>>,
Has<RenderViewLightProbes<IrradianceVolume>>,
Has<SkipDeferredLighting>,
Has<Atmosphere>,
)>,
) {
for (
Expand All @@ -466,6 +472,7 @@ pub fn prepare_deferred_lighting_pipelines(
has_environment_maps,
has_irradiance_volumes,
skip_deferred_lighting,
has_atmosphere,
) in &views
{
// If there is no deferred prepass or we want to skip the deferred lighting pass,
Expand All @@ -490,6 +497,10 @@ pub fn prepare_deferred_lighting_pipelines(
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
}

if has_atmosphere {
view_key |= MeshPipelineKey::ATMOSPHERE;
}

// Always true, since we're in the deferred lighting pipeline
view_key |= MeshPipelineKey::DEFERRED_PREPASS;

Expand Down
21 changes: 18 additions & 3 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::material_bind_groups::{MaterialBindGroupIndex, MaterialBindGroupSlot};
use crate::{
material_bind_groups::{MaterialBindGroupIndex, MaterialBindGroupSlot},
resources::write_atmosphere_buffer,
};
use bevy_asset::{embedded_asset, load_embedded_asset, AssetId};
use bevy_camera::{
primitives::Aabb,
Expand Down Expand Up @@ -194,7 +197,8 @@ impl Plugin for MeshRenderPlugin {
prepare_mesh_bind_groups.in_set(RenderSystems::PrepareBindGroups),
prepare_mesh_view_bind_groups
.in_set(RenderSystems::PrepareBindGroups)
.after(prepare_oit_buffers),
.after(prepare_oit_buffers)
.after(write_atmosphere_buffer),
no_gpu_preprocessing::clear_batched_cpu_instance_buffers::<MeshPipeline>
.in_set(RenderSystems::Cleanup)
.after(RenderSystems::Render),
Expand Down Expand Up @@ -324,6 +328,7 @@ pub fn check_views_need_specialization(
Has<RenderViewLightProbes<IrradianceVolume>>,
),
Has<OrderIndependentTransparencySettings>,
Has<Atmosphere>,
)>,
ticks: SystemChangeTick,
) {
Expand All @@ -341,6 +346,7 @@ pub fn check_views_need_specialization(
distance_fog,
(has_environment_maps, has_irradiance_volumes),
has_oit,
has_atmosphere,
) in views.iter_mut()
{
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
Expand Down Expand Up @@ -378,6 +384,10 @@ pub fn check_views_need_specialization(
view_key |= MeshPipelineKey::OIT_ENABLED;
}

if has_atmosphere {
view_key |= MeshPipelineKey::ATMOSPHERE;
}

if let Some(projection) = projection {
view_key |= match projection {
Projection::Perspective(_) => MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE,
Expand Down Expand Up @@ -2093,7 +2103,8 @@ bitflags::bitflags! {
const HAS_PREVIOUS_MORPH = 1 << 19;
const OIT_ENABLED = 1 << 20;
const DISTANCE_FOG = 1 << 21;
const LAST_FLAG = Self::DISTANCE_FOG.bits();
const ATMOSPHERE = 1 << 22;
const LAST_FLAG = Self::ATMOSPHERE.bits();

// Bitfields
const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
Expand Down Expand Up @@ -2563,6 +2574,10 @@ impl SpecializedMeshPipeline for MeshPipeline {
shader_defs.push("DISTANCE_FOG".into());
}

if key.contains(MeshPipelineKey::ATMOSPHERE) {
shader_defs.push("ATMOSPHERE".into());
}

if self.binding_arrays_are_usable {
shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
Expand Down
Loading