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