Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_post_process/src/bloom/settings.rs
9550 views
1
use super::downsampling_pipeline::BloomUniforms;
2
use bevy_camera::{Camera, Hdr};
3
use bevy_ecs::{
4
prelude::Component,
5
query::{QueryItem, With},
6
reflect::ReflectComponent,
7
};
8
use bevy_math::{AspectRatio, URect, UVec4, Vec2, Vec4};
9
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
10
use bevy_render::{extract_component::ExtractComponent, sync_component::SyncComponent};
11
12
/// Applies a bloom effect to an HDR-enabled 2d or 3d camera.
13
///
14
/// Bloom emulates an effect found in real cameras and the human eye,
15
/// causing halos to appear around very bright parts of the scene.
16
///
17
/// See also <https://en.wikipedia.org/wiki/Bloom_(shader_effect)>.
18
///
19
/// # Usage Notes
20
///
21
/// Often used in conjunction with `bevy_pbr::StandardMaterial::emissive` for 3d meshes.
22
///
23
/// Bloom is best used alongside a tonemapping function that desaturates bright colors,
24
/// such as [`bevy_core_pipeline::tonemapping::Tonemapping::TonyMcMapface`].
25
///
26
/// Bevy's implementation uses a parametric curve to blend between a set of
27
/// blurred (lower frequency) images generated from the camera's view.
28
/// See <https://starlederer.github.io/bloom/> for a visualization of the parametric curve
29
/// used in Bevy as well as a visualization of the curve's respective scattering profile.
30
#[derive(Component, Reflect, Clone)]
31
#[reflect(Component, Default, Clone)]
32
#[require(Hdr)]
33
pub struct Bloom {
34
/// Controls the baseline of how much the image is scattered (default: 0.15).
35
///
36
/// This parameter should be used only to control the strength of the bloom
37
/// for the scene as a whole. Increasing it too much will make the scene appear
38
/// blurry and over-exposed.
39
///
40
/// To make a mesh glow brighter, rather than increase the bloom intensity,
41
/// you should increase the mesh's `emissive` value.
42
///
43
/// # In energy-conserving mode
44
/// The value represents how likely the light is to scatter.
45
///
46
/// The value should be between 0.0 and 1.0 where:
47
/// * 0.0 means no bloom
48
/// * 1.0 means the light is scattered as much as possible
49
///
50
/// # In additive mode
51
/// The value represents how much scattered light is added to
52
/// the image to create the glow effect.
53
///
54
/// In this configuration:
55
/// * 0.0 means no bloom
56
/// * Greater than 0.0 means a proportionate amount of scattered light is added
57
pub intensity: f32,
58
59
/// Low frequency contribution boost.
60
/// Controls how much more likely the light
61
/// is to scatter completely sideways (low frequency image).
62
///
63
/// Comparable to a low shelf boost on an equalizer.
64
///
65
/// # In energy-conserving mode
66
/// The value should be between 0.0 and 1.0 where:
67
/// * 0.0 means low frequency light uses base intensity for blend factor calculation
68
/// * 1.0 means low frequency light contributes at full power
69
///
70
/// # In additive mode
71
/// The value represents how much scattered light is added to
72
/// the image to create the glow effect.
73
///
74
/// In this configuration:
75
/// * 0.0 means no bloom
76
/// * Greater than 0.0 means a proportionate amount of scattered light is added
77
pub low_frequency_boost: f32,
78
79
/// Low frequency contribution boost curve.
80
/// Controls the curvature of the blend factor function
81
/// making frequencies next to the lowest ones contribute more.
82
///
83
/// Somewhat comparable to the Q factor of an equalizer node.
84
///
85
/// Valid range:
86
/// * 0.0 - base intensity and boosted intensity are linearly interpolated
87
/// * 1.0 - all frequencies below maximum are at boosted intensity level
88
pub low_frequency_boost_curvature: f32,
89
90
/// Tightens how much the light scatters (default: 1.0).
91
///
92
/// Valid range:
93
/// * 0.0 - maximum scattering angle is 0 degrees (no scattering)
94
/// * 1.0 - maximum scattering angle is 90 degrees
95
pub high_pass_frequency: f32,
96
97
/// Controls the threshold filter used for extracting the brightest regions from the input image
98
/// before blurring them and compositing back onto the original image.
99
///
100
/// Changing these settings creates a physically inaccurate image and makes it easy to make
101
/// the final result look worse. However, they can be useful when emulating the 1990s-2000s game look.
102
/// See [`BloomPrefilter`] for more information.
103
pub prefilter: BloomPrefilter,
104
105
/// Controls whether bloom textures
106
/// are blended between or added to each other. Useful
107
/// if image brightening is desired and a must-change
108
/// if `prefilter` is used.
109
///
110
/// # Recommendation
111
/// Set to [`BloomCompositeMode::Additive`] if `prefilter` is
112
/// configured in a non-energy-conserving way,
113
/// otherwise set to [`BloomCompositeMode::EnergyConserving`].
114
pub composite_mode: BloomCompositeMode,
115
116
/// Maximum size of each dimension for the largest mipchain texture used in downscaling/upscaling.
117
/// Only tweak if you are seeing visual artifacts.
118
pub max_mip_dimension: u32,
119
120
/// Amount to stretch the bloom on each axis. Artistic control, can be used to emulate
121
/// anamorphic blur by using a large x-value. For large values, you may need to increase
122
/// [`Bloom::max_mip_dimension`] to reduce sampling artifacts.
123
pub scale: Vec2,
124
}
125
126
impl Bloom {
127
const DEFAULT_MAX_MIP_DIMENSION: u32 = 512;
128
129
/// The default bloom preset.
130
///
131
/// This uses the [`EnergyConserving`](BloomCompositeMode::EnergyConserving) composite mode.
132
pub const NATURAL: Self = Self {
133
intensity: 0.15,
134
low_frequency_boost: 0.7,
135
low_frequency_boost_curvature: 0.95,
136
high_pass_frequency: 1.0,
137
prefilter: BloomPrefilter {
138
threshold: 0.0,
139
threshold_softness: 0.0,
140
},
141
composite_mode: BloomCompositeMode::EnergyConserving,
142
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,
143
scale: Vec2::ONE,
144
};
145
146
/// Emulates the look of stylized anamorphic bloom, stretched horizontally.
147
pub const ANAMORPHIC: Self = Self {
148
// The larger scale necessitates a larger resolution to reduce artifacts:
149
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION * 2,
150
scale: Vec2::new(4.0, 1.0),
151
..Self::NATURAL
152
};
153
154
/// A preset that's similar to how older games did bloom.
155
pub const OLD_SCHOOL: Self = Self {
156
intensity: 0.05,
157
low_frequency_boost: 0.7,
158
low_frequency_boost_curvature: 0.95,
159
high_pass_frequency: 1.0,
160
prefilter: BloomPrefilter {
161
threshold: 0.6,
162
threshold_softness: 0.2,
163
},
164
composite_mode: BloomCompositeMode::Additive,
165
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,
166
scale: Vec2::ONE,
167
};
168
169
/// A preset that applies a very strong bloom, and blurs the whole screen.
170
pub const SCREEN_BLUR: Self = Self {
171
intensity: 1.0,
172
low_frequency_boost: 0.0,
173
low_frequency_boost_curvature: 0.0,
174
high_pass_frequency: 1.0 / 3.0,
175
prefilter: BloomPrefilter {
176
threshold: 0.0,
177
threshold_softness: 0.0,
178
},
179
composite_mode: BloomCompositeMode::EnergyConserving,
180
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,
181
scale: Vec2::ONE,
182
};
183
}
184
185
impl Default for Bloom {
186
fn default() -> Self {
187
Self::NATURAL
188
}
189
}
190
191
/// Applies a threshold filter to the input image to extract the brightest
192
/// regions before blurring them and compositing back onto the original image.
193
/// These settings are useful when emulating the 1990s-2000s game look.
194
///
195
/// # Considerations
196
/// * Changing these settings creates a physically inaccurate image
197
/// * Changing these settings makes it easy to make the final result look worse
198
/// * Non-default prefilter settings should be used in conjunction with [`BloomCompositeMode::Additive`]
199
#[derive(Default, Clone, Reflect)]
200
#[reflect(Clone, Default)]
201
pub struct BloomPrefilter {
202
/// Baseline of the quadratic threshold curve (default: 0.0).
203
///
204
/// RGB values under the threshold curve will not contribute to the effect.
205
pub threshold: f32,
206
207
/// Controls how much to blend between the thresholded and non-thresholded colors (default: 0.0).
208
///
209
/// 0.0 = Abrupt threshold, no blending
210
/// 1.0 = Fully soft threshold
211
///
212
/// Values outside of the range [0.0, 1.0] will be clamped.
213
pub threshold_softness: f32,
214
}
215
216
#[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, Copy)]
217
#[reflect(Clone, Hash, PartialEq)]
218
pub enum BloomCompositeMode {
219
EnergyConserving,
220
Additive,
221
}
222
223
impl SyncComponent for Bloom {
224
type Out = (Self, BloomUniforms);
225
}
226
227
impl ExtractComponent for Bloom {
228
type QueryData = (&'static Self, &'static Camera);
229
type QueryFilter = With<Hdr>;
230
231
fn extract_component((bloom, camera): QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
232
match (
233
camera.physical_viewport_rect(),
234
camera.physical_viewport_size(),
235
camera.physical_target_size(),
236
camera.is_active,
237
) {
238
(Some(URect { min: origin, .. }), Some(size), Some(target_size), true)
239
if size.x != 0 && size.y != 0 =>
240
{
241
let threshold = bloom.prefilter.threshold;
242
let threshold_softness = bloom.prefilter.threshold_softness;
243
let knee = threshold * threshold_softness.clamp(0.0, 1.0);
244
245
let uniform = BloomUniforms {
246
threshold_precomputations: Vec4::new(
247
threshold,
248
threshold - knee,
249
2.0 * knee,
250
0.25 / (knee + 0.00001),
251
),
252
viewport: UVec4::new(origin.x, origin.y, size.x, size.y).as_vec4()
253
/ UVec4::new(target_size.x, target_size.y, target_size.x, target_size.y)
254
.as_vec4(),
255
aspect: AspectRatio::try_from_pixels(size.x, size.y)
256
.expect("Valid screen size values for Bloom settings")
257
.ratio(),
258
scale: bloom.scale,
259
};
260
261
Some((bloom.clone(), uniform))
262
}
263
_ => None,
264
}
265
}
266
}
267
268