Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/3d/auto_exposure.rs
6592 views
1
//! This example showcases auto exposure,
2
//! which automatically (but not instantly) adjusts the brightness of the scene in a way that mimics the function of the human eye.
3
//! Auto exposure requires compute shader capabilities, so it's not available on WebGL.
4
//!
5
//! ## Controls
6
//!
7
//! | Key Binding | Action |
8
//! |:-------------------|:---------------------------------------|
9
//! | `Left` / `Right` | Rotate Camera |
10
//! | `C` | Toggle Compensation Curve |
11
//! | `M` | Toggle Metering Mask |
12
//! | `V` | Visualize Metering Mask |
13
14
use bevy::{
15
core_pipeline::Skybox,
16
math::{cubic_splines::LinearSpline, primitives::Plane3d, vec2},
17
post_process::auto_exposure::{
18
AutoExposure, AutoExposureCompensationCurve, AutoExposurePlugin,
19
},
20
prelude::*,
21
};
22
23
fn main() {
24
App::new()
25
.add_plugins(DefaultPlugins)
26
.add_plugins(AutoExposurePlugin)
27
.add_systems(Startup, setup)
28
.add_systems(Update, example_control_system)
29
.run();
30
}
31
32
fn setup(
33
mut commands: Commands,
34
mut meshes: ResMut<Assets<Mesh>>,
35
mut materials: ResMut<Assets<StandardMaterial>>,
36
mut compensation_curves: ResMut<Assets<AutoExposureCompensationCurve>>,
37
asset_server: Res<AssetServer>,
38
) {
39
let metering_mask = asset_server.load("textures/basic_metering_mask.png");
40
41
commands.spawn((
42
Camera3d::default(),
43
Transform::from_xyz(1.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y),
44
AutoExposure {
45
metering_mask: metering_mask.clone(),
46
..default()
47
},
48
Skybox {
49
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
50
brightness: light_consts::lux::DIRECT_SUNLIGHT,
51
..default()
52
},
53
));
54
55
commands.insert_resource(ExampleResources {
56
basic_compensation_curve: compensation_curves.add(
57
AutoExposureCompensationCurve::from_curve(LinearSpline::new([
58
vec2(-4.0, -2.0),
59
vec2(0.0, 0.0),
60
vec2(2.0, 0.0),
61
vec2(4.0, 2.0),
62
]))
63
.unwrap(),
64
),
65
basic_metering_mask: metering_mask.clone(),
66
});
67
68
let plane = meshes.add(Mesh::from(
69
Plane3d {
70
normal: -Dir3::Z,
71
half_size: Vec2::new(2.0, 0.5),
72
}
73
.mesh(),
74
));
75
76
// Build a dimly lit box around the camera, with a slot to see the bright skybox.
77
for level in -1..=1 {
78
for side in [-Vec3::X, Vec3::X, -Vec3::Z, Vec3::Z] {
79
if level == 0 && Vec3::Z == side {
80
continue;
81
}
82
83
let height = Vec3::Y * level as f32;
84
85
commands.spawn((
86
Mesh3d(plane.clone()),
87
MeshMaterial3d(materials.add(StandardMaterial {
88
base_color: Color::srgb(
89
0.5 + side.x * 0.5,
90
0.75 - level as f32 * 0.25,
91
0.5 + side.z * 0.5,
92
),
93
..default()
94
})),
95
Transform::from_translation(side * 2.0 + height).looking_at(height, Vec3::Y),
96
));
97
}
98
}
99
100
commands.insert_resource(AmbientLight {
101
color: Color::WHITE,
102
brightness: 0.0,
103
..default()
104
});
105
106
commands.spawn((
107
PointLight {
108
intensity: 2000.0,
109
..default()
110
},
111
Transform::from_xyz(0.0, 0.0, 0.0),
112
));
113
114
commands.spawn((
115
ImageNode {
116
image: metering_mask,
117
..default()
118
},
119
Node {
120
width: percent(100),
121
height: percent(100),
122
..default()
123
},
124
));
125
126
let text_font = TextFont::default();
127
128
commands.spawn((Text::new("Left / Right - Rotate Camera\nC - Toggle Compensation Curve\nM - Toggle Metering Mask\nV - Visualize Metering Mask"),
129
text_font.clone(), Node {
130
position_type: PositionType::Absolute,
131
top: px(12),
132
left: px(12),
133
..default()
134
})
135
);
136
137
commands.spawn((
138
Text::default(),
139
text_font,
140
Node {
141
position_type: PositionType::Absolute,
142
top: px(12),
143
right: px(12),
144
..default()
145
},
146
ExampleDisplay,
147
));
148
}
149
150
#[derive(Component)]
151
struct ExampleDisplay;
152
153
#[derive(Resource)]
154
struct ExampleResources {
155
basic_compensation_curve: Handle<AutoExposureCompensationCurve>,
156
basic_metering_mask: Handle<Image>,
157
}
158
159
fn example_control_system(
160
camera: Single<(&mut Transform, &mut AutoExposure), With<Camera3d>>,
161
mut display: Single<&mut Text, With<ExampleDisplay>>,
162
mut mask_image: Single<&mut Node, With<ImageNode>>,
163
time: Res<Time>,
164
input: Res<ButtonInput<KeyCode>>,
165
resources: Res<ExampleResources>,
166
) {
167
let (mut camera_transform, mut auto_exposure) = camera.into_inner();
168
169
let rotation = if input.pressed(KeyCode::ArrowLeft) {
170
time.delta_secs()
171
} else if input.pressed(KeyCode::ArrowRight) {
172
-time.delta_secs()
173
} else {
174
0.0
175
};
176
177
camera_transform.rotate_around(Vec3::ZERO, Quat::from_rotation_y(rotation));
178
179
if input.just_pressed(KeyCode::KeyC) {
180
auto_exposure.compensation_curve =
181
if auto_exposure.compensation_curve == resources.basic_compensation_curve {
182
Handle::default()
183
} else {
184
resources.basic_compensation_curve.clone()
185
};
186
}
187
188
if input.just_pressed(KeyCode::KeyM) {
189
auto_exposure.metering_mask =
190
if auto_exposure.metering_mask == resources.basic_metering_mask {
191
Handle::default()
192
} else {
193
resources.basic_metering_mask.clone()
194
};
195
}
196
197
mask_image.display = if input.pressed(KeyCode::KeyV) {
198
Display::Flex
199
} else {
200
Display::None
201
};
202
203
display.0 = format!(
204
"Compensation Curve: {}\nMetering Mask: {}",
205
if auto_exposure.compensation_curve == resources.basic_compensation_curve {
206
"Enabled"
207
} else {
208
"Disabled"
209
},
210
if auto_exposure.metering_mask == resources.basic_metering_mask {
211
"Enabled"
212
} else {
213
"Disabled"
214
},
215
);
216
}
217
218