bevy_pbr/fog.rs
1use bevy_color::{Color, ColorToComponents, LinearRgba};
2use bevy_ecs::prelude::*;
3use bevy_math::{ops, Vec3};
4use bevy_reflect::{std_traits::ReflectDefault, Reflect};
5use bevy_render::{extract_component::ExtractComponent, prelude::Camera};
6
7/// Configures the “classic” computer graphics [distance fog](https://en.wikipedia.org/wiki/Distance_fog) effect,
8/// in which objects appear progressively more covered in atmospheric haze the further away they are from the camera.
9/// Affects meshes rendered via the PBR [`StandardMaterial`](crate::StandardMaterial).
10///
11/// ## Falloff
12///
13/// The rate at which fog intensity increases with distance is controlled by the falloff mode.
14/// Currently, the following fog falloff modes are supported:
15///
16/// - [`FogFalloff::Linear`]
17/// - [`FogFalloff::Exponential`]
18/// - [`FogFalloff::ExponentialSquared`]
19/// - [`FogFalloff::Atmospheric`]
20///
21/// ## Example
22///
23/// ```
24/// # use bevy_ecs::prelude::*;
25/// # use bevy_render::prelude::*;
26/// # use bevy_core_pipeline::prelude::*;
27/// # use bevy_pbr::prelude::*;
28/// # use bevy_color::Color;
29/// # fn system(mut commands: Commands) {
30/// commands.spawn((
31/// // Setup your camera as usual
32/// Camera3d::default(),
33/// // Add fog to the same entity
34/// DistanceFog {
35/// color: Color::WHITE,
36/// falloff: FogFalloff::Exponential { density: 1e-3 },
37/// ..Default::default()
38/// },
39/// ));
40/// # }
41/// # bevy_ecs::system::assert_is_system(system);
42/// ```
43///
44/// ## Material Override
45///
46/// Once enabled for a specific camera, the fog effect can also be disabled for individual
47/// [`StandardMaterial`](crate::StandardMaterial) instances via the `fog_enabled` flag.
48#[derive(Debug, Clone, Component, Reflect, ExtractComponent)]
49#[extract_component_filter(With<Camera>)]
50#[reflect(Component, Default, Debug, Clone)]
51pub struct DistanceFog {
52 /// The color of the fog effect.
53 ///
54 /// **Tip:** The alpha channel of the color can be used to “modulate” the fog effect without
55 /// changing the fog falloff mode or parameters.
56 pub color: Color,
57
58 /// Color used to modulate the influence of directional light colors on the
59 /// fog, where the view direction aligns with each directional light direction,
60 /// producing a “glow” or light dispersion effect. (e.g. around the sun)
61 ///
62 /// Use [`Color::NONE`] to disable the effect.
63 pub directional_light_color: Color,
64
65 /// The exponent applied to the directional light alignment calculation.
66 /// A higher value means a more concentrated “glow”.
67 pub directional_light_exponent: f32,
68
69 /// Determines which falloff mode to use, and its parameters.
70 pub falloff: FogFalloff,
71}
72
73/// Allows switching between different fog falloff modes, and configuring their parameters.
74///
75/// ## Convenience Methods
76///
77/// When using non-linear fog modes it can be hard to determine the right parameter values
78/// for a given scene.
79///
80/// For easier artistic control, instead of creating the enum variants directly, you can use the
81/// visibility-based convenience methods:
82///
83/// - For `FogFalloff::Exponential`:
84/// - [`FogFalloff::from_visibility()`]
85/// - [`FogFalloff::from_visibility_contrast()`]
86///
87/// - For `FogFalloff::ExponentialSquared`:
88/// - [`FogFalloff::from_visibility_squared()`]
89/// - [`FogFalloff::from_visibility_contrast_squared()`]
90///
91/// - For `FogFalloff::Atmospheric`:
92/// - [`FogFalloff::from_visibility_color()`]
93/// - [`FogFalloff::from_visibility_colors()`]
94/// - [`FogFalloff::from_visibility_contrast_color()`]
95/// - [`FogFalloff::from_visibility_contrast_colors()`]
96#[derive(Debug, Clone, Reflect)]
97#[reflect(Clone)]
98pub enum FogFalloff {
99 /// A linear fog falloff that grows in intensity between `start` and `end` distances.
100 ///
101 /// This falloff mode is simpler to control than other modes, however it can produce results that look “artificial”, depending on the scene.
102 ///
103 /// ## Formula
104 ///
105 /// The fog intensity for a given point in the scene is determined by the following formula:
106 ///
107 /// ```text
108 /// let fog_intensity = 1.0 - ((end - distance) / (end - start)).clamp(0.0, 1.0);
109 /// ```
110 ///
111 /// <svg width="370" height="212" viewBox="0 0 370 212" fill="none">
112 /// <title>Plot showing how linear fog falloff behaves for start and end values of 0.8 and 2.2, respectively.</title>
113 /// <path d="M331 151H42V49" stroke="currentColor" stroke-width="2"/>
114 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0em"><tspan x="136" y="173.864">1</tspan></text>
115 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0em"><tspan x="30" y="53.8636">1</tspan></text>
116 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0em"><tspan x="42" y="173.864">0</tspan></text>
117 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0em"><tspan x="232" y="173.864">2</tspan></text>
118 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0em"><tspan x="332" y="173.864">3</tspan></text>
119 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0em"><tspan x="161" y="190.864">distance</tspan></text>
120 /// <text font-family="sans-serif" transform="translate(10 132) rotate(-90)" fill="currentColor" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0em"><tspan x="0" y="11.8636">fog intensity</tspan></text>
121 /// <path d="M43 150H117.227L263 48H331" stroke="#FF00E5"/>
122 /// <path d="M118 151V49" stroke="#FF00E5" stroke-dasharray="1 4"/>
123 /// <path d="M263 151V49" stroke="#FF00E5" stroke-dasharray="1 4"/>
124 /// <text font-family="sans-serif" fill="#FF00E5" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0em"><tspan x="121" y="58.6364">start</tspan></text>
125 /// <text font-family="sans-serif" fill="#FF00E5" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0em"><tspan x="267" y="58.6364">end</tspan></text>
126 /// </svg>
127 Linear {
128 /// Distance from the camera where fog is completely transparent, in world units.
129 start: f32,
130
131 /// Distance from the camera where fog is completely opaque, in world units.
132 end: f32,
133 },
134
135 /// An exponential fog falloff with a given `density`.
136 ///
137 /// Initially gains intensity quickly with distance, then more slowly. Typically produces more natural results than [`FogFalloff::Linear`],
138 /// but is a bit harder to control.
139 ///
140 /// To move the fog “further away”, use lower density values. To move it “closer” use higher density values.
141 ///
142 /// ## Tips
143 ///
144 /// - Use the [`FogFalloff::from_visibility()`] convenience method to create an exponential falloff with the proper
145 /// density for a desired visibility distance in world units;
146 /// - It's not _unusual_ to have very large or very small values for the density, depending on the scene
147 /// scale. Typically, for scenes with objects in the scale of thousands of units, you might want density values
148 /// in the ballpark of `0.001`. Conversely, for really small scale scenes you might want really high values of
149 /// density;
150 /// - Combine the `density` parameter with the [`DistanceFog`] `color`'s alpha channel for easier artistic control.
151 ///
152 /// ## Formula
153 ///
154 /// The fog intensity for a given point in the scene is determined by the following formula:
155 ///
156 /// ```text
157 /// let fog_intensity = 1.0 - 1.0 / (distance * density).exp();
158 /// ```
159 ///
160 /// <svg width="370" height="212" viewBox="0 0 370 212" fill="none">
161 /// <title>Plot showing how exponential fog falloff behaves for different density values</title>
162 /// <mask id="mask0_3_31" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="42" y="42" width="286" height="108">
163 /// <rect x="42" y="42" width="286" height="108" fill="#D9D9D9"/>
164 /// </mask>
165 /// <g mask="url(#mask0_3_31)">
166 /// <path d="M42 150C42 150 98.3894 53 254.825 53L662 53" stroke="#FF003D" stroke-width="1"/>
167 /// <path d="M42 150C42 150 139.499 53 409.981 53L1114 53" stroke="#001AFF" stroke-width="1"/>
168 /// <path d="M42 150C42 150 206.348 53 662.281 53L1849 53" stroke="#14FF00" stroke-width="1"/>
169 /// </g>
170 /// <path d="M331 151H42V49" stroke="currentColor" stroke-width="2"/>
171 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="136" y="173.864">1</tspan></text>
172 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="30" y="53.8636">1</tspan></text>
173 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="42" y="173.864">0</tspan></text>
174 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="232" y="173.864">2</tspan></text>
175 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="332" y="173.864">3</tspan></text>
176 /// <text font-family="sans-serif" fill="#FF003D" style="white-space: pre" font-size="10" letter-spacing="0em"><tspan x="77" y="64.6364">density = 2</tspan></text>
177 /// <text font-family="sans-serif" fill="#001AFF" style="white-space: pre" font-size="10" letter-spacing="0em"><tspan x="236" y="76.6364">density = 1</tspan></text>
178 /// <text font-family="sans-serif" fill="#14FF00" style="white-space: pre" font-size="10" letter-spacing="0em"><tspan x="205" y="115.636">density = 0.5</tspan></text>
179 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="161" y="190.864">distance</tspan></text>
180 /// <text font-family="sans-serif" transform="translate(10 132) rotate(-90)" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="0" y="11.8636">fog intensity</tspan></text>
181 /// </svg>
182 Exponential {
183 /// Multiplier applied to the world distance (within the exponential fog falloff calculation).
184 density: f32,
185 },
186
187 /// A squared exponential fog falloff with a given `density`.
188 ///
189 /// Similar to [`FogFalloff::Exponential`], but grows more slowly in intensity for closer distances
190 /// before “catching up”.
191 ///
192 /// To move the fog “further away”, use lower density values. To move it “closer” use higher density values.
193 ///
194 /// ## Tips
195 ///
196 /// - Use the [`FogFalloff::from_visibility_squared()`] convenience method to create an exponential squared falloff
197 /// with the proper density for a desired visibility distance in world units;
198 /// - Combine the `density` parameter with the [`DistanceFog`] `color`'s alpha channel for easier artistic control.
199 ///
200 /// ## Formula
201 ///
202 /// The fog intensity for a given point in the scene is determined by the following formula:
203 ///
204 /// ```text
205 /// let fog_intensity = 1.0 - 1.0 / (distance * density).squared().exp();
206 /// ```
207 ///
208 /// <svg width="370" height="212" viewBox="0 0 370 212" fill="none">
209 /// <title>Plot showing how exponential squared fog falloff behaves for different density values</title>
210 /// <mask id="mask0_1_3" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="42" y="42" width="286" height="108">
211 /// <rect x="42" y="42" width="286" height="108" fill="#D9D9D9"/>
212 /// </mask>
213 /// <g mask="url(#mask0_1_3)">
214 /// <path d="M42 150C75.4552 150 74.9241 53.1724 166.262 53.1724L404 53.1724" stroke="#FF003D" stroke-width="1"/>
215 /// <path d="M42 150C107.986 150 106.939 53.1724 287.091 53.1724L756 53.1724" stroke="#001AFF" stroke-width="1"/>
216 /// <path d="M42 150C166.394 150 164.42 53.1724 504.035 53.1724L1388 53.1724" stroke="#14FF00" stroke-width="1"/>
217 /// </g>
218 /// <path d="M331 151H42V49" stroke="currentColor" stroke-width="2"/>
219 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="136" y="173.864">1</tspan></text>
220 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="30" y="53.8636">1</tspan></text>
221 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="42" y="173.864">0</tspan></text>
222 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="232" y="173.864">2</tspan></text>
223 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="332" y="173.864">3</tspan></text>
224 /// <text font-family="sans-serif" fill="#FF003D" style="white-space: pre" font-size="10" letter-spacing="0em"><tspan x="61" y="54.6364">density = 2</tspan></text>
225 /// <text font-family="sans-serif" fill="#001AFF" style="white-space: pre" font-size="10" letter-spacing="0em"><tspan x="168" y="84.6364">density = 1</tspan></text>
226 /// <text font-family="sans-serif" fill="#14FF00" style="white-space: pre" font-size="10" letter-spacing="0em"><tspan x="174" y="121.636">density = 0.5</tspan></text>
227 /// <text font-family="sans-serif" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="161" y="190.864">distance</tspan></text>
228 /// <text font-family="sans-serif" transform="translate(10 132) rotate(-90)" fill="currentColor" style="white-space: pre" font-size="12" letter-spacing="0em"><tspan x="0" y="11.8636">fog intensity</tspan></text>
229 /// </svg>
230 ExponentialSquared {
231 /// Multiplier applied to the world distance (within the exponential squared fog falloff calculation).
232 density: f32,
233 },
234
235 /// A more general form of the [`FogFalloff::Exponential`] mode. The falloff formula is separated into
236 /// two terms, `extinction` and `inscattering`, for a somewhat simplified atmospheric scattering model.
237 /// Additionally, individual color channels can have their own density values, resulting in a total of
238 /// six different configuration parameters.
239 ///
240 /// ## Tips
241 ///
242 /// - Use the [`FogFalloff::from_visibility_colors()`] or [`FogFalloff::from_visibility_color()`] convenience methods
243 /// to create an atmospheric falloff with the proper densities for a desired visibility distance in world units and
244 /// extinction and inscattering colors;
245 /// - Combine the atmospheric fog parameters with the [`DistanceFog`] `color`'s alpha channel for easier artistic control.
246 ///
247 /// ## Formula
248 ///
249 /// Unlike other modes, atmospheric falloff doesn't use a simple intensity-based blend of fog color with
250 /// object color. Instead, it calculates per-channel extinction and inscattering factors, which are
251 /// then used to calculate the final color.
252 ///
253 /// ```text
254 /// let extinction_factor = 1.0 - 1.0 / (distance * extinction).exp();
255 /// let inscattering_factor = 1.0 - 1.0 / (distance * inscattering).exp();
256 /// let result = input_color * (1.0 - extinction_factor) + fog_color * inscattering_factor;
257 /// ```
258 ///
259 /// ## Equivalence to [`FogFalloff::Exponential`]
260 ///
261 /// For a density value of `D`, the following two falloff modes will produce identical visual results:
262 ///
263 /// ```
264 /// # use bevy_pbr::prelude::*;
265 /// # use bevy_math::prelude::*;
266 /// # const D: f32 = 0.5;
267 /// #
268 /// let exponential = FogFalloff::Exponential {
269 /// density: D,
270 /// };
271 ///
272 /// let atmospheric = FogFalloff::Atmospheric {
273 /// extinction: Vec3::new(D, D, D),
274 /// inscattering: Vec3::new(D, D, D),
275 /// };
276 /// ```
277 ///
278 /// **Note:** While the results are identical, [`FogFalloff::Atmospheric`] is computationally more expensive.
279 Atmospheric {
280 /// Controls how much light is removed due to atmospheric “extinction”, i.e. loss of light due to
281 /// photons being absorbed by atmospheric particles.
282 ///
283 /// Each component can be thought of as an independent per `R`/`G`/`B` channel `density` factor from
284 /// [`FogFalloff::Exponential`]: Multiplier applied to the world distance (within the fog
285 /// falloff calculation) for that specific channel.
286 ///
287 /// **Note:**
288 /// This value is not a `Color`, since it affects the channels exponentially in a non-intuitive way.
289 /// For artistic control, use the [`FogFalloff::from_visibility_colors()`] convenience method.
290 extinction: Vec3,
291
292 /// Controls how much light is added due to light scattering from the sun through the atmosphere.
293 ///
294 /// Each component can be thought of as an independent per `R`/`G`/`B` channel `density` factor from
295 /// [`FogFalloff::Exponential`]: A multiplier applied to the world distance (within the fog
296 /// falloff calculation) for that specific channel.
297 ///
298 /// **Note:**
299 /// This value is not a `Color`, since it affects the channels exponentially in a non-intuitive way.
300 /// For artistic control, use the [`FogFalloff::from_visibility_colors()`] convenience method.
301 inscattering: Vec3,
302 },
303}
304
305impl FogFalloff {
306 /// Creates a [`FogFalloff::Exponential`] value from the given visibility distance in world units,
307 /// using the revised Koschmieder contrast threshold, [`FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD`].
308 pub fn from_visibility(visibility: f32) -> FogFalloff {
309 FogFalloff::from_visibility_contrast(
310 visibility,
311 FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD,
312 )
313 }
314
315 /// Creates a [`FogFalloff::Exponential`] value from the given visibility distance in world units,
316 /// and a given contrast threshold in the range of `0.0` to `1.0`.
317 pub fn from_visibility_contrast(visibility: f32, contrast_threshold: f32) -> FogFalloff {
318 FogFalloff::Exponential {
319 density: FogFalloff::koschmieder(visibility, contrast_threshold),
320 }
321 }
322
323 /// Creates a [`FogFalloff::ExponentialSquared`] value from the given visibility distance in world units,
324 /// using the revised Koschmieder contrast threshold, [`FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD`].
325 pub fn from_visibility_squared(visibility: f32) -> FogFalloff {
326 FogFalloff::from_visibility_contrast_squared(
327 visibility,
328 FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD,
329 )
330 }
331
332 /// Creates a [`FogFalloff::ExponentialSquared`] value from the given visibility distance in world units,
333 /// and a given contrast threshold in the range of `0.0` to `1.0`.
334 pub fn from_visibility_contrast_squared(
335 visibility: f32,
336 contrast_threshold: f32,
337 ) -> FogFalloff {
338 FogFalloff::ExponentialSquared {
339 density: (FogFalloff::koschmieder(visibility, contrast_threshold) / visibility).sqrt(),
340 }
341 }
342
343 /// Creates a [`FogFalloff::Atmospheric`] value from the given visibility distance in world units,
344 /// and a shared color for both extinction and inscattering, using the revised Koschmieder contrast threshold,
345 /// [`FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD`].
346 pub fn from_visibility_color(
347 visibility: f32,
348 extinction_inscattering_color: Color,
349 ) -> FogFalloff {
350 FogFalloff::from_visibility_contrast_colors(
351 visibility,
352 FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD,
353 extinction_inscattering_color,
354 extinction_inscattering_color,
355 )
356 }
357
358 /// Creates a [`FogFalloff::Atmospheric`] value from the given visibility distance in world units,
359 /// extinction and inscattering colors, using the revised Koschmieder contrast threshold,
360 /// [`FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD`].
361 ///
362 /// ## Tips
363 /// - Alpha values of the provided colors can modulate the `extinction` and `inscattering` effects;
364 /// - Using an `extinction_color` of [`Color::WHITE`] or [`Color::NONE`] disables the extinction effect;
365 /// - Using an `inscattering_color` of [`Color::BLACK`] or [`Color::NONE`] disables the inscattering effect.
366 pub fn from_visibility_colors(
367 visibility: f32,
368 extinction_color: Color,
369 inscattering_color: Color,
370 ) -> FogFalloff {
371 FogFalloff::from_visibility_contrast_colors(
372 visibility,
373 FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD,
374 extinction_color,
375 inscattering_color,
376 )
377 }
378
379 /// Creates a [`FogFalloff::Atmospheric`] value from the given visibility distance in world units,
380 /// a contrast threshold in the range of `0.0` to `1.0`, and a shared color for both extinction and inscattering.
381 pub fn from_visibility_contrast_color(
382 visibility: f32,
383 contrast_threshold: f32,
384 extinction_inscattering_color: Color,
385 ) -> FogFalloff {
386 FogFalloff::from_visibility_contrast_colors(
387 visibility,
388 contrast_threshold,
389 extinction_inscattering_color,
390 extinction_inscattering_color,
391 )
392 }
393
394 /// Creates a [`FogFalloff::Atmospheric`] value from the given visibility distance in world units,
395 /// a contrast threshold in the range of `0.0` to `1.0`, extinction and inscattering colors.
396 ///
397 /// ## Tips
398 /// - Alpha values of the provided colors can modulate the `extinction` and `inscattering` effects;
399 /// - Using an `extinction_color` of [`Color::WHITE`] or [`Color::NONE`] disables the extinction effect;
400 /// - Using an `inscattering_color` of [`Color::BLACK`] or [`Color::NONE`] disables the inscattering effect.
401 pub fn from_visibility_contrast_colors(
402 visibility: f32,
403 contrast_threshold: f32,
404 extinction_color: Color,
405 inscattering_color: Color,
406 ) -> FogFalloff {
407 use core::f32::consts::E;
408
409 let [r_e, g_e, b_e, a_e] = LinearRgba::from(extinction_color).to_f32_array();
410 let [r_i, g_i, b_i, a_i] = LinearRgba::from(inscattering_color).to_f32_array();
411
412 FogFalloff::Atmospheric {
413 extinction: Vec3::new(
414 // Values are subtracted from 1.0 here to preserve the intuitive/artistic meaning of
415 // colors, since they're later subtracted. (e.g. by giving a blue extinction color, you
416 // get blue and _not_ yellow results)
417 ops::powf(1.0 - r_e, E),
418 ops::powf(1.0 - g_e, E),
419 ops::powf(1.0 - b_e, E),
420 ) * FogFalloff::koschmieder(visibility, contrast_threshold)
421 * ops::powf(a_e, E),
422
423 inscattering: Vec3::new(ops::powf(r_i, E), ops::powf(g_i, E), ops::powf(b_i, E))
424 * FogFalloff::koschmieder(visibility, contrast_threshold)
425 * ops::powf(a_i, E),
426 }
427 }
428
429 /// A 2% contrast threshold was originally proposed by Koschmieder, being the
430 /// minimum visual contrast at which a human observer could detect an object.
431 /// We use a revised 5% contrast threshold, deemed more realistic for typical human observers.
432 pub const REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD: f32 = 0.05;
433
434 /// Calculates the extinction coefficient β, from V and Cₜ, where:
435 ///
436 /// - Cₜ is the contrast threshold, in the range of `0.0` to `1.0`
437 /// - V is the visibility distance in which a perfectly black object is still identifiable
438 /// against the horizon sky within the contrast threshold
439 ///
440 /// We start with Koschmieder's equation:
441 ///
442 /// ```text
443 /// -ln(Cₜ)
444 /// V = ─────────
445 /// β
446 /// ```
447 ///
448 /// Multiplying both sides by β/V, that gives us:
449 ///
450 /// ```text
451 /// -ln(Cₜ)
452 /// β = ─────────
453 /// V
454 /// ```
455 ///
456 /// See:
457 /// - <https://en.wikipedia.org/wiki/Visibility>
458 /// - <https://www.biral.com/wp-content/uploads/2015/02/Introduction_to_visibility-v2-2.pdf>
459 pub fn koschmieder(v: f32, c_t: f32) -> f32 {
460 -ops::ln(c_t) / v
461 }
462}
463
464impl Default for DistanceFog {
465 fn default() -> Self {
466 DistanceFog {
467 color: Color::WHITE,
468 falloff: FogFalloff::Linear {
469 start: 0.0,
470 end: 100.0,
471 },
472 directional_light_color: Color::NONE,
473 directional_light_exponent: 8.0,
474 }
475 }
476}