mirror of
https://github.com/bevyengine/bevy
synced 2025-02-17 22:48:38 +00:00
bevy_pbr2: Add support for most of the StandardMaterial textures (#4)
* bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
25de2d1819
commit
b1a91a823f
26 changed files with 891 additions and 245 deletions
|
@ -166,6 +166,10 @@ path = "examples/3d/parenting.rs"
|
||||||
name = "pbr"
|
name = "pbr"
|
||||||
path = "examples/3d/pbr.rs"
|
path = "examples/3d/pbr.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "pbr_pipelined"
|
||||||
|
path = "examples/3d/pbr_pipelined.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "render_to_texture"
|
name = "render_to_texture"
|
||||||
path = "examples/3d/render_to_texture.rs"
|
path = "examples/3d/render_to_texture.rs"
|
||||||
|
@ -178,6 +182,10 @@ path = "examples/3d/spawner.rs"
|
||||||
name = "texture"
|
name = "texture"
|
||||||
path = "examples/3d/texture.rs"
|
path = "examples/3d/texture.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "texture_pipelined"
|
||||||
|
path = "examples/3d/texture_pipelined.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "update_gltf_scene"
|
name = "update_gltf_scene"
|
||||||
path = "examples/3d/update_gltf_scene.rs"
|
path = "examples/3d/update_gltf_scene.rs"
|
||||||
|
|
|
@ -87,7 +87,10 @@ impl<Param: SystemParam> SystemState<Param> {
|
||||||
|
|
||||||
/// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
|
/// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get<'s, 'w>(&'s mut self, world: &'w World) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item
|
pub fn get<'s, 'w>(
|
||||||
|
&'s mut self,
|
||||||
|
world: &'w World,
|
||||||
|
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item
|
||||||
where
|
where
|
||||||
Param::Fetch: ReadOnlySystemParamFetch,
|
Param::Fetch: ReadOnlySystemParamFetch,
|
||||||
{
|
{
|
||||||
|
|
|
@ -372,7 +372,7 @@ where
|
||||||
pub fn get_mut(
|
pub fn get_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
) -> Result<<Q::Fetch as Fetch<'w,'s>>::Item, QueryEntityError> {
|
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFE: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -406,7 +406,10 @@ where
|
||||||
/// entity does not have the given component type or if the given component type does not match
|
/// entity does not have the given component type or if the given component type does not match
|
||||||
/// this query.
|
/// this query.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_component<T: Component>(&self, entity: Entity) -> Result<&'w T, QueryComponentError> {
|
pub fn get_component<T: Component>(
|
||||||
|
&self,
|
||||||
|
entity: Entity,
|
||||||
|
) -> Result<&'w T, QueryComponentError> {
|
||||||
let world = self.world;
|
let world = self.world;
|
||||||
let entity_ref = world
|
let entity_ref = world
|
||||||
.get_entity(entity)
|
.get_entity(entity)
|
||||||
|
|
|
@ -146,7 +146,8 @@ where
|
||||||
fn default_config() {}
|
fn default_config() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s,'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'s, 'w> for QueryState<Q, F>
|
impl<'s, 'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'s, 'w>
|
||||||
|
for QueryState<Q, F>
|
||||||
where
|
where
|
||||||
F::Fetch: FilterFetch,
|
F::Fetch: FilterFetch,
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl Command for PushChildren {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s, 'w, 'a> ChildBuilder<'s, 'w, 'a> {
|
impl<'s, 'w, 'a> ChildBuilder<'s, 'w, 'a> {
|
||||||
pub fn spawn_bundle(&mut self, bundle: impl Bundle) -> EntityCommands<'s,'w, '_> {
|
pub fn spawn_bundle(&mut self, bundle: impl Bundle) -> EntityCommands<'s, 'w, '_> {
|
||||||
let e = self.commands.spawn_bundle(bundle);
|
let e = self.commands.spawn_bundle(bundle);
|
||||||
self.push_children.children.push(e.id());
|
self.push_children.children.push(e.id());
|
||||||
e
|
e
|
||||||
|
|
|
@ -4,7 +4,7 @@ use bevy::{
|
||||||
ecs::prelude::*,
|
ecs::prelude::*,
|
||||||
input::Input,
|
input::Input,
|
||||||
math::Vec3,
|
math::Vec3,
|
||||||
pbr2::{PbrBundle, PointLightBundle, StandardMaterial},
|
pbr2::{OmniLightBundle, PbrBundle, StandardMaterial},
|
||||||
prelude::{App, Assets, KeyCode, Transform},
|
prelude::{App, Assets, KeyCode, Transform},
|
||||||
render2::{
|
render2::{
|
||||||
camera::PerspectiveCameraBundle,
|
camera::PerspectiveCameraBundle,
|
||||||
|
@ -36,8 +36,8 @@ fn setup(
|
||||||
commands.spawn_bundle(PbrBundle {
|
commands.spawn_bundle(PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
|
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
|
||||||
material: materials.add(StandardMaterial {
|
material: materials.add(StandardMaterial {
|
||||||
color: Color::INDIGO,
|
base_color: Color::INDIGO,
|
||||||
roughness: 1.0,
|
perceptual_roughness: 1.0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -47,8 +47,8 @@ fn setup(
|
||||||
.spawn_bundle(PbrBundle {
|
.spawn_bundle(PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
material: materials.add(StandardMaterial {
|
material: materials.add(StandardMaterial {
|
||||||
color: Color::PINK,
|
base_color: Color::PINK,
|
||||||
roughness: 0.0,
|
perceptual_roughness: 0.0,
|
||||||
metallic: 1.0,
|
metallic: 1.0,
|
||||||
reflectance: 1.0,
|
reflectance: 1.0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -65,7 +65,7 @@ fn setup(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})),
|
})),
|
||||||
material: materials.add(StandardMaterial {
|
material: materials.add(StandardMaterial {
|
||||||
color: Color::LIME_GREEN,
|
base_color: Color::LIME_GREEN,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
transform: Transform::from_xyz(1.5, 1.0, 1.5),
|
transform: Transform::from_xyz(1.5, 1.0, 1.5),
|
||||||
|
@ -73,7 +73,7 @@ fn setup(
|
||||||
})
|
})
|
||||||
.insert(Movable);
|
.insert(Movable);
|
||||||
// light
|
// light
|
||||||
commands.spawn_bundle(PointLightBundle {
|
commands.spawn_bundle(OmniLightBundle {
|
||||||
transform: Transform::from_xyz(5.0, 8.0, 2.0),
|
transform: Transform::from_xyz(5.0, 8.0, 2.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
86
examples/3d/pbr_pipelined.rs
Normal file
86
examples/3d/pbr_pipelined.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use bevy::{
|
||||||
|
ecs::prelude::*,
|
||||||
|
math::Vec3,
|
||||||
|
pbr2::{OmniLight, OmniLightBundle, PbrBundle, StandardMaterial},
|
||||||
|
prelude::{App, Assets, Transform},
|
||||||
|
render2::{
|
||||||
|
camera::{OrthographicCameraBundle, OrthographicProjection},
|
||||||
|
color::Color,
|
||||||
|
mesh::{shape, Mesh},
|
||||||
|
},
|
||||||
|
PipelinedDefaultPlugins,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This example shows how to configure Physically Based Rendering (PBR) parameters.
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(PipelinedDefaultPlugins)
|
||||||
|
.add_startup_system(setup.system())
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set up a simple 3D scene
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// 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_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Icosphere {
|
||||||
|
radius: 0.45,
|
||||||
|
subdivisions: 32,
|
||||||
|
})),
|
||||||
|
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::default()
|
||||||
|
}),
|
||||||
|
transform: Transform::from_xyz(x as f32, y as f32 + 0.5, 0.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// unlit sphere
|
||||||
|
commands.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Icosphere {
|
||||||
|
radius: 0.45,
|
||||||
|
subdivisions: 32,
|
||||||
|
})),
|
||||||
|
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::default()
|
||||||
|
}),
|
||||||
|
transform: Transform::from_xyz(-5.0, -2.5, 0.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
// light
|
||||||
|
commands.spawn_bundle(OmniLightBundle {
|
||||||
|
transform: Transform::from_translation(Vec3::new(50.0, 50.0, 50.0)),
|
||||||
|
omni_light: OmniLight {
|
||||||
|
intensity: 50000.,
|
||||||
|
range: 100.,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
// camera
|
||||||
|
commands.spawn_bundle(OrthographicCameraBundle {
|
||||||
|
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 8.0))
|
||||||
|
.looking_at(Vec3::default(), Vec3::Y),
|
||||||
|
orthographic_projection: OrthographicProjection {
|
||||||
|
scale: 0.01,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..OrthographicCameraBundle::new_3d()
|
||||||
|
});
|
||||||
|
}
|
101
examples/3d/texture_pipelined.rs
Normal file
101
examples/3d/texture_pipelined.rs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
use bevy::{
|
||||||
|
ecs::prelude::*,
|
||||||
|
math::{Quat, Vec2, Vec3},
|
||||||
|
pbr2::{PbrBundle, StandardMaterial},
|
||||||
|
prelude::{App, AssetServer, Assets, Transform},
|
||||||
|
render2::{
|
||||||
|
camera::PerspectiveCameraBundle,
|
||||||
|
color::Color,
|
||||||
|
mesh::{shape, Mesh},
|
||||||
|
},
|
||||||
|
PipelinedDefaultPlugins,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This example shows various ways to configure texture materials in 3D
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(PipelinedDefaultPlugins)
|
||||||
|
.add_startup_system(setup.system())
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sets up a scene with textured entities
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// load a texture and retrieve its aspect ratio
|
||||||
|
let texture_handle = asset_server.load("branding/bevy_logo_dark_big.png");
|
||||||
|
let aspect = 0.25;
|
||||||
|
|
||||||
|
// create a new quad mesh. this is what we will apply the texture to
|
||||||
|
let quad_width = 8.0;
|
||||||
|
let quad_handle = meshes.add(Mesh::from(shape::Quad::new(Vec2::new(
|
||||||
|
quad_width,
|
||||||
|
quad_width * aspect,
|
||||||
|
))));
|
||||||
|
|
||||||
|
// this material renders the texture normally
|
||||||
|
let material_handle = materials.add(StandardMaterial {
|
||||||
|
base_color_texture: Some(texture_handle.clone()),
|
||||||
|
unlit: true,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// this material modulates the texture to make it red (and slightly transparent)
|
||||||
|
let red_material_handle = materials.add(StandardMaterial {
|
||||||
|
base_color: Color::rgba(1.0, 0.0, 0.0, 0.5),
|
||||||
|
base_color_texture: Some(texture_handle.clone()),
|
||||||
|
unlit: true,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// and lets make this one blue! (and also slightly transparent)
|
||||||
|
let blue_material_handle = materials.add(StandardMaterial {
|
||||||
|
base_color: Color::rgba(0.0, 0.0, 1.0, 0.5),
|
||||||
|
base_color_texture: Some(texture_handle),
|
||||||
|
unlit: true,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// textured quad - normal
|
||||||
|
commands.spawn_bundle(PbrBundle {
|
||||||
|
mesh: quad_handle.clone(),
|
||||||
|
material: material_handle,
|
||||||
|
transform: Transform {
|
||||||
|
translation: Vec3::new(0.0, 0.0, 1.5),
|
||||||
|
rotation: Quat::from_rotation_x(-std::f32::consts::PI / 5.0),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
// textured quad - modulated
|
||||||
|
commands.spawn_bundle(PbrBundle {
|
||||||
|
mesh: quad_handle.clone(),
|
||||||
|
material: red_material_handle,
|
||||||
|
transform: Transform {
|
||||||
|
translation: Vec3::new(0.0, 0.0, 0.0),
|
||||||
|
rotation: Quat::from_rotation_x(-std::f32::consts::PI / 5.0),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
// textured quad - modulated
|
||||||
|
commands.spawn_bundle(PbrBundle {
|
||||||
|
mesh: quad_handle,
|
||||||
|
material: blue_material_handle,
|
||||||
|
transform: Transform {
|
||||||
|
translation: Vec3::new(0.0, 0.0, -1.5),
|
||||||
|
rotation: Quat::from_rotation_x(-std::f32::consts::PI / 5.0),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
// camera
|
||||||
|
commands.spawn_bundle(PerspectiveCameraBundle {
|
||||||
|
transform: Transform::from_xyz(3.0, 5.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
|
@ -25,6 +25,8 @@ bevy_transform = { path = "../../crates/bevy_transform", version = "0.5.0" }
|
||||||
bevy_utils = { path = "../../crates/bevy_utils", version = "0.5.0" }
|
bevy_utils = { path = "../../crates/bevy_utils", version = "0.5.0" }
|
||||||
|
|
||||||
# other
|
# other
|
||||||
|
bitflags = "1.2"
|
||||||
# direct dependency required for derive macro
|
# direct dependency required for derive macro
|
||||||
bytemuck = { version = "1", features = ["derive"] }
|
bytemuck = { version = "1", features = ["derive"] }
|
||||||
crevice = { path = "../../crates/crevice" }
|
crevice = { path = "../../crates/crevice" }
|
||||||
|
wgpu = "0.8"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{PointLight, StandardMaterial};
|
use crate::{OmniLight, StandardMaterial};
|
||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
use bevy_ecs::bundle::Bundle;
|
use bevy_ecs::bundle::Bundle;
|
||||||
use bevy_render2::mesh::Mesh;
|
use bevy_render2::mesh::Mesh;
|
||||||
|
@ -25,8 +25,8 @@ impl Default for PbrBundle {
|
||||||
|
|
||||||
/// A component bundle for "light" entities
|
/// A component bundle for "light" entities
|
||||||
#[derive(Debug, Bundle, Default)]
|
#[derive(Debug, Bundle, Default)]
|
||||||
pub struct PointLightBundle {
|
pub struct OmniLightBundle {
|
||||||
pub point_light: PointLight,
|
pub omni_light: OmniLight,
|
||||||
pub transform: Transform,
|
pub transform: Transform,
|
||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@ pub struct PbrPlugin;
|
||||||
|
|
||||||
impl Plugin for PbrPlugin {
|
impl Plugin for PbrPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugin(StandardMaterialPlugin);
|
app.add_plugin(StandardMaterialPlugin)
|
||||||
|
.init_resource::<AmbientLight>();
|
||||||
|
|
||||||
let render_app = app.sub_app_mut(0);
|
let render_app = app.sub_app_mut(0);
|
||||||
render_app
|
render_app
|
||||||
|
@ -46,6 +47,8 @@ impl Plugin for PbrPlugin {
|
||||||
RenderStage::PhaseSort,
|
RenderStage::PhaseSort,
|
||||||
sort_phase_system::<ShadowPhase>.system(),
|
sort_phase_system::<ShadowPhase>.system(),
|
||||||
)
|
)
|
||||||
|
// FIXME: Hack to ensure RenderCommandQueue is initialized when PbrShaders is being initialized
|
||||||
|
// .init_resource::<RenderCommandQueue>()
|
||||||
.init_resource::<PbrShaders>()
|
.init_resource::<PbrShaders>()
|
||||||
.init_resource::<ShadowShaders>()
|
.init_resource::<ShadowShaders>()
|
||||||
.init_resource::<MeshMeta>()
|
.init_resource::<MeshMeta>()
|
||||||
|
|
|
@ -2,19 +2,19 @@ use bevy_ecs::reflect::ReflectComponent;
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_render2::color::Color;
|
use bevy_render2::color::Color;
|
||||||
|
|
||||||
/// A point light
|
/// An omnidirectional light
|
||||||
#[derive(Debug, Clone, Copy, Reflect)]
|
#[derive(Debug, Clone, Copy, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct PointLight {
|
pub struct OmniLight {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub intensity: f32,
|
pub intensity: f32,
|
||||||
pub range: f32,
|
pub range: f32,
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PointLight {
|
impl Default for OmniLight {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
PointLight {
|
OmniLight {
|
||||||
color: Color::rgb(1.0, 1.0, 1.0),
|
color: Color::rgb(1.0, 1.0, 1.0),
|
||||||
intensity: 200.0,
|
intensity: 200.0,
|
||||||
range: 20.0,
|
range: 20.0,
|
||||||
|
@ -22,3 +22,20 @@ impl Default for PointLight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ambient light color.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AmbientLight {
|
||||||
|
pub color: Color,
|
||||||
|
/// Color is premultiplied by brightness before being passed to the shader
|
||||||
|
pub brightness: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AmbientLight {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
color: Color::rgb(1.0, 1.0, 1.0),
|
||||||
|
brightness: 0.05,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,20 +1,29 @@
|
||||||
use bevy_app::{App, CoreStage, EventReader, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{AddAsset, AssetEvent, Assets};
|
use bevy_asset::{AddAsset, Handle};
|
||||||
use bevy_ecs::prelude::*;
|
|
||||||
use bevy_math::Vec4;
|
use bevy_math::Vec4;
|
||||||
use bevy_reflect::TypeUuid;
|
use bevy_reflect::TypeUuid;
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
color::Color,
|
color::Color,
|
||||||
|
render_asset::{RenderAsset, RenderAssetPlugin},
|
||||||
render_resource::{Buffer, BufferInitDescriptor, BufferUsage},
|
render_resource::{Buffer, BufferInitDescriptor, BufferUsage},
|
||||||
renderer::RenderDevice,
|
renderer::{RenderDevice, RenderQueue},
|
||||||
|
texture::Image,
|
||||||
};
|
};
|
||||||
use bevy_utils::HashSet;
|
|
||||||
use crevice::std140::{AsStd140, Std140};
|
use crevice::std140::{AsStd140, Std140};
|
||||||
|
|
||||||
// TODO: this shouldn't live in the StandardMaterial type
|
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.frag!
|
||||||
#[derive(Debug, Clone)]
|
bitflags::bitflags! {
|
||||||
pub struct StandardMaterialGpuData {
|
#[repr(transparent)]
|
||||||
pub buffer: Buffer,
|
struct StandardMaterialFlags: u32 {
|
||||||
|
const BASE_COLOR_TEXTURE = (1 << 0);
|
||||||
|
const EMISSIVE_TEXTURE = (1 << 1);
|
||||||
|
const METALLIC_ROUGHNESS_TEXTURE = (1 << 2);
|
||||||
|
const OCCLUSION_TEXTURE = (1 << 3);
|
||||||
|
const DOUBLE_SIDED = (1 << 4);
|
||||||
|
const UNLIT = (1 << 5);
|
||||||
|
const NONE = 0;
|
||||||
|
const UNINITIALIZED = 0xFFFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A material with "standard" properties used in PBR lighting
|
/// A material with "standard" properties used in PBR lighting
|
||||||
|
@ -23,46 +32,55 @@ pub struct StandardMaterialGpuData {
|
||||||
#[uuid = "7494888b-c082-457b-aacf-517228cc0c22"]
|
#[uuid = "7494888b-c082-457b-aacf-517228cc0c22"]
|
||||||
pub struct StandardMaterial {
|
pub struct StandardMaterial {
|
||||||
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
||||||
/// in between.
|
/// in between. If used together with a base_color_texture, this is factored into the final
|
||||||
pub color: Color,
|
/// base color as `base_color * base_color_texture_value`
|
||||||
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
|
pub base_color: Color,
|
||||||
/// Defaults to minimum of 0.089
|
pub base_color_texture: Option<Handle<Image>>,
|
||||||
pub roughness: f32,
|
|
||||||
/// From [0.0, 1.0], dielectric to pure metallic
|
|
||||||
pub metallic: f32,
|
|
||||||
/// Specular intensity for non-metals on a linear scale of [0.0, 1.0]
|
|
||||||
/// defaults to 0.5 which is mapped to 4% reflectance in the shader
|
|
||||||
pub reflectance: f32,
|
|
||||||
// Use a color for user friendliness even though we technically don't use the alpha channel
|
// Use a color for user friendliness even though we technically don't use the alpha channel
|
||||||
// Might be used in the future for exposure correction in HDR
|
// Might be used in the future for exposure correction in HDR
|
||||||
pub emissive: Color,
|
pub emissive: Color,
|
||||||
pub gpu_data: Option<StandardMaterialGpuData>,
|
pub emissive_texture: Option<Handle<Image>>,
|
||||||
}
|
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
|
||||||
|
/// Defaults to minimum of 0.089
|
||||||
impl StandardMaterial {
|
/// If used together with a roughness/metallic texture, this is factored into the final base
|
||||||
pub fn gpu_data(&self) -> Option<&StandardMaterialGpuData> {
|
/// color as `roughness * roughness_texture_value`
|
||||||
self.gpu_data.as_ref()
|
pub perceptual_roughness: f32,
|
||||||
}
|
/// From [0.0, 1.0], dielectric to pure metallic
|
||||||
|
/// If used together with a roughness/metallic texture, this is factored into the final base
|
||||||
|
/// color as `metallic * metallic_texture_value`
|
||||||
|
pub metallic: f32,
|
||||||
|
pub metallic_roughness_texture: Option<Handle<Image>>,
|
||||||
|
/// Specular intensity for non-metals on a linear scale of [0.0, 1.0]
|
||||||
|
/// defaults to 0.5 which is mapped to 4% reflectance in the shader
|
||||||
|
pub reflectance: f32,
|
||||||
|
pub occlusion_texture: Option<Handle<Image>>,
|
||||||
|
pub double_sided: bool,
|
||||||
|
pub unlit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for StandardMaterial {
|
impl Default for StandardMaterial {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
StandardMaterial {
|
StandardMaterial {
|
||||||
color: Color::rgb(1.0, 1.0, 1.0),
|
base_color: Color::rgb(1.0, 1.0, 1.0),
|
||||||
|
base_color_texture: None,
|
||||||
|
emissive: Color::BLACK,
|
||||||
|
emissive_texture: None,
|
||||||
// This is the minimum the roughness is clamped to in shader code
|
// This is the minimum the roughness is clamped to in shader code
|
||||||
// See https://google.github.io/filament/Filament.html#materialsystem/parameterization/
|
// See https://google.github.io/filament/Filament.html#materialsystem/parameterization/
|
||||||
// It's the minimum floating point value that won't be rounded down to 0 in the
|
// It's the minimum floating point value that won't be rounded down to 0 in the
|
||||||
// calculations used. Although technically for 32-bit floats, 0.045 could be
|
// calculations used. Although technically for 32-bit floats, 0.045 could be
|
||||||
// used.
|
// used.
|
||||||
roughness: 0.089,
|
perceptual_roughness: 0.089,
|
||||||
// Few materials are purely dielectric or metallic
|
// Few materials are purely dielectric or metallic
|
||||||
// This is just a default for mostly-dielectric
|
// This is just a default for mostly-dielectric
|
||||||
metallic: 0.01,
|
metallic: 0.01,
|
||||||
|
metallic_roughness_texture: None,
|
||||||
// Minimum real-world reflectance is 2%, most materials between 2-5%
|
// Minimum real-world reflectance is 2%, most materials between 2-5%
|
||||||
// Expressed in a linear scale and equivalent to 4% reflectance see https://google.github.io/filament/Material%20Properties.pdf
|
// Expressed in a linear scale and equivalent to 4% reflectance see https://google.github.io/filament/Material%20Properties.pdf
|
||||||
reflectance: 0.5,
|
reflectance: 0.5,
|
||||||
emissive: Color::BLACK,
|
occlusion_texture: None,
|
||||||
gpu_data: None,
|
double_sided: false,
|
||||||
|
unlit: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +88,16 @@ impl Default for StandardMaterial {
|
||||||
impl From<Color> for StandardMaterial {
|
impl From<Color> for StandardMaterial {
|
||||||
fn from(color: Color) -> Self {
|
fn from(color: Color) -> Self {
|
||||||
StandardMaterial {
|
StandardMaterial {
|
||||||
color,
|
base_color: color,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Handle<Image>> for StandardMaterial {
|
||||||
|
fn from(texture: Handle<Image>) -> Self {
|
||||||
|
StandardMaterial {
|
||||||
|
base_color_texture: Some(texture),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +107,10 @@ impl From<Color> for StandardMaterial {
|
||||||
pub struct StandardMaterialUniformData {
|
pub struct StandardMaterialUniformData {
|
||||||
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
||||||
/// in between.
|
/// in between.
|
||||||
pub color: Vec4,
|
pub base_color: Vec4,
|
||||||
|
// Use a color for user friendliness even though we technically don't use the alpha channel
|
||||||
|
// Might be used in the future for exposure correction in HDR
|
||||||
|
pub emissive: Vec4,
|
||||||
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
|
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
|
||||||
/// Defaults to minimum of 0.089
|
/// Defaults to minimum of 0.089
|
||||||
pub roughness: f32,
|
pub roughness: f32,
|
||||||
|
@ -89,71 +119,81 @@ pub struct StandardMaterialUniformData {
|
||||||
/// Specular intensity for non-metals on a linear scale of [0.0, 1.0]
|
/// Specular intensity for non-metals on a linear scale of [0.0, 1.0]
|
||||||
/// defaults to 0.5 which is mapped to 4% reflectance in the shader
|
/// defaults to 0.5 which is mapped to 4% reflectance in the shader
|
||||||
pub reflectance: f32,
|
pub reflectance: f32,
|
||||||
// Use a color for user friendliness even though we technically don't use the alpha channel
|
pub flags: u32,
|
||||||
// Might be used in the future for exposure correction in HDR
|
|
||||||
pub emissive: Vec4,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StandardMaterialPlugin;
|
pub struct StandardMaterialPlugin;
|
||||||
|
|
||||||
impl Plugin for StandardMaterialPlugin {
|
impl Plugin for StandardMaterialPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_asset::<StandardMaterial>().add_system_to_stage(
|
app.add_plugin(RenderAssetPlugin::<StandardMaterial>::default())
|
||||||
CoreStage::PostUpdate,
|
.add_asset::<StandardMaterial>();
|
||||||
standard_material_resource_system.system(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn standard_material_resource_system(
|
#[derive(Debug, Clone)]
|
||||||
render_device: Res<RenderDevice>,
|
pub struct GpuStandardMaterial {
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
pub buffer: Buffer,
|
||||||
mut material_events: EventReader<AssetEvent<StandardMaterial>>,
|
// FIXME: image handles feel unnecessary here but the extracted asset is discarded
|
||||||
) {
|
pub base_color_texture: Option<Handle<Image>>,
|
||||||
let mut changed_materials = HashSet::default();
|
pub emissive_texture: Option<Handle<Image>>,
|
||||||
for event in material_events.iter() {
|
pub metallic_roughness_texture: Option<Handle<Image>>,
|
||||||
match event {
|
pub occlusion_texture: Option<Handle<Image>>,
|
||||||
AssetEvent::Created { ref handle } => {
|
}
|
||||||
changed_materials.insert(handle.clone_weak());
|
|
||||||
}
|
impl RenderAsset for StandardMaterial {
|
||||||
AssetEvent::Modified { ref handle } => {
|
type ExtractedAsset = StandardMaterial;
|
||||||
changed_materials.insert(handle.clone_weak());
|
type PreparedAsset = GpuStandardMaterial;
|
||||||
// TODO: uncomment this to support mutated materials
|
|
||||||
// remove_current_material_resources(render_resource_context, handle, &mut materials);
|
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||||
}
|
self.clone()
|
||||||
AssetEvent::Removed { ref handle } => {
|
|
||||||
// if material was modified and removed in the same update, ignore the modification
|
|
||||||
// events are ordered so future modification events are ok
|
|
||||||
changed_materials.remove(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update changed material data
|
fn prepare_asset(
|
||||||
for changed_material_handle in changed_materials.iter() {
|
material: Self::ExtractedAsset,
|
||||||
if let Some(material) = materials.get_mut(changed_material_handle) {
|
render_device: &RenderDevice,
|
||||||
// TODO: this avoids creating new materials each frame because storing gpu data in the material flags it as
|
_render_queue: &RenderQueue,
|
||||||
// modified. this prevents hot reloading and therefore can't be used in an actual impl.
|
) -> Self::PreparedAsset {
|
||||||
if material.gpu_data.is_some() {
|
let mut flags = StandardMaterialFlags::NONE;
|
||||||
continue;
|
if material.base_color_texture.is_some() {
|
||||||
}
|
flags |= StandardMaterialFlags::BASE_COLOR_TEXTURE;
|
||||||
|
}
|
||||||
|
if material.emissive_texture.is_some() {
|
||||||
|
flags |= StandardMaterialFlags::EMISSIVE_TEXTURE;
|
||||||
|
}
|
||||||
|
if material.metallic_roughness_texture.is_some() {
|
||||||
|
flags |= StandardMaterialFlags::METALLIC_ROUGHNESS_TEXTURE;
|
||||||
|
}
|
||||||
|
if material.occlusion_texture.is_some() {
|
||||||
|
flags |= StandardMaterialFlags::OCCLUSION_TEXTURE;
|
||||||
|
}
|
||||||
|
if material.double_sided {
|
||||||
|
flags |= StandardMaterialFlags::DOUBLE_SIDED;
|
||||||
|
}
|
||||||
|
if material.unlit {
|
||||||
|
flags |= StandardMaterialFlags::UNLIT;
|
||||||
|
}
|
||||||
|
let value = StandardMaterialUniformData {
|
||||||
|
base_color: material.base_color.as_rgba_linear().into(),
|
||||||
|
emissive: material.emissive.into(),
|
||||||
|
roughness: material.perceptual_roughness,
|
||||||
|
metallic: material.metallic,
|
||||||
|
reflectance: material.reflectance,
|
||||||
|
flags: flags.bits,
|
||||||
|
};
|
||||||
|
let value_std140 = value.as_std140();
|
||||||
|
|
||||||
let value = StandardMaterialUniformData {
|
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||||
color: material.color.into(),
|
label: None,
|
||||||
roughness: material.roughness,
|
usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
|
||||||
metallic: material.metallic,
|
contents: value_std140.as_bytes(),
|
||||||
reflectance: material.reflectance,
|
});
|
||||||
emissive: material.emissive.into(),
|
GpuStandardMaterial {
|
||||||
};
|
buffer,
|
||||||
let value_std140 = value.as_std140();
|
base_color_texture: material.base_color_texture,
|
||||||
|
emissive_texture: material.emissive_texture,
|
||||||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
metallic_roughness_texture: material.metallic_roughness_texture,
|
||||||
label: None,
|
occlusion_texture: material.occlusion_texture,
|
||||||
usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
|
|
||||||
contents: value_std140.as_bytes(),
|
|
||||||
});
|
|
||||||
|
|
||||||
material.gpu_data = Some(StandardMaterialGpuData { buffer });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{ExtractedMeshes, MeshMeta, PbrShaders, PointLight};
|
use crate::{AmbientLight, ExtractedMeshes, MeshMeta, OmniLight, PbrShaders};
|
||||||
use bevy_ecs::{prelude::*, system::SystemState};
|
use bevy_ecs::{prelude::*, system::SystemState};
|
||||||
use bevy_math::{Mat4, Vec3, Vec4};
|
use bevy_math::{Mat4, Vec3, Vec4};
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
|
@ -17,6 +17,11 @@ use bevy_transform::components::GlobalTransform;
|
||||||
use crevice::std140::AsStd140;
|
use crevice::std140::AsStd140;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
|
pub struct ExtractedAmbientLight {
|
||||||
|
color: Color,
|
||||||
|
brightness: f32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ExtractedPointLight {
|
pub struct ExtractedPointLight {
|
||||||
color: Color,
|
color: Color,
|
||||||
intensity: f32,
|
intensity: f32,
|
||||||
|
@ -38,16 +43,17 @@ pub struct GpuLight {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug, AsStd140)]
|
#[derive(Copy, Clone, Debug, AsStd140)]
|
||||||
pub struct GpuLights {
|
pub struct GpuLights {
|
||||||
|
ambient_color: Vec4,
|
||||||
len: u32,
|
len: u32,
|
||||||
lights: [GpuLight; MAX_POINT_LIGHTS],
|
lights: [GpuLight; MAX_OMNI_LIGHTS],
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: this must be kept in sync MAX_POINT_LIGHTS in pbr.frag
|
// NOTE: this must be kept in sync MAX_OMNI_LIGHTS in pbr.frag
|
||||||
pub const MAX_POINT_LIGHTS: usize = 10;
|
pub const MAX_OMNI_LIGHTS: usize = 10;
|
||||||
pub const SHADOW_SIZE: Extent3d = Extent3d {
|
pub const SHADOW_SIZE: Extent3d = Extent3d {
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 1024,
|
height: 1024,
|
||||||
depth_or_array_layers: MAX_POINT_LIGHTS as u32,
|
depth_or_array_layers: MAX_OMNI_LIGHTS as u32,
|
||||||
};
|
};
|
||||||
pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float;
|
pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float;
|
||||||
|
|
||||||
|
@ -167,8 +173,13 @@ impl FromWorld for ShadowShaders {
|
||||||
// TODO: ultimately these could be filtered down to lights relevant to actual views
|
// TODO: ultimately these could be filtered down to lights relevant to actual views
|
||||||
pub fn extract_lights(
|
pub fn extract_lights(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
lights: Query<(Entity, &PointLight, &GlobalTransform)>,
|
ambient_light: Res<AmbientLight>,
|
||||||
|
lights: Query<(Entity, &OmniLight, &GlobalTransform)>,
|
||||||
) {
|
) {
|
||||||
|
commands.insert_resource(ExtractedAmbientLight {
|
||||||
|
color: ambient_light.color,
|
||||||
|
brightness: ambient_light.brightness,
|
||||||
|
});
|
||||||
for (entity, light, transform) in lights.iter() {
|
for (entity, light, transform) in lights.iter() {
|
||||||
commands.get_or_spawn(entity).insert(ExtractedPointLight {
|
commands.get_or_spawn(entity).insert(ExtractedPointLight {
|
||||||
color: light.color,
|
color: light.color,
|
||||||
|
@ -203,6 +214,7 @@ pub fn prepare_lights(
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
mut light_meta: ResMut<LightMeta>,
|
mut light_meta: ResMut<LightMeta>,
|
||||||
views: Query<Entity, With<RenderPhase<Transparent3dPhase>>>,
|
views: Query<Entity, With<RenderPhase<Transparent3dPhase>>>,
|
||||||
|
ambient_light: Res<ExtractedAmbientLight>,
|
||||||
lights: Query<&ExtractedPointLight>,
|
lights: Query<&ExtractedPointLight>,
|
||||||
) {
|
) {
|
||||||
// PERF: view.iter().count() could be views.iter().len() if we implemented ExactSizeIterator for archetype-only filters
|
// PERF: view.iter().count() could be views.iter().len() if we implemented ExactSizeIterator for archetype-only filters
|
||||||
|
@ -210,6 +222,7 @@ pub fn prepare_lights(
|
||||||
.view_gpu_lights
|
.view_gpu_lights
|
||||||
.reserve_and_clear(views.iter().count(), &render_device);
|
.reserve_and_clear(views.iter().count(), &render_device);
|
||||||
|
|
||||||
|
let ambient_color = ambient_light.color.as_rgba_linear() * ambient_light.brightness;
|
||||||
// set up light data for each view
|
// set up light data for each view
|
||||||
for entity in views.iter() {
|
for entity in views.iter() {
|
||||||
let light_depth_texture = texture_cache.get(
|
let light_depth_texture = texture_cache.get(
|
||||||
|
@ -227,12 +240,13 @@ pub fn prepare_lights(
|
||||||
let mut view_lights = Vec::new();
|
let mut view_lights = Vec::new();
|
||||||
|
|
||||||
let mut gpu_lights = GpuLights {
|
let mut gpu_lights = GpuLights {
|
||||||
|
ambient_color: ambient_color.into(),
|
||||||
len: lights.iter().len() as u32,
|
len: lights.iter().len() as u32,
|
||||||
lights: [GpuLight::default(); MAX_POINT_LIGHTS],
|
lights: [GpuLight::default(); MAX_OMNI_LIGHTS],
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
|
// TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
|
||||||
for (i, light) in lights.iter().enumerate().take(MAX_POINT_LIGHTS) {
|
for (i, light) in lights.iter().enumerate().take(MAX_OMNI_LIGHTS) {
|
||||||
let depth_texture_view =
|
let depth_texture_view =
|
||||||
light_depth_texture
|
light_depth_texture
|
||||||
.texture
|
.texture
|
||||||
|
@ -250,12 +264,13 @@ pub fn prepare_lights(
|
||||||
let view_transform = GlobalTransform::from_translation(light.transform.translation)
|
let view_transform = GlobalTransform::from_translation(light.transform.translation)
|
||||||
.looking_at(Vec3::default(), Vec3::Y);
|
.looking_at(Vec3::default(), Vec3::Y);
|
||||||
// TODO: configure light projection based on light configuration
|
// TODO: configure light projection based on light configuration
|
||||||
let projection = Mat4::perspective_rh(1.0472, 1.0, 1.0, 20.0);
|
let projection =
|
||||||
|
Mat4::perspective_rh(std::f32::consts::FRAC_PI_2, 1.0, 0.1, light.range);
|
||||||
|
|
||||||
gpu_lights.lights[i] = GpuLight {
|
gpu_lights.lights[i] = GpuLight {
|
||||||
// premultiply color by intensity
|
// premultiply color by intensity
|
||||||
// we don't use the alpha at all, so no reason to multiply only [0..3]
|
// we don't use the alpha at all, so no reason to multiply only [0..3]
|
||||||
color: (light.color * light.intensity).into(),
|
color: (light.color.as_rgba_linear() * light.intensity).into(),
|
||||||
radius: light.radius.into(),
|
radius: light.radius.into(),
|
||||||
position: light.transform.translation.into(),
|
position: light.transform.translation.into(),
|
||||||
range: 1.0 / (light.range * light.range),
|
range: 1.0 / (light.range * light.range),
|
||||||
|
|
|
@ -11,15 +11,19 @@ use bevy_render2::{
|
||||||
render_graph::{Node, NodeRunError, RenderGraphContext},
|
render_graph::{Node, NodeRunError, RenderGraphContext},
|
||||||
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
|
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
|
||||||
render_resource::*,
|
render_resource::*,
|
||||||
renderer::{RenderContext, RenderDevice},
|
renderer::{RenderContext, RenderDevice, RenderQueue},
|
||||||
shader::Shader,
|
shader::Shader,
|
||||||
texture::BevyDefault,
|
texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo},
|
||||||
view::{ViewMeta, ViewUniform, ViewUniformOffset},
|
view::{ExtractedView, ViewMeta, ViewUniform, ViewUniformOffset},
|
||||||
};
|
};
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
use bevy_utils::slab::{FrameSlabMap, FrameSlabMapKey};
|
use bevy_utils::slab::{FrameSlabMap, FrameSlabMapKey};
|
||||||
use crevice::std140::AsStd140;
|
use crevice::std140::AsStd140;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use wgpu::{
|
||||||
|
Extent3d, ImageCopyTexture, ImageDataLayout, Origin3d, TextureDimension, TextureFormat,
|
||||||
|
TextureViewDescriptor,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{StandardMaterial, StandardMaterialUniformData};
|
use crate::{StandardMaterial, StandardMaterialUniformData};
|
||||||
|
|
||||||
|
@ -29,6 +33,8 @@ pub struct PbrShaders {
|
||||||
view_layout: BindGroupLayout,
|
view_layout: BindGroupLayout,
|
||||||
material_layout: BindGroupLayout,
|
material_layout: BindGroupLayout,
|
||||||
mesh_layout: BindGroupLayout,
|
mesh_layout: BindGroupLayout,
|
||||||
|
// This dummy white texture is to be used in place of optional StandardMaterial textures
|
||||||
|
dummy_white_gpu_image: GpuImage,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
|
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
|
||||||
|
@ -122,18 +128,104 @@ impl FromWorld for PbrShaders {
|
||||||
});
|
});
|
||||||
|
|
||||||
let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
entries: &[BindGroupLayoutEntry {
|
entries: &[
|
||||||
binding: 0,
|
BindGroupLayoutEntry {
|
||||||
visibility: ShaderStage::FRAGMENT,
|
binding: 0,
|
||||||
ty: BindingType::Buffer {
|
visibility: ShaderStage::FRAGMENT,
|
||||||
ty: BufferBindingType::Uniform,
|
ty: BindingType::Buffer {
|
||||||
has_dynamic_offset: false,
|
ty: BufferBindingType::Uniform,
|
||||||
min_binding_size: BufferSize::new(
|
has_dynamic_offset: false,
|
||||||
StandardMaterialUniformData::std140_size_static() as u64,
|
min_binding_size: BufferSize::new(
|
||||||
),
|
StandardMaterialUniformData::std140_size_static() as u64,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
},
|
},
|
||||||
count: None,
|
// Base Color Texture
|
||||||
}],
|
BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: ShaderStage::FRAGMENT,
|
||||||
|
ty: BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
sample_type: TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: TextureViewDimension::D2,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// Base Color Texture Sampler
|
||||||
|
BindGroupLayoutEntry {
|
||||||
|
binding: 2,
|
||||||
|
visibility: ShaderStage::FRAGMENT,
|
||||||
|
ty: BindingType::Sampler {
|
||||||
|
comparison: false,
|
||||||
|
filtering: true,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// Emissive Texture
|
||||||
|
BindGroupLayoutEntry {
|
||||||
|
binding: 3,
|
||||||
|
visibility: ShaderStage::FRAGMENT,
|
||||||
|
ty: BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
sample_type: TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: TextureViewDimension::D2,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// Emissive Texture Sampler
|
||||||
|
BindGroupLayoutEntry {
|
||||||
|
binding: 4,
|
||||||
|
visibility: ShaderStage::FRAGMENT,
|
||||||
|
ty: BindingType::Sampler {
|
||||||
|
comparison: false,
|
||||||
|
filtering: true,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// Metallic Roughness Texture
|
||||||
|
BindGroupLayoutEntry {
|
||||||
|
binding: 5,
|
||||||
|
visibility: ShaderStage::FRAGMENT,
|
||||||
|
ty: BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
sample_type: TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: TextureViewDimension::D2,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// Metallic Roughness Texture Sampler
|
||||||
|
BindGroupLayoutEntry {
|
||||||
|
binding: 6,
|
||||||
|
visibility: ShaderStage::FRAGMENT,
|
||||||
|
ty: BindingType::Sampler {
|
||||||
|
comparison: false,
|
||||||
|
filtering: true,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// Occlusion Texture
|
||||||
|
BindGroupLayoutEntry {
|
||||||
|
binding: 7,
|
||||||
|
visibility: ShaderStage::FRAGMENT,
|
||||||
|
ty: BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
sample_type: TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: TextureViewDimension::D2,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// Occlusion Texture Sampler
|
||||||
|
BindGroupLayoutEntry {
|
||||||
|
binding: 8,
|
||||||
|
visibility: ShaderStage::FRAGMENT,
|
||||||
|
ty: BindingType::Sampler {
|
||||||
|
comparison: false,
|
||||||
|
filtering: true,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
label: None,
|
label: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -222,12 +314,53 @@ impl FromWorld for PbrShaders {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// A 1x1x1 'all 1.0' texture to use as a dummy texture to use in place of optional StandardMaterial textures
|
||||||
|
let dummy_white_gpu_image = {
|
||||||
|
let image = Image::new_fill(
|
||||||
|
Extent3d::default(),
|
||||||
|
TextureDimension::D2,
|
||||||
|
&[255u8; 4],
|
||||||
|
TextureFormat::bevy_default(),
|
||||||
|
);
|
||||||
|
let texture = render_device.create_texture(&image.texture_descriptor);
|
||||||
|
let sampler = render_device.create_sampler(&image.sampler_descriptor);
|
||||||
|
|
||||||
|
let format_size = image.texture_descriptor.format.pixel_size();
|
||||||
|
let render_queue = world.get_resource_mut::<RenderQueue>().unwrap();
|
||||||
|
render_queue.write_texture(
|
||||||
|
ImageCopyTexture {
|
||||||
|
texture: &texture,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: Origin3d::ZERO,
|
||||||
|
},
|
||||||
|
&image.data,
|
||||||
|
ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(
|
||||||
|
std::num::NonZeroU32::new(
|
||||||
|
image.texture_descriptor.size.width * format_size as u32,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
|
rows_per_image: None,
|
||||||
|
},
|
||||||
|
image.texture_descriptor.size,
|
||||||
|
);
|
||||||
|
|
||||||
|
let texture_view = texture.create_view(&TextureViewDescriptor::default());
|
||||||
|
GpuImage {
|
||||||
|
texture,
|
||||||
|
texture_view,
|
||||||
|
sampler,
|
||||||
|
}
|
||||||
|
};
|
||||||
PbrShaders {
|
PbrShaders {
|
||||||
pipeline,
|
pipeline,
|
||||||
view_layout,
|
view_layout,
|
||||||
material_layout,
|
material_layout,
|
||||||
mesh_layout,
|
mesh_layout,
|
||||||
vertex_shader_module,
|
vertex_shader_module,
|
||||||
|
dummy_white_gpu_image,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,8 +368,8 @@ impl FromWorld for PbrShaders {
|
||||||
struct ExtractedMesh {
|
struct ExtractedMesh {
|
||||||
transform: Mat4,
|
transform: Mat4,
|
||||||
mesh: Handle<Mesh>,
|
mesh: Handle<Mesh>,
|
||||||
material_buffer: Buffer,
|
|
||||||
transform_binding_offset: u32,
|
transform_binding_offset: u32,
|
||||||
|
material_handle: Handle<StandardMaterial>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExtractedMeshes {
|
pub struct ExtractedMeshes {
|
||||||
|
@ -247,6 +380,7 @@ pub fn extract_meshes(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
meshes: Res<Assets<Mesh>>,
|
meshes: Res<Assets<Mesh>>,
|
||||||
materials: Res<Assets<StandardMaterial>>,
|
materials: Res<Assets<StandardMaterial>>,
|
||||||
|
images: Res<Assets<Image>>,
|
||||||
query: Query<(&GlobalTransform, &Handle<Mesh>, &Handle<StandardMaterial>)>,
|
query: Query<(&GlobalTransform, &Handle<Mesh>, &Handle<StandardMaterial>)>,
|
||||||
) {
|
) {
|
||||||
let mut extracted_meshes = Vec::new();
|
let mut extracted_meshes = Vec::new();
|
||||||
|
@ -254,15 +388,37 @@ pub fn extract_meshes(
|
||||||
if !meshes.contains(mesh_handle) {
|
if !meshes.contains(mesh_handle) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(material) = materials.get(material_handle) {
|
if let Some(material) = materials.get(material_handle) {
|
||||||
if let Some(material_gpu_data) = &material.gpu_data() {
|
if let Some(ref image) = material.base_color_texture {
|
||||||
extracted_meshes.push(ExtractedMesh {
|
if !images.contains(image) {
|
||||||
transform: transform.compute_matrix(),
|
continue;
|
||||||
mesh: mesh_handle.clone_weak(),
|
}
|
||||||
material_buffer: material_gpu_data.buffer.clone(),
|
|
||||||
transform_binding_offset: 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ref image) = material.emissive_texture {
|
||||||
|
if !images.contains(image) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref image) = material.metallic_roughness_texture {
|
||||||
|
if !images.contains(image) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref image) = material.occlusion_texture {
|
||||||
|
if !images.contains(image) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extracted_meshes.push(ExtractedMesh {
|
||||||
|
transform: transform.compute_matrix(),
|
||||||
|
mesh: mesh_handle.clone_weak(),
|
||||||
|
transform_binding_offset: 0,
|
||||||
|
material_handle: material_handle.clone_weak(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,6 +462,23 @@ pub struct MeshViewBindGroups {
|
||||||
view: BindGroup,
|
view: BindGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn image_handle_to_view_sampler<'a>(
|
||||||
|
pbr_shaders: &'a PbrShaders,
|
||||||
|
gpu_images: &'a RenderAssets<Image>,
|
||||||
|
image_option: &Option<Handle<Image>>,
|
||||||
|
) -> (&'a TextureView, &'a Sampler) {
|
||||||
|
image_option.as_ref().map_or(
|
||||||
|
(
|
||||||
|
&pbr_shaders.dummy_white_gpu_image.texture_view,
|
||||||
|
&pbr_shaders.dummy_white_gpu_image.sampler,
|
||||||
|
),
|
||||||
|
|image_handle| {
|
||||||
|
let gpu_image = gpu_images.get(image_handle).expect("only materials with valid textures should be drawn");
|
||||||
|
(&gpu_image.texture_view, &gpu_image.sampler)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn queue_meshes(
|
pub fn queue_meshes(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
draw_functions: Res<DrawFunctions>,
|
draw_functions: Res<DrawFunctions>,
|
||||||
|
@ -316,7 +489,14 @@ pub fn queue_meshes(
|
||||||
mut light_meta: ResMut<LightMeta>,
|
mut light_meta: ResMut<LightMeta>,
|
||||||
view_meta: Res<ViewMeta>,
|
view_meta: Res<ViewMeta>,
|
||||||
mut extracted_meshes: ResMut<ExtractedMeshes>,
|
mut extracted_meshes: ResMut<ExtractedMeshes>,
|
||||||
mut views: Query<(Entity, &ViewLights, &mut RenderPhase<Transparent3dPhase>)>,
|
gpu_images: Res<RenderAssets<Image>>,
|
||||||
|
render_materials: Res<RenderAssets<StandardMaterial>>,
|
||||||
|
mut views: Query<(
|
||||||
|
Entity,
|
||||||
|
&ExtractedView,
|
||||||
|
&ViewLights,
|
||||||
|
&mut RenderPhase<Transparent3dPhase>,
|
||||||
|
)>,
|
||||||
mut view_light_shadow_phases: Query<&mut RenderPhase<ShadowPhase>>,
|
mut view_light_shadow_phases: Query<&mut RenderPhase<ShadowPhase>>,
|
||||||
) {
|
) {
|
||||||
let mesh_meta = mesh_meta.into_inner();
|
let mesh_meta = mesh_meta.into_inner();
|
||||||
|
@ -346,7 +526,7 @@ pub fn queue_meshes(
|
||||||
layout: &pbr_shaders.mesh_layout,
|
layout: &pbr_shaders.mesh_layout,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
for (entity, view_lights, mut transparent_phase) in views.iter_mut() {
|
for (entity, view, view_lights, mut transparent_phase) in views.iter_mut() {
|
||||||
// TODO: cache this?
|
// TODO: cache this?
|
||||||
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
entries: &[
|
entries: &[
|
||||||
|
@ -379,30 +559,107 @@ pub fn queue_meshes(
|
||||||
mesh_meta.mesh_draw_info.clear();
|
mesh_meta.mesh_draw_info.clear();
|
||||||
mesh_meta.material_bind_groups.next_frame();
|
mesh_meta.material_bind_groups.next_frame();
|
||||||
|
|
||||||
|
let view_matrix = view.transform.compute_matrix();
|
||||||
|
let view_row_2 = view_matrix.row(2);
|
||||||
for (i, mesh) in extracted_meshes.meshes.iter_mut().enumerate() {
|
for (i, mesh) in extracted_meshes.meshes.iter_mut().enumerate() {
|
||||||
let material_bind_group_key = mesh_meta.material_bind_groups.get_or_insert_with(
|
let gpu_material = &render_materials
|
||||||
mesh.material_buffer.id(),
|
.get(&mesh.material_handle)
|
||||||
|| {
|
.expect("Failed to get StandardMaterial PreparedAsset");
|
||||||
render_device.create_bind_group(&BindGroupDescriptor {
|
let material_bind_group_key =
|
||||||
entries: &[BindGroupEntry {
|
mesh_meta
|
||||||
binding: 0,
|
.material_bind_groups
|
||||||
resource: mesh.material_buffer.as_entire_binding(),
|
.get_or_insert_with(gpu_material.buffer.id(), || {
|
||||||
}],
|
let (base_color_texture_view, base_color_sampler) =
|
||||||
label: None,
|
image_handle_to_view_sampler(
|
||||||
layout: &pbr_shaders.material_layout,
|
&pbr_shaders,
|
||||||
})
|
&gpu_images,
|
||||||
},
|
&gpu_material.base_color_texture,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let (emissive_texture_view, emissive_sampler) =
|
||||||
|
image_handle_to_view_sampler(
|
||||||
|
&pbr_shaders,
|
||||||
|
&gpu_images,
|
||||||
|
&gpu_material.emissive_texture,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (metallic_roughness_texture_view, metallic_roughness_sampler) =
|
||||||
|
image_handle_to_view_sampler(
|
||||||
|
&pbr_shaders,
|
||||||
|
&gpu_images,
|
||||||
|
&gpu_material.metallic_roughness_texture,
|
||||||
|
);
|
||||||
|
let (occlusion_texture_view, occlusion_sampler) =
|
||||||
|
image_handle_to_view_sampler(
|
||||||
|
&pbr_shaders,
|
||||||
|
&gpu_images,
|
||||||
|
&gpu_material.occlusion_texture,
|
||||||
|
);
|
||||||
|
render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
|
entries: &[
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: gpu_material.buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: BindingResource::TextureView(
|
||||||
|
&base_color_texture_view,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 2,
|
||||||
|
resource: BindingResource::Sampler(&base_color_sampler),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 3,
|
||||||
|
resource: BindingResource::TextureView(&emissive_texture_view),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 4,
|
||||||
|
resource: BindingResource::Sampler(&emissive_sampler),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 5,
|
||||||
|
resource: BindingResource::TextureView(
|
||||||
|
&metallic_roughness_texture_view,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 6,
|
||||||
|
resource: BindingResource::Sampler(&metallic_roughness_sampler),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 7,
|
||||||
|
resource: BindingResource::TextureView(&occlusion_texture_view),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 8,
|
||||||
|
resource: BindingResource::Sampler(&occlusion_sampler),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
layout: &pbr_shaders.material_layout,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
mesh_meta.mesh_draw_info.push(MeshDrawInfo {
|
mesh_meta.mesh_draw_info.push(MeshDrawInfo {
|
||||||
material_bind_group_key,
|
material_bind_group_key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NOTE: row 2 of the view matrix dotted with column 3 of the model matrix
|
||||||
|
// gives the z component of translation of the mesh in view space
|
||||||
|
let mesh_z = view_row_2.dot(mesh.transform.col(3));
|
||||||
|
// FIXME: Switch from usize to u64 for portability and use sort key encoding
|
||||||
|
// similar to https://realtimecollisiondetection.net/blog/?p=86 as appropriate
|
||||||
|
// FIXME: What is the best way to map from view space z to a number of bits of unsigned integer?
|
||||||
|
let sort_key = (((mesh_z * 1000.0) as usize) << 10)
|
||||||
|
| (material_bind_group_key.index() & ((1 << 10) - 1));
|
||||||
// TODO: currently there is only "transparent phase". this should pick transparent vs opaque according to the mesh material
|
// TODO: currently there is only "transparent phase". this should pick transparent vs opaque according to the mesh material
|
||||||
transparent_phase.add(Drawable {
|
transparent_phase.add(Drawable {
|
||||||
draw_function: draw_pbr,
|
draw_function: draw_pbr,
|
||||||
draw_key: i,
|
draw_key: i,
|
||||||
sort_key: material_bind_group_key.index(), // TODO: sort back-to-front, sorting by material for now
|
sort_key,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,6 +756,7 @@ impl Draw for DrawPbr {
|
||||||
let mesh_draw_info = &mesh_meta.mesh_draw_info[draw_key];
|
let mesh_draw_info = &mesh_meta.mesh_draw_info[draw_key];
|
||||||
pass.set_bind_group(
|
pass.set_bind_group(
|
||||||
2,
|
2,
|
||||||
|
// &mesh_meta.material_bind_groups[sort_key & ((1 << 10) - 1)],
|
||||||
&mesh_meta.material_bind_groups[mesh_draw_info.material_bind_group_key],
|
&mesh_meta.material_bind_groups[mesh_draw_info.material_bind_group_key],
|
||||||
&[],
|
&[],
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,22 +1,74 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
layout(location = 0) in vec4 v_WorldPosition;
|
layout(location = 0) in vec4 v_WorldPosition;
|
||||||
layout(location = 1) in vec3 v_WorldNormal;
|
layout(location = 1) in vec3 v_WorldNormal;
|
||||||
layout(location = 2) in vec2 v_Uv;
|
layout(location = 2) in vec2 v_Uv;
|
||||||
|
|
||||||
layout(location = 0) out vec4 o_Target;
|
layout(location = 0) out vec4 o_Target;
|
||||||
|
|
||||||
struct PointLight {
|
struct OmniLight {
|
||||||
vec4 color;
|
vec4 color;
|
||||||
float range;
|
float range;
|
||||||
float radius;
|
float radius;
|
||||||
vec3 position;
|
vec3 position;
|
||||||
mat4 projection;
|
mat4 view_projection;
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: this must be kept in sync with lights::MAX_LIGHTS
|
// NOTE: this must be kept in sync with the constants defined bevy_pbr2/src/render/light.rs
|
||||||
// TODO: this can be removed if we move to storage buffers for light arrays
|
// TODO: this can be removed if we move to storage buffers for light arrays
|
||||||
const int MAX_POINT_LIGHTS = 10;
|
const int MAX_OMNI_LIGHTS = 10;
|
||||||
|
|
||||||
|
struct StandardMaterial_t {
|
||||||
|
vec4 base_color;
|
||||||
|
vec4 emissive;
|
||||||
|
float perceptual_roughness;
|
||||||
|
float metallic;
|
||||||
|
float reflectance;
|
||||||
|
// 'flags' is a bit field indicating various option. uint is 32 bits so we have up to 32 options.
|
||||||
|
uint flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: These must match those defined in bevy_pbr2/src/material.rs
|
||||||
|
const uint FLAGS_BASE_COLOR_TEXTURE_BIT = (1 << 0);
|
||||||
|
const uint FLAGS_EMISSIVE_TEXTURE_BIT = (1 << 1);
|
||||||
|
const uint FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT = (1 << 2);
|
||||||
|
const uint FLAGS_OCCLUSION_TEXTURE_BIT = (1 << 3);
|
||||||
|
const uint FLAGS_DOUBLE_SIDED_BIT = (1 << 4);
|
||||||
|
const uint FLAGS_UNLIT_BIT = (1 << 5);
|
||||||
|
|
||||||
// View bindings - set 0
|
// View bindings - set 0
|
||||||
layout(set = 0, binding = 0) uniform View {
|
layout(set = 0, binding = 0) uniform View {
|
||||||
|
@ -24,24 +76,30 @@ layout(set = 0, binding = 0) uniform View {
|
||||||
vec3 ViewWorldPosition;
|
vec3 ViewWorldPosition;
|
||||||
};
|
};
|
||||||
layout(std140, set = 0, binding = 1) uniform Lights {
|
layout(std140, set = 0, binding = 1) uniform Lights {
|
||||||
|
vec4 AmbientColor;
|
||||||
uint NumLights;
|
uint NumLights;
|
||||||
PointLight PointLights[MAX_POINT_LIGHTS];
|
OmniLight OmniLights[MAX_OMNI_LIGHTS];
|
||||||
};
|
};
|
||||||
layout(set = 0, binding = 2) uniform texture2DArray t_Shadow;
|
layout(set = 0, binding = 2) uniform texture2DArray t_Shadow;
|
||||||
layout(set = 0, binding = 3) uniform samplerShadow s_Shadow;
|
layout(set = 0, binding = 3) uniform samplerShadow s_Shadow;
|
||||||
|
|
||||||
// Material bindings - set 2
|
// Material bindings - set 2
|
||||||
struct StandardMaterial_t {
|
|
||||||
vec4 color;
|
|
||||||
float roughness;
|
|
||||||
float metallic;
|
|
||||||
float reflectance;
|
|
||||||
vec4 emissive;
|
|
||||||
};
|
|
||||||
layout(set = 2, binding = 0) uniform StandardMaterial {
|
layout(set = 2, binding = 0) uniform StandardMaterial {
|
||||||
StandardMaterial_t Material;
|
StandardMaterial_t Material;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
layout(set = 2, binding = 1) uniform texture2D base_color_texture;
|
||||||
|
layout(set = 2, binding = 2) uniform sampler base_color_sampler;
|
||||||
|
|
||||||
|
layout(set = 2, binding = 3) uniform texture2D emissive_texture;
|
||||||
|
layout(set = 2, binding = 4) uniform sampler emissive_sampler;
|
||||||
|
|
||||||
|
layout(set = 2, binding = 5) uniform texture2D metallic_roughness_texture;
|
||||||
|
layout(set = 2, binding = 6) uniform sampler metallic_roughness_sampler;
|
||||||
|
|
||||||
|
layout(set = 2, binding = 7) uniform texture2D occlusion_texture;
|
||||||
|
layout(set = 2, binding = 8) uniform sampler occlusion_sampler;
|
||||||
|
|
||||||
# define saturate(x) clamp(x, 0.0, 1.0)
|
# define saturate(x) clamp(x, 0.0, 1.0)
|
||||||
const float PI = 3.141592653589793;
|
const float PI = 3.141592653589793;
|
||||||
|
|
||||||
|
@ -200,7 +258,7 @@ vec3 reinhard_extended_luminance(vec3 color, float max_white_l) {
|
||||||
return change_luminance(color, l_new);
|
return change_luminance(color, l_new);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 point_light(PointLight light, float roughness, float NdotV, vec3 N, vec3 V, vec3 R, vec3 F0, vec3 diffuseColor) {
|
vec3 omni_light(OmniLight light, float roughness, float NdotV, vec3 N, vec3 V, vec3 R, vec3 F0, vec3 diffuseColor) {
|
||||||
vec3 light_to_frag = light.position.xyz - v_WorldPosition.xyz;
|
vec3 light_to_frag = light.position.xyz - v_WorldPosition.xyz;
|
||||||
float distance_square = dot(light_to_frag, light_to_frag);
|
float distance_square = dot(light_to_frag, light_to_frag);
|
||||||
float rangeAttenuation =
|
float rangeAttenuation =
|
||||||
|
@ -265,47 +323,101 @@ float fetch_shadow(int light_id, vec4 homogeneous_coords) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color = Material.color;
|
vec4 output_color = Material.base_color;
|
||||||
float metallic = Material.metallic;
|
if ((Material.flags & FLAGS_BASE_COLOR_TEXTURE_BIT) != 0) {
|
||||||
float reflectance = Material.reflectance;
|
output_color *= texture(sampler2D(base_color_texture, base_color_sampler), v_Uv);
|
||||||
float perceptual_roughness = Material.roughness;
|
|
||||||
vec3 emissive = Material.emissive.xyz;
|
|
||||||
vec3 ambient_color = vec3(0.1, 0.1, 0.1);
|
|
||||||
float occlusion = 1.0;
|
|
||||||
|
|
||||||
float roughness = perceptualRoughnessToRoughness(perceptual_roughness);
|
|
||||||
vec3 N = normalize(v_WorldNormal);
|
|
||||||
vec3 V = normalize(ViewWorldPosition.xyz - v_WorldPosition.xyz);
|
|
||||||
vec3 R = reflect(-V, N);
|
|
||||||
// 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) + color.rgb * metallic;
|
|
||||||
|
|
||||||
// Diffuse strength inversely related to metallicity
|
|
||||||
vec3 diffuse_color = color.rgb * (1.0 - metallic);
|
|
||||||
|
|
||||||
vec3 output_color = vec3(0.0);
|
|
||||||
for (int i = 0; i < int(NumLights); ++i) {
|
|
||||||
PointLight light = PointLights[i];
|
|
||||||
vec3 light_contrib = point_light(light, roughness, NdotV, N, V, R, F0, diffuse_color);
|
|
||||||
float shadow = fetch_shadow(i, light.projection * v_WorldPosition);
|
|
||||||
output_color += light_contrib * shadow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 diffuse_ambient = EnvBRDFApprox(diffuse_color, 1.0, NdotV);
|
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
|
||||||
vec3 specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV);
|
if ((Material.flags & FLAGS_UNLIT_BIT) == 0) {
|
||||||
|
// TODO use .a for exposure compensation in HDR
|
||||||
|
vec4 emissive = Material.emissive;
|
||||||
|
if ((Material.flags & FLAGS_EMISSIVE_TEXTURE_BIT) != 0) {
|
||||||
|
emissive.rgb *= texture(sampler2D(emissive_texture, emissive_sampler), v_Uv).rgb;
|
||||||
|
}
|
||||||
|
|
||||||
output_color += (diffuse_ambient + specular_ambient) * ambient_color * occlusion;
|
// calculate non-linear roughness from linear perceptualRoughness
|
||||||
output_color += emissive * color.a;
|
float metallic = Material.metallic;
|
||||||
|
float perceptual_roughness = Material.perceptual_roughness;
|
||||||
|
if ((Material.flags & FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0) {
|
||||||
|
vec4 metallic_roughness = texture(sampler2D(metallic_roughness_texture, metallic_roughness_sampler), v_Uv);
|
||||||
|
// Sampling from GLTF standard channels for now
|
||||||
|
metallic *= metallic_roughness.b;
|
||||||
|
perceptual_roughness *= metallic_roughness.g;
|
||||||
|
}
|
||||||
|
float roughness = perceptualRoughnessToRoughness(perceptual_roughness);
|
||||||
|
|
||||||
// tone_mapping
|
float occlusion = 1.0;
|
||||||
output_color = reinhard_luminance(output_color);
|
if ((Material.flags & FLAGS_OCCLUSION_TEXTURE_BIT) != 0) {
|
||||||
// Gamma correction.
|
occlusion = texture(sampler2D(occlusion_texture, occlusion_sampler), v_Uv).r;
|
||||||
// Not needed with sRGB buffer
|
}
|
||||||
// output_color = pow(output_color, vec3(1.0 / 2.2));
|
|
||||||
|
|
||||||
o_Target = vec4(output_color, 1.0);
|
vec3 N = normalize(v_WorldNormal);
|
||||||
|
|
||||||
|
// FIXME: Normal maps need an additional vertex attribute and vertex stage output/fragment stage input
|
||||||
|
// Just use a separate shader for lit with normal maps?
|
||||||
|
// # ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||||
|
// vec3 T = normalize(v_WorldTangent.xyz);
|
||||||
|
// vec3 B = cross(N, T) * v_WorldTangent.w;
|
||||||
|
// # endif
|
||||||
|
|
||||||
|
if ((Material.flags & FLAGS_DOUBLE_SIDED_BIT) != 0) {
|
||||||
|
N = gl_FrontFacing ? N : -N;
|
||||||
|
// # ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||||
|
// T = gl_FrontFacing ? T : -T;
|
||||||
|
// B = gl_FrontFacing ? B : -B;
|
||||||
|
// # endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// # ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||||
|
// mat3 TBN = mat3(T, B, N);
|
||||||
|
// N = TBN * normalize(texture(sampler2D(normal_map, normal_map_sampler), v_Uv).rgb * 2.0 - 1.0);
|
||||||
|
// # endif
|
||||||
|
|
||||||
|
vec3 V;
|
||||||
|
if (ViewProj[3][3] != 1.0) { // If the projection is not orthographic
|
||||||
|
// Only valid for a perpective projection
|
||||||
|
V = normalize(ViewWorldPosition.xyz - v_WorldPosition.xyz);
|
||||||
|
} else {
|
||||||
|
// Ortho view vec
|
||||||
|
V = normalize(vec3(-ViewProj[0][2], -ViewProj[1][2], -ViewProj[2][2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
float reflectance = Material.reflectance;
|
||||||
|
vec3 F0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + output_color.rgb * metallic;
|
||||||
|
|
||||||
|
// Diffuse strength inversely related to metallicity
|
||||||
|
vec3 diffuse_color = output_color.rgb * (1.0 - metallic);
|
||||||
|
|
||||||
|
vec3 R = reflect(-V, N);
|
||||||
|
|
||||||
|
// accumulate color
|
||||||
|
vec3 light_accum = vec3(0.0);
|
||||||
|
for (int i = 0; i < int(NumLights); ++i) {
|
||||||
|
OmniLight light = OmniLights[i];
|
||||||
|
vec3 light_contrib = omni_light(light, roughness, NdotV, N, V, R, F0, diffuse_color);
|
||||||
|
float shadow = fetch_shadow(i, light.view_projection * v_WorldPosition);
|
||||||
|
light_accum += light_contrib * shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 diffuse_ambient = EnvBRDFApprox(diffuse_color, 1.0, NdotV);
|
||||||
|
vec3 specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV);
|
||||||
|
|
||||||
|
output_color.rgb = light_accum;
|
||||||
|
output_color.rgb += (diffuse_ambient + specular_ambient) * AmbientColor.rgb * 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
o_Target = output_color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,10 @@ layout(set = 1, binding = 0) uniform MeshTransform {
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
v_Uv = Vertex_Uv;
|
v_Uv = Vertex_Uv;
|
||||||
v_WorldPosition = Model * vec4(Vertex_Position, 1.0);
|
vec4 world_position = Model * vec4(Vertex_Position, 1.0);
|
||||||
|
v_WorldPosition = world_position;
|
||||||
|
// FIXME: The inverse transpose of the model matrix should be used to correctly handle scaling
|
||||||
|
// of normals
|
||||||
v_WorldNormal = mat3(Model) * Vertex_Normal;
|
v_WorldNormal = mat3(Model) * Vertex_Normal;
|
||||||
gl_Position = ViewProj * v_WorldPosition;
|
gl_Position = ViewProj * world_position;
|
||||||
}
|
}
|
||||||
|
|
|
@ -544,22 +544,18 @@ impl RenderAsset for Mesh {
|
||||||
_render_queue: &RenderQueue,
|
_render_queue: &RenderQueue,
|
||||||
) -> Self::PreparedAsset {
|
) -> Self::PreparedAsset {
|
||||||
let vertex_buffer_data = mesh.get_vertex_buffer_data();
|
let vertex_buffer_data = mesh.get_vertex_buffer_data();
|
||||||
let vertex_buffer = Buffer::from(render_device.create_buffer_with_data(
|
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||||
&BufferInitDescriptor {
|
usage: BufferUsage::VERTEX,
|
||||||
usage: BufferUsage::VERTEX,
|
label: None,
|
||||||
label: None,
|
contents: &vertex_buffer_data,
|
||||||
contents: &vertex_buffer_data,
|
});
|
||||||
},
|
|
||||||
));
|
|
||||||
|
|
||||||
let index_info = mesh.get_index_buffer_bytes().map(|data| GpuIndexInfo {
|
let index_info = mesh.get_index_buffer_bytes().map(|data| GpuIndexInfo {
|
||||||
buffer: Buffer::from(render_device.create_buffer_with_data(
|
buffer: render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||||
&BufferInitDescriptor {
|
usage: BufferUsage::INDEX,
|
||||||
usage: BufferUsage::INDEX,
|
contents: &data,
|
||||||
contents: &data,
|
label: None,
|
||||||
label: None,
|
}),
|
||||||
},
|
|
||||||
)),
|
|
||||||
count: mesh.indices().unwrap().len() as u32,
|
count: mesh.indices().unwrap().len() as u32,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use crate::{
|
use crate::mesh::{Indices, Mesh};
|
||||||
mesh::{Indices, Mesh},
|
|
||||||
};
|
|
||||||
use bevy_math::{Vec2, Vec3};
|
use bevy_math::{Vec2, Vec3};
|
||||||
use wgpu::PrimitiveTopology;
|
use wgpu::PrimitiveTopology;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use bevy_utils::Uuid;
|
use bevy_utils::Uuid;
|
||||||
use std::{ops::{Bound, Deref, RangeBounds}, sync::Arc};
|
use std::{
|
||||||
|
ops::{Bound, Deref, RangeBounds},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
pub struct BufferId(Uuid);
|
pub struct BufferId(Uuid);
|
||||||
|
|
|
@ -21,11 +21,12 @@ pub use wgpu::{
|
||||||
BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent, BlendFactor,
|
BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent, BlendFactor,
|
||||||
BlendOperation, BlendState, BufferAddress, BufferBindingType, BufferSize, BufferUsage,
|
BlendOperation, BlendState, BufferAddress, BufferBindingType, BufferSize, BufferUsage,
|
||||||
ColorTargetState, ColorWrite, CompareFunction, DepthBiasState, DepthStencilState, Extent3d,
|
ColorTargetState, ColorWrite, CompareFunction, DepthBiasState, DepthStencilState, Extent3d,
|
||||||
Face, FilterMode, FragmentState, FrontFace, IndexFormat, InputStepMode, MultisampleState,
|
Face, FilterMode, FragmentState, FrontFace, IndexFormat, InputStepMode, LoadOp,
|
||||||
PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology,
|
MultisampleState, Operations, PipelineLayoutDescriptor, PolygonMode, PrimitiveState,
|
||||||
RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, SamplerDescriptor,
|
PrimitiveTopology, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
|
||||||
ShaderFlags, ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStage, StencilFaceState,
|
RenderPassDescriptor, RenderPipelineDescriptor, SamplerDescriptor, ShaderFlags, ShaderModule,
|
||||||
StencilState, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat,
|
ShaderModuleDescriptor, ShaderSource, ShaderStage, StencilFaceState, StencilState,
|
||||||
TextureSampleType, TextureUsage, TextureViewDescriptor, TextureViewDimension, VertexAttribute,
|
TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType,
|
||||||
VertexBufferLayout, VertexFormat, VertexState,RenderPassDepthStencilAttachment, Operations, LoadOp
|
TextureUsage, TextureViewDescriptor, TextureViewDimension, VertexAttribute, VertexBufferLayout,
|
||||||
|
VertexFormat, VertexState,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
||||||
use bevy_reflect::{TypeUuid, Uuid};
|
use bevy_reflect::{TypeUuid, Uuid};
|
||||||
use bevy_utils::{tracing::error, BoxedFuture};
|
use bevy_utils::{tracing::error, BoxedFuture};
|
||||||
use wgpu::ShaderStage;
|
|
||||||
use std::marker::Copy;
|
use std::marker::Copy;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use wgpu::ShaderStage;
|
||||||
|
|
||||||
/// An error that occurs during shader handling.
|
/// An error that occurs during shader handling.
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
|
@ -7,18 +7,15 @@ mod texture_cache;
|
||||||
|
|
||||||
pub(crate) mod image_texture_conversion;
|
pub(crate) mod image_texture_conversion;
|
||||||
|
|
||||||
|
pub use self::image::*;
|
||||||
#[cfg(feature = "hdr")]
|
#[cfg(feature = "hdr")]
|
||||||
pub use hdr_texture_loader::*;
|
pub use hdr_texture_loader::*;
|
||||||
pub use self::image::*;
|
|
||||||
pub use image_texture_loader::*;
|
pub use image_texture_loader::*;
|
||||||
pub use texture_cache::*;
|
pub use texture_cache::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{render_asset::RenderAssetPlugin, RenderStage};
|
||||||
render_asset::RenderAssetPlugin,
|
|
||||||
RenderStage,
|
|
||||||
};
|
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{AddAsset};
|
use bevy_asset::AddAsset;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
// TODO: replace Texture names with Image names?
|
// TODO: replace Texture names with Image names?
|
||||||
|
|
Loading…
Add table
Reference in a new issue