Skip to content

Commit f95dee8

Browse files
authored
Tweak exponential falloff in ScatteringTerm (#21644)
# Objective current exponential falloff parametrization isn't intuitive, strays from previous "scale height" parametrization common in atmospheric scattering literature ## Solution - Switch to a parametrization of the form ``` -(1-p)/s -1/s e - e f(p,s) = ----------------- -1/s 1.0 - e ``` This is basically equivalent to the "scale height" parametrization `e^-(1-p)/s` for small values of `scale`, but also has the nice properties `f(0, s) = 0, f(1, s) = 1` and has a broader domain for values of `scale`. ## Testing - Ran the example, results looked good
1 parent bf44234 commit f95dee8

File tree

1 file changed

+41
-23
lines changed

1 file changed

+41
-23
lines changed

crates/bevy_pbr/src/medium.rs

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -117,23 +117,31 @@ impl ScatteringMedium {
117117
}
118118

119119
/// Returns a scattering medium representing an earthlike atmosphere.
120+
///
121+
/// Uses physically-based scale heights from Earth's atmosphere, assuming
122+
/// a 60 km atmosphere height:
123+
/// - Rayleigh (molecular) scattering: 8 km scale height
124+
/// - Mie (aerosol) scattering: 1.2 km scale height
120125
pub fn earthlike(falloff_resolution: u32, phase_resolution: u32) -> Self {
121126
Self::new(
122127
falloff_resolution,
123128
phase_resolution,
124129
[
130+
// Rayleigh scattering Term
125131
ScatteringTerm {
126132
absorption: Vec3::ZERO,
127133
scattering: Vec3::new(5.802e-6, 13.558e-6, 33.100e-6),
128-
falloff: Falloff::Exponential { strength: 12.5 },
134+
falloff: Falloff::Exponential { scale: 8.0 / 60.0 },
129135
phase: PhaseFunction::Rayleigh,
130136
},
137+
// Mie scattering Term
131138
ScatteringTerm {
132139
absorption: Vec3::splat(3.996e-6),
133140
scattering: Vec3::splat(0.444e-6),
134-
falloff: Falloff::Exponential { strength: 83.5 },
141+
falloff: Falloff::Exponential { scale: 1.2 / 60.0 },
135142
phase: PhaseFunction::Mie { asymmetry: 0.8 },
136143
},
144+
// Ozone scattering Term
137145
ScatteringTerm {
138146
absorption: Vec3::new(0.650e-6, 1.881e-6, 0.085e-6),
139147
scattering: Vec3::ZERO,
@@ -203,18 +211,30 @@ pub enum Falloff {
203211
/// f(p) = p
204212
#[default]
205213
Linear,
206-
/// An exponential falloff function with adjustable strength.
214+
/// An exponential falloff function parametrized by a proportional scale.
215+
/// When paired with an absolute "falloff distance" like the distance from
216+
/// Earth's surface to the edge of space, this is analogous to the "height
217+
/// scale" value common in atmospheric scattering literature, though it will
218+
/// diverge from this for large or negative `scale` values.
207219
///
208220
/// f(1) = 1
209221
/// f(0) = 0
210-
/// f(p) = (e^sp - 1)/(e^s - 1)
222+
/// f(p) = (e^((1-p)/s) - e^(1/s))/(e - e^(1/s))
211223
Exponential {
212-
/// The "strength" of the exponential falloff. The higher
213-
/// this value is, the quicker the medium's density will
214-
/// decrease with distance.
224+
/// The "scale" of the exponential falloff. Values closer to zero will
225+
/// produce steeper falloff, and values farther from zero will produce
226+
/// gentler falloff, approaching linear falloff as scale goes to `+-∞`.
227+
///
228+
/// Negative values change the *concavity* of the falloff function:
229+
/// rather than an initial narrow region of steep falloff followed by a
230+
/// wide region of gentle falloff, there will be an initial wide region
231+
/// of gentle falloff followed by a narrow region of steep falloff.
215232
///
216233
/// domain: (-∞, ∞)
217-
strength: f32,
234+
///
235+
/// NOTE, this function is not defined when `scale == 0`.
236+
/// In that case, it will fall back to linear falloff.
237+
scale: f32,
218238
},
219239
/// A tent-shaped falloff function, which produces a triangular
220240
/// peak at the center and linearly falls off to either side.
@@ -244,25 +264,23 @@ impl Falloff {
244264
Self::Curve(Arc::new(curve))
245265
}
246266

247-
fn sample(&self, falloff: f32) -> f32 {
267+
fn sample(&self, p: f32) -> f32 {
248268
match self {
249-
Falloff::Linear => falloff,
250-
Falloff::Exponential { strength } => {
251-
// fill discontinuity at strength == 0
252-
if *strength == 0.0 {
253-
falloff
269+
Falloff::Linear => p,
270+
Falloff::Exponential { scale } => {
271+
// fill discontinuity at scale == 0,
272+
// arbitrarily choose linear falloff
273+
if *scale == 0.0 {
274+
p
254275
} else {
255-
let scale_exp_m1 = ops::exp_m1(*strength);
256-
let domain_offset = ops::ln(scale_exp_m1.abs());
257-
let range_offset = scale_exp_m1.recip();
258-
let eval_pos = falloff * strength - domain_offset;
259-
scale_exp_m1.signum() * ops::exp(eval_pos) - range_offset
276+
let s = -1.0 / scale;
277+
let exp_p_s = ops::exp((1.0 - p) * s);
278+
let exp_s = ops::exp(s);
279+
(exp_p_s - exp_s) / (1.0 - exp_s)
260280
}
261281
}
262-
Falloff::Tent { center, width } => {
263-
(1.0 - (falloff - center).abs() / (0.5 * width)).max(0.0)
264-
}
265-
Falloff::Curve(curve) => curve.sample(falloff).unwrap_or(0.0),
282+
Falloff::Tent { center, width } => (1.0 - (p - center).abs() / (0.5 * width)).max(0.0),
283+
Falloff::Curve(curve) => curve.sample(p).unwrap_or(0.0),
266284
}
267285
}
268286
}

0 commit comments

Comments
 (0)