Path: blob/main/crates/bevy_post_process/src/bloom/settings.rs
9550 views
use super::downsampling_pipeline::BloomUniforms;1use bevy_camera::{Camera, Hdr};2use bevy_ecs::{3prelude::Component,4query::{QueryItem, With},5reflect::ReflectComponent,6};7use bevy_math::{AspectRatio, URect, UVec4, Vec2, Vec4};8use bevy_reflect::{std_traits::ReflectDefault, Reflect};9use bevy_render::{extract_component::ExtractComponent, sync_component::SyncComponent};1011/// Applies a bloom effect to an HDR-enabled 2d or 3d camera.12///13/// Bloom emulates an effect found in real cameras and the human eye,14/// causing halos to appear around very bright parts of the scene.15///16/// See also <https://en.wikipedia.org/wiki/Bloom_(shader_effect)>.17///18/// # Usage Notes19///20/// Often used in conjunction with `bevy_pbr::StandardMaterial::emissive` for 3d meshes.21///22/// Bloom is best used alongside a tonemapping function that desaturates bright colors,23/// such as [`bevy_core_pipeline::tonemapping::Tonemapping::TonyMcMapface`].24///25/// Bevy's implementation uses a parametric curve to blend between a set of26/// blurred (lower frequency) images generated from the camera's view.27/// See <https://starlederer.github.io/bloom/> for a visualization of the parametric curve28/// used in Bevy as well as a visualization of the curve's respective scattering profile.29#[derive(Component, Reflect, Clone)]30#[reflect(Component, Default, Clone)]31#[require(Hdr)]32pub struct Bloom {33/// Controls the baseline of how much the image is scattered (default: 0.15).34///35/// This parameter should be used only to control the strength of the bloom36/// for the scene as a whole. Increasing it too much will make the scene appear37/// blurry and over-exposed.38///39/// To make a mesh glow brighter, rather than increase the bloom intensity,40/// you should increase the mesh's `emissive` value.41///42/// # In energy-conserving mode43/// The value represents how likely the light is to scatter.44///45/// The value should be between 0.0 and 1.0 where:46/// * 0.0 means no bloom47/// * 1.0 means the light is scattered as much as possible48///49/// # In additive mode50/// The value represents how much scattered light is added to51/// the image to create the glow effect.52///53/// In this configuration:54/// * 0.0 means no bloom55/// * Greater than 0.0 means a proportionate amount of scattered light is added56pub intensity: f32,5758/// Low frequency contribution boost.59/// Controls how much more likely the light60/// is to scatter completely sideways (low frequency image).61///62/// Comparable to a low shelf boost on an equalizer.63///64/// # In energy-conserving mode65/// The value should be between 0.0 and 1.0 where:66/// * 0.0 means low frequency light uses base intensity for blend factor calculation67/// * 1.0 means low frequency light contributes at full power68///69/// # In additive mode70/// The value represents how much scattered light is added to71/// the image to create the glow effect.72///73/// In this configuration:74/// * 0.0 means no bloom75/// * Greater than 0.0 means a proportionate amount of scattered light is added76pub low_frequency_boost: f32,7778/// Low frequency contribution boost curve.79/// Controls the curvature of the blend factor function80/// making frequencies next to the lowest ones contribute more.81///82/// Somewhat comparable to the Q factor of an equalizer node.83///84/// Valid range:85/// * 0.0 - base intensity and boosted intensity are linearly interpolated86/// * 1.0 - all frequencies below maximum are at boosted intensity level87pub low_frequency_boost_curvature: f32,8889/// Tightens how much the light scatters (default: 1.0).90///91/// Valid range:92/// * 0.0 - maximum scattering angle is 0 degrees (no scattering)93/// * 1.0 - maximum scattering angle is 90 degrees94pub high_pass_frequency: f32,9596/// Controls the threshold filter used for extracting the brightest regions from the input image97/// before blurring them and compositing back onto the original image.98///99/// Changing these settings creates a physically inaccurate image and makes it easy to make100/// the final result look worse. However, they can be useful when emulating the 1990s-2000s game look.101/// See [`BloomPrefilter`] for more information.102pub prefilter: BloomPrefilter,103104/// Controls whether bloom textures105/// are blended between or added to each other. Useful106/// if image brightening is desired and a must-change107/// if `prefilter` is used.108///109/// # Recommendation110/// Set to [`BloomCompositeMode::Additive`] if `prefilter` is111/// configured in a non-energy-conserving way,112/// otherwise set to [`BloomCompositeMode::EnergyConserving`].113pub composite_mode: BloomCompositeMode,114115/// Maximum size of each dimension for the largest mipchain texture used in downscaling/upscaling.116/// Only tweak if you are seeing visual artifacts.117pub max_mip_dimension: u32,118119/// Amount to stretch the bloom on each axis. Artistic control, can be used to emulate120/// anamorphic blur by using a large x-value. For large values, you may need to increase121/// [`Bloom::max_mip_dimension`] to reduce sampling artifacts.122pub scale: Vec2,123}124125impl Bloom {126const DEFAULT_MAX_MIP_DIMENSION: u32 = 512;127128/// The default bloom preset.129///130/// This uses the [`EnergyConserving`](BloomCompositeMode::EnergyConserving) composite mode.131pub const NATURAL: Self = Self {132intensity: 0.15,133low_frequency_boost: 0.7,134low_frequency_boost_curvature: 0.95,135high_pass_frequency: 1.0,136prefilter: BloomPrefilter {137threshold: 0.0,138threshold_softness: 0.0,139},140composite_mode: BloomCompositeMode::EnergyConserving,141max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,142scale: Vec2::ONE,143};144145/// Emulates the look of stylized anamorphic bloom, stretched horizontally.146pub const ANAMORPHIC: Self = Self {147// The larger scale necessitates a larger resolution to reduce artifacts:148max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION * 2,149scale: Vec2::new(4.0, 1.0),150..Self::NATURAL151};152153/// A preset that's similar to how older games did bloom.154pub const OLD_SCHOOL: Self = Self {155intensity: 0.05,156low_frequency_boost: 0.7,157low_frequency_boost_curvature: 0.95,158high_pass_frequency: 1.0,159prefilter: BloomPrefilter {160threshold: 0.6,161threshold_softness: 0.2,162},163composite_mode: BloomCompositeMode::Additive,164max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,165scale: Vec2::ONE,166};167168/// A preset that applies a very strong bloom, and blurs the whole screen.169pub const SCREEN_BLUR: Self = Self {170intensity: 1.0,171low_frequency_boost: 0.0,172low_frequency_boost_curvature: 0.0,173high_pass_frequency: 1.0 / 3.0,174prefilter: BloomPrefilter {175threshold: 0.0,176threshold_softness: 0.0,177},178composite_mode: BloomCompositeMode::EnergyConserving,179max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,180scale: Vec2::ONE,181};182}183184impl Default for Bloom {185fn default() -> Self {186Self::NATURAL187}188}189190/// Applies a threshold filter to the input image to extract the brightest191/// regions before blurring them and compositing back onto the original image.192/// These settings are useful when emulating the 1990s-2000s game look.193///194/// # Considerations195/// * Changing these settings creates a physically inaccurate image196/// * Changing these settings makes it easy to make the final result look worse197/// * Non-default prefilter settings should be used in conjunction with [`BloomCompositeMode::Additive`]198#[derive(Default, Clone, Reflect)]199#[reflect(Clone, Default)]200pub struct BloomPrefilter {201/// Baseline of the quadratic threshold curve (default: 0.0).202///203/// RGB values under the threshold curve will not contribute to the effect.204pub threshold: f32,205206/// Controls how much to blend between the thresholded and non-thresholded colors (default: 0.0).207///208/// 0.0 = Abrupt threshold, no blending209/// 1.0 = Fully soft threshold210///211/// Values outside of the range [0.0, 1.0] will be clamped.212pub threshold_softness: f32,213}214215#[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, Copy)]216#[reflect(Clone, Hash, PartialEq)]217pub enum BloomCompositeMode {218EnergyConserving,219Additive,220}221222impl SyncComponent for Bloom {223type Out = (Self, BloomUniforms);224}225226impl ExtractComponent for Bloom {227type QueryData = (&'static Self, &'static Camera);228type QueryFilter = With<Hdr>;229230fn extract_component((bloom, camera): QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {231match (232camera.physical_viewport_rect(),233camera.physical_viewport_size(),234camera.physical_target_size(),235camera.is_active,236) {237(Some(URect { min: origin, .. }), Some(size), Some(target_size), true)238if size.x != 0 && size.y != 0 =>239{240let threshold = bloom.prefilter.threshold;241let threshold_softness = bloom.prefilter.threshold_softness;242let knee = threshold * threshold_softness.clamp(0.0, 1.0);243244let uniform = BloomUniforms {245threshold_precomputations: Vec4::new(246threshold,247threshold - knee,2482.0 * knee,2490.25 / (knee + 0.00001),250),251viewport: UVec4::new(origin.x, origin.y, size.x, size.y).as_vec4()252/ UVec4::new(target_size.x, target_size.y, target_size.x, target_size.y)253.as_vec4(),254aspect: AspectRatio::try_from_pixels(size.x, size.y)255.expect("Valid screen size values for Bloom settings")256.ratio(),257scale: bloom.scale,258};259260Some((bloom.clone(), uniform))261}262_ => None,263}264}265}266267268