Compare commits
No commits in common. "03ebb738f3b6b4420f6084c18056df4f9550ae28" and "6bb73bbe2db297c82f852eb7b61014d6445c0934" have entirely different histories.
03ebb738f3
...
6bb73bbe2d
13 changed files with 80 additions and 843 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -457,15 +457,6 @@ dependencies = [
|
||||||
"glam 0.13.1",
|
"glam 0.13.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bevy_mod_raycast"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b699d78034aaf5730a4c10a09fd5a2e6886964a42fa7453b9e5f8b926cd846e"
|
|
||||||
dependencies = [
|
|
||||||
"bevy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_pbr"
|
name = "bevy_pbr"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -2133,7 +2124,6 @@ name = "moria"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy_mod_raycast",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -7,4 +7,3 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.5.0"
|
bevy = "0.5.0"
|
||||||
bevy_mod_raycast = "0.2.2"
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 8.7 KiB |
|
@ -1,58 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_mod_raycast::{RayCastMethod, RayCastSource};
|
|
||||||
|
|
||||||
use crate::player::*;
|
|
||||||
|
|
||||||
pub struct MyRaycastSet;
|
|
||||||
|
|
||||||
pub struct Camera;
|
|
||||||
pub fn spawn_camera(commands: &mut Commands) {
|
|
||||||
commands
|
|
||||||
.spawn_bundle(PerspectiveCameraBundle::default())
|
|
||||||
.insert(Camera)
|
|
||||||
.insert(RayCastSource::<MyRaycastSet>::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn camera_follow_player(
|
|
||||||
player: Query<(&Transform, &Player), Without<Camera>>,
|
|
||||||
mut camera: Query<(&mut Transform, &Camera), Without<Player>>,
|
|
||||||
) {
|
|
||||||
let player_pos = if let Some(player) = player.iter().next() {
|
|
||||||
player.0.translation
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (mut trans, _) in camera.iter_mut() {
|
|
||||||
trans.translation = player_pos + Vec3::new(-50.0, 100.0, 50.0);
|
|
||||||
trans.look_at(player_pos, Vec3::Y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update our `RayCastSource` with the current cursor position every frame.
|
|
||||||
pub fn update_raycast_with_cursor(
|
|
||||||
mut cursor: EventReader<CursorMoved>,
|
|
||||||
mut query: Query<&mut RayCastSource<MyRaycastSet>>,
|
|
||||||
) {
|
|
||||||
for mut pick_source in query.iter_mut() {
|
|
||||||
// Grab the most recent cursor event if it exists:
|
|
||||||
if let Some(cursor_latest) = cursor.iter().last() {
|
|
||||||
pick_source.cast_method = RayCastMethod::Screenspace(cursor_latest.position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MouseCoords(pub Vec3);
|
|
||||||
|
|
||||||
// Update our `RayCastSource` with the current cursor position every frame.
|
|
||||||
pub fn update_mouse_coords(
|
|
||||||
query: Query<&RayCastSource<MyRaycastSet>>,
|
|
||||||
mut coords: ResMut<MouseCoords>,
|
|
||||||
) {
|
|
||||||
for pick_source in query.iter() {
|
|
||||||
if let Some((_, intersection)) = pick_source.intersect_top() {
|
|
||||||
coords.0 = intersection.position();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
const DIS: f32 = 300.0;
|
const DIS: f32 = 300.0;
|
||||||
|
|
|
@ -2,8 +2,6 @@ use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::player::Player;
|
use crate::player::Player;
|
||||||
|
|
||||||
const RANGE: f32 = 25.0;
|
|
||||||
|
|
||||||
pub struct LightBall;
|
pub struct LightBall;
|
||||||
pub fn light_up_ball_when_close_to_player(
|
pub fn light_up_ball_when_close_to_player(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
@ -25,17 +23,17 @@ pub fn light_up_ball_when_close_to_player(
|
||||||
|
|
||||||
for (entity, trans, _, mut material, light) in thingies.iter_mut() {
|
for (entity, trans, _, mut material, light) in thingies.iter_mut() {
|
||||||
let dis = trans.translation.distance(player_pos);
|
let dis = trans.translation.distance(player_pos);
|
||||||
if dis < RANGE {
|
if dis < 20.0 {
|
||||||
*material = materials.lit.clone();
|
*material = materials.lit.clone();
|
||||||
|
|
||||||
// change intensity,
|
// change intensity,
|
||||||
// add light if there isn't one
|
// add light if there isn't one
|
||||||
if let Some(mut l) = light {
|
if let Some(mut l) = light {
|
||||||
l.intensity = 300.0 * (RANGE - dis) / RANGE;
|
l.intensity = 300.0 * (20.0 - dis) / 20.0;
|
||||||
} else {
|
} else {
|
||||||
commands.entity(entity).insert(Light {
|
commands.entity(entity).insert(Light {
|
||||||
color: Color::rgb(15.0, 15.0, 15.0),
|
color: Color::rgb(15.0, 15.0, 15.0),
|
||||||
intensity: 300.0 * (RANGE - dis) / RANGE,
|
intensity: 300.0 * (20.0 - dis) / 20.0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
use bevy::{
|
|
||||||
prelude::*,
|
|
||||||
render::texture::{AddressMode, FilterMode},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::AppState;
|
|
||||||
|
|
||||||
pub struct LoadedAssets {
|
|
||||||
pub floor: Handle<Texture>,
|
|
||||||
}
|
|
||||||
impl FromWorld for LoadedAssets {
|
|
||||||
fn from_world(world: &mut World) -> Self {
|
|
||||||
let server = world.get_resource::<AssetServer>().unwrap();
|
|
||||||
|
|
||||||
let floor = server.load("textures/floor.png");
|
|
||||||
Self { floor }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn loading(
|
|
||||||
assets: Res<LoadedAssets>,
|
|
||||||
mut textures: ResMut<Assets<Texture>>,
|
|
||||||
mut state: ResMut<State<AppState>>,
|
|
||||||
) {
|
|
||||||
if let Some(t) = textures.get_mut(&assets.floor) {
|
|
||||||
t.sampler.address_mode_u = AddressMode::MirrorRepeat;
|
|
||||||
t.sampler.address_mode_v = AddressMode::Repeat;
|
|
||||||
t.sampler.address_mode_w = AddressMode::Repeat;
|
|
||||||
t.sampler.mipmap_filter = FilterMode::Linear;
|
|
||||||
|
|
||||||
state.set(AppState::Game).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
110
src/main.rs
110
src/main.rs
|
@ -1,110 +1,53 @@
|
||||||
use bevy::{input::system::exit_on_esc_system, pbr::AmbientLight, prelude::*};
|
use bevy::{input::system::exit_on_esc_system, pbr::AmbientLight, prelude::*};
|
||||||
use bevy_mod_raycast::{build_rays, update_raycast, PluginState, RayCastMesh, RaycastSystem};
|
|
||||||
|
|
||||||
mod camera;
|
|
||||||
mod columns;
|
|
||||||
mod light_balls;
|
mod light_balls;
|
||||||
mod loading;
|
|
||||||
mod player;
|
|
||||||
mod rendering;
|
|
||||||
|
|
||||||
use camera::*;
|
|
||||||
use light_balls::*;
|
use light_balls::*;
|
||||||
|
mod player;
|
||||||
use player::*;
|
use player::*;
|
||||||
|
mod columns;
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
use columns::*;
|
||||||
pub enum AppState {
|
|
||||||
Loading,
|
|
||||||
Game,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::build()
|
App::build()
|
||||||
.insert_resource(Msaa { samples: 4 })
|
.insert_resource(Msaa { samples: 4 })
|
||||||
.insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
|
.add_plugins(DefaultPlugins)
|
||||||
.add_plugins(rendering::CustomPlugins)
|
|
||||||
.init_resource::<LightBallMaterials>()
|
.init_resource::<LightBallMaterials>()
|
||||||
.init_resource::<MouseCoords>()
|
.add_startup_system(setup.system())
|
||||||
.init_resource::<FloatingOrbsInterpolationState>()
|
.add_system(exit_on_esc_system.system())
|
||||||
.init_resource::<loading::LoadedAssets>()
|
.add_system(light_movement.system())
|
||||||
.add_state(AppState::Loading)
|
.add_system(move_player.system())
|
||||||
// raycasting
|
.add_system(camera_follow_player.system())
|
||||||
.init_resource::<PluginState<MyRaycastSet>>()
|
.add_system(light_up_ball_when_close_to_player.system())
|
||||||
.add_system_to_stage(
|
|
||||||
CoreStage::PostUpdate,
|
|
||||||
build_rays::<MyRaycastSet>
|
|
||||||
.system()
|
|
||||||
.label(RaycastSystem::BuildRays),
|
|
||||||
)
|
|
||||||
.add_system_to_stage(
|
|
||||||
CoreStage::PostUpdate,
|
|
||||||
update_raycast::<MyRaycastSet>
|
|
||||||
.system()
|
|
||||||
.label(RaycastSystem::UpdateRaycast)
|
|
||||||
.after(RaycastSystem::BuildRays),
|
|
||||||
)
|
|
||||||
.add_system_to_stage(
|
|
||||||
CoreStage::PreUpdate,
|
|
||||||
update_raycast_with_cursor
|
|
||||||
.system()
|
|
||||||
.before(RaycastSystem::BuildRays),
|
|
||||||
)
|
|
||||||
// loading
|
|
||||||
.add_system_set(
|
|
||||||
SystemSet::on_update(AppState::Loading).with_system(loading::loading.system()),
|
|
||||||
)
|
|
||||||
// game
|
|
||||||
.add_system_set(SystemSet::on_enter(AppState::Game).with_system(setup.system()))
|
|
||||||
.add_system_set(
|
|
||||||
SystemSet::on_update(AppState::Game)
|
|
||||||
.with_system(update_mouse_coords.system())
|
|
||||||
.with_system(exit_on_esc_system.system())
|
|
||||||
.with_system(light_movement.system())
|
|
||||||
.with_system(move_light_friends.system())
|
|
||||||
.with_system(update_floating_orbs_interpolation.system())
|
|
||||||
.with_system(move_player.system())
|
|
||||||
.with_system(camera_follow_player.system())
|
|
||||||
.with_system(light_up_ball_when_close_to_player.system()),
|
|
||||||
)
|
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Camera;
|
||||||
|
|
||||||
|
/// set up a simple 3D scene
|
||||||
fn setup(
|
fn setup(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
assets: Res<loading::LoadedAssets>,
|
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
mut ambient: ResMut<AmbientLight>,
|
mut ambient: ResMut<AmbientLight>,
|
||||||
light_ball_materials: Res<LightBallMaterials>,
|
light_ball_materials: Res<LightBallMaterials>,
|
||||||
) {
|
) {
|
||||||
// set ambient light to very low
|
// set ambient light to 0
|
||||||
ambient.color = Color::rgb(0.3, 0.3, 0.3);
|
ambient.color = Color::rgb(0.3, 0.3, 0.3);
|
||||||
ambient.brightness = 0.01;
|
ambient.brightness = 0.02;
|
||||||
|
|
||||||
// floor
|
// floor
|
||||||
let mut floor_material = StandardMaterial {
|
let mut floor_material: StandardMaterial = Color::rgb(0.1, 0.1, 0.1).into();
|
||||||
base_color_texture: Some(assets.floor.clone()),
|
|
||||||
base_color: Color::rgb(0.3, 0.3, 0.3),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
floor_material.metallic = 0.0;
|
floor_material.metallic = 0.0;
|
||||||
floor_material.reflectance = 0.0;
|
floor_material.reflectance = 0.0;
|
||||||
let floor_material = materials.add(floor_material);
|
let floor_material = materials.add(floor_material);
|
||||||
for i in -10..10 {
|
commands.spawn_bundle(PbrBundle {
|
||||||
for j in -10..10 {
|
mesh: meshes.add(Mesh::from(shape::Plane { size: 10000.0 })),
|
||||||
commands
|
material: floor_material,
|
||||||
.spawn_bundle(PbrBundle {
|
|
||||||
mesh: meshes.add(Mesh::from(shape::Plane { size: 100.0 })),
|
|
||||||
material: floor_material.clone(),
|
|
||||||
transform: Transform::from_xyz(100.0 * i as f32, 0.0, 100.0 * j as f32),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
});
|
||||||
.insert(RayCastMesh::<camera::MyRaycastSet>::default());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// columns
|
// columns
|
||||||
// columns::spawn_columns(&mut commands, &mut meshes, &mut materials);
|
spawn_columns(&mut commands, &mut meshes, &mut materials);
|
||||||
|
|
||||||
// player
|
// player
|
||||||
spawn_player(&mut commands, &mut meshes, &mut materials);
|
spawn_player(&mut commands, &mut meshes, &mut materials);
|
||||||
|
@ -114,15 +57,20 @@ fn setup(
|
||||||
spawn_light_ball(
|
spawn_light_ball(
|
||||||
&mut commands,
|
&mut commands,
|
||||||
&light_ball_materials,
|
&light_ball_materials,
|
||||||
Vec3::new(i as f32 * 30.0, 2.0, 10.0),
|
Vec3::new(i as f32 * 30.0, 2.0, 6.0),
|
||||||
);
|
);
|
||||||
spawn_light_ball(
|
spawn_light_ball(
|
||||||
&mut commands,
|
&mut commands,
|
||||||
&light_ball_materials,
|
&light_ball_materials,
|
||||||
Vec3::new(i as f32 * 30.0, 2.0, -10.0),
|
Vec3::new(i as f32 * 30.0, 2.0, -6.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// camera
|
// camera
|
||||||
spawn_camera(&mut commands);
|
commands
|
||||||
|
.spawn_bundle(PerspectiveCameraBundle {
|
||||||
|
transform: Transform::from_xyz(-20.0, 100.0, 50.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(Camera);
|
||||||
}
|
}
|
||||||
|
|
103
src/player.rs
103
src/player.rs
|
@ -1,6 +1,6 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::camera::MouseCoords;
|
use crate::Camera;
|
||||||
|
|
||||||
pub struct Player;
|
pub struct Player;
|
||||||
pub struct PlayerLight {
|
pub struct PlayerLight {
|
||||||
|
@ -19,26 +19,20 @@ pub fn spawn_player(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})),
|
})),
|
||||||
material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
|
material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
|
||||||
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
transform: Transform::from_xyz(33.0, 1.0, 0.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.insert(Player);
|
.insert(Player)
|
||||||
|
.with_children(|parent| {
|
||||||
// light
|
// light
|
||||||
let mut light_material: StandardMaterial = Color::rgb(0.737255, 0.560784, 0.560784).into();
|
let mut light_material: StandardMaterial = Color::rgb(0.3, 0.5, 0.3).into();
|
||||||
light_material.metallic = 0.5;
|
light_material.metallic = 0.5;
|
||||||
light_material.reflectance = 0.5;
|
light_material.reflectance = 0.5;
|
||||||
light_material.emissive = Color::rgb(7.52, 5.72, 5.72);
|
light_material.emissive = Color::rgb(15.0, 15.0, 15.0);
|
||||||
let light_material = materials.add(light_material);
|
let light_material = materials.add(light_material);
|
||||||
|
|
||||||
commands
|
for i in 0..5 {
|
||||||
.spawn()
|
parent
|
||||||
.insert(LightFriends)
|
|
||||||
.insert(Transform::default())
|
|
||||||
.insert(GlobalTransform::default())
|
|
||||||
.with_children(|children| {
|
|
||||||
for i in 0..6 {
|
|
||||||
children
|
|
||||||
.spawn_bundle(PbrBundle {
|
.spawn_bundle(PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(shape::Icosphere {
|
mesh: meshes.add(Mesh::from(shape::Icosphere {
|
||||||
radius: 0.2,
|
radius: 0.2,
|
||||||
|
@ -51,7 +45,6 @@ pub fn spawn_player(
|
||||||
.insert_bundle(LightBundle {
|
.insert_bundle(LightBundle {
|
||||||
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
light: Light {
|
light: Light {
|
||||||
color: Color::rgb(7.52, 5.72, 5.72),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -61,56 +54,14 @@ pub fn spawn_player(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LightFriends;
|
|
||||||
pub fn move_light_friends(
|
|
||||||
mut query: Query<(&mut Transform, &LightFriends)>,
|
|
||||||
player: Query<(&Transform, &Player), Without<LightFriends>>,
|
|
||||||
coords: Res<MouseCoords>,
|
|
||||||
interpolation: Res<FloatingOrbsInterpolationState>,
|
|
||||||
time: Res<Time>,
|
|
||||||
) {
|
|
||||||
let player_pos = if let Some(player) = player.iter().next() {
|
|
||||||
player.0.translation
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (mut trans, _light) in query.iter_mut() {
|
|
||||||
// interpolate between player pos and mouse click pos
|
|
||||||
let i = interpolation.0;
|
|
||||||
let center = i * coords.0 + (1.0 - i) * player_pos;
|
|
||||||
|
|
||||||
let mut d = trans.translation - center;
|
|
||||||
d.y = 0.0;
|
|
||||||
trans.translation -= d * time.delta_seconds();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn light_movement(mut query: Query<(&mut Transform, &PlayerLight)>, time: Res<Time>) {
|
pub fn light_movement(mut query: Query<(&mut Transform, &PlayerLight)>, time: Res<Time>) {
|
||||||
let t = time.seconds_since_startup();
|
let t = time.seconds_since_startup();
|
||||||
for (mut trans, light) in query.iter_mut() {
|
for (mut trans, light) in query.iter_mut() {
|
||||||
let i = light.i as f64;
|
let i = light.i as f64;
|
||||||
let i2 = i / 2.0;
|
let i2 = i / 2.0;
|
||||||
trans.translation = Vec3::new(
|
trans.translation.y = 6.0 * (t * 1.1 * i2 + i).sin() as f32 + 7.0;
|
||||||
5.0 * i2 as f32 * (t * 0.4 * i2 + i).cos() as f32,
|
trans.translation.x = 5.0 * i2 as f32 * (t * 0.4 * i2 + i).cos() as f32;
|
||||||
3.0 * (t * 1.1 * i2 + i).sin() as f32 + 4.0,
|
trans.translation.z = 5.0 * (t * 0.4 * i + i).sin() as f32;
|
||||||
5.0 * (t * 0.4 * i + i).sin() as f32,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct FloatingOrbsInterpolationState(f32);
|
|
||||||
pub fn update_floating_orbs_interpolation(
|
|
||||||
mut state: ResMut<FloatingOrbsInterpolationState>,
|
|
||||||
input: Res<Input<MouseButton>>,
|
|
||||||
time: Res<Time>,
|
|
||||||
) {
|
|
||||||
let ds = time.delta_seconds();
|
|
||||||
if input.pressed(MouseButton::Left) {
|
|
||||||
state.0 = (state.0 + 0.5 * ds).clamp(0.0, 1.0);
|
|
||||||
} else {
|
|
||||||
state.0 = (state.0 - ds).clamp(0.0, 1.0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +72,9 @@ pub fn move_player(
|
||||||
) {
|
) {
|
||||||
let ds = time.delta_seconds() * 5.0;
|
let ds = time.delta_seconds() * 5.0;
|
||||||
|
|
||||||
|
// TODO rotate according to camera position
|
||||||
|
// but make it snap to coord system
|
||||||
|
|
||||||
for (mut transform, _) in query.iter_mut() {
|
for (mut transform, _) in query.iter_mut() {
|
||||||
if input.pressed(KeyCode::W) {
|
if input.pressed(KeyCode::W) {
|
||||||
transform.translation += Vec3::X * ds;
|
transform.translation += Vec3::X * ds;
|
||||||
|
@ -136,3 +90,32 @@ pub fn move_player(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn camera_follow_player(
|
||||||
|
player: Query<(&Transform, &Player), Without<Camera>>,
|
||||||
|
mut camera: Query<(&mut Transform, &Camera), Without<Player>>,
|
||||||
|
time: Res<Time>,
|
||||||
|
) {
|
||||||
|
let player_pos = if let Some(player) = player.iter().next() {
|
||||||
|
player.0.translation
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let ds = time.delta_seconds() * 5.0;
|
||||||
|
for (mut trans, _) in camera.iter_mut() {
|
||||||
|
trans.look_at(player_pos, Vec3::Y);
|
||||||
|
|
||||||
|
// keep a distance to the player
|
||||||
|
if trans.translation.distance(player_pos) > 170.0 {
|
||||||
|
let mut d = trans.rotation * Vec3::Z * ds;
|
||||||
|
d.y = 0.0;
|
||||||
|
trans.translation -= d;
|
||||||
|
}
|
||||||
|
if trans.translation.distance(player_pos) < 100.0 {
|
||||||
|
let mut d = trans.rotation * Vec3::Z * ds;
|
||||||
|
d.y = 0.0;
|
||||||
|
trans.translation += d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
use bevy::app::{PluginGroup, PluginGroupBuilder};
|
|
||||||
use bevy::pbr::render_graph::{LightsNode, PBR_PIPELINE_HANDLE};
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy::render::{
|
|
||||||
pipeline::PipelineDescriptor,
|
|
||||||
render_graph::{base, AssetRenderResourcesNode, RenderGraph, RenderResourcesNode},
|
|
||||||
shader::Shader,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod pipeline;
|
|
||||||
use pipeline::build_pbr_pipeline;
|
|
||||||
|
|
||||||
pub struct CustomPlugins;
|
|
||||||
impl PluginGroup for CustomPlugins {
|
|
||||||
fn build(&mut self, group: &mut PluginGroupBuilder) {
|
|
||||||
group.add(bevy::log::LogPlugin::default());
|
|
||||||
group.add(bevy::core::CorePlugin::default());
|
|
||||||
group.add(bevy::transform::TransformPlugin::default());
|
|
||||||
group.add(bevy::diagnostic::DiagnosticsPlugin::default());
|
|
||||||
group.add(bevy::input::InputPlugin::default());
|
|
||||||
group.add(bevy::window::WindowPlugin::default());
|
|
||||||
group.add(bevy::asset::AssetPlugin::default());
|
|
||||||
group.add(bevy::scene::ScenePlugin::default());
|
|
||||||
|
|
||||||
group.add(bevy::render::RenderPlugin::default());
|
|
||||||
group.add(bevy::sprite::SpritePlugin::default());
|
|
||||||
group.add(CustomPbrPlugin::default());
|
|
||||||
group.add(bevy::ui::UiPlugin::default());
|
|
||||||
group.add(bevy::text::TextPlugin::default());
|
|
||||||
group.add(bevy::gilrs::GilrsPlugin::default());
|
|
||||||
group.add(bevy::gltf::GltfPlugin::default());
|
|
||||||
group.add(bevy::winit::WinitPlugin::default());
|
|
||||||
group.add(bevy::wgpu::WgpuPlugin::default());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct CustomPbrPlugin;
|
|
||||||
impl Plugin for CustomPbrPlugin {
|
|
||||||
fn build(&self, app: &mut AppBuilder) {
|
|
||||||
app.add_asset::<StandardMaterial>()
|
|
||||||
.register_type::<Light>()
|
|
||||||
.add_system_to_stage(
|
|
||||||
CoreStage::PostUpdate,
|
|
||||||
bevy::render::shader::asset_shader_defs_system::<StandardMaterial>.system(),
|
|
||||||
)
|
|
||||||
.init_resource::<bevy::pbr::AmbientLight>();
|
|
||||||
add_pbr_graph(app.world_mut());
|
|
||||||
|
|
||||||
// add default StandardMaterial
|
|
||||||
let mut materials = app
|
|
||||||
.world_mut()
|
|
||||||
.get_resource_mut::<Assets<StandardMaterial>>()
|
|
||||||
.unwrap();
|
|
||||||
materials.set_untracked(
|
|
||||||
Handle::<StandardMaterial>::default(),
|
|
||||||
StandardMaterial {
|
|
||||||
base_color: Color::PINK,
|
|
||||||
unlit: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the names of pbr graph nodes
|
|
||||||
mod node {
|
|
||||||
pub const TRANSFORM: &str = "transform";
|
|
||||||
pub const STANDARD_MATERIAL: &str = "standard_material";
|
|
||||||
pub const LIGHTS: &str = "lights";
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_pbr_graph(world: &mut World) {
|
|
||||||
{
|
|
||||||
let mut graph = world.get_resource_mut::<RenderGraph>().unwrap();
|
|
||||||
graph.add_system_node(
|
|
||||||
node::TRANSFORM,
|
|
||||||
RenderResourcesNode::<GlobalTransform>::new(true),
|
|
||||||
);
|
|
||||||
graph.add_system_node(
|
|
||||||
node::STANDARD_MATERIAL,
|
|
||||||
AssetRenderResourcesNode::<StandardMaterial>::new(true),
|
|
||||||
);
|
|
||||||
graph.add_system_node(node::LIGHTS, LightsNode::new(30));
|
|
||||||
|
|
||||||
// TODO: replace these with "autowire" groups
|
|
||||||
graph
|
|
||||||
.add_node_edge(node::STANDARD_MATERIAL, base::node::MAIN_PASS)
|
|
||||||
.unwrap();
|
|
||||||
graph
|
|
||||||
.add_node_edge(node::TRANSFORM, base::node::MAIN_PASS)
|
|
||||||
.unwrap();
|
|
||||||
graph
|
|
||||||
.add_node_edge(node::LIGHTS, base::node::MAIN_PASS)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
let pipeline = build_pbr_pipeline(&mut world.get_resource_mut::<Assets<Shader>>().unwrap());
|
|
||||||
let mut pipelines = world
|
|
||||||
.get_resource_mut::<Assets<PipelineDescriptor>>()
|
|
||||||
.unwrap();
|
|
||||||
pipelines.set_untracked(PBR_PIPELINE_HANDLE, pipeline);
|
|
||||||
}
|
|
|
@ -1,392 +0,0 @@
|
||||||
// From the Filament design doc
|
|
||||||
// https://google.github.io/filament/Filament.html#table_symbols
|
|
||||||
// Symbol Definition
|
|
||||||
// v View unit vector
|
|
||||||
// l Incident light unit vector
|
|
||||||
// n Surface normal unit vector
|
|
||||||
// h Half unit vector between l and v
|
|
||||||
// f BRDF
|
|
||||||
// f_d Diffuse component of a BRDF
|
|
||||||
// f_r Specular component of a BRDF
|
|
||||||
// α Roughness, remapped from using input perceptualRoughness
|
|
||||||
// σ Diffuse reflectance
|
|
||||||
// Ω Spherical domain
|
|
||||||
// f0 Reflectance at normal incidence
|
|
||||||
// f90 Reflectance at grazing angle
|
|
||||||
// χ+(a) Heaviside function (1 if a>0 and 0 otherwise)
|
|
||||||
// nior Index of refraction (IOR) of an interface
|
|
||||||
// ⟨n⋅l⟩ Dot product clamped to [0..1]
|
|
||||||
// ⟨a⟩ Saturated value (clamped to [0..1])
|
|
||||||
|
|
||||||
// The Bidirectional Reflectance Distribution Function (BRDF) describes the surface response of a standard material
|
|
||||||
// and consists of two components, the diffuse component (f_d) and the specular component (f_r):
|
|
||||||
// f(v,l) = f_d(v,l) + f_r(v,l)
|
|
||||||
//
|
|
||||||
// The form of the microfacet model is the same for diffuse and specular
|
|
||||||
// f_r(v,l) = f_d(v,l) = 1 / { |n⋅v||n⋅l| } ∫_Ω D(m,α) G(v,l,m) f_m(v,l,m) (v⋅m) (l⋅m) dm
|
|
||||||
//
|
|
||||||
// In which:
|
|
||||||
// D, also called the Normal Distribution Function (NDF) models the distribution of the microfacets
|
|
||||||
// G models the visibility (or occlusion or shadow-masking) of the microfacets
|
|
||||||
// f_m is the microfacet BRDF and differs between specular and diffuse components
|
|
||||||
//
|
|
||||||
// The above integration needs to be approximated.
|
|
||||||
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
const int MAX_LIGHTS = 30;
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
mat4 proj;
|
|
||||||
vec4 pos;
|
|
||||||
vec4 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 v_WorldPosition;
|
|
||||||
layout(location = 1) in vec3 v_WorldNormal;
|
|
||||||
layout(location = 2) in vec2 v_Uv;
|
|
||||||
|
|
||||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
|
||||||
layout(location = 3) in vec4 v_WorldTangent;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 o_Target;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform CameraViewProj {
|
|
||||||
mat4 ViewProj;
|
|
||||||
};
|
|
||||||
layout(std140, set = 0, binding = 1) uniform CameraPosition {
|
|
||||||
vec4 CameraPos;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std140, set = 1, binding = 0) uniform Lights {
|
|
||||||
vec4 AmbientColor;
|
|
||||||
uvec4 NumLights;
|
|
||||||
Light SceneLights[MAX_LIGHTS];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(set = 3, binding = 0) uniform StandardMaterial_base_color {
|
|
||||||
vec4 base_color;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef STANDARDMATERIAL_BASE_COLOR_TEXTURE
|
|
||||||
layout(set = 3, binding = 1) uniform texture2D StandardMaterial_base_color_texture;
|
|
||||||
layout(set = 3,
|
|
||||||
binding = 2) uniform sampler StandardMaterial_base_color_texture_sampler;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STANDARDMATERIAL_UNLIT
|
|
||||||
|
|
||||||
layout(set = 3, binding = 3) uniform StandardMaterial_roughness {
|
|
||||||
float perceptual_roughness;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(set = 3, binding = 4) uniform StandardMaterial_metallic {
|
|
||||||
float metallic;
|
|
||||||
};
|
|
||||||
|
|
||||||
# ifdef STANDARDMATERIAL_METALLIC_ROUGHNESS_TEXTURE
|
|
||||||
layout(set = 3, binding = 5) uniform texture2D StandardMaterial_metallic_roughness_texture;
|
|
||||||
layout(set = 3,
|
|
||||||
binding = 6) uniform sampler StandardMaterial_metallic_roughness_texture_sampler;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
layout(set = 3, binding = 7) uniform StandardMaterial_reflectance {
|
|
||||||
float reflectance;
|
|
||||||
};
|
|
||||||
|
|
||||||
# ifdef STANDARDMATERIAL_NORMAL_MAP
|
|
||||||
layout(set = 3, binding = 8) uniform texture2D StandardMaterial_normal_map;
|
|
||||||
layout(set = 3,
|
|
||||||
binding = 9) uniform sampler StandardMaterial_normal_map_sampler;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# if defined(STANDARDMATERIAL_OCCLUSION_TEXTURE)
|
|
||||||
layout(set = 3, binding = 10) uniform texture2D StandardMaterial_occlusion_texture;
|
|
||||||
layout(set = 3,
|
|
||||||
binding = 11) uniform sampler StandardMaterial_occlusion_texture_sampler;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
layout(set = 3, binding = 12) uniform StandardMaterial_emissive {
|
|
||||||
vec4 emissive;
|
|
||||||
};
|
|
||||||
|
|
||||||
# if defined(STANDARDMATERIAL_EMISSIVE_TEXTURE)
|
|
||||||
layout(set = 3, binding = 13) uniform texture2D StandardMaterial_emissive_texture;
|
|
||||||
layout(set = 3,
|
|
||||||
binding = 14) uniform sampler StandardMaterial_emissive_texture_sampler;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# define saturate(x) clamp(x, 0.0, 1.0)
|
|
||||||
const float PI = 3.141592653589793;
|
|
||||||
|
|
||||||
float pow5(float x) {
|
|
||||||
float x2 = x * x;
|
|
||||||
return x2 * x2 * x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// distanceAttenuation is simply the square falloff of light intensity
|
|
||||||
// combined with a smooth attenuation at the edge of the light radius
|
|
||||||
//
|
|
||||||
// light radius is a non-physical construct for efficiency purposes,
|
|
||||||
// because otherwise every light affects every fragment in the scene
|
|
||||||
float getDistanceAttenuation(const vec3 posToLight, float inverseRadiusSquared) {
|
|
||||||
float distanceSquare = dot(posToLight, posToLight);
|
|
||||||
float factor = distanceSquare * inverseRadiusSquared;
|
|
||||||
float smoothFactor = saturate(1.0 - factor * factor);
|
|
||||||
float attenuation = smoothFactor * smoothFactor;
|
|
||||||
return attenuation * 1.0 / max(distanceSquare, 1e-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normal distribution function (specular D)
|
|
||||||
// Based on https://google.github.io/filament/Filament.html#citation-walter07
|
|
||||||
|
|
||||||
// D_GGX(h,α) = α^2 / { π ((n⋅h)^2 (α2−1) + 1)^2 }
|
|
||||||
|
|
||||||
// Simple implementation, has precision problems when using fp16 instead of fp32
|
|
||||||
// see https://google.github.io/filament/Filament.html#listing_speculardfp16
|
|
||||||
float D_GGX(float roughness, float NoH, const vec3 h) {
|
|
||||||
float oneMinusNoHSquared = 1.0 - NoH * NoH;
|
|
||||||
float a = NoH * roughness;
|
|
||||||
float k = roughness / (oneMinusNoHSquared + a * a);
|
|
||||||
float d = k * k * (1.0 / PI);
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visibility function (Specular G)
|
|
||||||
// V(v,l,a) = G(v,l,α) / { 4 (n⋅v) (n⋅l) }
|
|
||||||
// such that f_r becomes
|
|
||||||
// f_r(v,l) = D(h,α) V(v,l,α) F(v,h,f0)
|
|
||||||
// where
|
|
||||||
// V(v,l,α) = 0.5 / { n⋅l sqrt((n⋅v)^2 (1−α2) + α2) + n⋅v sqrt((n⋅l)^2 (1−α2) + α2) }
|
|
||||||
// Note the two sqrt's, that may be slow on mobile, see https://google.github.io/filament/Filament.html#listing_approximatedspecularv
|
|
||||||
float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) {
|
|
||||||
float a2 = roughness * roughness;
|
|
||||||
float lambdaV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);
|
|
||||||
float lambdaL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);
|
|
||||||
float v = 0.5 / (lambdaV + lambdaL);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fresnel function
|
|
||||||
// see https://google.github.io/filament/Filament.html#citation-schlick94
|
|
||||||
// F_Schlick(v,h,f_0,f_90) = f_0 + (f_90 − f_0) (1 − v⋅h)^5
|
|
||||||
vec3 F_Schlick(const vec3 f0, float f90, float VoH) {
|
|
||||||
// not using mix to keep the vec3 and float versions identical
|
|
||||||
return f0 + (f90 - f0) * pow5(1.0 - VoH);
|
|
||||||
}
|
|
||||||
|
|
||||||
float F_Schlick(float f0, float f90, float VoH) {
|
|
||||||
// not using mix to keep the vec3 and float versions identical
|
|
||||||
return f0 + (f90 - f0) * pow5(1.0 - VoH);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 fresnel(vec3 f0, float LoH) {
|
|
||||||
// f_90 suitable for ambient occlusion
|
|
||||||
// see https://google.github.io/filament/Filament.html#lighting/occlusion
|
|
||||||
float f90 = saturate(dot(f0, vec3(50.0 * 0.33)));
|
|
||||||
return F_Schlick(f0, f90, LoH);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specular BRDF
|
|
||||||
// https://google.github.io/filament/Filament.html#materialsystem/specularbrdf
|
|
||||||
|
|
||||||
// Cook-Torrance approximation of the microfacet model integration using Fresnel law F to model f_m
|
|
||||||
// f_r(v,l) = { D(h,α) G(v,l,α) F(v,h,f0) } / { 4 (n⋅v) (n⋅l) }
|
|
||||||
vec3 specular(vec3 f0, float roughness, const vec3 h, float NoV, float NoL,
|
|
||||||
float NoH, float LoH) {
|
|
||||||
float D = D_GGX(roughness, NoH, h);
|
|
||||||
float V = V_SmithGGXCorrelated(roughness, NoV, NoL);
|
|
||||||
vec3 F = fresnel(f0, LoH);
|
|
||||||
|
|
||||||
return (D * V) * F;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Diffuse BRDF
|
|
||||||
// https://google.github.io/filament/Filament.html#materialsystem/diffusebrdf
|
|
||||||
// fd(v,l) = σ/π * 1 / { |n⋅v||n⋅l| } ∫Ω D(m,α) G(v,l,m) (v⋅m) (l⋅m) dm
|
|
||||||
|
|
||||||
// simplest approximation
|
|
||||||
// float Fd_Lambert() {
|
|
||||||
// return 1.0 / PI;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// vec3 Fd = diffuseColor * Fd_Lambert();
|
|
||||||
|
|
||||||
// Disney approximation
|
|
||||||
// See https://google.github.io/filament/Filament.html#citation-burley12
|
|
||||||
// minimal quality difference
|
|
||||||
float Fd_Burley(float roughness, float NoV, float NoL, float LoH) {
|
|
||||||
float f90 = 0.5 + 2.0 * roughness * LoH * LoH;
|
|
||||||
float lightScatter = F_Schlick(1.0, f90, NoL);
|
|
||||||
float viewScatter = F_Schlick(1.0, f90, NoV);
|
|
||||||
return lightScatter * viewScatter * (1.0 / PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
// From https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
|
|
||||||
vec3 EnvBRDFApprox(vec3 f0, float perceptual_roughness, float NoV) {
|
|
||||||
const vec4 c0 = { -1, -0.0275, -0.572, 0.022 };
|
|
||||||
const vec4 c1 = { 1, 0.0425, 1.04, -0.04 };
|
|
||||||
vec4 r = perceptual_roughness * c0 + c1;
|
|
||||||
float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
|
|
||||||
vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
|
|
||||||
return f0 * AB.x + AB.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
float perceptualRoughnessToRoughness(float perceptualRoughness) {
|
|
||||||
// clamp perceptual roughness to prevent precision problems
|
|
||||||
// According to Filament design 0.089 is recommended for mobile
|
|
||||||
// Filament uses 0.045 for non-mobile
|
|
||||||
float clampedPerceptualRoughness = clamp(perceptualRoughness, 0.089, 1.0);
|
|
||||||
return clampedPerceptualRoughness * clampedPerceptualRoughness;
|
|
||||||
}
|
|
||||||
|
|
||||||
// from https://64.github.io/tonemapping/
|
|
||||||
// reinhard on RGB oversaturates colors
|
|
||||||
vec3 reinhard(vec3 color) {
|
|
||||||
return color / (1.0 + color);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 reinhard_extended(vec3 color, float max_white) {
|
|
||||||
vec3 numerator = color * (1.0f + (color / vec3(max_white * max_white)));
|
|
||||||
return numerator / (1.0 + color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// luminance coefficients from Rec. 709.
|
|
||||||
// https://en.wikipedia.org/wiki/Rec._709
|
|
||||||
float luminance(vec3 v) {
|
|
||||||
return dot(v, vec3(0.2126, 0.7152, 0.0722));
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 change_luminance(vec3 c_in, float l_out) {
|
|
||||||
float l_in = luminance(c_in);
|
|
||||||
return c_in * (l_out / l_in);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 reinhard_luminance(vec3 color) {
|
|
||||||
float l_old = luminance(color);
|
|
||||||
float l_new = l_old / (1.0f + l_old);
|
|
||||||
return change_luminance(color, l_new);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 reinhard_extended_luminance(vec3 color, float max_white_l) {
|
|
||||||
float l_old = luminance(color);
|
|
||||||
float numerator = l_old * (1.0f + (l_old / (max_white_l * max_white_l)));
|
|
||||||
float l_new = numerator / (1.0f + l_old);
|
|
||||||
return change_luminance(color, l_new);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 output_color = base_color;
|
|
||||||
#ifdef STANDARDMATERIAL_BASE_COLOR_TEXTURE
|
|
||||||
output_color *= texture(sampler2D(StandardMaterial_base_color_texture,
|
|
||||||
StandardMaterial_base_color_texture_sampler),
|
|
||||||
v_Uv);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STANDARDMATERIAL_UNLIT
|
|
||||||
// calculate non-linear roughness from linear perceptualRoughness
|
|
||||||
# ifdef STANDARDMATERIAL_METALLIC_ROUGHNESS_TEXTURE
|
|
||||||
vec4 metallic_roughness = texture(sampler2D(StandardMaterial_metallic_roughness_texture, StandardMaterial_metallic_roughness_texture_sampler), v_Uv);
|
|
||||||
// Sampling from GLTF standard channels for now
|
|
||||||
float metallic = metallic * metallic_roughness.b;
|
|
||||||
float perceptual_roughness = perceptual_roughness * metallic_roughness.g;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
float roughness = perceptualRoughnessToRoughness(perceptual_roughness);
|
|
||||||
|
|
||||||
vec3 N = normalize(v_WorldNormal);
|
|
||||||
|
|
||||||
# ifdef STANDARDMATERIAL_NORMAL_MAP
|
|
||||||
vec3 T = normalize(v_WorldTangent.xyz);
|
|
||||||
vec3 B = cross(N, T) * v_WorldTangent.w;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# ifdef STANDARDMATERIAL_DOUBLE_SIDED
|
|
||||||
N = gl_FrontFacing ? N : -N;
|
|
||||||
# ifdef STANDARDMATERIAL_NORMAL_MAP
|
|
||||||
T = gl_FrontFacing ? T : -T;
|
|
||||||
B = gl_FrontFacing ? B : -B;
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# ifdef STANDARDMATERIAL_NORMAL_MAP
|
|
||||||
mat3 TBN = mat3(T, B, N);
|
|
||||||
N = TBN * normalize(texture(sampler2D(StandardMaterial_normal_map, StandardMaterial_normal_map_sampler), v_Uv).rgb * 2.0 - 1.0);
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# ifdef STANDARDMATERIAL_OCCLUSION_TEXTURE
|
|
||||||
float occlusion = texture(sampler2D(StandardMaterial_occlusion_texture, StandardMaterial_occlusion_texture_sampler), v_Uv).r;
|
|
||||||
# else
|
|
||||||
float occlusion = 1.0;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# ifdef STANDARDMATERIAL_EMISSIVE_TEXTURE
|
|
||||||
vec4 emissive = emissive;
|
|
||||||
// TODO use .a for exposure compensation in HDR
|
|
||||||
emissive.rgb *= texture(sampler2D(StandardMaterial_emissive_texture, StandardMaterial_emissive_texture_sampler), v_Uv).rgb;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
vec3 V = normalize(CameraPos.xyz - v_WorldPosition.xyz);
|
|
||||||
// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
|
|
||||||
float NdotV = max(dot(N, V), 1e-4);
|
|
||||||
|
|
||||||
// Remapping [0,1] reflectance to F0
|
|
||||||
// See https://google.github.io/filament/Filament.html#materialsystem/parameterization/remapping
|
|
||||||
vec3 F0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + output_color.rgb * metallic;
|
|
||||||
|
|
||||||
// Diffuse strength inversely related to metallicity
|
|
||||||
vec3 diffuseColor = output_color.rgb * (1.0 - metallic);
|
|
||||||
|
|
||||||
// accumulate color
|
|
||||||
vec3 light_accum = vec3(0.0);
|
|
||||||
for (int i = 0; i < int(NumLights.x) && i < MAX_LIGHTS; ++i) {
|
|
||||||
Light light = SceneLights[i];
|
|
||||||
|
|
||||||
vec3 lightDir = light.pos.xyz - v_WorldPosition.xyz;
|
|
||||||
vec3 L = normalize(lightDir);
|
|
||||||
|
|
||||||
float rangeAttenuation =
|
|
||||||
getDistanceAttenuation(lightDir, light.pos.w);
|
|
||||||
|
|
||||||
vec3 H = normalize(L + V);
|
|
||||||
float NoL = saturate(dot(N, L));
|
|
||||||
float NoH = saturate(dot(N, H));
|
|
||||||
float LoH = saturate(dot(L, H));
|
|
||||||
|
|
||||||
vec3 specular = specular(F0, roughness, H, NdotV, NoL, NoH, LoH);
|
|
||||||
vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH);
|
|
||||||
|
|
||||||
// Lout = f(v,l) Φ / { 4 π d^2 }⟨n⋅l⟩
|
|
||||||
// where
|
|
||||||
// f(v,l) = (f_d(v,l) + f_r(v,l)) * light_color
|
|
||||||
// Φ is light intensity
|
|
||||||
|
|
||||||
// our rangeAttentuation = 1 / d^2 multiplied with an attenuation factor for smoothing at the edge of the non-physical maximum light radius
|
|
||||||
// It's not 100% clear where the 1/4π goes in the derivation, but we follow the filament shader and leave it out
|
|
||||||
|
|
||||||
// See https://google.github.io/filament/Filament.html#mjx-eqn-pointLightLuminanceEquation
|
|
||||||
// TODO compensate for energy loss https://google.github.io/filament/Filament.html#materialsystem/improvingthebrdfs/energylossinspecularreflectance
|
|
||||||
// light.color.rgb is premultiplied with light.intensity on the CPU
|
|
||||||
light_accum +=
|
|
||||||
((diffuse + specular) * light.color.rgb) * (rangeAttenuation * NoL);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 diffuse_ambient = EnvBRDFApprox(diffuseColor, 1.0, NdotV);
|
|
||||||
vec3 specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV);
|
|
||||||
|
|
||||||
output_color.rgb = light_accum;
|
|
||||||
output_color.rgb += (diffuse_ambient + specular_ambient) * AmbientColor.xyz * occlusion;
|
|
||||||
output_color.rgb += emissive.rgb * output_color.a;
|
|
||||||
|
|
||||||
// tone_mapping
|
|
||||||
output_color.rgb = reinhard_luminance(output_color.rgb);
|
|
||||||
// Gamma correction.
|
|
||||||
// Not needed with sRGB buffer
|
|
||||||
// output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
o_Target = output_color;
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 Vertex_Position;
|
|
||||||
layout(location = 1) in vec3 Vertex_Normal;
|
|
||||||
layout(location = 2) in vec2 Vertex_Uv;
|
|
||||||
|
|
||||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
|
||||||
layout(location = 3) in vec4 Vertex_Tangent;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
layout(location = 0) out vec3 v_WorldPosition;
|
|
||||||
layout(location = 1) out vec3 v_WorldNormal;
|
|
||||||
layout(location = 2) out vec2 v_Uv;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform CameraViewProj {
|
|
||||||
mat4 ViewProj;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
|
||||||
layout(location = 3) out vec4 v_WorldTangent;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
layout(set = 2, binding = 0) uniform Transform {
|
|
||||||
mat4 Model;
|
|
||||||
};
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 world_position = Model * vec4(Vertex_Position, 1.0);
|
|
||||||
v_WorldPosition = world_position.xyz;
|
|
||||||
v_WorldNormal = mat3(Model) * Vertex_Normal;
|
|
||||||
v_Uv = Vertex_Uv;
|
|
||||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
|
||||||
v_WorldTangent = vec4(mat3(Model) * Vertex_Tangent.xyz, Vertex_Tangent.w);
|
|
||||||
#endif
|
|
||||||
gl_Position = ViewProj * world_position;
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
use bevy::asset::Assets;
|
|
||||||
use bevy::render::{
|
|
||||||
pipeline::{
|
|
||||||
BlendFactor, BlendOperation, BlendState, ColorTargetState, ColorWrite, CompareFunction,
|
|
||||||
DepthBiasState, DepthStencilState, PipelineDescriptor, StencilFaceState, StencilState,
|
|
||||||
},
|
|
||||||
shader::{Shader, ShaderStage, ShaderStages},
|
|
||||||
texture::TextureFormat,
|
|
||||||
};
|
|
||||||
|
|
||||||
// pub const PBR_PIPELINE_HANDLE: HandleUntyped =
|
|
||||||
// HandleUntyped::weak_from_u64(PipelineDescriptor::TYPE_UUID, 13148362314012771389);
|
|
||||||
|
|
||||||
pub(crate) fn build_pbr_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
|
|
||||||
PipelineDescriptor {
|
|
||||||
depth_stencil: Some(DepthStencilState {
|
|
||||||
format: TextureFormat::Depth32Float,
|
|
||||||
depth_write_enabled: true,
|
|
||||||
depth_compare: CompareFunction::Less,
|
|
||||||
stencil: StencilState {
|
|
||||||
front: StencilFaceState::IGNORE,
|
|
||||||
back: StencilFaceState::IGNORE,
|
|
||||||
read_mask: 0,
|
|
||||||
write_mask: 0,
|
|
||||||
},
|
|
||||||
bias: DepthBiasState {
|
|
||||||
constant: 0,
|
|
||||||
slope_scale: 0.0,
|
|
||||||
clamp: 0.0,
|
|
||||||
},
|
|
||||||
clamp_depth: false,
|
|
||||||
}),
|
|
||||||
color_target_states: vec![ColorTargetState {
|
|
||||||
format: TextureFormat::default(),
|
|
||||||
color_blend: BlendState {
|
|
||||||
src_factor: BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: BlendFactor::OneMinusSrcAlpha,
|
|
||||||
operation: BlendOperation::Add,
|
|
||||||
},
|
|
||||||
alpha_blend: BlendState {
|
|
||||||
src_factor: BlendFactor::One,
|
|
||||||
dst_factor: BlendFactor::One,
|
|
||||||
operation: BlendOperation::Add,
|
|
||||||
},
|
|
||||||
write_mask: ColorWrite::ALL,
|
|
||||||
}],
|
|
||||||
..PipelineDescriptor::new(ShaderStages {
|
|
||||||
vertex: shaders.add(Shader::from_glsl(
|
|
||||||
ShaderStage::Vertex,
|
|
||||||
include_str!("pbr.vert"),
|
|
||||||
)),
|
|
||||||
fragment: Some(shaders.add(Shader::from_glsl(
|
|
||||||
ShaderStage::Fragment,
|
|
||||||
include_str!("pbr.frag"),
|
|
||||||
))),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue