use super::downsampling_pipeline::BloomUniforms;
use bevy_camera::Camera;
use bevy_ecs::{
prelude::Component,
query::{QueryItem, With},
reflect::ReflectComponent,
};
use bevy_math::{AspectRatio, URect, UVec4, Vec2, Vec4};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{extract_component::ExtractComponent, view::Hdr};
#[derive(Component, Reflect, Clone)]
#[reflect(Component, Default, Clone)]
#[require(Hdr)]
pub struct Bloom {
pub intensity: f32,
pub low_frequency_boost: f32,
pub low_frequency_boost_curvature: f32,
pub high_pass_frequency: f32,
pub prefilter: BloomPrefilter,
pub composite_mode: BloomCompositeMode,
pub max_mip_dimension: u32,
pub scale: Vec2,
}
impl Bloom {
const DEFAULT_MAX_MIP_DIMENSION: u32 = 512;
pub const NATURAL: Self = Self {
intensity: 0.15,
low_frequency_boost: 0.7,
low_frequency_boost_curvature: 0.95,
high_pass_frequency: 1.0,
prefilter: BloomPrefilter {
threshold: 0.0,
threshold_softness: 0.0,
},
composite_mode: BloomCompositeMode::EnergyConserving,
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,
scale: Vec2::ONE,
};
pub const ANAMORPHIC: Self = Self {
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION * 2,
scale: Vec2::new(4.0, 1.0),
..Self::NATURAL
};
pub const OLD_SCHOOL: Self = Self {
intensity: 0.05,
low_frequency_boost: 0.7,
low_frequency_boost_curvature: 0.95,
high_pass_frequency: 1.0,
prefilter: BloomPrefilter {
threshold: 0.6,
threshold_softness: 0.2,
},
composite_mode: BloomCompositeMode::Additive,
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,
scale: Vec2::ONE,
};
pub const SCREEN_BLUR: Self = Self {
intensity: 1.0,
low_frequency_boost: 0.0,
low_frequency_boost_curvature: 0.0,
high_pass_frequency: 1.0 / 3.0,
prefilter: BloomPrefilter {
threshold: 0.0,
threshold_softness: 0.0,
},
composite_mode: BloomCompositeMode::EnergyConserving,
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,
scale: Vec2::ONE,
};
}
impl Default for Bloom {
fn default() -> Self {
Self::NATURAL
}
}
#[derive(Default, Clone, Reflect)]
#[reflect(Clone, Default)]
pub struct BloomPrefilter {
pub threshold: f32,
pub threshold_softness: f32,
}
#[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, Copy)]
#[reflect(Clone, Hash, PartialEq)]
pub enum BloomCompositeMode {
EnergyConserving,
Additive,
}
impl ExtractComponent for Bloom {
type QueryData = (&'static Self, &'static Camera);
type QueryFilter = With<Hdr>;
type Out = (Self, BloomUniforms);
fn extract_component((bloom, camera): QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
match (
camera.physical_viewport_rect(),
camera.physical_viewport_size(),
camera.physical_target_size(),
camera.is_active,
) {
(Some(URect { min: origin, .. }), Some(size), Some(target_size), true)
if size.x != 0 && size.y != 0 =>
{
let threshold = bloom.prefilter.threshold;
let threshold_softness = bloom.prefilter.threshold_softness;
let knee = threshold * threshold_softness.clamp(0.0, 1.0);
let uniform = BloomUniforms {
threshold_precomputations: Vec4::new(
threshold,
threshold - knee,
2.0 * knee,
0.25 / (knee + 0.00001),
),
viewport: UVec4::new(origin.x, origin.y, size.x, size.y).as_vec4()
/ UVec4::new(target_size.x, target_size.y, target_size.x, target_size.y)
.as_vec4(),
aspect: AspectRatio::try_from_pixels(size.x, size.y)
.expect("Valid screen size values for Bloom settings")
.ratio(),
scale: bloom.scale,
};
Some((bloom.clone(), uniform))
}
_ => None,
}
}
}