Skip to main content

bevy_pbr/volumetric_fog/
mod.rs

1//! Volumetric fog and volumetric lighting, also known as light shafts or god
2//! rays.
3//!
4//! This module implements a more physically-accurate, but slower, form of fog
5//! than the [`crate::fog`] module does. Notably, this *volumetric fog* allows
6//! for light beams from directional lights to shine through, creating what is
7//! known as *light shafts* or *god rays*.
8//!
9//! To add volumetric fog to a scene, add [`bevy_light::VolumetricFog`] to the
10//! camera, and add [`bevy_light::VolumetricLight`] to directional lights that you wish to
11//! be volumetric. [`bevy_light::VolumetricFog`] feature numerous settings that
12//! allow you to define the accuracy of the simulation, as well as the look of
13//! the fog. Currently, only interaction with directional lights that have
14//! shadow maps is supported. Note that the overhead of the effect scales
15//! directly with the number of directional lights in use, so apply
16//! [`bevy_light::VolumetricLight`] sparingly for the best results.
17//!
18//! The overall algorithm, which is implemented as a postprocessing effect, is a
19//! combination of the techniques described in [Scratchapixel] and [this blog
20//! post]. It uses raymarching in screen space, transformed into shadow map
21//! space for sampling and combined with physically-based modeling of absorption
22//! and scattering. Bevy employs the widely-used [Henyey-Greenstein phase
23//! function] to model asymmetry; this essentially allows light shafts to fade
24//! into and out of existence as the user views them.
25//!
26//! [Scratchapixel]: https://www.scratchapixel.com/lessons/3d-basic-rendering/volume-rendering-for-developers/intro-volume-rendering.html
27//!
28//! [this blog post]: https://www.alexandre-pestana.com/volumetric-lights/
29//!
30//! [Henyey-Greenstein phase function]: https://www.pbr-book.org/4ed/Volume_Scattering/Phase_Functions#TheHenyeyndashGreensteinPhaseFunction
31
32use bevy_app::{App, Plugin};
33use bevy_asset::{embedded_asset, Assets, Handle};
34use bevy_core_pipeline::{
35    core_3d::prepare_core_3d_depth_textures,
36    schedule::{Core3d, Core3dSystems},
37};
38use bevy_ecs::{resource::Resource, schedule::IntoScheduleConfigs as _};
39use bevy_light::FogVolume;
40use bevy_math::{
41    primitives::{Cuboid, Plane3d},
42    Vec2, Vec3,
43};
44use bevy_mesh::{Mesh, Meshable};
45use bevy_render::{
46    render_resource::SpecializedRenderPipelines,
47    sync_component::{SyncComponent, SyncComponentPlugin},
48    ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderStartup, RenderSystems,
49};
50use render::{volumetric_fog, VolumetricFogPipeline, VolumetricFogUniformBuffer};
51
52use crate::{volumetric_fog::render::init_volumetric_fog_pipeline, MeshPipelineSystems};
53
54pub mod render;
55
56/// A plugin that implements volumetric fog.
57pub struct VolumetricFogPlugin;
58
59#[derive(Resource)]
60pub struct FogAssets {
61    plane_mesh: Handle<Mesh>,
62    cube_mesh: Handle<Mesh>,
63}
64
65impl Plugin for VolumetricFogPlugin {
66    fn build(&self, app: &mut App) {
67        embedded_asset!(app, "volumetric_fog.wgsl");
68
69        let mut meshes = app.world_mut().resource_mut::<Assets<Mesh>>();
70        let plane_mesh = meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE).mesh());
71        let cube_mesh = meshes.add(Cuboid::new(1.0, 1.0, 1.0).mesh());
72
73        app.add_plugins(SyncComponentPlugin::<FogVolume, Self>::default());
74
75        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
76            return;
77        };
78
79        render_app
80            .insert_resource(FogAssets {
81                plane_mesh,
82                cube_mesh,
83            })
84            .init_gpu_resource::<SpecializedRenderPipelines<VolumetricFogPipeline>>()
85            .init_gpu_resource::<VolumetricFogUniformBuffer>()
86            .add_systems(
87                RenderStartup,
88                init_volumetric_fog_pipeline.after(MeshPipelineSystems),
89            )
90            .add_systems(ExtractSchedule, render::extract_volumetric_fog)
91            .add_systems(
92                Render,
93                (
94                    render::prepare_volumetric_fog_pipelines.in_set(RenderSystems::Prepare),
95                    render::prepare_volumetric_fog_uniforms.in_set(RenderSystems::Prepare),
96                    render::prepare_view_depth_textures_for_volumetric_fog
97                        .in_set(RenderSystems::Prepare)
98                        .before(prepare_core_3d_depth_textures),
99                ),
100            )
101            .add_systems(
102                Core3d,
103                volumetric_fog
104                    .after(Core3dSystems::MainPass)
105                    .before(Core3dSystems::EarlyPostProcess),
106            );
107    }
108}
109
110impl SyncComponent<VolumetricFogPlugin> for FogVolume {
111    type Target = Self;
112}