Skip to content
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

Closed
wants to merge 79 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
014a9cb
Add fog support to bevy_pbr
coreh Oct 29, 2022
6458025
Add flag to `StandardMaterial` to disable fog
coreh Oct 29, 2022
dfbd7f6
Add fog documentation
coreh Oct 29, 2022
d7afc3e
Fix example code that's failing on CI
coreh Oct 29, 2022
fb588a5
Add additional fog documentation
coreh Oct 30, 2022
0d2afb9
Make `Fog` a `Component` instead of a `Resource`
coreh Nov 1, 2022
8855bdd
Consolidate fog-related setup into `FogPlugin`
coreh Nov 1, 2022
f7fb67e
Fix example code that's failing on CI
coreh Nov 1, 2022
8c8aa79
Remove `FogMode::Off`
coreh Nov 2, 2022
d999308
Add Fog example
coreh Nov 2, 2022
c446fcc
Fix clippy issues
coreh Nov 2, 2022
2138dfc
Apply feedback on example and fix mismatched variable names
coreh Nov 3, 2022
b7c478f
Add controls table to example module documentation
coreh Nov 3, 2022
db1eca0
Add semicolon
coreh Nov 3, 2022
1e7f6c3
Merge branch 'main' into fog
coreh Nov 3, 2022
6b8a196
Rename fog component and falloff enum, for consistency and reusability
coreh Nov 3, 2022
6d2a41a
Add initial implementation of atmospheric fog and directional light s…
coreh Nov 3, 2022
7de98fd
Fix documentation broken/missing links
coreh Nov 3, 2022
505a5a2
Fix example code now that there are more fog settings
coreh Nov 3, 2022
e13c5c3
Fix clippy issue
coreh Nov 3, 2022
5d8f708
Fix the spelling of the word ‘exponent’
coreh Nov 4, 2022
e7a4a98
Use `Time::elapsed_seconds()` and `Time::delta_seconds()`
coreh Nov 4, 2022
ddb4eb9
Improve documentation, in response to PR feedback
coreh Nov 4, 2022
48295a0
Improve `GpuFog` documentation comments
coreh Nov 4, 2022
7a88d0e
Optimize/cleanup shader code
coreh Nov 4, 2022
50f0f50
Take directional light colors into account for scattering
coreh Nov 4, 2022
00226a3
Rename `scattering_color` to `directional_light_color`
coreh Nov 4, 2022
e18f682
Merge branch 'main' into fog
coreh Nov 4, 2022
e4f01e2
Merge branch 'main' into fog
coreh Nov 4, 2022
e626862
Add atmospheric fog example
coreh Nov 4, 2022
270dcd9
Fix clippy issue
coreh Nov 4, 2022
8ecba3b
Add methods for more easily configuring atmospheric fog
coreh Nov 4, 2022
68447a2
Use new visibility helper method in atmospheric fog example
coreh Nov 4, 2022
a2a5b07
Improve documentation
coreh Nov 4, 2022
a4ee547
Use `Vec3` instead of `Color` for extinction and inscattering
coreh Nov 4, 2022
e0195d3
Improve documentation
coreh Nov 4, 2022
e830d93
Add convenience methods for `FogFalloff::ExponentialSquared`
coreh Nov 4, 2022
a998fa8
Tweaks to documentation formatting and punctuation
coreh Nov 4, 2022
c608a75
Improve documentation
coreh Nov 4, 2022
aeafcc7
Remove misleading note about exponential density values
coreh Nov 4, 2022
ecea533
Fix clippy issue
coreh Nov 4, 2022
73ad380
Remove now-unnecessary UI camera
coreh Nov 4, 2022
3323542
Add comments explaining values
coreh Nov 4, 2022
92dac89
Fix documentation tests
coreh Nov 4, 2022
256fce6
Update examples/README.md
coreh Nov 4, 2022
be50d07
Merge branch 'main' into fog
coreh Nov 4, 2022
50a41cd
Use positive `fog_enabled` flag (instead of `no_fog`)
coreh Nov 4, 2022
a35783a
Add minor clarification that this is the color obtained after extinction
coreh Nov 4, 2022
8261011
Rename `result` to `fog_color` in shader
coreh Nov 4, 2022
11f1bb5
Fix search and replace mishap
coreh Nov 4, 2022
cbb330c
Use names consistent with documentation
coreh Nov 4, 2022
bf668da
Move fog to outside of `pbr()`, so that it applies to unlit materials
coreh Nov 4, 2022
e3b65c8
Merge branch 'main' into fog
coreh Nov 6, 2022
036fbd3
Add controls table and move system to last
coreh Nov 6, 2022
9f3b0c4
Improve documentation
coreh Nov 6, 2022
49a3f1e
Use “glow” instead of “halo” in explanation
coreh Nov 6, 2022
f9056c0
Improve documentation
coreh Nov 6, 2022
3c9b359
Fix constants typo in documentation comments
coreh Nov 8, 2022
e123574
Fix additional constant typos
coreh Nov 9, 2022
2664ca1
Merge branch 'main' into fog
coreh Nov 12, 2022
d54dce2
Reduce sky size, fixing (floating point precision?) flicker in WASM /…
coreh Nov 20, 2022
ccfb0ce
Merge branch 'main' into fog
coreh Nov 24, 2022
6ab1188
Break up long lines in WGSL shaders for readability
coreh Jan 15, 2023
f44e381
Remove incorrect claim that extinction factor is ‘unitless’ from docu…
coreh Jan 15, 2023
aa3fef7
Improve documentation wording
coreh Jan 15, 2023
f1cebd2
Increase font size in fog examples
coreh Jan 15, 2023
f9ceb4c
Merge branch 'main' into fog
coreh Jan 15, 2023
820a6b1
Merge branch 'main' into fog
coreh Jan 21, 2023
cd11d43
Make sure `apply_fog()` function is not defined during normal prepass
coreh Jan 21, 2023
df77ce3
Merge branch 'main' into fog
coreh Jan 21, 2023
15afdfb
Fix code for creating exponential squared fog
coreh Jan 28, 2023
c4eb571
Remove unused `KOSCHMIEDER_CONTRAST_THRESHOLD` constant
coreh Jan 28, 2023
28f4fb0
Tweak wording, remove trailing whitespace
coreh Jan 28, 2023
8b99f6f
Reorder `GpuFog` fields for better struct packing
coreh Jan 28, 2023
1e3ab2c
Extract repeated scattering code into function
coreh Jan 28, 2023
5fd6a42
Merge branch 'main' into fog
coreh Jan 28, 2023
8c4d4e9
Properly configure cascade shadow map for scene
coreh Jan 28, 2023
4608dbd
Use multiplication instead of `pow()` with `2.0`
coreh Jan 28, 2023
fee838d
Pass fragment and view positions and calculate `view_to_world` in `ap…
coreh Jan 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,17 @@ description = "A scene showcasing the built-in 3D shapes"
category = "3D Rendering"
wasm = true

[[example]]
name = "fog"
path = "examples/3d/fog.rs"

[package.metadata.example.fog]
name = "Fog"
description = "A scene showcasing the distance fog effect"
category = "3D Rendering"
wasm = true


[[example]]
name = "lighting"
path = "examples/3d/lighting.rs"
Expand Down
20 changes: 14 additions & 6 deletions crates/bevy_pbr/src/fog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use bevy_render::{color::Color, extract_component::ExtractComponent, prelude::Ca
///
/// 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, Default, Component, Reflect)]
#[derive(Debug, Clone, Component, Reflect)]
#[reflect(Component)]
pub struct Fog {
/// The color of the fog effect.
Expand All @@ -56,12 +56,8 @@ pub struct Fog {
}

/// Allows switching between different the [`Fog`] “modes”, and configuring their parameters.
#[derive(Debug, Clone, Default, Reflect)]
#[derive(Debug, Clone, Reflect)]
pub enum FogMode {
/// Fog effect is disabled (the default)
#[default]
Off,

/// 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.
Expand Down Expand Up @@ -182,6 +178,18 @@ pub enum FogMode {
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>;
Expand Down
4 changes: 0 additions & 4 deletions crates/bevy_pbr/src/render/fog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ pub fn prepare_fog(
for (entity, fog) in &views {
let gpu_fog = if let Some(fog) = fog {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using an if let else and then a match, you can use a match like Some(Linear) => ..., Some(Exponential) => ..., None => GPU_FOG_MODE_OFF.

Copy link
Contributor Author

@coreh coreh Jan 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hadn't considered using a nested match pattern... Just tried it out, I think the need for matching color, directional_light_color and directional_light_exponent for every case makes it a little bit less readable than the if/else + match combo, but I'm open to changing it, so your call.

It would end up looking like this:

let gpu_fog = match &fog {
    Some(FogSettings {
        falloff: FogFalloff::Linear { start, end },
        color,
        directional_light_color,
        directional_light_exponent,
    }) => GpuFog {
        mode: GPU_FOG_MODE_LINEAR,
        base_color: (*color).into(),
        directional_light_color: (*directional_light_color).into(),
        directional_light_exponent: *directional_light_exponent,
        be: Vec3::new(*start, *end, 0.0),
        ..Default::default()
    },
    Some(FogSettings {
        falloff: FogFalloff::Exponential { density },
        color,
        directional_light_color,
        directional_light_exponent,
    }) => GpuFog {
        mode: GPU_FOG_MODE_EXPONENTIAL,
        base_color: (*color).into(),
        directional_light_color: (*directional_light_color).into(),
        directional_light_exponent: *directional_light_exponent,
        be: Vec3::new(*density, 0.0, 0.0),
        ..Default::default()
    },
    Some(FogSettings {
        falloff: FogFalloff::ExponentialSquared { density },
        color,
        directional_light_color,
        directional_light_exponent,
    }) => GpuFog {
        mode: GPU_FOG_MODE_EXPONENTIAL_SQUARED,
        base_color: (*color).into(),
        directional_light_color: (*directional_light_color).into(),
        directional_light_exponent: *directional_light_exponent,
        be: Vec3::new(*density, 0.0, 0.0),
        ..Default::default()
    },
    Some(FogSettings {
        falloff:
            FogFalloff::Atmospheric {
                extinction,
                inscattering,
            },
        color,
        directional_light_color,
        directional_light_exponent,
    }) => GpuFog {
        mode: GPU_FOG_MODE_ATMOSPHERIC,
        base_color: (*color).into(),
        directional_light_color: (*directional_light_color).into(),
        directional_light_exponent: *directional_light_exponent,
        be: *extinction,
        bi: *inscattering,
    },
    // If no fog is added to a camera, by default it's off
    None => GpuFog {
        mode: GPU_FOG_MODE_OFF,
        ..Default::default()
    },
};

match &fog.mode {
FogMode::Off => GpuFog {
mode: GPU_FOG_MODE_OFF,
..Default::default()
},
FogMode::Linear { start, end } => GpuFog {
mode: GPU_FOG_MODE_LINEAR,
color: fog.color.into(),
Expand Down
310 changes: 310 additions & 0 deletions examples/3d/fog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
//! This example shows how to use distance fog
coreh marked this conversation as resolved.
Show resolved Hide resolved

use bevy::{
pbr::{NotShadowCaster, NotShadowReceiver},
prelude::*,
};

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup_camera_fog)
.add_startup_system(setup_pyramid_scene)
.add_startup_system(setup_instructions)
.add_system(update_system)
.run();
}

fn setup_camera_fog(mut commands: Commands) {
commands.spawn((
Camera3dBundle::default(),
Fog {
color: Color::rgba(0.05, 0.05, 0.05, 1.0),
mode: FogMode::Linear {
start: 5.0,
end: 20.0,
},
},
));
}

fn setup_pyramid_scene(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let stone = materials.add(StandardMaterial {
base_color: Color::hex("28221B").unwrap(),
perceptual_roughness: 1.0,
..default()
});

// pillars
for (x, z) in vec![(-1.5, -1.5), (1.5, -1.5), (1.5, 1.5), (-1.5, 1.5)] {
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Box {
min_x: -0.5,
max_x: 0.5,
min_z: -0.5,
max_z: 0.5,
min_y: 0.0,
max_y: 3.0,
})),
material: stone.clone(),
transform: Transform::from_xyz(x, 0.0, z),
..default()
});
}

// orb
commands.spawn((
PbrBundle {
mesh: meshes.add(Mesh::from(shape::Icosphere::default())),
material: materials.add(StandardMaterial {
base_color: Color::hex("126212CC").unwrap(),
reflectance: 1.0,
perceptual_roughness: 0.0,
metallic: 0.5,
alpha_mode: AlphaMode::Blend,
..default()
}),
transform: Transform::from_scale(Vec3::splat(1.75))
.with_translation(Vec3::new(0.0, 4.0, 0.0)),
..default()
},
NotShadowCaster,
NotShadowReceiver,
));

// steps
for i in 0..50 {
let size = i as f32 / 2.0 + 3.0;
let y = -i as f32 / 2.0;
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Box {
min_x: -size,
max_x: size,
min_z: -size,
max_z: size,
min_y: 0.0,
max_y: 0.5,
})),
material: stone.clone(),
transform: Transform::from_xyz(0.0, y, 0.0),
..default()
});
}

// sky
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Box::default())),
material: materials.add(StandardMaterial {
base_color: Color::hex("28221B").unwrap(),
cull_mode: None,
perceptual_roughness: 1.0,
..default()
}),
transform: Transform::from_scale(Vec3::splat(10_000_000.0)),
..default()
});

// light
commands.spawn(PointLightBundle {
transform: Transform::from_xyz(0.0, 1.0, 0.0),
point_light: PointLight {
intensity: 1500.,
range: 100.,
shadows_enabled: true,
..default()
},
..default()
});
}

fn setup_instructions(mut commands: Commands, asset_server: Res<AssetServer>) {
// UI camera
commands.spawn(Camera2dBundle {
coreh marked this conversation as resolved.
Show resolved Hide resolved
camera: Camera {
priority: -1,
..default()
},
..default()
});

commands.spawn((TextBundle::from_section(
"",
TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 12.0,
color: Color::WHITE,
},
)
.with_style(Style {
position_type: PositionType::Absolute,
position: UiRect {
top: Val::Px(10.0),
left: Val::Px(10.0),
..default()
},
..default()
}),));
}

fn update_system(
mut camera: Query<(&mut Fog, &mut Transform)>,
mut text: Query<&mut Text>,
time: Res<Time>,
keycode: Res<Input<KeyCode>>,
) {
let now = time.elapsed().as_millis() as f32 / 1000.0;
let delta = time.delta().as_millis() as f32 / 1000.0;

let (mut fog, mut transform) = camera.single_mut();
let mut text = text.single_mut();

// Orbit camera around pyramid
let orbit_scale = 8.0 + (now / 10.0).sin() * 7.0;
*transform = Transform::from_xyz(
(now / 5.0).cos() * orbit_scale,
12.0 - orbit_scale / 2.0,
(now / 5.0).sin() * orbit_scale,
)
.looking_at(Vec3::ZERO, Vec3::Y);

// Fog Information
text.sections[0].value = format!("Fog Mode: {:?}\nFog Color: {:?}", fog.mode, fog.color);

// Fog Mode Switching
text.sections[0]
.value
.push_str(format!("\n\n1 / 2 / 3 - Switch Fog Mode").as_str());

if keycode.pressed(KeyCode::Key1) {
fog.mode = match fog.mode {
coreh marked this conversation as resolved.
Show resolved Hide resolved
FogMode::Linear { start, end } => FogMode::Linear { start, end },
_ => FogMode::Linear {
start: 5.0,
end: 20.0,
},
};
}

if keycode.pressed(KeyCode::Key2) {
fog.mode = match fog.mode {
FogMode::Exponential { density } => FogMode::Exponential { density },
FogMode::ExponentialSquared { density } => FogMode::Exponential { density },
_ => FogMode::Exponential { density: 0.07 },
};
}

if keycode.pressed(KeyCode::Key3) {
fog.mode = match fog.mode {
FogMode::Exponential { density } => FogMode::ExponentialSquared { density },
FogMode::ExponentialSquared { density } => FogMode::ExponentialSquared { density },
_ => FogMode::ExponentialSquared { density: 0.07 },
};
}

// Linear Fog Controls
if let FogMode::Linear {
ref mut start,
ref mut end,
} = &mut fog.mode
{
text.sections[0]
.value
.push_str(format!("\nA / S - Move Start Distance\nZ / X - Move End Distance").as_str());

if keycode.pressed(KeyCode::A) {
*start -= delta * 3.0;
}
if keycode.pressed(KeyCode::S) {
*start += delta * 3.0;
}
if keycode.pressed(KeyCode::Z) {
*end -= delta * 3.0;
}
if keycode.pressed(KeyCode::X) {
*end += delta * 3.0;
}
}

// Exponential Fog Controls
if let FogMode::Exponential { ref mut density } = &mut fog.mode {
text.sections[0]
.value
.push_str(format!("\nA / S - Change Density").as_str());

if keycode.pressed(KeyCode::A) {
*density -= delta * 0.5 * *density;
if *density < 0.0 {
*density = 0.0
}
}
if keycode.pressed(KeyCode::S) {
*density += delta * 0.5 * *density;
}
}

// ExponentialSquared Fog Controls
if let FogMode::ExponentialSquared { ref mut density } = &mut fog.mode {
text.sections[0]
.value
.push_str(format!("\nA / S - Change Density").as_str());

if keycode.pressed(KeyCode::A) {
*density -= delta * 0.5 * *density;
if *density < 0.0 {
*density = 0.0
}
}
if keycode.pressed(KeyCode::S) {
*density += delta * 0.5 * *density;
}
}

// RGBA Controls
text.sections[0]
.value
.push_str(format!("\n\n- / = - Red\n[ / ] - Green\n; / ' - Blue\n. / ? - Alpha").as_str());

if keycode.pressed(KeyCode::Minus) {
let r = (fog.color.r() - 0.1 * delta).max(0.0);
fog.color.set_r(r);
}

if keycode.pressed(KeyCode::Equals) {
let r = (fog.color.r() + 0.1 * delta).min(1.0);
fog.color.set_r(r);
}

if keycode.pressed(KeyCode::LBracket) {
let r = (fog.color.g() - 0.1 * delta).max(0.0);
coreh marked this conversation as resolved.
Show resolved Hide resolved
fog.color.set_g(r);
}

if keycode.pressed(KeyCode::RBracket) {
let r = (fog.color.g() + 0.1 * delta).min(1.0);
fog.color.set_g(r);
}

if keycode.pressed(KeyCode::Semicolon) {
let r = (fog.color.b() - 0.1 * delta).max(0.0);
fog.color.set_b(r);
}

if keycode.pressed(KeyCode::Apostrophe) {
let r = (fog.color.b() + 0.1 * delta).min(1.0);
fog.color.set_b(r);
}

if keycode.pressed(KeyCode::Period) {
let r = (fog.color.a() - 0.1 * delta).max(0.0);
fog.color.set_a(r);
}

if keycode.pressed(KeyCode::Slash) {
let r = (fog.color.a() + 0.1 * delta).min(1.0);
fog.color.set_a(r);
}
}
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ Example | Description
--- | ---
[3D Scene](../examples/3d/3d_scene.rs) | Simple 3D scene with basic shapes and lighting
[3D Shapes](../examples/3d/3d_shapes.rs) | A scene showcasing the built-in 3D shapes
[Fog](../examples/3d/fog.rs) | A scene showcasing the distance fog effect
[Lighting](../examples/3d/lighting.rs) | Illustrates various lighting options in a simple scene
[Lines](../examples/3d/lines.rs) | Create a custom material to draw 3d lines
[Load glTF](../examples/3d/load_gltf.rs) | Loads and renders a glTF file as a scene
Expand Down