Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/3d/atmosphere.rs
9351 views
1
//! This example showcases pbr atmospheric scattering
2
#[cfg(feature = "free_camera")]
3
use bevy::camera_controller::free_camera::{FreeCamera, FreeCameraPlugin};
4
use std::f32::consts::PI;
5
6
use bevy::{
7
anti_alias::taa::TemporalAntiAliasing,
8
camera::Exposure,
9
color::palettes::css::BLACK,
10
core_pipeline::tonemapping::Tonemapping,
11
image::{
12
ImageAddressMode, ImageFilterMode, ImageLoaderSettings, ImageSampler,
13
ImageSamplerDescriptor,
14
},
15
input::keyboard::KeyCode,
16
light::{
17
atmosphere::ScatteringMedium, light_consts::lux, Atmosphere, AtmosphereEnvironmentMapLight,
18
CascadeShadowConfigBuilder, FogVolume, VolumetricFog, VolumetricLight,
19
},
20
pbr::{
21
AtmosphereMode, AtmosphereSettings, DefaultOpaqueRendererMethod, ExtendedMaterial,
22
MaterialExtension, ScreenSpaceReflections,
23
},
24
post_process::bloom::Bloom,
25
prelude::*,
26
render::render_resource::{AsBindGroup, ShaderType},
27
shader::ShaderRef,
28
};
29
30
#[derive(Resource, Default)]
31
struct GameState {
32
paused: bool,
33
}
34
35
fn main() {
36
App::new()
37
.insert_resource(DefaultOpaqueRendererMethod::deferred())
38
.insert_resource(ClearColor(Color::BLACK))
39
.insert_resource(GameState::default())
40
.insert_resource(GlobalAmbientLight::NONE)
41
.add_plugins((
42
DefaultPlugins,
43
#[cfg(feature = "free_camera")]
44
FreeCameraPlugin,
45
))
46
.add_plugins(MaterialPlugin::<ExtendedMaterial<StandardMaterial, Water>>::default())
47
.add_systems(
48
Startup,
49
(setup_camera_fog, setup_terrain_scene, print_controls),
50
)
51
.add_systems(Update, (dynamic_scene, atmosphere_controls))
52
.run();
53
}
54
55
fn print_controls() {
56
println!("Atmosphere Example Controls:");
57
println!(" 1 - Switch to lookup texture rendering method");
58
println!(" 2 - Switch to raymarched rendering method");
59
println!(" Enter - Pause/Resume sun motion");
60
println!(" Up/Down - Increase/Decrease exposure");
61
}
62
63
fn atmosphere_controls(
64
keyboard_input: Res<ButtonInput<KeyCode>>,
65
mut atmosphere_settings: Query<&mut AtmosphereSettings>,
66
mut game_state: ResMut<GameState>,
67
mut camera_exposure: Query<&mut Exposure, With<Camera3d>>,
68
time: Res<Time>,
69
) {
70
if keyboard_input.just_pressed(KeyCode::Digit1) {
71
for mut settings in &mut atmosphere_settings {
72
settings.rendering_method = AtmosphereMode::LookupTexture;
73
println!("Switched to lookup texture rendering method");
74
}
75
}
76
77
if keyboard_input.just_pressed(KeyCode::Digit2) {
78
for mut settings in &mut atmosphere_settings {
79
settings.rendering_method = AtmosphereMode::Raymarched;
80
println!("Switched to raymarched rendering method");
81
}
82
}
83
84
if keyboard_input.just_pressed(KeyCode::Enter) {
85
game_state.paused = !game_state.paused;
86
}
87
88
if keyboard_input.pressed(KeyCode::ArrowUp) {
89
for mut exposure in &mut camera_exposure {
90
exposure.ev100 -= time.delta_secs() * 2.0;
91
}
92
}
93
94
if keyboard_input.pressed(KeyCode::ArrowDown) {
95
for mut exposure in &mut camera_exposure {
96
exposure.ev100 += time.delta_secs() * 2.0;
97
}
98
}
99
}
100
101
fn setup_camera_fog(
102
mut commands: Commands,
103
mut scattering_mediums: ResMut<Assets<ScatteringMedium>>,
104
) {
105
commands.spawn((
106
Camera3d::default(),
107
Transform::from_xyz(-2.4, 0.04, 0.0).looking_at(Vec3::Y * 0.1, Vec3::Y),
108
// Earthlike atmosphere
109
Atmosphere::earthlike(scattering_mediums.add(ScatteringMedium::default())),
110
// Can be adjusted to change the scene scale and rendering quality
111
AtmosphereSettings::default(),
112
// The directional light illuminance used in this scene
113
// (the one recommended for use with this feature) is
114
// quite bright, so raising the exposure compensation helps
115
// bring the scene to a nicer brightness range.
116
Exposure { ev100: 13.0 },
117
// Tonemapper chosen just because it looked good with the scene, any
118
// tonemapper would be fine :)
119
Tonemapping::AcesFitted,
120
// Bloom gives the sun a much more natural look.
121
Bloom::NATURAL,
122
// Enables the atmosphere to drive reflections and ambient lighting (IBL) for this view
123
AtmosphereEnvironmentMapLight::default(),
124
#[cfg(feature = "free_camera")]
125
FreeCamera::default(),
126
VolumetricFog {
127
ambient_intensity: 0.0,
128
..default()
129
},
130
Msaa::Off,
131
TemporalAntiAliasing::default(),
132
ScreenSpaceReflections::default(),
133
));
134
}
135
136
#[derive(Component)]
137
struct Terrain;
138
139
/// A custom [`ExtendedMaterial`] that creates animated water ripples.
140
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
141
struct Water {
142
/// The normal map image.
143
///
144
/// Note that, like all normal maps, this must not be loaded as sRGB.
145
#[texture(100)]
146
#[sampler(101)]
147
normals: Handle<Image>,
148
149
// Parameters to the water shader.
150
#[uniform(102)]
151
settings: WaterSettings,
152
}
153
154
/// Parameters to the water shader.
155
#[derive(ShaderType, Debug, Clone)]
156
struct WaterSettings {
157
/// How much to displace each octave each frame, in the u and v directions.
158
/// Two octaves are packed into each `vec4`.
159
octave_vectors: [Vec4; 2],
160
/// How wide the waves are in each octave.
161
octave_scales: Vec4,
162
/// How high the waves are in each octave.
163
octave_strengths: Vec4,
164
}
165
166
impl MaterialExtension for Water {
167
fn deferred_fragment_shader() -> ShaderRef {
168
"shaders/water_material.wgsl".into()
169
}
170
}
171
172
fn setup_terrain_scene(
173
mut commands: Commands,
174
mut meshes: ResMut<Assets<Mesh>>,
175
mut materials: ResMut<Assets<StandardMaterial>>,
176
mut water_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, Water>>>,
177
asset_server: Res<AssetServer>,
178
) {
179
// Configure a properly scaled cascade shadow map for this scene (defaults are too large, mesh units are in km)
180
let cascade_shadow_config = CascadeShadowConfigBuilder {
181
first_cascade_far_bound: 0.3,
182
maximum_distance: 15.0,
183
..default()
184
}
185
.build();
186
187
// Sun
188
commands.spawn((
189
DirectionalLight {
190
shadow_maps_enabled: true,
191
// lux::RAW_SUNLIGHT is recommended for use with this feature, since
192
// other values approximate sunlight *post-scattering* in various
193
// conditions. RAW_SUNLIGHT in comparison is the illuminance of the
194
// sun unfiltered by the atmosphere, so it is the proper input for
195
// sunlight to be filtered by the atmosphere.
196
illuminance: lux::RAW_SUNLIGHT,
197
..default()
198
},
199
Transform::from_xyz(1.0, 0.4, 0.0).looking_at(Vec3::ZERO, Vec3::Y),
200
VolumetricLight,
201
cascade_shadow_config,
202
));
203
204
// spawn the fog volume
205
commands.spawn((
206
FogVolume::default(),
207
Transform::from_scale(Vec3::new(10.0, 1.0, 10.0)).with_translation(Vec3::Y * 0.5),
208
));
209
210
let sphere_mesh = meshes.add(Mesh::from(Sphere { radius: 1.0 }));
211
212
// light probe spheres
213
commands.spawn((
214
Mesh3d(sphere_mesh.clone()),
215
MeshMaterial3d(materials.add(StandardMaterial {
216
base_color: Color::WHITE,
217
metallic: 1.0,
218
perceptual_roughness: 0.0,
219
..default()
220
})),
221
Transform::from_xyz(-1.0, 0.1, -0.1).with_scale(Vec3::splat(0.05)),
222
));
223
224
commands.spawn((
225
Mesh3d(sphere_mesh.clone()),
226
MeshMaterial3d(materials.add(StandardMaterial {
227
base_color: Color::WHITE,
228
metallic: 0.0,
229
perceptual_roughness: 1.0,
230
..default()
231
})),
232
Transform::from_xyz(-1.0, 0.1, 0.1).with_scale(Vec3::splat(0.05)),
233
));
234
235
// Terrain
236
commands.spawn((
237
Terrain,
238
SceneRoot(
239
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/terrain/terrain.glb")),
240
),
241
Transform::from_xyz(-1.0, 0.0, -0.5)
242
.with_scale(Vec3::splat(0.5))
243
.with_rotation(Quat::from_rotation_y(PI / 2.0)),
244
));
245
246
spawn_water(
247
&mut commands,
248
&asset_server,
249
&mut meshes,
250
&mut water_materials,
251
);
252
}
253
254
// Spawns the water plane.
255
fn spawn_water(
256
commands: &mut Commands,
257
asset_server: &AssetServer,
258
meshes: &mut Assets<Mesh>,
259
water_materials: &mut Assets<ExtendedMaterial<StandardMaterial, Water>>,
260
) {
261
commands.spawn((
262
Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))),
263
MeshMaterial3d(water_materials.add(ExtendedMaterial {
264
base: StandardMaterial {
265
base_color: BLACK.into(),
266
perceptual_roughness: 0.0,
267
..default()
268
},
269
extension: Water {
270
normals: asset_server.load_with_settings::<Image, ImageLoaderSettings>(
271
"textures/water_normals.png",
272
|settings| {
273
settings.is_srgb = false;
274
settings.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor {
275
address_mode_u: ImageAddressMode::Repeat,
276
address_mode_v: ImageAddressMode::Repeat,
277
mag_filter: ImageFilterMode::Linear,
278
min_filter: ImageFilterMode::Linear,
279
..default()
280
});
281
},
282
),
283
// These water settings are just random values to create some
284
// variety.
285
settings: WaterSettings {
286
octave_vectors: [
287
vec4(0.080, 0.059, 0.073, -0.062),
288
vec4(0.153, 0.138, -0.149, -0.195),
289
],
290
octave_scales: vec4(1.0, 2.1, 7.9, 14.9) * 500.0,
291
octave_strengths: vec4(0.16, 0.18, 0.093, 0.044) * 0.2,
292
},
293
},
294
})),
295
Transform::from_scale(Vec3::splat(100.0)),
296
));
297
}
298
299
fn dynamic_scene(
300
mut suns: Query<&mut Transform, With<DirectionalLight>>,
301
time: Res<Time>,
302
sun_motion_state: Res<GameState>,
303
) {
304
// Only rotate the sun if motion is not paused
305
if !sun_motion_state.paused {
306
suns.iter_mut()
307
.for_each(|mut tf| tf.rotate_x(-time.delta_secs() * PI / 10.0));
308
}
309
}
310
311