bevy_exponential_height_fog 0.1.0

Standalone exponential height fog extension for Bevy volumetric fog.
Documentation
use bevy::camera::{Camera, Camera3d};
use bevy::color::LinearRgba;
use bevy::core_pipeline::prepass::DepthPrepass;
use bevy::prelude::*;
use bevy::render::extract_component::ExtractComponent;
use bevy::render::render_resource::ShaderType;

#[derive(Debug, Clone, Reflect)]
pub struct ExponentialHeightFogLayer {
    pub enabled: bool,
    pub density: f32,
    pub height_falloff: f32,
    pub height_offset: f32,
}

impl Default for ExponentialHeightFogLayer {
    fn default() -> Self {
        Self {
            enabled: true,
            density: 0.02,
            height_falloff: 0.2,
            height_offset: 0.0,
        }
    }
}

#[derive(Debug, Clone, Copy, Reflect, Default, PartialEq, Eq)]
pub enum ExponentialHeightFogDistanceMode {
    #[default]
    Camera,
    SuppliedWorldCoordinates,
}

#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component, Default, Clone)]
#[require(Camera3d, DepthPrepass)]
pub struct ExponentialHeightFog {
    pub enabled: bool,
    pub fog_color: LinearRgba,
    pub primary: ExponentialHeightFogLayer,
    pub secondary: ExponentialHeightFogLayer,
    pub distance_mode: ExponentialHeightFogDistanceMode,
    pub distance_reference_world_position: Vec3,
    pub start_distance: f32,
    pub cutoff_distance: f32,
    pub distance_fade: f32,
    pub max_opacity: f32,
    pub min_density_weight: f32,
    pub min_fog_alpha: f32,
    pub sky_depth_threshold: f32,
}

impl Default for ExponentialHeightFog {
    fn default() -> Self {
        Self {
            enabled: true,
            fog_color: LinearRgba::new(0.60, 0.66, 0.72, 1.0),
            primary: ExponentialHeightFogLayer::default(),
            secondary: ExponentialHeightFogLayer {
                enabled: false,
                density: 0.01,
                height_falloff: 0.35,
                height_offset: 2.5,
            },
            distance_mode: ExponentialHeightFogDistanceMode::Camera,
            distance_reference_world_position: Vec3::ZERO,
            start_distance: 0.0,
            cutoff_distance: 0.0,
            distance_fade: 2.0,
            max_opacity: 1.0,
            min_density_weight: 0.0,
            min_fog_alpha: 0.001,
            sky_depth_threshold: 0.999999,
        }
    }
}

#[derive(Component, ShaderType, Clone)]
pub struct ExponentialHeightFogUniform {
    pub fog_color: Vec4,
    pub primary_params: Vec4,
    pub secondary_params: Vec4,
    pub distance_params: Vec4,
    pub distance_reference_params: Vec4,
    pub optimization_params: Vec4,
}

impl ExtractComponent for ExponentialHeightFog {
    type QueryData = &'static Self;
    type QueryFilter = With<Camera>;
    type Out = ExponentialHeightFogUniform;

    fn extract_component(item: bevy::ecs::query::QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
        let fog_color = item.fog_color.to_vec4();
        let primary_params = Vec4::new(
            item.primary.density.max(0.0),
            item.primary.height_falloff.max(0.00001),
            item.primary.height_offset,
            if item.primary.enabled { 1.0 } else { 0.0 },
        );
        let secondary_params = Vec4::new(
            item.secondary.density.max(0.0),
            item.secondary.height_falloff.max(0.00001),
            item.secondary.height_offset,
            if item.secondary.enabled { 1.0 } else { 0.0 },
        );
        let distance_params = Vec4::new(
            item.start_distance.max(0.0),
            item.cutoff_distance.max(0.0),
            item.distance_fade.max(0.001),
            item.max_opacity.clamp(0.0, 1.0),
        );
        let distance_reference_params = Vec4::new(
            item.distance_reference_world_position.x,
            item.distance_reference_world_position.y,
            item.distance_reference_world_position.z,
            match item.distance_mode {
                ExponentialHeightFogDistanceMode::Camera => 0.0,
                ExponentialHeightFogDistanceMode::SuppliedWorldCoordinates => 1.0,
            },
        );
        let optimization_params = Vec4::new(
            item.min_density_weight.clamp(0.0, 1.0),
            item.min_fog_alpha.clamp(0.0, 1.0),
            item.sky_depth_threshold.clamp(0.0, 1.0),
            if item.enabled { 1.0 } else { 0.0 },
        );

        Some(ExponentialHeightFogUniform {
            fog_color,
            primary_params,
            secondary_params,
            distance_params,
            distance_reference_params,
            optimization_params,
        })
    }
}