mirror of
https://github.com/bevyengine/bevy.git
synced 2026-05-06 06:06:42 -04:00
535cf401cc
Part 2 of #23619 In **Bevy 0.19** we are landing a subset of Bevy's Next Generation Scene system (often known as BSN), which now lives in the `bevy_scene` / `bevy::scene` crate. However the old `bevy_scene` system still needs to stick around for a bit longer, as it provides some features that Bevy's Next Generation Scene system doesn't (yet!): 1. It is not _yet_ possible to write a World _to_ BSN, so the old system is still necessary for "round trip World serialization". 2. The GLTF scene loader has not yet been ported to BSN, so the old system is still necessary to spawn GLTF scenes in Bevy. For this reason, we have renamed the old `bevy_scene` crate to `bevy_world_serialization`. If you were referencing `bevy_scene::*` or `bevy::scene::*` types, rename those paths to `bevy_world_serialization::*` and `bevy::world_serialization::*` respectively. Additionally, to avoid confusion / conflicts with the new scene system, all "scene" terminology / types have been reframed as "world serialization": - `Scene` -> `WorldAsset` (as this was always just a World wrapper) - `SceneRoot` -> `WorldAssetRoot` - `DynamicScene` -> `DynamicWorld` - `DynamicScene::from_scene` -> `DynamicWorld::from_world_asset` - `DynamicSceneBuilder` -> `DynamicWorldBuilder` - `DynamicSceneRoot` -> `DynamicWorldRoot` - `SceneInstanceReady` -> `WorldInstanceReady` - `SceneLoader` -> `WorldAssetLoader` - `ScenePlugin` -> `WorldSerializationPlugin` - `SceneRootTemplate` -> `WorldAssetRootTemplate` - `SceneSpawner` -> `WorldInstanceSpawner` - `SceneFilter` -> `WorldFilter` - `SceneLoaderError` -> `WorldAssetLoaderError` - `SceneSpawnError` -> `WorldInstanceSpawnError` Note that I went with `bevy_world_serialization` over `bevy_ecs_serialization`, as that is what all of the internal features described themselves as. I think it is both more specific and does a better job of making itself decoupled from `bevy_ecs` proper.
312 lines
10 KiB
Rust
312 lines
10 KiB
Rust
//! This example showcases pbr atmospheric scattering
|
|
#[cfg(feature = "free_camera")]
|
|
use bevy::camera_controller::free_camera::{FreeCamera, FreeCameraPlugin};
|
|
use std::f32::consts::PI;
|
|
|
|
use bevy::{
|
|
anti_alias::taa::TemporalAntiAliasing,
|
|
camera::Exposure,
|
|
color::palettes::css::BLACK,
|
|
core_pipeline::tonemapping::Tonemapping,
|
|
image::{
|
|
ImageAddressMode, ImageFilterMode, ImageLoaderSettings, ImageSampler,
|
|
ImageSamplerDescriptor,
|
|
},
|
|
input::keyboard::KeyCode,
|
|
light::{
|
|
atmosphere::ScatteringMedium, light_consts::lux, Atmosphere, AtmosphereEnvironmentMapLight,
|
|
FogVolume, VolumetricFog, VolumetricLight,
|
|
},
|
|
pbr::{
|
|
AtmosphereMode, AtmosphereSettings, DefaultOpaqueRendererMethod, ExtendedMaterial,
|
|
MaterialExtension, ScreenSpaceReflections,
|
|
},
|
|
post_process::bloom::Bloom,
|
|
prelude::*,
|
|
render::render_resource::{AsBindGroup, ShaderType},
|
|
shader::ShaderRef,
|
|
};
|
|
|
|
#[derive(Resource, Default)]
|
|
struct GameState {
|
|
paused: bool,
|
|
}
|
|
|
|
#[derive(Resource)]
|
|
struct AtmospherePresets {
|
|
earth: Handle<ScatteringMedium>,
|
|
mars: Handle<ScatteringMedium>,
|
|
}
|
|
|
|
fn main() {
|
|
App::new()
|
|
.insert_resource(DefaultOpaqueRendererMethod::deferred())
|
|
.insert_resource(ClearColor(Color::BLACK))
|
|
.insert_resource(GameState::default())
|
|
.insert_resource(GlobalAmbientLight::NONE)
|
|
.add_plugins((
|
|
DefaultPlugins,
|
|
#[cfg(feature = "free_camera")]
|
|
FreeCameraPlugin,
|
|
))
|
|
.add_plugins(MaterialPlugin::<ExtendedMaterial<StandardMaterial, Water>>::default())
|
|
.add_systems(
|
|
Startup,
|
|
(setup_camera_fog, setup_terrain_scene, print_controls),
|
|
)
|
|
.add_systems(Update, (dynamic_scene, atmosphere_controls))
|
|
.run();
|
|
}
|
|
|
|
fn print_controls() {
|
|
println!("Atmosphere Example Controls:");
|
|
println!(" 1 - Switch to lookup texture rendering method");
|
|
println!(" 2 - Switch to raymarched rendering method");
|
|
println!(" 3 - Switch to Earth atmosphere");
|
|
println!(" 4 - Switch to Mars atmosphere");
|
|
println!(" Enter - Pause/Resume sun motion");
|
|
println!(" Up/Down - Increase/Decrease exposure");
|
|
}
|
|
|
|
fn atmosphere_controls(
|
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
|
mut atmosphere_settings: Query<&mut AtmosphereSettings>,
|
|
mut atmosphere_query: Query<&mut Atmosphere>,
|
|
atmosphere_presets: Res<AtmospherePresets>,
|
|
mut game_state: ResMut<GameState>,
|
|
mut camera_exposure: Query<&mut Exposure, With<Camera3d>>,
|
|
time: Res<Time>,
|
|
) {
|
|
if keyboard_input.just_pressed(KeyCode::Digit3) {
|
|
for mut atmosphere in &mut atmosphere_query {
|
|
*atmosphere = Atmosphere::earth(atmosphere_presets.earth.clone());
|
|
println!("Switched to Earth atmosphere");
|
|
}
|
|
}
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Digit4) {
|
|
for mut atmosphere in &mut atmosphere_query {
|
|
*atmosphere = Atmosphere::mars(atmosphere_presets.mars.clone());
|
|
println!("Switched to Mars atmosphere");
|
|
}
|
|
}
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Digit1) {
|
|
for mut settings in &mut atmosphere_settings {
|
|
settings.rendering_method = AtmosphereMode::LookupTexture;
|
|
println!("Switched to lookup texture rendering method");
|
|
}
|
|
}
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Digit2) {
|
|
for mut settings in &mut atmosphere_settings {
|
|
settings.rendering_method = AtmosphereMode::Raymarched;
|
|
println!("Switched to raymarched rendering method");
|
|
}
|
|
}
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Enter) {
|
|
game_state.paused = !game_state.paused;
|
|
}
|
|
|
|
if keyboard_input.pressed(KeyCode::ArrowUp) {
|
|
for mut exposure in &mut camera_exposure {
|
|
exposure.ev100 -= time.delta_secs() * 2.0;
|
|
}
|
|
}
|
|
|
|
if keyboard_input.pressed(KeyCode::ArrowDown) {
|
|
for mut exposure in &mut camera_exposure {
|
|
exposure.ev100 += time.delta_secs() * 2.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn setup_camera_fog(
|
|
mut commands: Commands,
|
|
mut scattering_mediums: ResMut<Assets<ScatteringMedium>>,
|
|
asset_server: Res<AssetServer>,
|
|
) {
|
|
let earth_medium = scattering_mediums.add(ScatteringMedium::earth(256, 256));
|
|
let mars_phase = asset_server.load("textures/mars_mie_phase.ktx2");
|
|
let mars_medium = scattering_mediums.add(ScatteringMedium::mars(256, 256, mars_phase));
|
|
|
|
commands.insert_resource(AtmospherePresets {
|
|
earth: earth_medium.clone(),
|
|
mars: mars_medium.clone(),
|
|
});
|
|
|
|
commands.spawn((
|
|
Camera3d::default(),
|
|
Transform::from_xyz(-2.8, 0.045, 0.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
// Earth atmosphere
|
|
Atmosphere::earth(earth_medium),
|
|
// Can be adjusted to change the scene scale and rendering quality
|
|
AtmosphereSettings::default(),
|
|
// The directional light illuminance used in this scene
|
|
// (the one recommended for use with this feature) is
|
|
// quite bright, so raising the exposure compensation helps
|
|
// bring the scene to a nicer brightness range.
|
|
Exposure { ev100: 13.0 },
|
|
// Tonemapper chosen just because it looked good with the scene, any
|
|
// tonemapper would be fine :)
|
|
Tonemapping::AcesFitted,
|
|
// Bloom gives the sun a much more natural look.
|
|
Bloom::NATURAL,
|
|
// Enables the atmosphere to drive reflections and ambient lighting (IBL) for this view
|
|
AtmosphereEnvironmentMapLight::default(),
|
|
#[cfg(feature = "free_camera")]
|
|
FreeCamera::default(),
|
|
VolumetricFog {
|
|
ambient_intensity: 0.0,
|
|
..default()
|
|
},
|
|
Msaa::Off,
|
|
TemporalAntiAliasing::default(),
|
|
ScreenSpaceReflections {
|
|
min_perceptual_roughness: 0.0..0.0,
|
|
..default()
|
|
},
|
|
));
|
|
}
|
|
|
|
#[derive(Component)]
|
|
struct Terrain;
|
|
|
|
/// A custom [`ExtendedMaterial`] that creates animated water ripples.
|
|
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
|
struct Water {
|
|
/// The normal map image.
|
|
///
|
|
/// Note that, like all normal maps, this must not be loaded as sRGB.
|
|
#[texture(100)]
|
|
#[sampler(101)]
|
|
normals: Handle<Image>,
|
|
|
|
// Parameters to the water shader.
|
|
#[uniform(102)]
|
|
settings: WaterSettings,
|
|
}
|
|
|
|
/// Parameters to the water shader.
|
|
#[derive(ShaderType, Debug, Clone)]
|
|
struct WaterSettings {
|
|
/// How much to displace each octave each frame, in the u and v directions.
|
|
/// Two octaves are packed into each `vec4`.
|
|
octave_vectors: [Vec4; 2],
|
|
/// How wide the waves are in each octave.
|
|
octave_scales: Vec4,
|
|
/// How high the waves are in each octave.
|
|
octave_strengths: Vec4,
|
|
}
|
|
|
|
impl MaterialExtension for Water {
|
|
fn deferred_fragment_shader() -> ShaderRef {
|
|
"shaders/water_material.wgsl".into()
|
|
}
|
|
}
|
|
|
|
fn setup_terrain_scene(
|
|
mut commands: Commands,
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
mut water_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, Water>>>,
|
|
asset_server: Res<AssetServer>,
|
|
) {
|
|
// Sun
|
|
commands.spawn((
|
|
DirectionalLight {
|
|
shadow_maps_enabled: true,
|
|
// lux::RAW_SUNLIGHT is recommended for use with this feature, since
|
|
// other values approximate sunlight *post-scattering* in various
|
|
// conditions. RAW_SUNLIGHT in comparison is the illuminance of the
|
|
// sun unfiltered by the atmosphere, so it is the proper input for
|
|
// sunlight to be filtered by the atmosphere.
|
|
illuminance: lux::RAW_SUNLIGHT,
|
|
..default()
|
|
},
|
|
Transform::from_xyz(1.0, 0.4, 0.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
VolumetricLight,
|
|
));
|
|
|
|
// spawn the fog volume
|
|
commands.spawn((
|
|
FogVolume::default(),
|
|
Transform::from_scale(Vec3::new(10.0, 1.0, 10.0)).with_translation(Vec3::Y * 0.5),
|
|
));
|
|
|
|
// Terrain
|
|
commands.spawn((
|
|
Terrain,
|
|
WorldAssetRoot(
|
|
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/terrain/terrain.glb")),
|
|
),
|
|
Transform::from_xyz(-1.0, 0.0, -0.5)
|
|
.with_scale(Vec3::splat(0.5))
|
|
.with_rotation(Quat::from_rotation_y(PI / 2.0)),
|
|
));
|
|
|
|
spawn_water(
|
|
&mut commands,
|
|
&asset_server,
|
|
&mut meshes,
|
|
&mut water_materials,
|
|
);
|
|
}
|
|
|
|
// Spawns the water plane.
|
|
fn spawn_water(
|
|
commands: &mut Commands,
|
|
asset_server: &AssetServer,
|
|
meshes: &mut Assets<Mesh>,
|
|
water_materials: &mut Assets<ExtendedMaterial<StandardMaterial, Water>>,
|
|
) {
|
|
commands.spawn((
|
|
Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))),
|
|
MeshMaterial3d(water_materials.add(ExtendedMaterial {
|
|
base: StandardMaterial {
|
|
base_color: BLACK.into(),
|
|
perceptual_roughness: 0.0,
|
|
..default()
|
|
},
|
|
extension: Water {
|
|
normals: asset_server.load_with_settings::<Image, ImageLoaderSettings>(
|
|
"textures/water_normals.png",
|
|
|settings| {
|
|
settings.is_srgb = false;
|
|
settings.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor {
|
|
address_mode_u: ImageAddressMode::Repeat,
|
|
address_mode_v: ImageAddressMode::Repeat,
|
|
mag_filter: ImageFilterMode::Linear,
|
|
min_filter: ImageFilterMode::Linear,
|
|
..default()
|
|
});
|
|
},
|
|
),
|
|
// These water settings are just random values to create some
|
|
// variety.
|
|
settings: WaterSettings {
|
|
octave_vectors: [
|
|
vec4(0.080, 0.059, 0.073, -0.062),
|
|
vec4(0.153, 0.138, -0.149, -0.195),
|
|
],
|
|
octave_scales: vec4(1.0, 2.1, 7.9, 14.9) * 500.0,
|
|
octave_strengths: vec4(0.16, 0.18, 0.093, 0.044) * 0.2,
|
|
},
|
|
},
|
|
})),
|
|
Transform::from_scale(Vec3::splat(100.0)),
|
|
));
|
|
}
|
|
|
|
fn dynamic_scene(
|
|
mut suns: Query<&mut Transform, With<DirectionalLight>>,
|
|
time: Res<Time>,
|
|
sun_motion_state: Res<GameState>,
|
|
) {
|
|
// Only rotate the sun if motion is not paused
|
|
if !sun_motion_state.paused {
|
|
suns.iter_mut()
|
|
.for_each(|mut tf| tf.rotate_x(-time.delta_secs() * PI / 10.0));
|
|
}
|
|
}
|