
# Objective - Fixes #17960 ## Solution - Followed the [edition upgrade guide](https://doc.rust-lang.org/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html) ## Testing - CI --- ## Summary of Changes ### Documentation Indentation When using lists in documentation, proper indentation is now linted for. This means subsequent lines within the same list item must start at the same indentation level as the item. ```rust /* Valid */ /// - Item 1 /// Run-on sentence. /// - Item 2 struct Foo; /* Invalid */ /// - Item 1 /// Run-on sentence. /// - Item 2 struct Foo; ``` ### Implicit `!` to `()` Conversion `!` (the never return type, returned by `panic!`, etc.) no longer implicitly converts to `()`. This is particularly painful for systems with `todo!` or `panic!` statements, as they will no longer be functions returning `()` (or `Result<()>`), making them invalid systems for functions like `add_systems`. The ideal fix would be to accept functions returning `!` (or rather, _not_ returning), but this is blocked on the [stabilisation of the `!` type itself](https://doc.rust-lang.org/std/primitive.never.html), which is not done. The "simple" fix would be to add an explicit `-> ()` to system signatures (e.g., `|| { todo!() }` becomes `|| -> () { todo!() }`). However, this is _also_ banned, as there is an existing lint which (IMO, incorrectly) marks this as an unnecessary annotation. So, the "fix" (read: workaround) is to put these kinds of `|| -> ! { ... }` closuers into variables and give the variable an explicit type (e.g., `fn()`). ```rust // Valid let system: fn() = || todo!("Not implemented yet!"); app.add_systems(..., system); // Invalid app.add_systems(..., || todo!("Not implemented yet!")); ``` ### Temporary Variable Lifetimes The order in which temporary variables are dropped has changed. The simple fix here is _usually_ to just assign temporaries to a named variable before use. ### `gen` is a keyword We can no longer use the name `gen` as it is reserved for a future generator syntax. This involved replacing uses of the name `gen` with `r#gen` (the raw-identifier syntax). ### Formatting has changed Use statements have had the order of imports changed, causing a substantial +/-3,000 diff when applied. For now, I have opted-out of this change by amending `rustfmt.toml` ```toml style_edition = "2021" ``` This preserves the original formatting for now, reducing the size of this PR. It would be a simple followup to update this to 2024 and run `cargo fmt`. ### New `use<>` Opt-Out Syntax Lifetimes are now implicitly included in RPIT types. There was a handful of instances where it needed to be added to satisfy the borrow checker, but there may be more cases where it _should_ be added to avoid breakages in user code. ### `MyUnitStruct { .. }` is an invalid pattern Previously, you could match against unit structs (and unit enum variants) with a `{ .. }` destructuring. This is no longer valid. ### Pretty much every use of `ref` and `mut` are gone Pattern binding has changed to the point where these terms are largely unused now. They still serve a purpose, but it is far more niche now. ### `iter::repeat(...).take(...)` is bad New lint recommends using the more explicit `iter::repeat_n(..., ...)` instead. ## Migration Guide The lifetimes of functions using return-position impl-trait (RPIT) are likely _more_ conservative than they had been previously. If you encounter lifetime issues with such a function, please create an issue to investigate the addition of `+ use<...>`. ## Notes - Check the individual commits for a clearer breakdown for what _actually_ changed. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com>
476 lines
25 KiB
Rust
476 lines
25 KiB
Rust
use bevy_color::{Color, ColorToComponents, LinearRgba};
|
|
use bevy_ecs::prelude::*;
|
|
use bevy_math::{ops, Vec3};
|
|
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
|
use bevy_render::{extract_component::ExtractComponent, prelude::Camera};
|
|
|
|
/// Configures the “classic” computer graphics [distance fog](https://en.wikipedia.org/wiki/Distance_fog) effect,
|
|
/// in which objects appear progressively more covered in atmospheric haze the further away they are from the camera.
|
|
/// Affects meshes rendered via the PBR [`StandardMaterial`](crate::StandardMaterial).
|
|
///
|
|
/// ## Falloff
|
|
///
|
|
/// The rate at which fog intensity increases with distance is controlled by the falloff mode.
|
|
/// Currently, the following fog falloff modes are supported:
|
|
///
|
|
/// - [`FogFalloff::Linear`]
|
|
/// - [`FogFalloff::Exponential`]
|
|
/// - [`FogFalloff::ExponentialSquared`]
|
|
/// - [`FogFalloff::Atmospheric`]
|
|
///
|
|
/// ## Example
|
|
///
|
|
/// ```
|
|
/// # use bevy_ecs::prelude::*;
|
|
/// # use bevy_render::prelude::*;
|
|
/// # use bevy_core_pipeline::prelude::*;
|
|
/// # use bevy_pbr::prelude::*;
|
|
/// # use bevy_color::Color;
|
|
/// # fn system(mut commands: Commands) {
|
|
/// commands.spawn((
|
|
/// // Setup your camera as usual
|
|
/// Camera3d::default(),
|
|
/// // Add fog to the same entity
|
|
/// DistanceFog {
|
|
/// color: Color::WHITE,
|
|
/// falloff: FogFalloff::Exponential { density: 1e-3 },
|
|
/// ..Default::default()
|
|
/// },
|
|
/// ));
|
|
/// # }
|
|
/// # bevy_ecs::system::assert_is_system(system);
|
|
/// ```
|
|
///
|
|
/// ## Material Override
|
|
///
|
|
/// Once enabled for a specific camera, the fog effect can also be disabled for individual
|
|
/// [`StandardMaterial`](crate::StandardMaterial) instances via the `fog_enabled` flag.
|
|
#[derive(Debug, Clone, Component, Reflect, ExtractComponent)]
|
|
#[extract_component_filter(With<Camera>)]
|
|
#[reflect(Component, Default, Debug)]
|
|
pub struct DistanceFog {
|
|
/// The color of the fog effect.
|
|
///
|
|
/// **Tip:** The alpha channel of the color can be used to “modulate” the fog effect without
|
|
/// changing the fog falloff mode or parameters.
|
|
pub color: Color,
|
|
|
|
/// Color used to modulate the influence of directional light colors on the
|
|
/// fog, where the view direction aligns with each directional light direction,
|
|
/// producing a “glow” or light dispersion effect. (e.g. around the sun)
|
|
///
|
|
/// Use [`Color::NONE`] to disable the effect.
|
|
pub directional_light_color: Color,
|
|
|
|
/// The exponent applied to the directional light alignment calculation.
|
|
/// A higher value means a more concentrated “glow”.
|
|
pub directional_light_exponent: f32,
|
|
|
|
/// Determines which falloff mode to use, and its parameters.
|
|
pub falloff: FogFalloff,
|
|
}
|
|
|
|
/// Allows switching between different fog falloff modes, and configuring their parameters.
|
|
///
|
|
/// ## Convenience Methods
|
|
///
|
|
/// When using non-linear fog modes it can be hard to determine the right parameter values
|
|
/// for a given scene.
|
|
///
|
|
/// For easier artistic control, instead of creating the enum variants directly, you can use the
|
|
/// visibility-based convenience methods:
|
|
///
|
|
/// - For `FogFalloff::Exponential`:
|
|
/// - [`FogFalloff::from_visibility()`]
|
|
/// - [`FogFalloff::from_visibility_contrast()`]
|
|
///
|
|
/// - For `FogFalloff::ExponentialSquared`:
|
|
/// - [`FogFalloff::from_visibility_squared()`]
|
|
/// - [`FogFalloff::from_visibility_contrast_squared()`]
|
|
///
|
|
/// - For `FogFalloff::Atmospheric`:
|
|
/// - [`FogFalloff::from_visibility_color()`]
|
|
/// - [`FogFalloff::from_visibility_colors()`]
|
|
/// - [`FogFalloff::from_visibility_contrast_color()`]
|
|
/// - [`FogFalloff::from_visibility_contrast_colors()`]
|
|
#[derive(Debug, Clone, Reflect)]
|
|
pub enum FogFalloff {
|
|
/// A linear fog falloff that grows in intensity between `start` and `end` distances.
|
|
///
|
|
/// This falloff mode is simpler to control than other modes, however it can produce results that look “artificial”, depending on the scene.
|
|
///
|
|
/// ## Formula
|
|
///
|
|
/// The fog intensity for a given point in the scene is determined by the following formula:
|
|
///
|
|
/// ```text
|
|
/// let fog_intensity = 1.0 - ((end - distance) / (end - start)).clamp(0.0, 1.0);
|
|
/// ```
|
|
///
|
|
/// <svg width="370" height="212" viewBox="0 0 370 212" fill="none">
|
|
/// <title>Plot showing how linear fog falloff behaves for start and end values of 0.8 and 2.2, respectively.</title>
|
|
/// <path d="M331 151H42V49" stroke="currentColor" stroke-width="2"/>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <path d="M43 150H117.227L263 48H331" stroke="#FF00E5"/>
|
|
/// <path d="M118 151V49" stroke="#FF00E5" stroke-dasharray="1 4"/>
|
|
/// <path d="M263 151V49" stroke="#FF00E5" stroke-dasharray="1 4"/>
|
|
/// <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>
|
|
/// <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>
|
|
/// </svg>
|
|
Linear {
|
|
/// Distance from the camera where fog is completely transparent, in world units.
|
|
start: f32,
|
|
|
|
/// Distance from the camera where fog is completely opaque, in world units.
|
|
end: f32,
|
|
},
|
|
|
|
/// An exponential fog falloff with a given `density`.
|
|
///
|
|
/// Initially gains intensity quickly with distance, then more slowly. Typically produces more natural results than [`FogFalloff::Linear`],
|
|
/// but is a bit harder to control.
|
|
///
|
|
/// To move the fog “further away”, use lower density values. To move it “closer” use higher density values.
|
|
///
|
|
/// ## Tips
|
|
///
|
|
/// - Use the [`FogFalloff::from_visibility()`] convenience method to create an exponential falloff with the proper
|
|
/// density for a desired visibility distance in world units;
|
|
/// - It's not _unusual_ to have very large or very small values for the density, depending on the scene
|
|
/// scale. Typically, for scenes with objects in the scale of thousands of units, you might want density values
|
|
/// in the ballpark of `0.001`. Conversely, for really small scale scenes you might want really high values of
|
|
/// density;
|
|
/// - Combine the `density` parameter with the [`DistanceFog`] `color`'s alpha channel for easier artistic control.
|
|
///
|
|
/// ## Formula
|
|
///
|
|
/// The fog intensity for a given point in the scene is determined by the following formula:
|
|
///
|
|
/// ```text
|
|
/// let fog_intensity = 1.0 - 1.0 / (distance * density).exp();
|
|
/// ```
|
|
///
|
|
/// <svg width="370" height="212" viewBox="0 0 370 212" fill="none">
|
|
/// <title>Plot showing how exponential fog falloff behaves for different density values</title>
|
|
/// <mask id="mask0_3_31" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="42" y="42" width="286" height="108">
|
|
/// <rect x="42" y="42" width="286" height="108" fill="#D9D9D9"/>
|
|
/// </mask>
|
|
/// <g mask="url(#mask0_3_31)">
|
|
/// <path d="M42 150C42 150 98.3894 53 254.825 53L662 53" stroke="#FF003D" stroke-width="1"/>
|
|
/// <path d="M42 150C42 150 139.499 53 409.981 53L1114 53" stroke="#001AFF" stroke-width="1"/>
|
|
/// <path d="M42 150C42 150 206.348 53 662.281 53L1849 53" stroke="#14FF00" stroke-width="1"/>
|
|
/// </g>
|
|
/// <path d="M331 151H42V49" stroke="currentColor" stroke-width="2"/>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// </svg>
|
|
Exponential {
|
|
/// Multiplier applied to the world distance (within the exponential fog falloff calculation).
|
|
density: f32,
|
|
},
|
|
|
|
/// A squared exponential fog falloff with a given `density`.
|
|
///
|
|
/// Similar to [`FogFalloff::Exponential`], but grows more slowly in intensity for closer distances
|
|
/// before “catching up”.
|
|
///
|
|
/// To move the fog “further away”, use lower density values. To move it “closer” use higher density values.
|
|
///
|
|
/// ## Tips
|
|
///
|
|
/// - Use the [`FogFalloff::from_visibility_squared()`] convenience method to create an exponential squared falloff
|
|
/// with the proper density for a desired visibility distance in world units;
|
|
/// - Combine the `density` parameter with the [`DistanceFog`] `color`'s alpha channel for easier artistic control.
|
|
///
|
|
/// ## Formula
|
|
///
|
|
/// The fog intensity for a given point in the scene is determined by the following formula:
|
|
///
|
|
/// ```text
|
|
/// let fog_intensity = 1.0 - 1.0 / (distance * density).squared().exp();
|
|
/// ```
|
|
///
|
|
/// <svg width="370" height="212" viewBox="0 0 370 212" fill="none">
|
|
/// <title>Plot showing how exponential squared fog falloff behaves for different density values</title>
|
|
/// <mask id="mask0_1_3" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="42" y="42" width="286" height="108">
|
|
/// <rect x="42" y="42" width="286" height="108" fill="#D9D9D9"/>
|
|
/// </mask>
|
|
/// <g mask="url(#mask0_1_3)">
|
|
/// <path d="M42 150C75.4552 150 74.9241 53.1724 166.262 53.1724L404 53.1724" stroke="#FF003D" stroke-width="1"/>
|
|
/// <path d="M42 150C107.986 150 106.939 53.1724 287.091 53.1724L756 53.1724" stroke="#001AFF" stroke-width="1"/>
|
|
/// <path d="M42 150C166.394 150 164.42 53.1724 504.035 53.1724L1388 53.1724" stroke="#14FF00" stroke-width="1"/>
|
|
/// </g>
|
|
/// <path d="M331 151H42V49" stroke="currentColor" stroke-width="2"/>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// <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>
|
|
/// </svg>
|
|
ExponentialSquared {
|
|
/// Multiplier applied to the world distance (within the exponential squared fog falloff calculation).
|
|
density: f32,
|
|
},
|
|
|
|
/// A more general form of the [`FogFalloff::Exponential`] mode. The falloff formula is separated into
|
|
/// two terms, `extinction` and `inscattering`, for a somewhat simplified atmospheric scattering model.
|
|
/// Additionally, individual color channels can have their own density values, resulting in a total of
|
|
/// six different configuration parameters.
|
|
///
|
|
/// ## Tips
|
|
///
|
|
/// - Use the [`FogFalloff::from_visibility_colors()`] or [`FogFalloff::from_visibility_color()`] convenience methods
|
|
/// to create an atmospheric falloff with the proper densities for a desired visibility distance in world units and
|
|
/// extinction and inscattering colors;
|
|
/// - Combine the atmospheric fog parameters with the [`DistanceFog`] `color`'s alpha channel for easier artistic control.
|
|
///
|
|
/// ## Formula
|
|
///
|
|
/// Unlike other modes, atmospheric falloff doesn't use a simple intensity-based blend of fog color with
|
|
/// object color. Instead, it calculates per-channel extinction and inscattering factors, which are
|
|
/// then used to calculate the final color.
|
|
///
|
|
/// ```text
|
|
/// let extinction_factor = 1.0 - 1.0 / (distance * extinction).exp();
|
|
/// let inscattering_factor = 1.0 - 1.0 / (distance * inscattering).exp();
|
|
/// let result = input_color * (1.0 - extinction_factor) + fog_color * inscattering_factor;
|
|
/// ```
|
|
///
|
|
/// ## Equivalence to [`FogFalloff::Exponential`]
|
|
///
|
|
/// For a density value of `D`, the following two falloff modes will produce identical visual results:
|
|
///
|
|
/// ```
|
|
/// # use bevy_pbr::prelude::*;
|
|
/// # use bevy_math::prelude::*;
|
|
/// # const D: f32 = 0.5;
|
|
/// #
|
|
/// let exponential = FogFalloff::Exponential {
|
|
/// density: D,
|
|
/// };
|
|
///
|
|
/// let atmospheric = FogFalloff::Atmospheric {
|
|
/// extinction: Vec3::new(D, D, D),
|
|
/// inscattering: Vec3::new(D, D, D),
|
|
/// };
|
|
/// ```
|
|
///
|
|
/// **Note:** While the results are identical, [`FogFalloff::Atmospheric`] is computationally more expensive.
|
|
Atmospheric {
|
|
/// Controls how much light is removed due to atmospheric “extinction”, i.e. loss of light due to
|
|
/// photons being absorbed by atmospheric particles.
|
|
///
|
|
/// Each component can be thought of as an independent per `R`/`G`/`B` channel `density` factor from
|
|
/// [`FogFalloff::Exponential`]: Multiplier applied to the world distance (within the fog
|
|
/// falloff calculation) for that specific channel.
|
|
///
|
|
/// **Note:**
|
|
/// This value is not a `Color`, since it affects the channels exponentially in a non-intuitive way.
|
|
/// For artistic control, use the [`FogFalloff::from_visibility_colors()`] convenience method.
|
|
extinction: Vec3,
|
|
|
|
/// Controls how much light is added due to light scattering from the sun through the atmosphere.
|
|
///
|
|
/// Each component can be thought of as an independent per `R`/`G`/`B` channel `density` factor from
|
|
/// [`FogFalloff::Exponential`]: A multiplier applied to the world distance (within the fog
|
|
/// falloff calculation) for that specific channel.
|
|
///
|
|
/// **Note:**
|
|
/// This value is not a `Color`, since it affects the channels exponentially in a non-intuitive way.
|
|
/// For artistic control, use the [`FogFalloff::from_visibility_colors()`] convenience method.
|
|
inscattering: Vec3,
|
|
},
|
|
}
|
|
|
|
impl FogFalloff {
|
|
/// Creates a [`FogFalloff::Exponential`] value from the given visibility distance in world units,
|
|
/// using the revised Koschmieder contrast threshold, [`FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD`].
|
|
pub fn from_visibility(visibility: f32) -> FogFalloff {
|
|
FogFalloff::from_visibility_contrast(
|
|
visibility,
|
|
FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD,
|
|
)
|
|
}
|
|
|
|
/// Creates a [`FogFalloff::Exponential`] value from the given visibility distance in world units,
|
|
/// and a given contrast threshold in the range of `0.0` to `1.0`.
|
|
pub fn from_visibility_contrast(visibility: f32, contrast_threshold: f32) -> FogFalloff {
|
|
FogFalloff::Exponential {
|
|
density: FogFalloff::koschmieder(visibility, contrast_threshold),
|
|
}
|
|
}
|
|
|
|
/// Creates a [`FogFalloff::ExponentialSquared`] value from the given visibility distance in world units,
|
|
/// using the revised Koschmieder contrast threshold, [`FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD`].
|
|
pub fn from_visibility_squared(visibility: f32) -> FogFalloff {
|
|
FogFalloff::from_visibility_contrast_squared(
|
|
visibility,
|
|
FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD,
|
|
)
|
|
}
|
|
|
|
/// Creates a [`FogFalloff::ExponentialSquared`] value from the given visibility distance in world units,
|
|
/// and a given contrast threshold in the range of `0.0` to `1.0`.
|
|
pub fn from_visibility_contrast_squared(
|
|
visibility: f32,
|
|
contrast_threshold: f32,
|
|
) -> FogFalloff {
|
|
FogFalloff::ExponentialSquared {
|
|
density: (FogFalloff::koschmieder(visibility, contrast_threshold) / visibility).sqrt(),
|
|
}
|
|
}
|
|
|
|
/// Creates a [`FogFalloff::Atmospheric`] value from the given visibility distance in world units,
|
|
/// and a shared color for both extinction and inscattering, using the revised Koschmieder contrast threshold,
|
|
/// [`FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD`].
|
|
pub fn from_visibility_color(
|
|
visibility: f32,
|
|
extinction_inscattering_color: Color,
|
|
) -> FogFalloff {
|
|
FogFalloff::from_visibility_contrast_colors(
|
|
visibility,
|
|
FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD,
|
|
extinction_inscattering_color,
|
|
extinction_inscattering_color,
|
|
)
|
|
}
|
|
|
|
/// Creates a [`FogFalloff::Atmospheric`] value from the given visibility distance in world units,
|
|
/// extinction and inscattering colors, using the revised Koschmieder contrast threshold,
|
|
/// [`FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD`].
|
|
///
|
|
/// ## Tips
|
|
/// - Alpha values of the provided colors can modulate the `extinction` and `inscattering` effects;
|
|
/// - Using an `extinction_color` of [`Color::WHITE`] or [`Color::NONE`] disables the extinction effect;
|
|
/// - Using an `inscattering_color` of [`Color::BLACK`] or [`Color::NONE`] disables the inscattering effect.
|
|
pub fn from_visibility_colors(
|
|
visibility: f32,
|
|
extinction_color: Color,
|
|
inscattering_color: Color,
|
|
) -> FogFalloff {
|
|
FogFalloff::from_visibility_contrast_colors(
|
|
visibility,
|
|
FogFalloff::REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD,
|
|
extinction_color,
|
|
inscattering_color,
|
|
)
|
|
}
|
|
|
|
/// Creates a [`FogFalloff::Atmospheric`] value from the given visibility distance in world units,
|
|
/// a contrast threshold in the range of `0.0` to `1.0`, and a shared color for both extinction and inscattering.
|
|
pub fn from_visibility_contrast_color(
|
|
visibility: f32,
|
|
contrast_threshold: f32,
|
|
extinction_inscattering_color: Color,
|
|
) -> FogFalloff {
|
|
FogFalloff::from_visibility_contrast_colors(
|
|
visibility,
|
|
contrast_threshold,
|
|
extinction_inscattering_color,
|
|
extinction_inscattering_color,
|
|
)
|
|
}
|
|
|
|
/// Creates a [`FogFalloff::Atmospheric`] value from the given visibility distance in world units,
|
|
/// a contrast threshold in the range of `0.0` to `1.0`, extinction and inscattering colors.
|
|
///
|
|
/// ## Tips
|
|
/// - Alpha values of the provided colors can modulate the `extinction` and `inscattering` effects;
|
|
/// - Using an `extinction_color` of [`Color::WHITE`] or [`Color::NONE`] disables the extinction effect;
|
|
/// - Using an `inscattering_color` of [`Color::BLACK`] or [`Color::NONE`] disables the inscattering effect.
|
|
pub fn from_visibility_contrast_colors(
|
|
visibility: f32,
|
|
contrast_threshold: f32,
|
|
extinction_color: Color,
|
|
inscattering_color: Color,
|
|
) -> FogFalloff {
|
|
use core::f32::consts::E;
|
|
|
|
let [r_e, g_e, b_e, a_e] = LinearRgba::from(extinction_color).to_f32_array();
|
|
let [r_i, g_i, b_i, a_i] = LinearRgba::from(inscattering_color).to_f32_array();
|
|
|
|
FogFalloff::Atmospheric {
|
|
extinction: Vec3::new(
|
|
// Values are subtracted from 1.0 here to preserve the intuitive/artistic meaning of
|
|
// colors, since they're later subtracted. (e.g. by giving a blue extinction color, you
|
|
// get blue and _not_ yellow results)
|
|
ops::powf(1.0 - r_e, E),
|
|
ops::powf(1.0 - g_e, E),
|
|
ops::powf(1.0 - b_e, E),
|
|
) * FogFalloff::koschmieder(visibility, contrast_threshold)
|
|
* ops::powf(a_e, E),
|
|
|
|
inscattering: Vec3::new(ops::powf(r_i, E), ops::powf(g_i, E), ops::powf(b_i, E))
|
|
* FogFalloff::koschmieder(visibility, contrast_threshold)
|
|
* ops::powf(a_i, E),
|
|
}
|
|
}
|
|
|
|
/// A 2% contrast threshold was originally proposed by Koschmieder, being the
|
|
/// minimum visual contrast at which a human observer could detect an object.
|
|
/// We use a revised 5% contrast threshold, deemed more realistic for typical human observers.
|
|
pub const REVISED_KOSCHMIEDER_CONTRAST_THRESHOLD: f32 = 0.05;
|
|
|
|
/// Calculates the extinction coefficient β, from V and Cₜ, where:
|
|
///
|
|
/// - Cₜ is the contrast threshold, in the range of `0.0` to `1.0`
|
|
/// - V is the visibility distance in which a perfectly black object is still identifiable
|
|
/// against the horizon sky within the contrast threshold
|
|
///
|
|
/// We start with Koschmieder's equation:
|
|
///
|
|
/// ```text
|
|
/// -ln(Cₜ)
|
|
/// V = ─────────
|
|
/// β
|
|
/// ```
|
|
///
|
|
/// Multiplying both sides by β/V, that gives us:
|
|
///
|
|
/// ```text
|
|
/// -ln(Cₜ)
|
|
/// β = ─────────
|
|
/// V
|
|
/// ```
|
|
///
|
|
/// See:
|
|
/// - <https://en.wikipedia.org/wiki/Visibility>
|
|
/// - <https://www.biral.com/wp-content/uploads/2015/02/Introduction_to_visibility-v2-2.pdf>
|
|
pub fn koschmieder(v: f32, c_t: f32) -> f32 {
|
|
-ops::ln(c_t) / v
|
|
}
|
|
}
|
|
|
|
impl Default for DistanceFog {
|
|
fn default() -> Self {
|
|
DistanceFog {
|
|
color: Color::WHITE,
|
|
falloff: FogFalloff::Linear {
|
|
start: 0.0,
|
|
end: 100.0,
|
|
},
|
|
directional_light_color: Color::NONE,
|
|
directional_light_exponent: 8.0,
|
|
}
|
|
}
|
|
}
|