mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 06:30:19 +00:00
dd4299bcf9
(Before) ![image](https://user-images.githubusercontent.com/47158642/213946111-15ec758f-1f1d-443c-b196-1fdcd4ae49da.png) (After) ![image](https://user-images.githubusercontent.com/47158642/217051179-67381e73-dd44-461b-a2c7-87b0440ef8de.png) ![image](https://user-images.githubusercontent.com/47158642/212492404-524e4ad3-7837-4ed4-8b20-2abc276aa8e8.png) # Objective - Improve lighting; especially reflections. - Closes https://github.com/bevyengine/bevy/issues/4581. ## Solution - Implement environment maps, providing better ambient light. - Add microfacet multibounce approximation for specular highlights from Filament. - Occlusion is no longer incorrectly applied to direct lighting. It now only applies to diffuse indirect light. Unsure if it's also supposed to apply to specular indirect light - the glTF specification just says "indirect light". In the case of ambient occlusion, for instance, that's usually only calculated as diffuse though. For now, I'm choosing to apply this just to indirect diffuse light, and not specular. - Modified the PBR example to use an environment map, and have labels. - Added `FallbackImageCubemap`. ## Implementation - IBL technique references can be found in environment_map.wgsl. - It's more accurate to use a LUT for the scale/bias. Filament has a good reference on generating this LUT. For now, I just used an analytic approximation. - For now, environment maps must first be prefiltered outside of bevy using a 3rd party tool. See the `EnvironmentMap` documentation. - Eventually, we should have our own prefiltering code, so that we can have dynamically changing environment maps, as well as let users drop in an HDR image and use asset preprocessing to create the needed textures using only bevy. --- ## Changelog - Added an `EnvironmentMapLight` camera component that adds additional ambient light to a scene. - StandardMaterials will now appear brighter and more saturated at high roughness, due to internal material changes. This is more physically correct. - Fixed StandardMaterial occlusion being incorrectly applied to direct lighting. - Added `FallbackImageCubemap`. Co-authored-by: IceSentry <c.giguere42@gmail.com> Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: Rob Parrett <robparrett@gmail.com>
179 lines
5.3 KiB
Rust
179 lines
5.3 KiB
Rust
//! This example shows how to configure Physically Based Rendering (PBR) parameters.
|
|
|
|
use bevy::{asset::LoadState, prelude::*};
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_startup_system(setup)
|
|
.add_system(environment_map_load_finish)
|
|
.run();
|
|
}
|
|
|
|
/// set up a simple 3D scene
|
|
fn setup(
|
|
mut commands: Commands,
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
asset_server: Res<AssetServer>,
|
|
) {
|
|
// add entities to the world
|
|
for y in -2..=2 {
|
|
for x in -5..=5 {
|
|
let x01 = (x + 5) as f32 / 10.0;
|
|
let y01 = (y + 2) as f32 / 4.0;
|
|
// sphere
|
|
commands.spawn(PbrBundle {
|
|
mesh: meshes.add(
|
|
Mesh::try_from(shape::Icosphere {
|
|
radius: 0.45,
|
|
subdivisions: 32,
|
|
})
|
|
.unwrap(),
|
|
),
|
|
material: materials.add(StandardMaterial {
|
|
base_color: Color::hex("#ffd891").unwrap(),
|
|
// vary key PBR parameters on a grid of spheres to show the effect
|
|
metallic: y01,
|
|
perceptual_roughness: x01,
|
|
..default()
|
|
}),
|
|
transform: Transform::from_xyz(x as f32, y as f32 + 0.5, 0.0),
|
|
..default()
|
|
});
|
|
}
|
|
}
|
|
// unlit sphere
|
|
commands.spawn(PbrBundle {
|
|
mesh: meshes.add(
|
|
Mesh::try_from(shape::Icosphere {
|
|
radius: 0.45,
|
|
subdivisions: 32,
|
|
})
|
|
.unwrap(),
|
|
),
|
|
material: materials.add(StandardMaterial {
|
|
base_color: Color::hex("#ffd891").unwrap(),
|
|
// vary key PBR parameters on a grid of spheres to show the effect
|
|
unlit: true,
|
|
..default()
|
|
}),
|
|
transform: Transform::from_xyz(-5.0, -2.5, 0.0),
|
|
..default()
|
|
});
|
|
|
|
// light
|
|
commands.spawn(PointLightBundle {
|
|
transform: Transform::from_xyz(50.0, 50.0, 50.0),
|
|
point_light: PointLight {
|
|
intensity: 600000.,
|
|
range: 100.,
|
|
..default()
|
|
},
|
|
..default()
|
|
});
|
|
|
|
// labels
|
|
commands.spawn(
|
|
TextBundle::from_section(
|
|
"Perceptual Roughness",
|
|
TextStyle {
|
|
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
|
font_size: 36.0,
|
|
color: Color::WHITE,
|
|
},
|
|
)
|
|
.with_style(Style {
|
|
position_type: PositionType::Absolute,
|
|
position: UiRect {
|
|
top: Val::Px(20.0),
|
|
left: Val::Px(100.0),
|
|
..default()
|
|
},
|
|
..default()
|
|
}),
|
|
);
|
|
|
|
commands.spawn(TextBundle {
|
|
text: Text::from_section(
|
|
"Metallic",
|
|
TextStyle {
|
|
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
|
font_size: 36.0,
|
|
color: Color::WHITE,
|
|
},
|
|
),
|
|
style: Style {
|
|
position_type: PositionType::Absolute,
|
|
position: UiRect {
|
|
top: Val::Px(130.0),
|
|
right: Val::Px(0.0),
|
|
..default()
|
|
},
|
|
..default()
|
|
},
|
|
transform: Transform {
|
|
rotation: Quat::from_rotation_z(std::f32::consts::PI / 2.0),
|
|
..default()
|
|
},
|
|
..default()
|
|
});
|
|
|
|
commands.spawn((
|
|
TextBundle::from_section(
|
|
"Loading Environment Map...",
|
|
TextStyle {
|
|
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
|
font_size: 36.0,
|
|
color: Color::RED,
|
|
},
|
|
)
|
|
.with_style(Style {
|
|
position_type: PositionType::Absolute,
|
|
position: UiRect {
|
|
bottom: Val::Px(20.0),
|
|
right: Val::Px(20.0),
|
|
..default()
|
|
},
|
|
..default()
|
|
}),
|
|
EnvironmentMapLabel,
|
|
));
|
|
|
|
// camera
|
|
commands.spawn((
|
|
Camera3dBundle {
|
|
transform: Transform::from_xyz(0.0, 0.0, 8.0).looking_at(Vec3::default(), Vec3::Y),
|
|
projection: OrthographicProjection {
|
|
scale: 0.01,
|
|
..default()
|
|
}
|
|
.into(),
|
|
..default()
|
|
},
|
|
EnvironmentMapLight {
|
|
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
|
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
|
},
|
|
));
|
|
}
|
|
|
|
fn environment_map_load_finish(
|
|
mut commands: Commands,
|
|
asset_server: Res<AssetServer>,
|
|
environment_maps: Query<&EnvironmentMapLight>,
|
|
label_query: Query<Entity, With<EnvironmentMapLabel>>,
|
|
) {
|
|
if let Ok(environment_map) = environment_maps.get_single() {
|
|
if asset_server.get_load_state(&environment_map.diffuse_map) == LoadState::Loaded
|
|
&& asset_server.get_load_state(&environment_map.specular_map) == LoadState::Loaded
|
|
{
|
|
if let Ok(label_entity) = label_query.get_single() {
|
|
commands.entity(label_entity).despawn();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Component)]
|
|
struct EnvironmentMapLabel;
|