 81a25bb0c7
			
		
	
	
		81a25bb0c7
		
			
		
	
	
	
	
		
			
			Implement procedural atmospheric scattering from [Sebastien Hillaire's 2020 paper](https://sebh.github.io/publications/egsr2020.pdf). This approach should scale well even down to mobile hardware, and is physically accurate. ## Co-author: @mate-h He helped massively with getting this over the finish line, ensuring everything was physically correct, correcting several places where I had misunderstood or misapplied the paper, and improving the performance in several places as well. Thanks! ## Credits @aevyrie: helped find numerous bugs and improve the example to best show off this feature :) Built off of @mtsr's original branch, which handled the transmittance lut (arguably the most important part) ## Showcase:   ## For followup - Integrate with pcwalton's volumetrics code - refactor/reorganize for better integration with other effects - have atmosphere transmittance affect directional lights - add support for generating skybox/environment map --------- Co-authored-by: Emerson Coskey <56370779+EmersonCoskey@users.noreply.github.com> Co-authored-by: atlv <email@atlasdostal.com> Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com> Co-authored-by: Emerson Coskey <coskey@emerlabs.net> Co-authored-by: Máté Homolya <mate.homolya@gmail.com>
		
			
				
	
	
		
			126 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! This example showcases pbr atmospheric scattering
 | |
| 
 | |
| use std::f32::consts::PI;
 | |
| 
 | |
| use bevy::{
 | |
|     core_pipeline::{bloom::Bloom, tonemapping::Tonemapping},
 | |
|     pbr::{light_consts::lux, Atmosphere, AtmosphereSettings, CascadeShadowConfigBuilder},
 | |
|     prelude::*,
 | |
|     render::camera::Exposure,
 | |
| };
 | |
| 
 | |
| fn main() {
 | |
|     App::new()
 | |
|         .add_plugins(DefaultPlugins)
 | |
|         .add_systems(Startup, (setup_camera_fog, setup_terrain_scene))
 | |
|         .add_systems(Update, dynamic_scene)
 | |
|         .run();
 | |
| }
 | |
| 
 | |
| fn setup_camera_fog(mut commands: Commands) {
 | |
|     commands.spawn((
 | |
|         Camera3d::default(),
 | |
|         // HDR is required for atmospheric scattering to be properly applied to the scene
 | |
|         Camera {
 | |
|             hdr: true,
 | |
|             ..default()
 | |
|         },
 | |
|         Transform::from_xyz(-1.2, 0.15, 0.0).looking_at(Vec3::Y * 0.1, Vec3::Y),
 | |
|         // This is the component that enables atmospheric scattering for a camera
 | |
|         Atmosphere::EARTH,
 | |
|         // The scene is in units of 10km, so we need to scale up the
 | |
|         // aerial view lut distance and set the scene scale accordingly.
 | |
|         // Most usages of this feature will not need to adjust this.
 | |
|         AtmosphereSettings {
 | |
|             aerial_view_lut_max_distance: 3.2e5,
 | |
|             scene_units_to_m: 1e+4,
 | |
|             ..Default::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::SUNLIGHT,
 | |
|         // 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,
 | |
|     ));
 | |
| }
 | |
| 
 | |
| #[derive(Component)]
 | |
| struct Terrain;
 | |
| 
 | |
| fn setup_terrain_scene(
 | |
|     mut commands: Commands,
 | |
|     mut meshes: ResMut<Assets<Mesh>>,
 | |
|     mut materials: ResMut<Assets<StandardMaterial>>,
 | |
|     asset_server: Res<AssetServer>,
 | |
| ) {
 | |
|     // Configure a properly scaled cascade shadow map for this scene (defaults are too large, mesh units are in km)
 | |
|     let cascade_shadow_config = CascadeShadowConfigBuilder {
 | |
|         first_cascade_far_bound: 0.3,
 | |
|         maximum_distance: 3.0,
 | |
|         ..default()
 | |
|     }
 | |
|     .build();
 | |
| 
 | |
|     // Sun
 | |
|     commands.spawn((
 | |
|         DirectionalLight {
 | |
|             shadows_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),
 | |
|         cascade_shadow_config,
 | |
|     ));
 | |
| 
 | |
|     let sphere_mesh = meshes.add(Mesh::from(Sphere { radius: 1.0 }));
 | |
| 
 | |
|     // light probe spheres
 | |
|     commands.spawn((
 | |
|         Mesh3d(sphere_mesh.clone()),
 | |
|         MeshMaterial3d(materials.add(StandardMaterial {
 | |
|             base_color: Color::WHITE,
 | |
|             metallic: 1.0,
 | |
|             perceptual_roughness: 0.0,
 | |
|             ..default()
 | |
|         })),
 | |
|         Transform::from_xyz(-0.3, 0.1, -0.1).with_scale(Vec3::splat(0.05)),
 | |
|     ));
 | |
| 
 | |
|     commands.spawn((
 | |
|         Mesh3d(sphere_mesh.clone()),
 | |
|         MeshMaterial3d(materials.add(StandardMaterial {
 | |
|             base_color: Color::WHITE,
 | |
|             metallic: 0.0,
 | |
|             perceptual_roughness: 1.0,
 | |
|             ..default()
 | |
|         })),
 | |
|         Transform::from_xyz(-0.3, 0.1, 0.1).with_scale(Vec3::splat(0.05)),
 | |
|     ));
 | |
| 
 | |
|     // Terrain
 | |
|     commands.spawn((
 | |
|         Terrain,
 | |
|         SceneRoot(
 | |
|             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)),
 | |
|     ));
 | |
| }
 | |
| 
 | |
| fn dynamic_scene(mut suns: Query<&mut Transform, With<DirectionalLight>>, time: Res<Time>) {
 | |
|     suns.iter_mut()
 | |
|         .for_each(|mut tf| tf.rotate_x(-time.delta_secs() * PI / 10.0));
 | |
| }
 |