1use bevy_app::{App, Plugin};
2use bevy_color::{ColorToComponents, LinearRgba};
3use bevy_ecs::prelude::*;
4use bevy_math::{Vec3, Vec4};
5use bevy_render::{
6 extract_component::ExtractComponentPlugin,
7 render_resource::{DynamicUniformBuffer, ShaderType},
8 renderer::{RenderDevice, RenderQueue},
9 view::ExtractedView,
10 GpuResourceAppExt, Render, RenderApp, RenderSystems,
11};
12use bevy_shader::load_shader_library;
13
14use crate::{DistanceFog, FogFalloff};
15
16#[derive(#[automatically_derived]
impl ::core::marker::Copy for GpuFog { }Copy, #[automatically_derived]
impl ::core::clone::Clone for GpuFog {
#[inline]
fn clone(&self) -> GpuFog {
let _: ::core::clone::AssertParamIsClone<Vec4>;
let _: ::core::clone::AssertParamIsClone<Vec3>;
let _: ::core::clone::AssertParamIsClone<f32>;
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}Clone, impl bevy_render::render_resource::encase::private::ShaderSize for GpuFog
where Vec4: bevy_render::render_resource::encase::private::ShaderSize,
Vec4: bevy_render::render_resource::encase::private::ShaderSize,
Vec3: bevy_render::render_resource::encase::private::ShaderSize,
f32: bevy_render::render_resource::encase::private::ShaderSize,
Vec3: bevy_render::render_resource::encase::private::ShaderSize,
u32: bevy_render::render_resource::encase::private::ShaderSize {}ShaderType, #[automatically_derived]
impl ::core::default::Default for GpuFog {
#[inline]
fn default() -> GpuFog {
GpuFog {
base_color: ::core::default::Default::default(),
directional_light_color: ::core::default::Default::default(),
be: ::core::default::Default::default(),
directional_light_exponent: ::core::default::Default::default(),
bi: ::core::default::Default::default(),
mode: ::core::default::Default::default(),
}
}
}Default, #[automatically_derived]
impl ::core::fmt::Debug for GpuFog {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
let names: &'static _ =
&["base_color", "directional_light_color", "be",
"directional_light_exponent", "bi", "mode"];
let values: &[&dyn ::core::fmt::Debug] =
&[&self.base_color, &self.directional_light_color, &self.be,
&self.directional_light_exponent, &self.bi, &&self.mode];
::core::fmt::Formatter::debug_struct_fields_finish(f, "GpuFog", names,
values)
}
}Debug)]
18pub struct GpuFog {
19 base_color: Vec4,
21 directional_light_color: Vec4,
23 be: Vec3,
26 directional_light_exponent: f32,
28 bi: Vec3,
31 mode: u32,
33}
34
35#[expect(unused, reason = "Kept in sync with `mesh_view_types.wgsl`")]
37const GPU_FOG_MODE_OFF: u32 = 0;
38const GPU_FOG_MODE_LINEAR: u32 = 1;
39const GPU_FOG_MODE_EXPONENTIAL: u32 = 2;
40const GPU_FOG_MODE_EXPONENTIAL_SQUARED: u32 = 3;
41const GPU_FOG_MODE_ATMOSPHERIC: u32 = 4;
42
43#[derive(#[automatically_derived]
impl ::core::default::Default for FogMeta {
#[inline]
fn default() -> FogMeta {
FogMeta { gpu_fogs: ::core::default::Default::default() }
}
}Default, impl bevy_ecs::resource::Resource for FogMeta where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
45pub struct FogMeta {
46 pub gpu_fogs: DynamicUniformBuffer<GpuFog>,
47}
48
49pub fn prepare_fog(
51 mut commands: Commands,
52 render_device: Res<RenderDevice>,
53 render_queue: Res<RenderQueue>,
54 mut fog_meta: ResMut<FogMeta>,
55 views: Query<(Entity, &DistanceFog), With<ExtractedView>>,
56) {
57 let views_iter = views.iter();
58 let view_count = views_iter.len();
59 let Some(mut writer) = fog_meta
60 .gpu_fogs
61 .get_writer(view_count, &render_device, &render_queue)
62 else {
63 return;
64 };
65 for (entity, fog) in views_iter {
66 let gpu_fog = {
67 match &fog.falloff {
68 FogFalloff::Linear { start, end } => GpuFog {
69 mode: GPU_FOG_MODE_LINEAR,
70 base_color: LinearRgba::from(fog.color).to_vec4(),
71 directional_light_color: LinearRgba::from(fog.directional_light_color)
72 .to_vec4(),
73 directional_light_exponent: fog.directional_light_exponent,
74 be: Vec3::new(*start, *end, 0.0),
75 ..Default::default()
76 },
77 FogFalloff::Exponential { density } => GpuFog {
78 mode: GPU_FOG_MODE_EXPONENTIAL,
79 base_color: LinearRgba::from(fog.color).to_vec4(),
80 directional_light_color: LinearRgba::from(fog.directional_light_color)
81 .to_vec4(),
82 directional_light_exponent: fog.directional_light_exponent,
83 be: Vec3::new(*density, 0.0, 0.0),
84 ..Default::default()
85 },
86 FogFalloff::ExponentialSquared { density } => GpuFog {
87 mode: GPU_FOG_MODE_EXPONENTIAL_SQUARED,
88 base_color: LinearRgba::from(fog.color).to_vec4(),
89 directional_light_color: LinearRgba::from(fog.directional_light_color)
90 .to_vec4(),
91 directional_light_exponent: fog.directional_light_exponent,
92 be: Vec3::new(*density, 0.0, 0.0),
93 ..Default::default()
94 },
95 FogFalloff::Atmospheric {
96 extinction,
97 inscattering,
98 } => GpuFog {
99 mode: GPU_FOG_MODE_ATMOSPHERIC,
100 base_color: LinearRgba::from(fog.color).to_vec4(),
101 directional_light_color: LinearRgba::from(fog.directional_light_color)
102 .to_vec4(),
103 directional_light_exponent: fog.directional_light_exponent,
104 be: *extinction,
105 bi: *inscattering,
106 },
107 }
108 };
109
110 commands.entity(entity).insert(ViewFogUniformOffset {
112 offset: writer.write(&gpu_fog),
113 });
114 }
115}
116
117#[derive(impl bevy_ecs::component::Component for ViewFogUniformOffset where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::Table;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}Component)]
120pub struct ViewFogUniformOffset {
121 pub offset: u32,
122}
123
124pub struct FogPlugin;
126
127impl Plugin for FogPlugin {
128 fn build(&self, app: &mut App) {
129 {
{
let mut embedded =
app.world_mut().resource_mut::<::bevy_asset::io::embedded::EmbeddedAssetRegistry>();
let path =
{
let crate_name =
"bevy_pbr::render::fog".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/render/fog.rs".as_ref(),
"fog.wgsl".as_ref())
};
let watched_path =
::bevy_asset::io::embedded::watched_path("src/render/fog.rs",
"fog.wgsl");
embedded.insert_asset(watched_path, &path,
b"#define_import_path bevy_pbr::fog\n\n#import bevy_pbr::{\n mesh_view_bindings::fog,\n mesh_view_types::Fog,\n}\n\n// Fog formulas adapted from:\n// https://learn.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas\n// https://catlikecoding.com/unity/tutorials/rendering/part-14/\n// https://iquilezles.org/articles/fog/ (Atmospheric Fog and Scattering)\n\nfn scattering_adjusted_fog_color(\n fog_params: Fog,\n scattering: vec3<f32>,\n) -> vec4<f32> {\n if (fog_params.directional_light_color.a > 0.0) {\n return vec4<f32>(\n fog_params.base_color.rgb\n + scattering * fog_params.directional_light_color.rgb * fog_params.directional_light_color.a,\n fog_params.base_color.a,\n );\n } else {\n return fog_params.base_color;\n }\n}\n\nfn linear_fog(\n fog_params: Fog,\n input_color: vec4<f32>,\n distance: f32,\n scattering: vec3<f32>,\n) -> vec4<f32> {\n var fog_color = scattering_adjusted_fog_color(fog_params, scattering);\n let start = fog_params.be.x;\n let end = fog_params.be.y;\n fog_color.a *= 1.0 - clamp((end - distance) / (end - start), 0.0, 1.0);\n return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);\n}\n\nfn exponential_fog(\n fog_params: Fog,\n input_color: vec4<f32>,\n distance: f32,\n scattering: vec3<f32>,\n) -> vec4<f32> {\n var fog_color = scattering_adjusted_fog_color(fog_params, scattering);\n let density = fog_params.be.x;\n fog_color.a *= 1.0 - 1.0 / exp(distance * density);\n return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);\n}\n\nfn exponential_squared_fog(\n fog_params: Fog,\n input_color: vec4<f32>,\n distance: f32,\n scattering: vec3<f32>,\n) -> vec4<f32> {\n var fog_color = scattering_adjusted_fog_color(fog_params, scattering);\n let distance_times_density = distance * fog_params.be.x;\n fog_color.a *= 1.0 - 1.0 / exp(distance_times_density * distance_times_density);\n return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);\n}\n\nfn atmospheric_fog(\n fog_params: Fog,\n input_color: vec4<f32>,\n distance: f32,\n scattering: vec3<f32>,\n) -> vec4<f32> {\n var fog_color = scattering_adjusted_fog_color(fog_params, scattering);\n let extinction_factor = 1.0 - 1.0 / exp(distance * fog_params.be);\n let inscattering_factor = 1.0 - 1.0 / exp(distance * fog_params.bi);\n return vec4<f32>(\n input_color.rgb * (1.0 - extinction_factor * fog_color.a)\n + fog_color.rgb * inscattering_factor * fog_color.a,\n input_color.a\n );\n}\n");
}
};
let handle:
::bevy_shader::_macro::bevy_asset::prelude::Handle<::bevy_shader::prelude::Shader> =
{
let (path, asset_server) =
{
let path =
{
{
let crate_name =
"bevy_pbr::render::fog".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/render/fog.rs".as_ref(),
"fog.wgsl".as_ref())
}
};
let path =
::bevy_asset::AssetPath::from_path_buf(path).with_source("embedded");
let asset_server =
::bevy_asset::io::embedded::GetAssetServer::get_asset_server(app);
(path, asset_server)
};
asset_server.load(path)
};
core::mem::forget(handle);load_shader_library!(app, "fog.wgsl");
130
131 app.add_plugins(ExtractComponentPlugin::<DistanceFog>::default());
132
133 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
134 render_app
135 .init_gpu_resource::<FogMeta>()
136 .add_systems(Render, prepare_fog.in_set(RenderSystems::PrepareResources));
137 }
138 }
139}