mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +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"
|
||||
path = "examples/3d/pbr.rs"
|
||||
|
||||
[[example]]
|
||||
name = "pbr_pipelined"
|
||||
path = "examples/3d/pbr_pipelined.rs"
|
||||
|
||||
[[example]]
|
||||
name = "render_to_texture"
|
||||
path = "examples/3d/render_to_texture.rs"
|
||||
|
@ -178,6 +182,10 @@ path = "examples/3d/spawner.rs"
|
|||
name = "texture"
|
||||
path = "examples/3d/texture.rs"
|
||||
|
||||
[[example]]
|
||||
name = "texture_pipelined"
|
||||
path = "examples/3d/texture_pipelined.rs"
|
||||
|
||||
[[example]]
|
||||
name = "update_gltf_scene"
|
||||
path = "examples/3d/update_gltf_scene.rs"
|
||||
|
|
|
@ -160,7 +160,7 @@ pub struct EventReader<'s, 'w, T: Component> {
|
|||
#[derive(SystemParam)]
|
||||
pub struct EventWriter<'s, 'w, T: Component> {
|
||||
events: ResMut<'w, Events<T>>,
|
||||
// TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes?
|
||||
// TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes?
|
||||
#[system_param(ignore)]
|
||||
marker: PhantomData<&'s usize>,
|
||||
}
|
||||
|
|
|
@ -87,7 +87,10 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||
|
||||
/// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
|
||||
#[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
|
||||
Param::Fetch: ReadOnlySystemParamFetch,
|
||||
{
|
||||
|
|
|
@ -372,7 +372,7 @@ where
|
|||
pub fn get_mut(
|
||||
&mut self,
|
||||
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.
|
||||
// same-system queries have runtime borrow checks when they conflict
|
||||
unsafe {
|
||||
|
@ -406,7 +406,10 @@ where
|
|||
/// entity does not have the given component type or if the given component type does not match
|
||||
/// this query.
|
||||
#[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 entity_ref = world
|
||||
.get_entity(entity)
|
||||
|
|
|
@ -146,7 +146,8 @@ where
|
|||
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
|
||||
F::Fetch: FilterFetch,
|
||||
{
|
||||
|
|
|
@ -72,7 +72,7 @@ impl Command for PushChildren {
|
|||
}
|
||||
|
||||
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);
|
||||
self.push_children.children.push(e.id());
|
||||
e
|
||||
|
|
|
@ -4,7 +4,7 @@ use bevy::{
|
|||
ecs::prelude::*,
|
||||
input::Input,
|
||||
math::Vec3,
|
||||
pbr2::{PbrBundle, PointLightBundle, StandardMaterial},
|
||||
pbr2::{OmniLightBundle, PbrBundle, StandardMaterial},
|
||||
prelude::{App, Assets, KeyCode, Transform},
|
||||
render2::{
|
||||
camera::PerspectiveCameraBundle,
|
||||
|
@ -36,8 +36,8 @@ fn setup(
|
|||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
|
||||
material: materials.add(StandardMaterial {
|
||||
color: Color::INDIGO,
|
||||
roughness: 1.0,
|
||||
base_color: Color::INDIGO,
|
||||
perceptual_roughness: 1.0,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
|
@ -47,8 +47,8 @@ fn setup(
|
|||
.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||
material: materials.add(StandardMaterial {
|
||||
color: Color::PINK,
|
||||
roughness: 0.0,
|
||||
base_color: Color::PINK,
|
||||
perceptual_roughness: 0.0,
|
||||
metallic: 1.0,
|
||||
reflectance: 1.0,
|
||||
..Default::default()
|
||||
|
@ -65,7 +65,7 @@ fn setup(
|
|||
..Default::default()
|
||||
})),
|
||||
material: materials.add(StandardMaterial {
|
||||
color: Color::LIME_GREEN,
|
||||
base_color: Color::LIME_GREEN,
|
||||
..Default::default()
|
||||
}),
|
||||
transform: Transform::from_xyz(1.5, 1.0, 1.5),
|
||||
|
@ -73,7 +73,7 @@ fn setup(
|
|||
})
|
||||
.insert(Movable);
|
||||
// light
|
||||
commands.spawn_bundle(PointLightBundle {
|
||||
commands.spawn_bundle(OmniLightBundle {
|
||||
transform: Transform::from_xyz(5.0, 8.0, 2.0),
|
||||
..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" }
|
||||
|
||||
# other
|
||||
bitflags = "1.2"
|
||||
# direct dependency required for derive macro
|
||||
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_ecs::bundle::Bundle;
|
||||
use bevy_render2::mesh::Mesh;
|
||||
|
@ -25,8 +25,8 @@ impl Default for PbrBundle {
|
|||
|
||||
/// A component bundle for "light" entities
|
||||
#[derive(Debug, Bundle, Default)]
|
||||
pub struct PointLightBundle {
|
||||
pub point_light: PointLight,
|
||||
pub struct OmniLightBundle {
|
||||
pub omni_light: OmniLight,
|
||||
pub transform: Transform,
|
||||
pub global_transform: GlobalTransform,
|
||||
}
|
||||
|
|
|
@ -28,7 +28,8 @@ pub struct PbrPlugin;
|
|||
|
||||
impl Plugin for PbrPlugin {
|
||||
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);
|
||||
render_app
|
||||
|
@ -46,6 +47,8 @@ impl Plugin for PbrPlugin {
|
|||
RenderStage::PhaseSort,
|
||||
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::<ShadowShaders>()
|
||||
.init_resource::<MeshMeta>()
|
||||
|
|
|
@ -2,19 +2,19 @@ use bevy_ecs::reflect::ReflectComponent;
|
|||
use bevy_reflect::Reflect;
|
||||
use bevy_render2::color::Color;
|
||||
|
||||
/// A point light
|
||||
/// An omnidirectional light
|
||||
#[derive(Debug, Clone, Copy, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct PointLight {
|
||||
pub struct OmniLight {
|
||||
pub color: Color,
|
||||
pub intensity: f32,
|
||||
pub range: f32,
|
||||
pub radius: f32,
|
||||
}
|
||||
|
||||
impl Default for PointLight {
|
||||
impl Default for OmniLight {
|
||||
fn default() -> Self {
|
||||
PointLight {
|
||||
OmniLight {
|
||||
color: Color::rgb(1.0, 1.0, 1.0),
|
||||
intensity: 200.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_asset::{AddAsset, AssetEvent, Assets};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::{AddAsset, Handle};
|
||||
use bevy_math::Vec4;
|
||||
use bevy_reflect::TypeUuid;
|
||||
use bevy_render2::{
|
||||
color::Color,
|
||||
render_asset::{RenderAsset, RenderAssetPlugin},
|
||||
render_resource::{Buffer, BufferInitDescriptor, BufferUsage},
|
||||
renderer::RenderDevice,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::Image,
|
||||
};
|
||||
use bevy_utils::HashSet;
|
||||
use crevice::std140::{AsStd140, Std140};
|
||||
|
||||
// TODO: this shouldn't live in the StandardMaterial type
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StandardMaterialGpuData {
|
||||
pub buffer: Buffer,
|
||||
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.frag!
|
||||
bitflags::bitflags! {
|
||||
#[repr(transparent)]
|
||||
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
|
||||
|
@ -23,46 +32,55 @@ pub struct StandardMaterialGpuData {
|
|||
#[uuid = "7494888b-c082-457b-aacf-517228cc0c22"]
|
||||
pub struct StandardMaterial {
|
||||
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
||||
/// in between.
|
||||
pub color: Color,
|
||||
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
|
||||
/// Defaults to minimum of 0.089
|
||||
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,
|
||||
/// in between. If used together with a base_color_texture, this is factored into the final
|
||||
/// base color as `base_color * base_color_texture_value`
|
||||
pub base_color: Color,
|
||||
pub base_color_texture: Option<Handle<Image>>,
|
||||
// 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: Color,
|
||||
pub gpu_data: Option<StandardMaterialGpuData>,
|
||||
}
|
||||
|
||||
impl StandardMaterial {
|
||||
pub fn gpu_data(&self) -> Option<&StandardMaterialGpuData> {
|
||||
self.gpu_data.as_ref()
|
||||
}
|
||||
pub emissive_texture: Option<Handle<Image>>,
|
||||
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
|
||||
/// Defaults to minimum of 0.089
|
||||
/// If used together with a roughness/metallic texture, this is factored into the final base
|
||||
/// color as `roughness * roughness_texture_value`
|
||||
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 {
|
||||
fn default() -> Self {
|
||||
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
|
||||
// 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
|
||||
// calculations used. Although technically for 32-bit floats, 0.045 could be
|
||||
// used.
|
||||
roughness: 0.089,
|
||||
perceptual_roughness: 0.089,
|
||||
// Few materials are purely dielectric or metallic
|
||||
// This is just a default for mostly-dielectric
|
||||
metallic: 0.01,
|
||||
metallic_roughness_texture: None,
|
||||
// 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
|
||||
reflectance: 0.5,
|
||||
emissive: Color::BLACK,
|
||||
gpu_data: None,
|
||||
occlusion_texture: None,
|
||||
double_sided: false,
|
||||
unlit: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +88,16 @@ impl Default for StandardMaterial {
|
|||
impl From<Color> for StandardMaterial {
|
||||
fn from(color: Color) -> Self {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +107,10 @@ impl From<Color> for StandardMaterial {
|
|||
pub struct StandardMaterialUniformData {
|
||||
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
||||
/// 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
|
||||
/// Defaults to minimum of 0.089
|
||||
pub roughness: f32,
|
||||
|
@ -89,71 +119,81 @@ pub struct StandardMaterialUniformData {
|
|||
/// 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
|
||||
// Might be used in the future for exposure correction in HDR
|
||||
pub emissive: Vec4,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
pub struct StandardMaterialPlugin;
|
||||
|
||||
impl Plugin for StandardMaterialPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_asset::<StandardMaterial>().add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
standard_material_resource_system.system(),
|
||||
);
|
||||
app.add_plugin(RenderAssetPlugin::<StandardMaterial>::default())
|
||||
.add_asset::<StandardMaterial>();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn standard_material_resource_system(
|
||||
render_device: Res<RenderDevice>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut material_events: EventReader<AssetEvent<StandardMaterial>>,
|
||||
) {
|
||||
let mut changed_materials = HashSet::default();
|
||||
for event in material_events.iter() {
|
||||
match event {
|
||||
AssetEvent::Created { ref handle } => {
|
||||
changed_materials.insert(handle.clone_weak());
|
||||
}
|
||||
AssetEvent::Modified { ref handle } => {
|
||||
changed_materials.insert(handle.clone_weak());
|
||||
// TODO: uncomment this to support mutated materials
|
||||
// remove_current_material_resources(render_resource_context, handle, &mut materials);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GpuStandardMaterial {
|
||||
pub buffer: Buffer,
|
||||
// FIXME: image handles feel unnecessary here but the extracted asset is discarded
|
||||
pub base_color_texture: Option<Handle<Image>>,
|
||||
pub emissive_texture: Option<Handle<Image>>,
|
||||
pub metallic_roughness_texture: Option<Handle<Image>>,
|
||||
pub occlusion_texture: Option<Handle<Image>>,
|
||||
}
|
||||
|
||||
impl RenderAsset for StandardMaterial {
|
||||
type ExtractedAsset = StandardMaterial;
|
||||
type PreparedAsset = GpuStandardMaterial;
|
||||
|
||||
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
// update changed material data
|
||||
for changed_material_handle in changed_materials.iter() {
|
||||
if let Some(material) = materials.get_mut(changed_material_handle) {
|
||||
// TODO: this avoids creating new materials each frame because storing gpu data in the material flags it as
|
||||
// modified. this prevents hot reloading and therefore can't be used in an actual impl.
|
||||
if material.gpu_data.is_some() {
|
||||
continue;
|
||||
}
|
||||
fn prepare_asset(
|
||||
material: Self::ExtractedAsset,
|
||||
render_device: &RenderDevice,
|
||||
_render_queue: &RenderQueue,
|
||||
) -> Self::PreparedAsset {
|
||||
let mut flags = StandardMaterialFlags::NONE;
|
||||
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 {
|
||||
color: material.color.into(),
|
||||
roughness: material.roughness,
|
||||
metallic: material.metallic,
|
||||
reflectance: material.reflectance,
|
||||
emissive: material.emissive.into(),
|
||||
};
|
||||
let value_std140 = value.as_std140();
|
||||
|
||||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
label: None,
|
||||
usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
|
||||
contents: value_std140.as_bytes(),
|
||||
});
|
||||
|
||||
material.gpu_data = Some(StandardMaterialGpuData { buffer });
|
||||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
label: None,
|
||||
usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
|
||||
contents: value_std140.as_bytes(),
|
||||
});
|
||||
GpuStandardMaterial {
|
||||
buffer,
|
||||
base_color_texture: material.base_color_texture,
|
||||
emissive_texture: material.emissive_texture,
|
||||
metallic_roughness_texture: material.metallic_roughness_texture,
|
||||
occlusion_texture: material.occlusion_texture,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_math::{Mat4, Vec3, Vec4};
|
||||
use bevy_render2::{
|
||||
|
@ -17,6 +17,11 @@ use bevy_transform::components::GlobalTransform;
|
|||
use crevice::std140::AsStd140;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
pub struct ExtractedAmbientLight {
|
||||
color: Color,
|
||||
brightness: f32,
|
||||
}
|
||||
|
||||
pub struct ExtractedPointLight {
|
||||
color: Color,
|
||||
intensity: f32,
|
||||
|
@ -38,16 +43,17 @@ pub struct GpuLight {
|
|||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, AsStd140)]
|
||||
pub struct GpuLights {
|
||||
ambient_color: Vec4,
|
||||
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
|
||||
pub const MAX_POINT_LIGHTS: usize = 10;
|
||||
// NOTE: this must be kept in sync MAX_OMNI_LIGHTS in pbr.frag
|
||||
pub const MAX_OMNI_LIGHTS: usize = 10;
|
||||
pub const SHADOW_SIZE: Extent3d = Extent3d {
|
||||
width: 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;
|
||||
|
||||
|
@ -167,8 +173,13 @@ impl FromWorld for ShadowShaders {
|
|||
// TODO: ultimately these could be filtered down to lights relevant to actual views
|
||||
pub fn extract_lights(
|
||||
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() {
|
||||
commands.get_or_spawn(entity).insert(ExtractedPointLight {
|
||||
color: light.color,
|
||||
|
@ -203,6 +214,7 @@ pub fn prepare_lights(
|
|||
render_device: Res<RenderDevice>,
|
||||
mut light_meta: ResMut<LightMeta>,
|
||||
views: Query<Entity, With<RenderPhase<Transparent3dPhase>>>,
|
||||
ambient_light: Res<ExtractedAmbientLight>,
|
||||
lights: Query<&ExtractedPointLight>,
|
||||
) {
|
||||
// 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
|
||||
.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
|
||||
for entity in views.iter() {
|
||||
let light_depth_texture = texture_cache.get(
|
||||
|
@ -227,12 +240,13 @@ pub fn prepare_lights(
|
|||
let mut view_lights = Vec::new();
|
||||
|
||||
let mut gpu_lights = GpuLights {
|
||||
ambient_color: ambient_color.into(),
|
||||
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
|
||||
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 =
|
||||
light_depth_texture
|
||||
.texture
|
||||
|
@ -250,12 +264,13 @@ pub fn prepare_lights(
|
|||
let view_transform = GlobalTransform::from_translation(light.transform.translation)
|
||||
.looking_at(Vec3::default(), Vec3::Y);
|
||||
// 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 {
|
||||
// premultiply color by intensity
|
||||
// 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(),
|
||||
position: light.transform.translation.into(),
|
||||
range: 1.0 / (light.range * light.range),
|
||||
|
|
|
@ -11,15 +11,19 @@ use bevy_render2::{
|
|||
render_graph::{Node, NodeRunError, RenderGraphContext},
|
||||
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
|
||||
render_resource::*,
|
||||
renderer::{RenderContext, RenderDevice},
|
||||
renderer::{RenderContext, RenderDevice, RenderQueue},
|
||||
shader::Shader,
|
||||
texture::BevyDefault,
|
||||
view::{ViewMeta, ViewUniform, ViewUniformOffset},
|
||||
texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo},
|
||||
view::{ExtractedView, ViewMeta, ViewUniform, ViewUniformOffset},
|
||||
};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
use bevy_utils::slab::{FrameSlabMap, FrameSlabMapKey};
|
||||
use crevice::std140::AsStd140;
|
||||
use std::borrow::Cow;
|
||||
use wgpu::{
|
||||
Extent3d, ImageCopyTexture, ImageDataLayout, Origin3d, TextureDimension, TextureFormat,
|
||||
TextureViewDescriptor,
|
||||
};
|
||||
|
||||
use crate::{StandardMaterial, StandardMaterialUniformData};
|
||||
|
||||
|
@ -29,6 +33,8 @@ pub struct PbrShaders {
|
|||
view_layout: BindGroupLayout,
|
||||
material_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
|
||||
|
@ -122,18 +128,104 @@ impl FromWorld for PbrShaders {
|
|||
});
|
||||
|
||||
let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStage::FRAGMENT,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: BufferSize::new(
|
||||
StandardMaterialUniformData::std140_size_static() as u64,
|
||||
),
|
||||
entries: &[
|
||||
BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStage::FRAGMENT,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
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,
|
||||
});
|
||||
|
||||
|
@ -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 {
|
||||
pipeline,
|
||||
view_layout,
|
||||
material_layout,
|
||||
mesh_layout,
|
||||
vertex_shader_module,
|
||||
dummy_white_gpu_image,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -235,8 +368,8 @@ impl FromWorld for PbrShaders {
|
|||
struct ExtractedMesh {
|
||||
transform: Mat4,
|
||||
mesh: Handle<Mesh>,
|
||||
material_buffer: Buffer,
|
||||
transform_binding_offset: u32,
|
||||
material_handle: Handle<StandardMaterial>,
|
||||
}
|
||||
|
||||
pub struct ExtractedMeshes {
|
||||
|
@ -247,6 +380,7 @@ pub fn extract_meshes(
|
|||
mut commands: Commands,
|
||||
meshes: Res<Assets<Mesh>>,
|
||||
materials: Res<Assets<StandardMaterial>>,
|
||||
images: Res<Assets<Image>>,
|
||||
query: Query<(&GlobalTransform, &Handle<Mesh>, &Handle<StandardMaterial>)>,
|
||||
) {
|
||||
let mut extracted_meshes = Vec::new();
|
||||
|
@ -254,15 +388,37 @@ pub fn extract_meshes(
|
|||
if !meshes.contains(mesh_handle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(material) = materials.get(material_handle) {
|
||||
if let Some(material_gpu_data) = &material.gpu_data() {
|
||||
extracted_meshes.push(ExtractedMesh {
|
||||
transform: transform.compute_matrix(),
|
||||
mesh: mesh_handle.clone_weak(),
|
||||
material_buffer: material_gpu_data.buffer.clone(),
|
||||
transform_binding_offset: 0,
|
||||
});
|
||||
if let Some(ref image) = material.base_color_texture {
|
||||
if !images.contains(image) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
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(
|
||||
mut commands: Commands,
|
||||
draw_functions: Res<DrawFunctions>,
|
||||
|
@ -316,7 +489,14 @@ pub fn queue_meshes(
|
|||
mut light_meta: ResMut<LightMeta>,
|
||||
view_meta: Res<ViewMeta>,
|
||||
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>>,
|
||||
) {
|
||||
let mesh_meta = mesh_meta.into_inner();
|
||||
|
@ -346,7 +526,7 @@ pub fn queue_meshes(
|
|||
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?
|
||||
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||
entries: &[
|
||||
|
@ -379,30 +559,107 @@ pub fn queue_meshes(
|
|||
mesh_meta.mesh_draw_info.clear();
|
||||
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() {
|
||||
let material_bind_group_key = mesh_meta.material_bind_groups.get_or_insert_with(
|
||||
mesh.material_buffer.id(),
|
||||
|| {
|
||||
render_device.create_bind_group(&BindGroupDescriptor {
|
||||
entries: &[BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: mesh.material_buffer.as_entire_binding(),
|
||||
}],
|
||||
label: None,
|
||||
layout: &pbr_shaders.material_layout,
|
||||
})
|
||||
},
|
||||
);
|
||||
let gpu_material = &render_materials
|
||||
.get(&mesh.material_handle)
|
||||
.expect("Failed to get StandardMaterial PreparedAsset");
|
||||
let material_bind_group_key =
|
||||
mesh_meta
|
||||
.material_bind_groups
|
||||
.get_or_insert_with(gpu_material.buffer.id(), || {
|
||||
let (base_color_texture_view, base_color_sampler) =
|
||||
image_handle_to_view_sampler(
|
||||
&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 {
|
||||
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
|
||||
transparent_phase.add(Drawable {
|
||||
draw_function: draw_pbr,
|
||||
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];
|
||||
pass.set_bind_group(
|
||||
2,
|
||||
// &mesh_meta.material_bind_groups[sort_key & ((1 << 10) - 1)],
|
||||
&mesh_meta.material_bind_groups[mesh_draw_info.material_bind_group_key],
|
||||
&[],
|
||||
);
|
||||
|
|
|
@ -1,22 +1,74 @@
|
|||
#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 = 1) in vec3 v_WorldNormal;
|
||||
layout(location = 2) in vec2 v_Uv;
|
||||
|
||||
layout(location = 0) out vec4 o_Target;
|
||||
|
||||
struct PointLight {
|
||||
struct OmniLight {
|
||||
vec4 color;
|
||||
float range;
|
||||
float radius;
|
||||
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
|
||||
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
|
||||
layout(set = 0, binding = 0) uniform View {
|
||||
|
@ -24,24 +76,30 @@ layout(set = 0, binding = 0) uniform View {
|
|||
vec3 ViewWorldPosition;
|
||||
};
|
||||
layout(std140, set = 0, binding = 1) uniform Lights {
|
||||
vec4 AmbientColor;
|
||||
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 = 3) uniform samplerShadow s_Shadow;
|
||||
|
||||
// Material bindings - set 2
|
||||
struct StandardMaterial_t {
|
||||
vec4 color;
|
||||
float roughness;
|
||||
float metallic;
|
||||
float reflectance;
|
||||
vec4 emissive;
|
||||
};
|
||||
layout(set = 2, binding = 0) uniform StandardMaterial {
|
||||
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)
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
float distance_square = dot(light_to_frag, light_to_frag);
|
||||
float rangeAttenuation =
|
||||
|
@ -265,47 +323,101 @@ float fetch_shadow(int light_id, vec4 homogeneous_coords) {
|
|||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = Material.color;
|
||||
float metallic = Material.metallic;
|
||||
float reflectance = Material.reflectance;
|
||||
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;
|
||||
vec4 output_color = Material.base_color;
|
||||
if ((Material.flags & FLAGS_BASE_COLOR_TEXTURE_BIT) != 0) {
|
||||
output_color *= texture(sampler2D(base_color_texture, base_color_sampler), v_Uv);
|
||||
}
|
||||
|
||||
vec3 diffuse_ambient = EnvBRDFApprox(diffuse_color, 1.0, NdotV);
|
||||
vec3 specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV);
|
||||
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
|
||||
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;
|
||||
output_color += emissive * color.a;
|
||||
// calculate non-linear roughness from linear perceptualRoughness
|
||||
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
|
||||
output_color = reinhard_luminance(output_color);
|
||||
// Gamma correction.
|
||||
// Not needed with sRGB buffer
|
||||
// output_color = pow(output_color, vec3(1.0 / 2.2));
|
||||
float occlusion = 1.0;
|
||||
if ((Material.flags & FLAGS_OCCLUSION_TEXTURE_BIT) != 0) {
|
||||
occlusion = texture(sampler2D(occlusion_texture, occlusion_sampler), v_Uv).r;
|
||||
}
|
||||
|
||||
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() {
|
||||
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;
|
||||
gl_Position = ViewProj * v_WorldPosition;
|
||||
gl_Position = ViewProj * world_position;
|
||||
}
|
||||
|
|
|
@ -544,22 +544,18 @@ impl RenderAsset for Mesh {
|
|||
_render_queue: &RenderQueue,
|
||||
) -> Self::PreparedAsset {
|
||||
let vertex_buffer_data = mesh.get_vertex_buffer_data();
|
||||
let vertex_buffer = Buffer::from(render_device.create_buffer_with_data(
|
||||
&BufferInitDescriptor {
|
||||
usage: BufferUsage::VERTEX,
|
||||
label: None,
|
||||
contents: &vertex_buffer_data,
|
||||
},
|
||||
));
|
||||
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
usage: BufferUsage::VERTEX,
|
||||
label: None,
|
||||
contents: &vertex_buffer_data,
|
||||
});
|
||||
|
||||
let index_info = mesh.get_index_buffer_bytes().map(|data| GpuIndexInfo {
|
||||
buffer: Buffer::from(render_device.create_buffer_with_data(
|
||||
&BufferInitDescriptor {
|
||||
usage: BufferUsage::INDEX,
|
||||
contents: &data,
|
||||
label: None,
|
||||
},
|
||||
)),
|
||||
buffer: render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
usage: BufferUsage::INDEX,
|
||||
contents: &data,
|
||||
label: None,
|
||||
}),
|
||||
count: mesh.indices().unwrap().len() as u32,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use crate::{
|
||||
mesh::{Indices, Mesh},
|
||||
};
|
||||
use crate::mesh::{Indices, Mesh};
|
||||
use bevy_math::{Vec2, Vec3};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
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)]
|
||||
pub struct BufferId(Uuid);
|
||||
|
@ -41,7 +44,7 @@ impl From<wgpu::Buffer> for Buffer {
|
|||
id: BufferId(Uuid::new_v4()),
|
||||
value: Arc::new(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Buffer {
|
||||
|
@ -79,4 +82,4 @@ impl<'a> Deref for BufferSlice<'a> {
|
|||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,11 +21,12 @@ pub use wgpu::{
|
|||
BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent, BlendFactor,
|
||||
BlendOperation, BlendState, BufferAddress, BufferBindingType, BufferSize, BufferUsage,
|
||||
ColorTargetState, ColorWrite, CompareFunction, DepthBiasState, DepthStencilState, Extent3d,
|
||||
Face, FilterMode, FragmentState, FrontFace, IndexFormat, InputStepMode, MultisampleState,
|
||||
PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology,
|
||||
RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, SamplerDescriptor,
|
||||
ShaderFlags, ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStage, StencilFaceState,
|
||||
StencilState, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat,
|
||||
TextureSampleType, TextureUsage, TextureViewDescriptor, TextureViewDimension, VertexAttribute,
|
||||
VertexBufferLayout, VertexFormat, VertexState,RenderPassDepthStencilAttachment, Operations, LoadOp
|
||||
Face, FilterMode, FragmentState, FrontFace, IndexFormat, InputStepMode, LoadOp,
|
||||
MultisampleState, Operations, PipelineLayoutDescriptor, PolygonMode, PrimitiveState,
|
||||
PrimitiveTopology, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
|
||||
RenderPassDescriptor, RenderPipelineDescriptor, SamplerDescriptor, ShaderFlags, ShaderModule,
|
||||
ShaderModuleDescriptor, ShaderSource, ShaderStage, StencilFaceState, StencilState,
|
||||
TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType,
|
||||
TextureUsage, TextureViewDescriptor, TextureViewDimension, VertexAttribute, VertexBufferLayout,
|
||||
VertexFormat, VertexState,
|
||||
};
|
||||
|
|
|
@ -154,4 +154,4 @@ impl Deref for SwapChainFrame {
|
|||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#[allow(clippy::module_inception)]
|
||||
mod shader;
|
||||
|
||||
pub use shader::*;
|
||||
pub use shader::*;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
||||
use bevy_reflect::{TypeUuid, Uuid};
|
||||
use bevy_utils::{tracing::error, BoxedFuture};
|
||||
use wgpu::ShaderStage;
|
||||
use std::marker::Copy;
|
||||
use thiserror::Error;
|
||||
|
||||
use wgpu::ShaderStage;
|
||||
|
||||
/// An error that occurs during shader handling.
|
||||
#[derive(Error, Debug)]
|
||||
|
|
|
@ -7,18 +7,15 @@ mod texture_cache;
|
|||
|
||||
pub(crate) mod image_texture_conversion;
|
||||
|
||||
pub use self::image::*;
|
||||
#[cfg(feature = "hdr")]
|
||||
pub use hdr_texture_loader::*;
|
||||
pub use self::image::*;
|
||||
pub use image_texture_loader::*;
|
||||
pub use texture_cache::*;
|
||||
|
||||
use crate::{
|
||||
render_asset::RenderAssetPlugin,
|
||||
RenderStage,
|
||||
};
|
||||
use crate::{render_asset::RenderAssetPlugin, RenderStage};
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::{AddAsset};
|
||||
use bevy_asset::AddAsset;
|
||||
use bevy_ecs::prelude::*;
|
||||
|
||||
// TODO: replace Texture names with Image names?
|
||||
|
|
Loading…
Reference in a new issue