Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/3d/bloom_3d.rs
6592 views
1
//! Illustrates bloom post-processing using HDR and emissive materials.
2
3
use bevy::{
4
core_pipeline::tonemapping::Tonemapping,
5
math::ops,
6
post_process::bloom::{Bloom, BloomCompositeMode},
7
prelude::*,
8
};
9
use std::{
10
collections::hash_map::DefaultHasher,
11
hash::{Hash, Hasher},
12
};
13
14
fn main() {
15
App::new()
16
.add_plugins(DefaultPlugins)
17
.add_systems(Startup, setup_scene)
18
.add_systems(Update, (update_bloom_settings, bounce_spheres))
19
.run();
20
}
21
22
fn setup_scene(
23
mut commands: Commands,
24
mut meshes: ResMut<Assets<Mesh>>,
25
mut materials: ResMut<Assets<StandardMaterial>>,
26
) {
27
commands.spawn((
28
Camera3d::default(),
29
Camera {
30
clear_color: ClearColorConfig::Custom(Color::BLACK),
31
..default()
32
},
33
Tonemapping::TonyMcMapface, // 1. Using a tonemapper that desaturates to white is recommended
34
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
35
Bloom::NATURAL, // 2. Enable bloom for the camera
36
));
37
38
let material_emissive1 = materials.add(StandardMaterial {
39
emissive: LinearRgba::rgb(0.0, 0.0, 150.0), // 3. Put something bright in a dark environment to see the effect
40
..default()
41
});
42
let material_emissive2 = materials.add(StandardMaterial {
43
emissive: LinearRgba::rgb(1000.0, 1000.0, 1000.0),
44
..default()
45
});
46
let material_emissive3 = materials.add(StandardMaterial {
47
emissive: LinearRgba::rgb(50.0, 0.0, 0.0),
48
..default()
49
});
50
let material_non_emissive = materials.add(StandardMaterial {
51
base_color: Color::BLACK,
52
..default()
53
});
54
55
let mesh = meshes.add(Sphere::new(0.4).mesh().ico(5).unwrap());
56
57
for x in -5..5 {
58
for z in -5..5 {
59
// This generates a pseudo-random integer between `[0, 6)`, but deterministically so
60
// the same spheres are always the same colors.
61
let mut hasher = DefaultHasher::new();
62
(x, z).hash(&mut hasher);
63
let rand = (hasher.finish() + 3) % 6;
64
65
let (material, scale) = match rand {
66
0 => (material_emissive1.clone(), 0.5),
67
1 => (material_emissive2.clone(), 0.1),
68
2 => (material_emissive3.clone(), 1.0),
69
3..=5 => (material_non_emissive.clone(), 1.5),
70
_ => unreachable!(),
71
};
72
73
commands.spawn((
74
Mesh3d(mesh.clone()),
75
MeshMaterial3d(material),
76
Transform::from_xyz(x as f32 * 2.0, 0.0, z as f32 * 2.0)
77
.with_scale(Vec3::splat(scale)),
78
Bouncing,
79
));
80
}
81
}
82
83
// example instructions
84
commands.spawn((
85
Text::default(),
86
Node {
87
position_type: PositionType::Absolute,
88
bottom: px(12),
89
left: px(12),
90
..default()
91
},
92
));
93
}
94
95
// ------------------------------------------------------------------------------------------------
96
97
fn update_bloom_settings(
98
camera: Single<(Entity, Option<&mut Bloom>), With<Camera>>,
99
mut text: Single<&mut Text>,
100
mut commands: Commands,
101
keycode: Res<ButtonInput<KeyCode>>,
102
time: Res<Time>,
103
) {
104
let bloom = camera.into_inner();
105
106
match bloom {
107
(entity, Some(mut bloom)) => {
108
text.0 = "Bloom (Toggle: Space)\n".to_string();
109
text.push_str(&format!("(Q/A) Intensity: {:.2}\n", bloom.intensity));
110
text.push_str(&format!(
111
"(W/S) Low-frequency boost: {:.2}\n",
112
bloom.low_frequency_boost
113
));
114
text.push_str(&format!(
115
"(E/D) Low-frequency boost curvature: {:.2}\n",
116
bloom.low_frequency_boost_curvature
117
));
118
text.push_str(&format!(
119
"(R/F) High-pass frequency: {:.2}\n",
120
bloom.high_pass_frequency
121
));
122
text.push_str(&format!(
123
"(T/G) Mode: {}\n",
124
match bloom.composite_mode {
125
BloomCompositeMode::EnergyConserving => "Energy-conserving",
126
BloomCompositeMode::Additive => "Additive",
127
}
128
));
129
text.push_str(&format!(
130
"(Y/H) Threshold: {:.2}\n",
131
bloom.prefilter.threshold
132
));
133
text.push_str(&format!(
134
"(U/J) Threshold softness: {:.2}\n",
135
bloom.prefilter.threshold_softness
136
));
137
text.push_str(&format!("(I/K) Horizontal Scale: {:.2}\n", bloom.scale.x));
138
139
if keycode.just_pressed(KeyCode::Space) {
140
commands.entity(entity).remove::<Bloom>();
141
}
142
143
let dt = time.delta_secs();
144
145
if keycode.pressed(KeyCode::KeyA) {
146
bloom.intensity -= dt / 10.0;
147
}
148
if keycode.pressed(KeyCode::KeyQ) {
149
bloom.intensity += dt / 10.0;
150
}
151
bloom.intensity = bloom.intensity.clamp(0.0, 1.0);
152
153
if keycode.pressed(KeyCode::KeyS) {
154
bloom.low_frequency_boost -= dt / 10.0;
155
}
156
if keycode.pressed(KeyCode::KeyW) {
157
bloom.low_frequency_boost += dt / 10.0;
158
}
159
bloom.low_frequency_boost = bloom.low_frequency_boost.clamp(0.0, 1.0);
160
161
if keycode.pressed(KeyCode::KeyD) {
162
bloom.low_frequency_boost_curvature -= dt / 10.0;
163
}
164
if keycode.pressed(KeyCode::KeyE) {
165
bloom.low_frequency_boost_curvature += dt / 10.0;
166
}
167
bloom.low_frequency_boost_curvature =
168
bloom.low_frequency_boost_curvature.clamp(0.0, 1.0);
169
170
if keycode.pressed(KeyCode::KeyF) {
171
bloom.high_pass_frequency -= dt / 10.0;
172
}
173
if keycode.pressed(KeyCode::KeyR) {
174
bloom.high_pass_frequency += dt / 10.0;
175
}
176
bloom.high_pass_frequency = bloom.high_pass_frequency.clamp(0.0, 1.0);
177
178
if keycode.pressed(KeyCode::KeyG) {
179
bloom.composite_mode = BloomCompositeMode::Additive;
180
}
181
if keycode.pressed(KeyCode::KeyT) {
182
bloom.composite_mode = BloomCompositeMode::EnergyConserving;
183
}
184
185
if keycode.pressed(KeyCode::KeyH) {
186
bloom.prefilter.threshold -= dt;
187
}
188
if keycode.pressed(KeyCode::KeyY) {
189
bloom.prefilter.threshold += dt;
190
}
191
bloom.prefilter.threshold = bloom.prefilter.threshold.max(0.0);
192
193
if keycode.pressed(KeyCode::KeyJ) {
194
bloom.prefilter.threshold_softness -= dt / 10.0;
195
}
196
if keycode.pressed(KeyCode::KeyU) {
197
bloom.prefilter.threshold_softness += dt / 10.0;
198
}
199
bloom.prefilter.threshold_softness = bloom.prefilter.threshold_softness.clamp(0.0, 1.0);
200
201
if keycode.pressed(KeyCode::KeyK) {
202
bloom.scale.x -= dt * 2.0;
203
}
204
if keycode.pressed(KeyCode::KeyI) {
205
bloom.scale.x += dt * 2.0;
206
}
207
bloom.scale.x = bloom.scale.x.clamp(0.0, 8.0);
208
}
209
210
(entity, None) => {
211
text.0 = "Bloom: Off (Toggle: Space)".to_string();
212
213
if keycode.just_pressed(KeyCode::Space) {
214
commands.entity(entity).insert(Bloom::NATURAL);
215
}
216
}
217
}
218
}
219
220
#[derive(Component)]
221
struct Bouncing;
222
223
fn bounce_spheres(time: Res<Time>, mut query: Query<&mut Transform, With<Bouncing>>) {
224
for mut transform in query.iter_mut() {
225
transform.translation.y =
226
ops::sin(transform.translation.x + transform.translation.z + time.elapsed_secs());
227
}
228
}
229
230