bevy
bevy copied to clipboard
Add Distance Fog support
Objective
- Add support for the “classic” distance fog effect, as well as a more advanced atmospheric fog effect.
Solution
This PR:
- Introduces a new
FogSettingscomponent that controls distance fog per-camera. - Adds support for three widely used “traditional” fog falloff modes:
Linear,ExponentialandExponentialSquared, as well as a more advancedAtmosphericfog; - Adds support for directional light influence over fog color;
- Extracts fog via
ExtractComponent, then uses a prepare system that sets up a new dynamic uniform struct (Fog), similar to other mesh view types; - Renders fog in PBR material shader, as a final adjustment to the
output_color, after PBR is computed (but before tone mapping); - Adds a new
StandardMaterialflag to enable fog; (fog_enabled) - Adds convenience methods for easier artistic control when creating non-linear fog types;
- Adds documentation around fog.
Changelog
Added
- Added support for distance-based fog effects for PBR materials, controllable per-camera via the new
FogSettingscomponent; - Added
FogFalloffenum for selecting between three widely used “traditional” fog falloff modes:Linear,ExponentialandExponentialSquared, as well as a more advancedAtmosphericfog;
Thanks for the speedy CR! Added additional documentation comments.
I do wonder if we can make these more... modular? Having to include this sort of effect in bevy_pbr, and adding a bitflag to StandardMaterial definitely feels like it might get messy.
Maybe? There are already some things that affect, for example, lights that end up as bitflags there instead of special light-related components, so it's not completely out of place, but maybe there's indeed a cleaner way? I think what Wireframe is doing is maybe too heavy handed for this, but I'm new to the bevy way of doing things. Is there some precedent in how to modularize stuff like this?
Re: PBR shader, I added it there after some conversations on #rendering-dev on Discord, since if I understand it correctly depth-based post processing effects are not yet possible. (or not straightforward?) One benefit is that it allows us to handle fog on transparency correctly, which would be trickier with post processing.
I also got some feedback also in #rendering-dev about maybe moving Fog itself from a resource to a component, so that it can vary per-camera, not sure how to implement extraction for that case.
Maybe? There are already some things that affect, for example, lights that end up as bitflags there instead of special light-related components, so it's not completely out of place, but maybe there's indeed a cleaner way?
Yep, not a problem for this PR, but wanted to surface the idea so we start chewing on it.
I also got some feedback also in #rendering-dev about maybe moving Fog itself from a resource to a component, so that it can vary per-camera, not sure how to implement extraction for that case.
Oh I rather like that. IIRC there's an ExtractComponentPlugin that should point you in the right direction.
IIRC there's an ExtractComponentPlugin that should point you in the right direction.
Thanks! That was a good starting point.
Fog is now a Component and can be set independently per-camera. Here's a modified split_screen example running with two different fogs, one for each camera:
I also consolidated the fog setup code into a single FogPlugin.
Some ergonomics/usability questions:
- Now that we can add/remove fog from cameras by simply adding and removing the component, does it still make sense to have a
FogMode::Offvariant? Is there some precedent to Components that are “added but disabled”, and does that feel idiomatic? - If we decide to keep the
Offvariant, should it still be the default? Or should we make one of the other variants a more “useful” out-of-the-box default? - Should we keep the short name
Fogor make it something more specific (e.g.DistanceFog)? That's longer to type/remember but could makes sense in a future where there are other fog effects which are not added to the camera, but rather as separate, independent entities withTransform/GlobalTransform(e.g.LinearFog,SphericalFogor evenVolumetricFog). That's kinda like lights work currently, naming-wise.
Could you add an example that show off the various fog modes? Maybe cycle through them on a timer, or on a key press
- I would remove the Off variant, and make the component not-added by default.
- I would go with the more verbose option, something like DistanceFogSettings (and DistanceFogPlugin).
In general, this is the patterns I've been seeing for post-processing effects recently:
- Make an ExamplePlugin plugin, that's either loaded by default as part of PbrPlugin (BloomPlugin), or optionally added by the user (FXAAPlugin)
- Make an ExampleSettings component to be applied to any view you want the effect on, whose presence enables the effect and which holds the settings (no off switch on this component).
- Optionally, add a setting to PbrPlugin or somewhere similar to globally toggle the effect off.
Added an interactive example, where you can tweak the parameters:
https://user-images.githubusercontent.com/418473/199402028-8d651f3f-bf33-4afa-bd89-28d251880fdb.mov
I would go with the more verbose option, something like DistanceFogSettings (and DistanceFogPlugin).
Renamed Fog to FogSettings. I want to propose this shorter name instead of DistanceFogSettings because I believe we can reuse these same settings for volumetric fogs, so in the future you could add FogCubeVolume { .. } and FogSettings { .. } to a non-camera entity to produce volumetric fog.
Also added the atmospheric extinction/inscattering falloff calculations (in a new Atmospheric fog falloff mode) and the sun pseudo-scattering math from Inigo Quilez's article, as suggested by @superdump:
Also, any chance of adding a demo of the final mode to the example? :)
I'm thinking about maybe adding a separate demo, since the scene on the fog demo doesn't have sufficiently apart far/near objects for the different color channel-specific effect of extinction/in-scattering to be really noticeable.
There's also far too many parameters to control via keybindings UI, so I was thinking about maybe just having a few preset modes (e.g. day, sunset, very foggy day)
Added an example showcasing the atmospheric fog effect, with toggles for disabling it, and also disabling the directional light influence.
Did some reading about atmospheric scattering and visibility, and I now have a much better grasp of what the parameters do and how to control them.
Added some utility methods based on Koschmieder's visibility equation:
-
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()
For atmospheric fog, these also convert colors into the right exponential scale needed for the extinction and inscattering parameters, (and invert the extinction color) so it's now much easier to have artistic control over the look of the fog:
https://github.com/bevyengine/bevy/blob/a35783a6ef9e7182c80ce74dfc2195910a368cd4/examples/3d/atmospheric_fog.rs#L26-L30
I've removed this from the 0.9 milestone. Sadly its just too big to properly review this late in the game.
That's completely understandable, no worries! 😉 I plan on using main for the foreseeable future for my project. Please take your time
BTW, merged main, and confirmed that @aevyrie's now-merged PR #5264 also fixes the banding effect on the fog:
Before
After
FYI, https://github.com/bevyengine/bevy/pull/6707 should reduce excess noise in darker areas of banded fog, when not using HDR.
Upon pixel peeping, the dithered screenshot above has a lot more noise than it should.
Hey 👋 Sorry for taking a while to reply; I didn't have a lot of bandwidth for open source due to holidays/new year. Catching up now!
@superdump @robtfm Applied feedback and updated to the latest main. Added tweaks so that the shadows still look correct, as we now have cascade shadow maps (#7064 🎉!)
Pull request successfully merged into main.
Build succeeded:
- build-and-install-on-iOS
- build-android
- build (macos-latest)
- build (ubuntu-latest)
- build-wasm
- build (windows-latest)
- build-without-default-features (bevy)
- build-without-default-features (bevy_ecs)
- build-without-default-features (bevy_reflect)
- check-compiles
- check-doc
- check-missing-examples-in-docs
- ci
- markdownlint
- msrv
- run-examples
- run-examples-on-wasm
- run-examples-on-windows-dx12