mirror of
https://github.com/bevyengine/bevy
synced 2024-12-22 02:53:07 +00:00
b1a91a823f
* 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>
199 lines
7.6 KiB
Rust
199 lines
7.6 KiB
Rust
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, RenderQueue},
|
|
texture::Image,
|
|
};
|
|
use crevice::std140::{AsStd140, Std140};
|
|
|
|
// 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
|
|
/// Standard property values with pictures here https://google.github.io/filament/Material%20Properties.pdf
|
|
#[derive(Debug, Clone, TypeUuid)]
|
|
#[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. 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 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 {
|
|
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.
|
|
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,
|
|
occlusion_texture: None,
|
|
double_sided: false,
|
|
unlit: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Color> for StandardMaterial {
|
|
fn from(color: Color) -> Self {
|
|
StandardMaterial {
|
|
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()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, AsStd140)]
|
|
pub struct StandardMaterialUniformData {
|
|
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
|
/// in between.
|
|
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,
|
|
/// 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,
|
|
pub flags: u32,
|
|
}
|
|
|
|
pub struct StandardMaterialPlugin;
|
|
|
|
impl Plugin for StandardMaterialPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.add_plugin(RenderAssetPlugin::<StandardMaterial>::default())
|
|
.add_asset::<StandardMaterial>();
|
|
}
|
|
}
|
|
|
|
#[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()
|
|
}
|
|
|
|
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 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,
|
|
}
|
|
}
|
|
}
|