-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merged by Bors] - Add Distance and Atmospheric Fog support #6412
Changes from 10 commits
014a9cb
6458025
dfbd7f6
d7afc3e
fb588a5
0d2afb9
8855bdd
f7fb67e
8c8aa79
d999308
c446fcc
2138dfc
b7c478f
db1eca0
1e7f6c3
6b8a196
6d2a41a
7de98fd
505a5a2
e13c5c3
5d8f708
e7a4a98
ddb4eb9
48295a0
7a88d0e
50f0f50
00226a3
e18f682
e4f01e2
e626862
270dcd9
8ecba3b
68447a2
a2a5b07
a4ee547
e0195d3
e830d93
a998fa8
c608a75
aeafcc7
ecea533
73ad380
3323542
92dac89
256fce6
be50d07
50a41cd
a35783a
8261011
11f1bb5
cbb330c
bf668da
e3b65c8
036fbd3
9f3b0c4
49a3f1e
f9056c0
3c9b359
e123574
2664ca1
d54dce2
ccfb0ce
6ab1188
f44e381
aa3fef7
f1cebd2
f9ceb4c
820a6b1
cd11d43
df77ce3
15afdfb
c4eb571
28f4fb0
8b99f6f
1e3ab2c
5fd6a42
8c4d4e9
4608dbd
fee838d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
use crate::ReflectComponent; | ||
use bevy_ecs::{prelude::*, query::QueryItem}; | ||
use bevy_reflect::Reflect; | ||
use bevy_render::{color::Color, extract_component::ExtractComponent, prelude::Camera}; | ||
|
||
/// A “classic” computer graphics [distance fog](https://en.wikipedia.org/wiki/Distance_fog) effect, that makes | ||
/// objects appear progressively more covered in atmospheric haze as they move further away from the camera. | ||
/// Affects meshes rendered via the PBR [`StandardMaterial`](crate::StandardMaterial). | ||
/// Configurable independently for each camera. | ||
/// | ||
/// Currently, the following fog modes are supported: | ||
/// | ||
/// - [`FogMode::Linear`] | ||
/// - [`FogMode::Exponential`] | ||
/// - [`FogMode::ExponentialSquared`] | ||
/// | ||
/// ## Example | ||
/// | ||
/// ``` | ||
/// # use bevy_ecs::prelude::*; | ||
/// # use bevy_render::prelude::*; | ||
/// # use bevy_core_pipeline::prelude::*; | ||
/// # use bevy_pbr::prelude::*; | ||
/// # fn system(mut commands: Commands) { | ||
/// commands.spawn(( | ||
/// // Setup your camera as usual | ||
/// Camera3dBundle { | ||
/// // ... camera options | ||
/// # ..Default::default() | ||
/// }, | ||
/// // Add fog to the same entity | ||
/// Fog { | ||
/// color: Color::WHITE, | ||
/// mode: FogMode::Exponential { density: 1e-3 }, | ||
/// }, | ||
/// )); | ||
/// # } | ||
/// # 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 `no_fog` flag. | ||
#[derive(Debug, Clone, Component, Reflect)] | ||
#[reflect(Component)] | ||
pub struct Fog { | ||
/// 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 mode or parameters. | ||
pub color: Color, | ||
|
||
/// Determines which “mode” of fog rendering to use, and provides parameters for each mode. | ||
pub mode: FogMode, | ||
} | ||
|
||
/// Allows switching between different the [`Fog`] “modes”, and configuring their parameters. | ||
#[derive(Debug, Clone, Reflect)] | ||
pub enum FogMode { | ||
/// A linear fog effect that grows in intensity between `start` and `end` distances. | ||
/// | ||
/// This 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 the linear fog mode 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 | ||
start: f32, | ||
|
||
// Distance from the camera where fog is completely opaque | ||
end: f32, | ||
}, | ||
coreh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// An exponential fog effect with a given `density`. | ||
/// | ||
/// Initially gains intensity quickly with distance, then more slowly. Typically produces more natural results than [`FogMode::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. | ||
/// | ||
/// **Note:** 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 `1e-3`. Conversely, for really small scale scenes you might want really high values of | ||
/// density. | ||
/// | ||
/// **Tip:** You can combine the `density` parameter with the [`Fog`] `color`'s alpha channel for easier 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 the exponential fog mode 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 { density: f32 }, | ||
coreh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// A squared exponential fog effect with a given `density`. | ||
/// | ||
/// Similar to [`FogMode::Exponential`], but grows more slowly in intensity for closer distances | ||
/// before “catching up”. | ||
/// | ||
/// ## 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).powi(2).exp(); | ||
/// ``` | ||
/// | ||
/// <svg width="370" height="212" viewBox="0 0 370 212" fill="none"> | ||
/// <title>Plot showing how the exponential squared fog mode 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 { density: f32 }, | ||
coreh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
impl Default for Fog { | ||
fn default() -> Self { | ||
Fog { | ||
color: Color::rgba(1.0, 1.0, 1.0, 1.0), | ||
mode: FogMode::Linear { | ||
start: 0.0, | ||
end: 100.0, | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl ExtractComponent for Fog { | ||
type Query = &'static Self; | ||
type Filter = With<Camera>; | ||
|
||
fn extract_component(item: QueryItem<Self::Query>) -> Self { | ||
item.clone() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -204,6 +204,9 @@ pub struct StandardMaterial { | |
/// shadows, alpha mode and ambient light are ignored if this is set to `true`. | ||
pub unlit: bool, | ||
|
||
/// Whether to disable fog for this material. | ||
pub no_fog: bool, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we make this the positive version? i.e. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can, I used a negative form mainly to make it consistent with BTW, do you think a per-entity setting would make sense, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think setting no_fog: false is more confusing. :) A per-mesh entity flag could be good but it could be done in a follow up PR if you prefer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Couldn't you just invert it were you are using it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed it, it's now a positive rather than a negative flag. Yeah, I think I prefer to do the entity flag on a follow up PR, this branch/thread is already running quite long, it's getting somewhat hard to follow. |
||
|
||
/// How to apply the alpha channel of the `base_color_texture`. | ||
/// | ||
/// See [`AlphaMode`] for details. Defaults to [`AlphaMode::Opaque`]. | ||
|
@@ -254,6 +257,7 @@ impl Default for StandardMaterial { | |
double_sided: false, | ||
cull_mode: Some(Face::Back), | ||
unlit: false, | ||
no_fog: false, | ||
alpha_mode: AlphaMode::Opaque, | ||
depth_bias: 0.0, | ||
} | ||
|
@@ -300,6 +304,7 @@ bitflags::bitflags! { | |
const ALPHA_MODE_BLEND = (1 << 8); | ||
const TWO_COMPONENT_NORMAL_MAP = (1 << 9); | ||
const FLIP_NORMAL_MAP_Y = (1 << 10); | ||
const NO_FOG = (1 << 11); | ||
const NONE = 0; | ||
const UNINITIALIZED = 0xFFFF; | ||
} | ||
|
@@ -350,6 +355,9 @@ impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial { | |
if self.unlit { | ||
flags |= StandardMaterialFlags::UNLIT; | ||
} | ||
if self.no_fog { | ||
flags |= StandardMaterialFlags::NO_FOG; | ||
} | ||
let has_normal_map = self.normal_map_texture.is_some(); | ||
if has_normal_map { | ||
if let Some(texture) = images.get(self.normal_map_texture.as_ref().unwrap()) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL you can embed SVG graphs inside of rustdoc!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw the
Color
constant docs that use styled<span>
s and decided to give it a try, was also pleasantly surprised