bevy/crates/bevy_pbr/src/atmosphere/transmittance_lut.wgsl
Máté Homolya f22ea72db0
Atmosphere LUT parameterization improvements (#17555)
# Objective

- Fix the atmosphere LUT parameterization in the aerial -view and
sky-view LUTs
- Correct the light accumulation according to a ray-marched reference
- Avoid negative values of the sun disk illuminance when the sun disk is
below the horizon

## Solution

- Adding a Newton's method iteration to `fast_sqrt` function
- Switched to using `fast_acos_4` for better precision of the sun angle
towards the horizon (view mu angle = 0)
- Simplified the function for mapping to and from the Sky View UV
coordinates by removing an if statement and correctly apply the method
proposed by the [Hillarie
paper](https://sebh.github.io/publications/egsr2020.pdf) detailed in
section 5.3 and 5.4.
- Replaced the `ray_dir_ws.y` term with a shadow factor in the
`sample_sun_illuminance` function that correctly approximates the sun
disk occluded by the earth from any view point

## Testing

- Ran the atmosphere and SSAO examples to make sure the shaders still
compile and run as expected.

---

## Showcase

<img width="1151" alt="showcase-img"
src="https://github.com/user-attachments/assets/de875533-42bd-41f9-9fd0-d7cc57d6e51c"
/>

---------

Co-authored-by: Emerson Coskey <emerson@coskey.dev>
2025-02-03 21:52:11 +00:00

49 lines
1.9 KiB
WebGPU Shading Language

#import bevy_pbr::atmosphere::{
types::{Atmosphere, AtmosphereSettings},
bindings::{settings, atmosphere},
functions::{AtmosphereSample, sample_atmosphere, get_local_r, max_atmosphere_distance, MIDPOINT_RATIO},
bruneton_functions::{transmittance_lut_uv_to_r_mu, distance_to_bottom_atmosphere_boundary, distance_to_top_atmosphere_boundary},
}
#import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput
@group(0) @binding(13) var transmittance_lut_out: texture_storage_2d<rgba16float, write>;
@compute
@workgroup_size(16, 16, 1)
fn main(@builtin(global_invocation_id) idx: vec3<u32>) {
let uv: vec2<f32> = (vec2<f32>(idx.xy) + 0.5) / vec2<f32>(settings.transmittance_lut_size);
// map UV coordinates to view height (r) and zenith cos angle (mu)
let r_mu = transmittance_lut_uv_to_r_mu(uv);
// compute the optical depth from view height r to the top atmosphere boundary
let optical_depth = ray_optical_depth(r_mu.x, r_mu.y, settings.transmittance_lut_samples);
let transmittance = exp(-optical_depth);
textureStore(transmittance_lut_out, idx.xy, 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 ray_optical_depth(r: f32, mu: f32, sample_count: u32) -> vec3<f32> {
let t_max = max_atmosphere_distance(r, mu);
var optical_depth = vec3<f32>(0.0f);
var prev_t = 0.0f;
for (var i = 0u; i < sample_count; i++) {
let t_i = t_max * (f32(i) + MIDPOINT_RATIO) / f32(sample_count);
let dt = t_i - prev_t;
prev_t = t_i;
let r_i = get_local_r(r, mu, t_i);
let atmosphere_sample = sample_atmosphere(r_i);
let sample_optical_depth = atmosphere_sample.extinction * dt;
optical_depth += sample_optical_depth;
}
return optical_depth;
}