diff --git a/crates/bevy_pbr/src/atmosphere/functions.wgsl b/crates/bevy_pbr/src/atmosphere/functions.wgsl index 0b40a87b968b9..0c0340314e1ec 100644 --- a/crates/bevy_pbr/src/atmosphere/functions.wgsl +++ b/crates/bevy_pbr/src/atmosphere/functions.wgsl @@ -281,6 +281,17 @@ fn calculate_visible_sun_ratio(atmosphere: Atmosphere, r: f32, mu: f32, sun_angu // TRANSFORM UTILITIES +/// Clamp a position to the planet surface (with a small epsilon) to avoid underground artifacts. +fn clamp_to_surface(atmosphere: Atmosphere, position: vec3) -> vec3 { + let min_radius = atmosphere.bottom_radius + EPSILON; + let r = length(position); + if r < min_radius { + let up = normalize(position); + return up * min_radius; + } + return position; +} + fn max_atmosphere_distance(r: f32, mu: f32) -> f32 { let t_top = distance_to_top_atmosphere_boundary(atmosphere, r, mu); let t_bottom = distance_to_bottom_atmosphere_boundary(r, mu); @@ -291,18 +302,7 @@ fn max_atmosphere_distance(r: f32, mu: f32) -> f32 { /// Returns the observer's position in the atmosphere fn get_view_position() -> vec3 { var world_pos = view.world_position * settings.scene_units_to_m + vec3(0.0, atmosphere.bottom_radius, 0.0); - - // If the camera is underground, clamp it to the ground surface along the local up. - let r = length(world_pos); - // Nudge r above ground to avoid sqrt cancellation, zero-length segments where - // r is equal to bottom_radius, which show up as black pixels - let min_radius = atmosphere.bottom_radius + EPSILON; - if r < min_radius { - let up = normalize(world_pos); - world_pos = up * min_radius; - } - - return world_pos; + return clamp_to_surface(atmosphere, world_pos); } // We assume the `up` vector at the view position is the y axis, since the world is locally flat/level. diff --git a/crates/bevy_pbr/src/render/pbr_lighting.wgsl b/crates/bevy_pbr/src/render/pbr_lighting.wgsl index c359109aad91e..6dff89c041a9f 100644 --- a/crates/bevy_pbr/src/render/pbr_lighting.wgsl +++ b/crates/bevy_pbr/src/render/pbr_lighting.wgsl @@ -3,7 +3,7 @@ #import bevy_pbr::{ mesh_view_types::POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE, mesh_view_bindings as view_bindings, - atmosphere::functions::calculate_visible_sun_ratio, + atmosphere::functions::{calculate_visible_sun_ratio, clamp_to_surface}, atmosphere::bruneton_functions::transmittance_lut_r_mu_to_uv, } #import bevy_render::maths::PI @@ -862,8 +862,9 @@ color *= (*light).color.rgb * texture_sample; let O = vec3(0.0, atmosphere.bottom_radius, 0.0); let P_scaled = P * vec3(view_bindings::atmosphere_data.settings.scene_units_to_m); let P_as = P_scaled + O; - let r = length(P_as); - let local_up = normalize(P_as); + let P_clamped = clamp_to_surface(atmosphere, P_as); + let r = length(P_clamped); + let local_up = normalize(P_clamped); let mu_light = dot(L, local_up); // Sample atmosphere