improve shader import model (#5703)

# Objective

operate on naga IR directly to improve handling of shader modules.
- give codespan reporting into imported modules
- allow glsl to be used from wgsl and vice-versa

the ultimate objective is to make it possible to 
- provide user hooks for core shader functions (to modify light
behaviour within the standard pbr pipeline, for example)
- make automatic binding slot allocation possible

but ... since this is already big, adds some value and (i think) is at
feature parity with the existing code, i wanted to push this now.

## Solution

i made a crate called naga_oil (https://github.com/robtfm/naga_oil -
unpublished for now, could be part of bevy) which manages modules by
- building each module independantly to naga IR
- creating "header" files for each supported language, which are used to
build dependent modules/shaders
- make final shaders by combining the shader IR with the IR for imported
modules

then integrated this into bevy, replacing some of the existing shader
processing stuff. also reworked examples to reflect this.

## Migration Guide

shaders that don't use `#import` directives should work without changes.

the most notable user-facing difference is that imported
functions/variables/etc need to be qualified at point of use, and
there's no "leakage" of visible stuff into your shader scope from the
imports of your imports, so if you used things imported by your imports,
you now need to import them directly and qualify them.

the current strategy of including/'spreading' `mesh_vertex_output`
directly into a struct doesn't work any more, so these need to be
modified as per the examples (e.g. color_material.wgsl, or many others).
mesh data is assumed to be in bindgroup 2 by default, if mesh data is
bound into bindgroup 1 instead then the shader def `MESH_BINDGROUP_1`
needs to be added to the pipeline shader_defs.
This commit is contained in:
robtfm 2023-06-27 01:29:22 +01:00 committed by GitHub
parent 0f4d16aa3c
commit 10f5c92068
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 950 additions and 3144 deletions

View File

@ -1,6 +1,6 @@
#import bevy_pbr::mesh_types
// The time since startup data is in the globals binding which is part of the mesh_view_bindings import // The time since startup data is in the globals binding which is part of the mesh_view_bindings import
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_view_bindings globals
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> { fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> {
let L = c.x; let L = c.x;
@ -22,12 +22,8 @@ fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> {
); );
} }
struct FragmentInput {
#import bevy_pbr::mesh_vertex_output
}
@fragment @fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> { fn fragment(in: MeshVertexOutput) -> @location(0) vec4<f32> {
let speed = 2.0; let speed = 2.0;
// The globals binding contains various global values like time // The globals binding contains various global values like time
// which is the time since startup in seconds // which is the time since startup in seconds

View File

@ -1,60 +1,51 @@
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_vertex_output MeshVertexOutput
#import bevy_pbr::mesh_bindings #import bevy_pbr::mesh_view_bindings view
#import bevy_pbr::pbr_types STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT
#import bevy_pbr::pbr_types #import bevy_core_pipeline::tonemapping tone_mapping
#import bevy_pbr::utils #import bevy_pbr::pbr_functions as fns
#import bevy_pbr::clustered_forward
#import bevy_pbr::lighting
#import bevy_pbr::shadows
#import bevy_pbr::fog
#import bevy_pbr::pbr_functions
#import bevy_pbr::pbr_ambient
@group(1) @binding(0) @group(1) @binding(0)
var my_array_texture: texture_2d_array<f32>; var my_array_texture: texture_2d_array<f32>;
@group(1) @binding(1) @group(1) @binding(1)
var my_array_texture_sampler: sampler; var my_array_texture_sampler: sampler;
struct FragmentInput {
@builtin(front_facing) is_front: bool,
@builtin(position) frag_coord: vec4<f32>,
#import bevy_pbr::mesh_vertex_output
};
@fragment @fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> { fn fragment(
let layer = i32(in.world_position.x) & 0x3; @builtin(front_facing) is_front: bool,
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
let layer = i32(mesh.world_position.x) & 0x3;
// Prepare a 'processed' StandardMaterial by sampling all textures to resolve // Prepare a 'processed' StandardMaterial by sampling all textures to resolve
// the material members // the material members
var pbr_input: PbrInput = pbr_input_new(); var pbr_input: fns::PbrInput = fns::pbr_input_new();
pbr_input.material.base_color = textureSample(my_array_texture, my_array_texture_sampler, in.uv, layer); pbr_input.material.base_color = textureSample(my_array_texture, my_array_texture_sampler, mesh.uv, layer);
#ifdef VERTEX_COLORS #ifdef VERTEX_COLORS
pbr_input.material.base_color = pbr_input.material.base_color * in.color; pbr_input.material.base_color = pbr_input.material.base_color * mesh.color;
#endif #endif
pbr_input.frag_coord = in.frag_coord; pbr_input.frag_coord = mesh.position;
pbr_input.world_position = in.world_position; pbr_input.world_position = mesh.world_position;
pbr_input.world_normal = prepare_world_normal( pbr_input.world_normal = fns::prepare_world_normal(
in.world_normal, mesh.world_normal,
(pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u, (pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
in.is_front, is_front,
); );
pbr_input.is_orthographic = view.projection[3].w == 1.0; pbr_input.is_orthographic = view.projection[3].w == 1.0;
pbr_input.N = apply_normal_mapping( pbr_input.N = fns::apply_normal_mapping(
pbr_input.material.flags, pbr_input.material.flags,
pbr_input.world_normal, mesh.world_normal,
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
#ifdef STANDARDMATERIAL_NORMAL_MAP #ifdef STANDARDMATERIAL_NORMAL_MAP
in.world_tangent, mesh.world_tangent,
#endif #endif
#endif #endif
in.uv, mesh.uv,
); );
pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); pbr_input.V = fns::calculate_view(mesh.world_position, pbr_input.is_orthographic);
return tone_mapping(pbr(pbr_input)); return tone_mapping(fns::pbr(pbr_input), view.color_grading);
} }

View File

@ -1,4 +1,4 @@
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_vertex_output MeshVertexOutput
#ifdef CUBEMAP_ARRAY #ifdef CUBEMAP_ARRAY
@group(1) @binding(0) @group(1) @binding(0)
@ -13,9 +13,9 @@ var base_color_sampler: sampler;
@fragment @fragment
fn fragment( fn fragment(
#import bevy_pbr::mesh_vertex_output mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
let fragment_position_view_lh = world_position.xyz * vec3<f32>(1.0, 1.0, -1.0); let fragment_position_view_lh = mesh.world_position.xyz * vec3<f32>(1.0, 1.0, -1.0);
return textureSample( return textureSample(
base_color_texture, base_color_texture,
base_color_sampler, base_color_sampler,

View File

@ -1,6 +1,6 @@
#import bevy_sprite::mesh2d_view_bindings #import bevy_sprite::mesh2d_view_bindings globals
#import bevy_sprite::mesh2d_bindings #import bevy_sprite::mesh2d_bindings mesh
#import bevy_sprite::mesh2d_functions #import bevy_sprite::mesh2d_functions mesh2d_position_local_to_clip
struct Vertex { struct Vertex {
@location(0) position: vec3<f32>, @location(0) position: vec3<f32>,

View File

@ -10,7 +10,11 @@ layout(set = 1, binding = 0) uniform CustomMaterial {
layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture; layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture;
layout(set = 1, binding = 2) uniform sampler CustomMaterial_sampler; layout(set = 1, binding = 2) uniform sampler CustomMaterial_sampler;
// wgsl modules can be imported and used in glsl
// FIXME - this doesn't work any more ...
// #import bevy_pbr::pbr_functions as PbrFuncs
void main() { void main() {
// o_Target = PbrFuncs::tone_mapping(Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv));
o_Target = Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv); o_Target = Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv);
} }

View File

@ -1,3 +1,5 @@
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
struct CustomMaterial { struct CustomMaterial {
color: vec4<f32>, color: vec4<f32>,
}; };
@ -11,7 +13,7 @@ var base_color_sampler: sampler;
@fragment @fragment
fn fragment( fn fragment(
#import bevy_pbr::mesh_vertex_output mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
return material.color * textureSample(base_color_texture, base_color_sampler, uv); return material.color * textureSample(base_color_texture, base_color_sampler, mesh.uv);
} }

View File

@ -1,5 +1,6 @@
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_view_bindings view
#import bevy_pbr::utils #import bevy_pbr::mesh_vertex_output MeshVertexOutput
#import bevy_pbr::utils coords_to_viewport_uv
@group(1) @binding(0) @group(1) @binding(0)
var texture: texture_2d<f32>; var texture: texture_2d<f32>;
@ -8,10 +9,9 @@ var texture_sampler: sampler;
@fragment @fragment
fn fragment( fn fragment(
@builtin(position) position: vec4<f32>, mesh: MeshVertexOutput,
#import bevy_pbr::mesh_vertex_output
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
let viewport_uv = coords_to_viewport_uv(position.xy, view.viewport); let viewport_uv = coords_to_viewport_uv(mesh.position.xy, view.viewport);
let color = textureSample(texture, texture_sampler, viewport_uv); let color = textureSample(texture, texture_sampler, viewport_uv);
return color; return color;
} }

View File

@ -1,5 +1,5 @@
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::mesh_bindings #import bevy_pbr::mesh_functions mesh_position_local_to_clip
struct CustomMaterial { struct CustomMaterial {
color: vec4<f32>, color: vec4<f32>,
@ -7,9 +7,6 @@ struct CustomMaterial {
@group(1) @binding(0) @group(1) @binding(0)
var<uniform> material: CustomMaterial; var<uniform> material: CustomMaterial;
// NOTE: Bindings must come before functions that use them!
#import bevy_pbr::mesh_functions
struct Vertex { struct Vertex {
@location(0) position: vec3<f32>, @location(0) position: vec3<f32>,
@location(1) blend_color: vec4<f32>, @location(1) blend_color: vec4<f32>,
@ -23,7 +20,10 @@ struct VertexOutput {
@vertex @vertex
fn vertex(vertex: Vertex) -> VertexOutput { fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0)); out.clip_position = mesh_position_local_to_clip(
mesh.model,
vec4<f32>(vertex.position, 1.0)
);
out.blend_color = vertex.blend_color; out.blend_color = vertex.blend_color;
return out; return out;
} }

View File

@ -1,11 +1,5 @@
#import bevy_pbr::mesh_types #import bevy_pbr::mesh_functions mesh_position_local_to_clip
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_bindings mesh
@group(1) @binding(0)
var<uniform> mesh: Mesh;
// NOTE: Bindings must come before functions that use them!
#import bevy_pbr::mesh_functions
struct Vertex { struct Vertex {
@location(0) position: vec3<f32>, @location(0) position: vec3<f32>,
@ -25,7 +19,10 @@ struct VertexOutput {
fn vertex(vertex: Vertex) -> VertexOutput { fn vertex(vertex: Vertex) -> VertexOutput {
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz; let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
var out: VertexOutput; var out: VertexOutput;
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(position, 1.0)); out.clip_position = mesh_position_local_to_clip(
mesh.model,
vec4<f32>(position, 1.0)
);
out.color = vertex.i_color; out.color = vertex.i_color;
return out; return out;
} }

View File

@ -1,3 +1,5 @@
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
struct LineMaterial { struct LineMaterial {
color: vec4<f32>, color: vec4<f32>,
}; };
@ -7,7 +9,7 @@ var<uniform> material: LineMaterial;
@fragment @fragment
fn fragment( fn fragment(
#import bevy_pbr::mesh_vertex_output mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
return material.color; return material.color;
} }

View File

@ -20,7 +20,7 @@
// As you can see, the triangle ends up bigger than the screen. // As you can see, the triangle ends up bigger than the screen.
// //
// You don't need to worry about this too much since bevy will compute the correct UVs for you. // You don't need to worry about this too much since bevy will compute the correct UVs for you.
#import bevy_core_pipeline::fullscreen_vertex_shader #import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
@group(0) @binding(0) @group(0) @binding(0)
var screen_texture: texture_2d<f32>; var screen_texture: texture_2d<f32>;

View File

@ -1,3 +1,5 @@
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
struct CustomMaterial { struct CustomMaterial {
color: vec4<f32>, color: vec4<f32>,
}; };
@ -7,7 +9,7 @@ var<uniform> material: CustomMaterial;
@fragment @fragment
fn fragment( fn fragment(
#import bevy_pbr::mesh_vertex_output mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
#ifdef IS_RED #ifdef IS_RED
return vec4<f32>(1.0, 0.0, 0.0, 1.0); return vec4<f32>(1.0, 0.0, 0.0, 1.0);

View File

@ -1,6 +1,7 @@
#import bevy_pbr::mesh_types #import bevy_pbr::mesh_types
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_view_bindings globals
#import bevy_pbr::prepass_utils #import bevy_pbr::prepass_utils
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
struct ShowPrepassSettings { struct ShowPrepassSettings {
show_depth: u32, show_depth: u32,
@ -14,23 +15,22 @@ var<uniform> settings: ShowPrepassSettings;
@fragment @fragment
fn fragment( fn fragment(
@builtin(position) frag_coord: vec4<f32>,
#ifdef MULTISAMPLED #ifdef MULTISAMPLED
@builtin(sample_index) sample_index: u32, @builtin(sample_index) sample_index: u32,
#endif #endif
#import bevy_pbr::mesh_vertex_output mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
#ifndef MULTISAMPLED #ifndef MULTISAMPLED
let sample_index = 0u; let sample_index = 0u;
#endif #endif
if settings.show_depth == 1u { if settings.show_depth == 1u {
let depth = prepass_depth(frag_coord, sample_index); let depth = bevy_pbr::prepass_utils::prepass_depth(mesh.position, sample_index);
return vec4(depth, depth, depth, 1.0); return vec4(depth, depth, depth, 1.0);
} else if settings.show_normals == 1u { } else if settings.show_normals == 1u {
let normal = prepass_normal(frag_coord, sample_index); let normal = bevy_pbr::prepass_utils::prepass_normal(mesh.position, sample_index);
return vec4(normal, 1.0); return vec4(normal, 1.0);
} else if settings.show_motion_vectors == 1u { } else if settings.show_motion_vectors == 1u {
let motion_vector = prepass_motion_vector(frag_coord, sample_index); let motion_vector = bevy_pbr::prepass_utils::prepass_motion_vector(mesh.position, sample_index);
return vec4(motion_vector / globals.delta_time, 0.0, 1.0); return vec4(motion_vector / globals.delta_time, 0.0, 1.0);
} }

View File

@ -1,3 +1,5 @@
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
@group(1) @binding(0) @group(1) @binding(0)
var textures: binding_array<texture_2d<f32>>; var textures: binding_array<texture_2d<f32>>;
@group(1) @binding(1) @group(1) @binding(1)
@ -7,11 +9,11 @@ var nearest_sampler: sampler;
@fragment @fragment
fn fragment( fn fragment(
#import bevy_pbr::mesh_vertex_output mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
// Select the texture to sample from using non-uniform uv coordinates // Select the texture to sample from using non-uniform uv coordinates
let coords = clamp(vec2<u32>(uv * 4.0), vec2<u32>(0u), vec2<u32>(3u)); let coords = clamp(vec2<u32>(mesh.uv * 4.0), vec2<u32>(0u), vec2<u32>(3u));
let index = coords.y * 4u + coords.x; let index = coords.y * 4u + coords.x;
let inner_uv = fract(uv * 4.0); let inner_uv = fract(mesh.uv * 4.0);
return textureSample(textures[index], nearest_sampler, inner_uv); return textureSample(textures[index], nearest_sampler, inner_uv);
} }

View File

@ -1,17 +1,12 @@
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_view_bindings
#import bevy_pbr::mesh_bindings #import bevy_pbr::mesh_bindings
#import bevy_pbr::utils #import bevy_pbr::mesh_vertex_output MeshVertexOutput
#import bevy_pbr::utils PI
#ifdef TONEMAP_IN_SHADER #ifdef TONEMAP_IN_SHADER
#import bevy_core_pipeline::tonemapping #import bevy_core_pipeline::tonemapping
#endif #endif
struct FragmentInput {
@builtin(front_facing) is_front: bool,
@builtin(position) frag_coord: vec4<f32>,
#import bevy_pbr::mesh_vertex_output
};
// Sweep across hues on y axis with value from 0.0 to +15EV across x axis // Sweep across hues on y axis with value from 0.0 to +15EV across x axis
// quantized into 24 steps for both axis. // quantized into 24 steps for both axis.
fn color_sweep(uv: vec2<f32>) -> vec3<f32> { fn color_sweep(uv: vec2<f32>) -> vec3<f32> {
@ -47,7 +42,9 @@ fn continuous_hue(uv: vec2<f32>) -> vec3<f32> {
} }
@fragment @fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> { fn fragment(
in: MeshVertexOutput,
) -> @location(0) vec4<f32> {
var uv = in.uv; var uv = in.uv;
var out = vec3(0.0); var out = vec3(0.0);
if uv.y > 0.5 { if uv.y > 0.5 {
@ -58,7 +55,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
} }
var color = vec4(out, 1.0); var color = vec4(out, 1.0);
#ifdef TONEMAP_IN_SHADER #ifdef TONEMAP_IN_SHADER
color = tone_mapping(color); color = tone_mapping(color, bevy_pbr::mesh_view_bindings::view.color_grading);
#endif #endif
return color; return color;
} }

View File

@ -406,7 +406,9 @@ impl AddAsset for App {
} }
} }
/// Loads an internal asset. /// Loads an internal asset from a project source file.
/// the file and its path are passed to the loader function, together with any additional parameters.
/// the resulting asset is stored in the app's asset server.
/// ///
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded /// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
/// using the conventional API. See [`DebugAssetServerPlugin`](crate::debug_asset_server::DebugAssetServerPlugin). /// using the conventional API. See [`DebugAssetServerPlugin`](crate::debug_asset_server::DebugAssetServerPlugin).
@ -427,20 +429,59 @@ macro_rules! load_internal_asset {
); );
} }
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
assets.set_untracked($handle, ($loader)(include_str!($path_str))); assets.set_untracked(
$handle,
($loader)(
include_str!($path_str),
std::path::Path::new(file!())
.parent()
.unwrap()
.join($path_str)
.to_string_lossy(),
)
);
}};
// we can't support params without variadic arguments, so internal assets with additional params can't be hot-reloaded
($app: ident, $handle: ident, $path_str: expr, $loader: expr $(, $param:expr)+) => {{
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
assets.set_untracked(
$handle,
($loader)(
include_str!($path_str),
std::path::Path::new(file!())
.parent()
.unwrap()
.join($path_str)
.to_string_lossy(),
$($param),+
),
);
}}; }};
} }
/// Loads an internal asset. /// Loads an internal asset from a project source file.
/// the file and its path are passed to the loader function, together with any additional parameters.
/// the resulting asset is stored in the app's asset server.
/// ///
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded /// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
/// using the conventional API. See `DebugAssetServerPlugin`. /// using the conventional API. See `DebugAssetServerPlugin`.
#[cfg(not(feature = "debug_asset_server"))] #[cfg(not(feature = "debug_asset_server"))]
#[macro_export] #[macro_export]
macro_rules! load_internal_asset { macro_rules! load_internal_asset {
($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{ ($app: ident, $handle: ident, $path_str: expr, $loader: expr $(, $param:expr)*) => {{
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
assets.set_untracked($handle, ($loader)(include_str!($path_str))); assets.set_untracked(
$handle,
($loader)(
include_str!($path_str),
std::path::Path::new(file!())
.parent()
.unwrap()
.join($path_str)
.to_string_lossy(),
$($param),*
),
);
}}; }};
} }
@ -465,7 +506,18 @@ macro_rules! load_internal_binary_asset {
); );
} }
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
assets.set_untracked($handle, ($loader)(include_bytes!($path_str).as_ref())); assets.set_untracked(
$handle,
($loader)(
include_bytes!($path_str).as_ref(),
std::path::Path::new(file!())
.parent()
.unwrap()
.join($path_str)
.to_string_lossy()
.into(),
),
);
}}; }};
} }
@ -478,7 +530,18 @@ macro_rules! load_internal_binary_asset {
macro_rules! load_internal_binary_asset { macro_rules! load_internal_binary_asset {
($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{ ($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
assets.set_untracked($handle, ($loader)(include_bytes!($path_str).as_ref())); assets.set_untracked(
$handle,
($loader)(
include_bytes!($path_str).as_ref(),
std::path::Path::new(file!())
.parent()
.unwrap()
.join($path_str)
.to_string_lossy()
.into(),
),
);
}}; }};
} }

View File

@ -114,7 +114,7 @@ pub(crate) fn sync_debug_assets<T: Asset + Clone>(
/// If this feels a bit odd ... that's because it is. This was built to improve the UX of the /// If this feels a bit odd ... that's because it is. This was built to improve the UX of the
/// `load_internal_asset` macro. /// `load_internal_asset` macro.
pub fn register_handle_with_loader<A: Asset, T>( pub fn register_handle_with_loader<A: Asset, T>(
_loader: fn(T) -> A, _loader: fn(T, String) -> A,
app: &mut DebugAssetApp, app: &mut DebugAssetApp,
handle: HandleUntyped, handle: HandleUntyped,
file_path: &str, file_path: &str,

View File

@ -1,4 +1,4 @@
#import bevy_core_pipeline::fullscreen_vertex_shader #import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
@group(0) @binding(0) @group(0) @binding(0)
var in_texture: texture_2d<f32>; var in_texture: texture_2d<f32>;

View File

@ -6,7 +6,7 @@
// //
// Tweaks by mrDIMAS - https://github.com/FyroxEngine/Fyrox/blob/master/src/renderer/shaders/fxaa_fs.glsl // Tweaks by mrDIMAS - https://github.com/FyroxEngine/Fyrox/blob/master/src/renderer/shaders/fxaa_fs.glsl
#import bevy_core_pipeline::fullscreen_vertex_shader #import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
@group(0) @binding(0) @group(0) @binding(0)
var screenTexture: texture_2d<f32>; var screenTexture: texture_2d<f32>;

View File

@ -1,4 +1,4 @@
#import bevy_render::view #import bevy_render::view View
@group(0) @binding(0) @group(0) @binding(0)
var skybox: texture_cube<f32>; var skybox: texture_cube<f32>;

View File

@ -1,6 +1,8 @@
#import bevy_core_pipeline::fullscreen_vertex_shader #define TONEMAPPING_PASS
#import bevy_render::view #import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
#import bevy_render::view View
#import bevy_core_pipeline::tonemapping tone_mapping, powsafe, screen_space_dither
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> view: View; var<uniform> view: View;
@ -20,11 +22,11 @@ var dt_lut_sampler: sampler;
fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> { fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
let hdr_color = textureSample(hdr_texture, hdr_sampler, in.uv); let hdr_color = textureSample(hdr_texture, hdr_sampler, in.uv);
var output_rgb = tone_mapping(hdr_color).rgb; var output_rgb = tone_mapping(hdr_color, view.color_grading).rgb;
#ifdef DEBAND_DITHER #ifdef DEBAND_DITHER
output_rgb = powsafe(output_rgb.rgb, 1.0 / 2.2); output_rgb = powsafe(output_rgb.rgb, 1.0 / 2.2);
output_rgb = output_rgb + screen_space_dither(in.position.xy); output_rgb = output_rgb + bevy_core_pipeline::tonemapping::screen_space_dither(in.position.xy);
// This conversion back to linear space is required because our output texture format is // This conversion back to linear space is required because our output texture format is
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion. // SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
output_rgb = powsafe(output_rgb.rgb, 2.2); output_rgb = powsafe(output_rgb.rgb, 2.2);

View File

@ -1,5 +1,19 @@
#define_import_path bevy_core_pipeline::tonemapping #define_import_path bevy_core_pipeline::tonemapping
#import bevy_render::view View, ColorGrading
// hack !! not sure what to do with this
#ifdef TONEMAPPING_PASS
@group(0) @binding(3)
var dt_lut_texture: texture_3d<f32>;
@group(0) @binding(4)
var dt_lut_sampler: sampler;
#else
@group(0) @binding(15)
var dt_lut_texture: texture_3d<f32>;
@group(0) @binding(16)
var dt_lut_sampler: sampler;
#endif
fn sample_current_lut(p: vec3<f32>) -> vec3<f32> { fn sample_current_lut(p: vec3<f32>) -> vec3<f32> {
// Don't include code that will try to sample from LUTs if tonemap method doesn't require it // Don't include code that will try to sample from LUTs if tonemap method doesn't require it
@ -48,7 +62,7 @@ fn tonemap_curve(v: f32) -> f32 {
#endif #endif
} }
fn tonemap_curve3(v: vec3<f32>) -> vec3<f32> { fn tonemap_curve3_(v: vec3<f32>) -> vec3<f32> {
return vec3(tonemap_curve(v.r), tonemap_curve(v.g), tonemap_curve(v.b)); return vec3(tonemap_curve(v.r), tonemap_curve(v.g), tonemap_curve(v.b));
} }
@ -65,7 +79,7 @@ fn somewhat_boring_display_transform(col: vec3<f32>) -> vec3<f32> {
let tm_luma = tonemap_curve(ycbcr.x); let tm_luma = tonemap_curve(ycbcr.x);
let tm0 = boring_color.rgb * max(0.0, tm_luma / max(1e-5, tonemapping_luminance(boring_color.rgb))); let tm0 = boring_color.rgb * max(0.0, tm_luma / max(1e-5, tonemapping_luminance(boring_color.rgb)));
let final_mult = 0.97; let final_mult = 0.97;
let tm1 = tonemap_curve3(desat_col); let tm1 = tonemap_curve3_(desat_col);
boring_color = mix(tm0, tm1, bt * bt); boring_color = mix(tm0, tm1, bt * bt);
@ -167,7 +181,7 @@ fn saturation(color: vec3<f32>, saturationAmount: f32) -> vec3<f32> {
Similar to OCIO lg2 AllocationTransform. Similar to OCIO lg2 AllocationTransform.
ref[0] ref[0]
*/ */
fn convertOpenDomainToNormalizedLog2(color: vec3<f32>, minimum_ev: f32, maximum_ev: f32) -> vec3<f32> { fn convertOpenDomainToNormalizedLog2_(color: vec3<f32>, minimum_ev: f32, maximum_ev: f32) -> vec3<f32> {
let in_midgray = 0.18; let in_midgray = 0.18;
// remove negative before log transform // remove negative before log transform
@ -210,7 +224,7 @@ fn applyAgXLog(Image: vec3<f32>) -> vec3<f32> {
let b = dot(prepared_image, vec3(0.04237565, 0.0784336, 0.87914297)); let b = dot(prepared_image, vec3(0.04237565, 0.0784336, 0.87914297));
prepared_image = vec3(r, g, b); prepared_image = vec3(r, g, b);
prepared_image = convertOpenDomainToNormalizedLog2(prepared_image, -10.0, 6.5); prepared_image = convertOpenDomainToNormalizedLog2_(prepared_image, -10.0, 6.5);
prepared_image = clamp(prepared_image, vec3(0.0), vec3(1.0)); prepared_image = clamp(prepared_image, vec3(0.0), vec3(1.0));
return prepared_image; return prepared_image;
@ -226,7 +240,7 @@ fn applyLUT3D(Image: vec3<f32>, block_size: f32) -> vec3<f32> {
fn sample_blender_filmic_lut(stimulus: vec3<f32>) -> vec3<f32> { fn sample_blender_filmic_lut(stimulus: vec3<f32>) -> vec3<f32> {
let block_size = 64.0; let block_size = 64.0;
let normalized = saturate(convertOpenDomainToNormalizedLog2(stimulus, -11.0, 12.0)); let normalized = saturate(convertOpenDomainToNormalizedLog2_(stimulus, -11.0, 12.0));
return applyLUT3D(normalized, block_size); return applyLUT3D(normalized, block_size);
} }
@ -270,7 +284,7 @@ fn screen_space_dither(frag_coord: vec2<f32>) -> vec3<f32> {
return (dither - 0.5) / 255.0; return (dither - 0.5) / 255.0;
} }
fn tone_mapping(in: vec4<f32>) -> vec4<f32> { fn tone_mapping(in: vec4<f32>, color_grading: ColorGrading) -> vec4<f32> {
var color = max(in.rgb, vec3(0.0)); var color = max(in.rgb, vec3(0.0));
// Possible future grading: // Possible future grading:
@ -282,9 +296,9 @@ fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
// color += color * luma.xxx * 1.0; // color += color * luma.xxx * 1.0;
// Linear pre tonemapping grading // Linear pre tonemapping grading
color = saturation(color, view.color_grading.pre_saturation); color = saturation(color, color_grading.pre_saturation);
color = powsafe(color, view.color_grading.gamma); color = powsafe(color, color_grading.gamma);
color = color * powsafe(vec3(2.0), view.color_grading.exposure); color = color * powsafe(vec3(2.0), color_grading.exposure);
color = max(color, vec3(0.0)); color = max(color, vec3(0.0));
// tone_mapping // tone_mapping
@ -308,7 +322,7 @@ fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
#endif #endif
// Perceptual post tonemapping grading // Perceptual post tonemapping grading
color = saturation(color, view.color_grading.post_saturation); color = saturation(color, color_grading.post_saturation);
return vec4(color, in.a); return vec4(color, in.a);
} }

View File

@ -1,8 +1,9 @@
#ifdef GIZMO_3D // TODO use common view binding
#import bevy_pbr::mesh_view_bindings #import bevy_render::view View
#else
#import bevy_sprite::mesh2d_view_bindings @group(0) @binding(0)
#endif var<uniform> view: View;
struct LineGizmoUniform { struct LineGizmoUniform {
line_width: f32, line_width: f32,

View File

@ -12,10 +12,7 @@ use bevy_ecs::{
system::{Query, Res, ResMut, Resource}, system::{Query, Res, ResMut, Resource},
world::{FromWorld, World}, world::{FromWorld, World},
}; };
use bevy_pbr::{ use bevy_pbr::{MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup};
MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup, MAX_CASCADES_PER_LIGHT,
MAX_DIRECTIONAL_LIGHTS,
};
use bevy_render::{ use bevy_render::{
render_asset::RenderAssets, render_asset::RenderAssets,
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
@ -78,15 +75,6 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
"SIXTEEN_BYTE_ALIGNMENT".into(), "SIXTEEN_BYTE_ALIGNMENT".into(),
]; ];
shader_defs.push(ShaderDefVal::Int(
"MAX_DIRECTIONAL_LIGHTS".to_string(),
MAX_DIRECTIONAL_LIGHTS as i32,
));
shader_defs.push(ShaderDefVal::Int(
"MAX_CASCADES_PER_LIGHT".to_string(),
MAX_CASCADES_PER_LIGHT as i32,
));
if key.perspective { if key.perspective {
shader_defs.push("PERSPECTIVE".into()); shader_defs.push("PERSPECTIVE".into());
} }

View File

@ -30,3 +30,4 @@ bitflags = "2.3"
# direct dependency required for derive macro # direct dependency required for derive macro
bytemuck = { version = "1", features = ["derive"] } bytemuck = { version = "1", features = ["derive"] }
radsort = "0.1" radsort = "0.1"
naga_oil = "0.8"

View File

@ -1,5 +1,6 @@
#define_import_path bevy_pbr::environment_map #define_import_path bevy_pbr::environment_map
#import bevy_pbr::mesh_view_bindings as bindings
struct EnvironmentMapLight { struct EnvironmentMapLight {
diffuse: vec3<f32>, diffuse: vec3<f32>,
@ -20,9 +21,9 @@ fn environment_map_light(
// Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf // Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
// Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform // Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform
// because textureNumLevels() does not work on WebGL2 // because textureNumLevels() does not work on WebGL2
let radiance_level = perceptual_roughness * f32(lights.environment_map_smallest_specular_mip_level); let radiance_level = perceptual_roughness * f32(bindings::lights.environment_map_smallest_specular_mip_level);
let irradiance = textureSample(environment_map_diffuse, environment_map_sampler, vec3(N.xy, -N.z)).rgb; let irradiance = textureSample(bindings::environment_map_diffuse, bindings::environment_map_sampler, vec3(N.xy, -N.z)).rgb;
let radiance = textureSampleLevel(environment_map_specular, environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb; let radiance = textureSampleLevel(bindings::environment_map_specular, bindings::environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb;
// Multiscattering approximation: https://www.jcgt.org/published/0008/01/03/paper.pdf // Multiscattering approximation: https://www.jcgt.org/published/0008/01/03/paper.pdf
// Useful reference: https://bruop.github.io/ibl // Useful reference: https://bruop.github.io/ibl

View File

@ -31,8 +31,8 @@ use bevy_render::{
BindGroupLayoutEntry, BindingResource, BindingType, BlendState, BufferBindingType, BindGroupLayoutEntry, BindingResource, BindingType, BlendState, BufferBindingType,
ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
DynamicUniformBuffer, FragmentState, FrontFace, MultisampleState, PipelineCache, DynamicUniformBuffer, FragmentState, FrontFace, MultisampleState, PipelineCache,
PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderDefVal, ShaderRef, PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderRef, ShaderStages,
ShaderStages, ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError, ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
SpecializedMeshPipelines, StencilFaceState, StencilState, TextureSampleType, SpecializedMeshPipelines, StencilFaceState, StencilState, TextureSampleType,
TextureViewDimension, VertexState, TextureViewDimension, VertexState,
}, },
@ -47,7 +47,7 @@ use bevy_utils::tracing::error;
use crate::{ use crate::{
prepare_lights, setup_morph_and_skinning_defs, AlphaMode, DrawMesh, Material, MaterialPipeline, prepare_lights, setup_morph_and_skinning_defs, AlphaMode, DrawMesh, Material, MaterialPipeline,
MaterialPipelineKey, MeshLayouts, MeshPipeline, MeshPipelineKey, MeshUniform, RenderMaterials, MaterialPipelineKey, MeshLayouts, MeshPipeline, MeshPipelineKey, MeshUniform, RenderMaterials,
SetMaterialBindGroup, SetMeshBindGroup, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS, SetMaterialBindGroup, SetMeshBindGroup,
}; };
use std::{hash::Hash, marker::PhantomData}; use std::{hash::Hash, marker::PhantomData};
@ -373,14 +373,6 @@ where
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
} }
shader_defs.push(ShaderDefVal::UInt(
"MAX_DIRECTIONAL_LIGHTS".to_string(),
MAX_DIRECTIONAL_LIGHTS as u32,
));
shader_defs.push(ShaderDefVal::UInt(
"MAX_CASCADES_PER_LIGHT".to_string(),
MAX_CASCADES_PER_LIGHT as u32,
));
if key.mesh_key.contains(MeshPipelineKey::DEPTH_CLAMP_ORTHO) { if key.mesh_key.contains(MeshPipelineKey::DEPTH_CLAMP_ORTHO) {
shader_defs.push("DEPTH_CLAMP_ORTHO".into()); shader_defs.push("DEPTH_CLAMP_ORTHO".into());
// PERF: This line forces the "prepass fragment shader" to always run in // PERF: This line forces the "prepass fragment shader" to always run in

View File

@ -1,5 +1,8 @@
#import bevy_pbr::prepass_bindings #import bevy_pbr::prepass_bindings
#import bevy_pbr::mesh_functions #import bevy_pbr::mesh_functions
#import bevy_pbr::skinning
#import bevy_pbr::morph
#import bevy_pbr::mesh_bindings mesh
// Most of these attributes are not used in the default prepass fragment shader, but they are still needed so we can // Most of these attributes are not used in the default prepass fragment shader, but they are still needed so we can
// pass them to custom prepass shaders like pbr_prepass.wgsl. // pass them to custom prepass shaders like pbr_prepass.wgsl.
@ -83,12 +86,12 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#endif #endif
#ifdef SKINNED #ifdef SKINNED
var model = skin_model(vertex.joint_indices, vertex.joint_weights); var model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
#else // SKINNED #else // SKINNED
var model = mesh.model; var model = mesh.model;
#endif // SKINNED #endif // SKINNED
out.clip_position = mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); out.clip_position = bevy_pbr::mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0));
#ifdef DEPTH_CLAMP_ORTHO #ifdef DEPTH_CLAMP_ORTHO
out.clip_position_unclamped = out.clip_position; out.clip_position_unclamped = out.clip_position;
out.clip_position.z = min(out.clip_position.z, 1.0); out.clip_position.z = min(out.clip_position.z, 1.0);
@ -100,19 +103,19 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#ifdef NORMAL_PREPASS #ifdef NORMAL_PREPASS
#ifdef SKINNED #ifdef SKINNED
out.world_normal = skin_normals(model, vertex.normal); out.world_normal = bevy_pbr::skinning::skin_normals(model, vertex.normal);
#else // SKINNED #else // SKINNED
out.world_normal = mesh_normal_local_to_world(vertex.normal); out.world_normal = bevy_pbr::mesh_functions::mesh_normal_local_to_world(vertex.normal);
#endif // SKINNED #endif // SKINNED
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
out.world_tangent = mesh_tangent_local_to_world(model, vertex.tangent); out.world_tangent = bevy_pbr::mesh_functions::mesh_tangent_local_to_world(model, vertex.tangent);
#endif // VERTEX_TANGENTS #endif // VERTEX_TANGENTS
#endif // NORMAL_PREPASS #endif // NORMAL_PREPASS
#ifdef MOTION_VECTOR_PREPASS #ifdef MOTION_VECTOR_PREPASS
out.world_position = mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0)); out.world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
out.previous_world_position = mesh_position_local_to_world(mesh.previous_model, vec4<f32>(vertex.position, 1.0)); out.previous_world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(mesh.previous_model, vec4<f32>(vertex.position, 1.0));
#endif // MOTION_VECTOR_PREPASS #endif // MOTION_VECTOR_PREPASS
return out; return out;
@ -165,9 +168,9 @@ fn fragment(in: FragmentInput) -> FragmentOutput {
#endif // DEPTH_CLAMP_ORTHO #endif // DEPTH_CLAMP_ORTHO
#ifdef MOTION_VECTOR_PREPASS #ifdef MOTION_VECTOR_PREPASS
let clip_position_t = view.unjittered_view_proj * in.world_position; let clip_position_t = bevy_pbr::prepass_bindings::view.unjittered_view_proj * in.world_position;
let clip_position = clip_position_t.xy / clip_position_t.w; let clip_position = clip_position_t.xy / clip_position_t.w;
let previous_clip_position_t = previous_view_proj * in.previous_world_position; let previous_clip_position_t = bevy_pbr::prepass_bindings::previous_view_proj * in.previous_world_position;
let previous_clip_position = previous_clip_position_t.xy / previous_clip_position_t.w; let previous_clip_position = previous_clip_position_t.xy / previous_clip_position_t.w;
// These motion vectors are used as offsets to UV positions and are stored // These motion vectors are used as offsets to UV positions and are stored
// in the range -1,1 to allow offsetting from the one corner to the // in the range -1,1 to allow offsetting from the one corner to the

View File

@ -1,6 +1,6 @@
#define_import_path bevy_pbr::prepass_bindings #define_import_path bevy_pbr::prepass_bindings
#import bevy_render::view View
#import bevy_pbr::mesh_view_types #import bevy_render::globals Globals
#import bevy_pbr::mesh_types #import bevy_pbr::mesh_types
@group(0) @binding(0) @group(0) @binding(0)
@ -16,18 +16,4 @@ var<uniform> previous_view_proj: mat4x4<f32>;
// Material bindings will be in @group(1) // Material bindings will be in @group(1)
@group(2) @binding(0) @group(2) @binding(0)
var<uniform> mesh: Mesh; var<uniform> mesh: bevy_pbr::mesh_types::Mesh;
#ifdef SKINNED
@group(2) @binding(1)
var<uniform> joint_matrices: SkinnedMesh;
#import bevy_pbr::skinning
#endif
#ifdef MORPH_TARGETS
@group(2) @binding(2)
var<uniform> morph_weights: MorphWeights;
@group(2) @binding(3)
var morph_targets: texture_3d<f32>;
#import bevy_pbr::morph
#endif

View File

@ -1,11 +1,13 @@
#define_import_path bevy_pbr::prepass_utils #define_import_path bevy_pbr::prepass_utils
#import bevy_pbr::mesh_view_bindings as view_bindings
#ifndef DEPTH_PREPASS #ifndef DEPTH_PREPASS
fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 { fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 {
#ifdef MULTISAMPLED #ifdef MULTISAMPLED
let depth_sample = textureLoad(depth_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index)); let depth_sample = textureLoad(view_bindings::depth_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
#else #else
let depth_sample = textureLoad(depth_prepass_texture, vec2<i32>(frag_coord.xy), 0); let depth_sample = textureLoad(view_bindings::depth_prepass_texture, vec2<i32>(frag_coord.xy), 0);
#endif #endif
return depth_sample; return depth_sample;
} }
@ -14,9 +16,9 @@ fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 {
#ifndef NORMAL_PREPASS #ifndef NORMAL_PREPASS
fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> { fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> {
#ifdef MULTISAMPLED #ifdef MULTISAMPLED
let normal_sample = textureLoad(normal_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index)); let normal_sample = textureLoad(view_bindings::normal_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
#else #else
let normal_sample = textureLoad(normal_prepass_texture, vec2<i32>(frag_coord.xy), 0); let normal_sample = textureLoad(view_bindings::normal_prepass_texture, vec2<i32>(frag_coord.xy), 0);
#endif // MULTISAMPLED #endif // MULTISAMPLED
return normal_sample.xyz * 2.0 - vec3(1.0); return normal_sample.xyz * 2.0 - vec3(1.0);
} }
@ -25,9 +27,9 @@ fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> {
#ifndef MOTION_VECTOR_PREPASS #ifndef MOTION_VECTOR_PREPASS
fn prepass_motion_vector(frag_coord: vec4<f32>, sample_index: u32) -> vec2<f32> { fn prepass_motion_vector(frag_coord: vec4<f32>, sample_index: u32) -> vec2<f32> {
#ifdef MULTISAMPLED #ifdef MULTISAMPLED
let motion_vector_sample = textureLoad(motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index)); let motion_vector_sample = textureLoad(view_bindings::motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
#else #else
let motion_vector_sample = textureLoad(motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), 0); let motion_vector_sample = textureLoad(view_bindings::motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), 0);
#endif #endif
return motion_vector_sample.rg; return motion_vector_sample.rg;
} }

View File

@ -1,28 +1,31 @@
#define_import_path bevy_pbr::clustered_forward #define_import_path bevy_pbr::clustered_forward
#import bevy_pbr::mesh_view_bindings as bindings
#import bevy_pbr::utils hsv2rgb
// NOTE: Keep in sync with bevy_pbr/src/light.rs // NOTE: Keep in sync with bevy_pbr/src/light.rs
fn view_z_to_z_slice(view_z: f32, is_orthographic: bool) -> u32 { fn view_z_to_z_slice(view_z: f32, is_orthographic: bool) -> u32 {
var z_slice: u32 = 0u; var z_slice: u32 = 0u;
if (is_orthographic) { if (is_orthographic) {
// NOTE: view_z is correct in the orthographic case // NOTE: view_z is correct in the orthographic case
z_slice = u32(floor((view_z - lights.cluster_factors.z) * lights.cluster_factors.w)); z_slice = u32(floor((view_z - bindings::lights.cluster_factors.z) * bindings::lights.cluster_factors.w));
} else { } else {
// NOTE: had to use -view_z to make it positive else log(negative) is nan // NOTE: had to use -view_z to make it positive else log(negative) is nan
z_slice = u32(log(-view_z) * lights.cluster_factors.z - lights.cluster_factors.w + 1.0); z_slice = u32(log(-view_z) * bindings::lights.cluster_factors.z - bindings::lights.cluster_factors.w + 1.0);
} }
// NOTE: We use min as we may limit the far z plane used for clustering to be closer than // NOTE: We use min as we may limit the far z plane used for clustering to be closer than
// the furthest thing being drawn. This means that we need to limit to the maximum cluster. // the furthest thing being drawn. This means that we need to limit to the maximum cluster.
return min(z_slice, lights.cluster_dimensions.z - 1u); return min(z_slice, bindings::lights.cluster_dimensions.z - 1u);
} }
fn fragment_cluster_index(frag_coord: vec2<f32>, view_z: f32, is_orthographic: bool) -> u32 { fn fragment_cluster_index(frag_coord: vec2<f32>, view_z: f32, is_orthographic: bool) -> u32 {
let xy = vec2<u32>(floor((frag_coord - view.viewport.xy) * lights.cluster_factors.xy)); let xy = vec2<u32>(floor((frag_coord - bindings::view.viewport.xy) * bindings::lights.cluster_factors.xy));
let z_slice = view_z_to_z_slice(view_z, is_orthographic); let z_slice = view_z_to_z_slice(view_z, is_orthographic);
// NOTE: Restricting cluster index to avoid undefined behavior when accessing uniform buffer // NOTE: Restricting cluster index to avoid undefined behavior when accessing uniform buffer
// arrays based on the cluster index. // arrays based on the cluster index.
return min( return min(
(xy.y * lights.cluster_dimensions.x + xy.x) * lights.cluster_dimensions.z + z_slice, (xy.y * bindings::lights.cluster_dimensions.x + xy.x) * bindings::lights.cluster_dimensions.z + z_slice,
lights.cluster_dimensions.w - 1u bindings::lights.cluster_dimensions.w - 1u
); );
} }
@ -30,9 +33,9 @@ fn fragment_cluster_index(frag_coord: vec2<f32>, view_z: f32, is_orthographic: b
const CLUSTER_COUNT_SIZE = 9u; const CLUSTER_COUNT_SIZE = 9u;
fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> { fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> {
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3 #if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
return cluster_offsets_and_counts.data[cluster_index].xyz; return bindings::cluster_offsets_and_counts.data[cluster_index].xyz;
#else #else
let offset_and_counts = cluster_offsets_and_counts.data[cluster_index >> 2u][cluster_index & ((1u << 2u) - 1u)]; let offset_and_counts = bindings::cluster_offsets_and_counts.data[cluster_index >> 2u][cluster_index & ((1u << 2u) - 1u)];
// [ 31 .. 18 | 17 .. 9 | 8 .. 0 ] // [ 31 .. 18 | 17 .. 9 | 8 .. 0 ]
// [ offset | point light count | spot light count ] // [ offset | point light count | spot light count ]
return vec3<u32>( return vec3<u32>(
@ -45,11 +48,11 @@ fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> {
fn get_light_id(index: u32) -> u32 { fn get_light_id(index: u32) -> u32 {
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3 #if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
return cluster_light_index_lists.data[index]; return bindings::cluster_light_index_lists.data[index];
#else #else
// The index is correct but in cluster_light_index_lists we pack 4 u8s into a u32 // The index is correct but in cluster_light_index_lists we pack 4 u8s into a u32
// This means the index into cluster_light_index_lists is index / 4 // This means the index into cluster_light_index_lists is index / 4
let indices = cluster_light_index_lists.data[index >> 4u][(index >> 2u) & ((1u << 2u) - 1u)]; let indices = bindings::cluster_light_index_lists.data[index >> 4u][(index >> 2u) & ((1u << 2u) - 1u)];
// And index % 4 gives the sub-index of the u8 within the u32 so we shift by 8 * sub-index // And index % 4 gives the sub-index of the u8 within the u32 so we shift by 8 * sub-index
return (indices >> (8u * (index & ((1u << 2u) - 1u)))) & ((1u << 8u) - 1u); return (indices >> (8u * (index & ((1u << 2u) - 1u)))) & ((1u << 8u) - 1u);
#endif #endif
@ -69,9 +72,9 @@ fn cluster_debug_visualization(
var z_slice: u32 = view_z_to_z_slice(view_z, is_orthographic); var z_slice: u32 = view_z_to_z_slice(view_z, is_orthographic);
// A hack to make the colors alternate a bit more // A hack to make the colors alternate a bit more
if ((z_slice & 1u) == 1u) { if ((z_slice & 1u) == 1u) {
z_slice = z_slice + lights.cluster_dimensions.z / 2u; z_slice = z_slice + bindings::lights.cluster_dimensions.z / 2u;
} }
let slice_color = hsv2rgb(f32(z_slice) / f32(lights.cluster_dimensions.z + 1u), 1.0, 0.5); let slice_color = hsv2rgb(f32(z_slice) / f32(bindings::lights.cluster_dimensions.z + 1u), 1.0, 0.5);
output_color = vec4<f32>( output_color = vec4<f32>(
(1.0 - cluster_overlay_alpha) * output_color.rgb + cluster_overlay_alpha * slice_color, (1.0 - cluster_overlay_alpha) * output_color.rgb + cluster_overlay_alpha * slice_color,
output_color.a output_color.a

View File

@ -1,5 +1,8 @@
#define_import_path bevy_pbr::fog #define_import_path bevy_pbr::fog
#import bevy_pbr::mesh_view_bindings fog
#import bevy_pbr::mesh_view_types Fog
// Fog formulas adapted from: // Fog formulas adapted from:
// https://learn.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas // https://learn.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas
// https://catlikecoding.com/unity/tutorials/rendering/part-14/ // https://catlikecoding.com/unity/tutorials/rendering/part-14/

View File

@ -86,7 +86,17 @@ impl Plugin for MeshRenderPlugin {
app, app,
MESH_VIEW_TYPES_HANDLE, MESH_VIEW_TYPES_HANDLE,
"mesh_view_types.wgsl", "mesh_view_types.wgsl",
Shader::from_wgsl Shader::from_wgsl_with_defs,
vec![
ShaderDefVal::UInt(
"MAX_DIRECTIONAL_LIGHTS".into(),
MAX_DIRECTIONAL_LIGHTS as u32
),
ShaderDefVal::UInt(
"MAX_CASCADES_PER_LIGHT".into(),
MAX_CASCADES_PER_LIGHT as u32,
)
]
); );
load_internal_asset!( load_internal_asset!(
app, app,
@ -709,15 +719,6 @@ impl SpecializedMeshPipeline for MeshPipeline {
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1)); vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
} }
shader_defs.push(ShaderDefVal::UInt(
"MAX_DIRECTIONAL_LIGHTS".to_string(),
MAX_DIRECTIONAL_LIGHTS as u32,
));
shader_defs.push(ShaderDefVal::UInt(
"MAX_CASCADES_PER_LIGHT".to_string(),
MAX_CASCADES_PER_LIGHT as u32,
));
if layout.contains(Mesh::ATTRIBUTE_UV_0) { if layout.contains(Mesh::ATTRIBUTE_UV_0) {
shader_defs.push("VERTEX_UVS".into()); shader_defs.push("VERTEX_UVS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2)); vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));

View File

@ -1,8 +1,8 @@
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_functions as mesh_functions
#import bevy_pbr::mesh_bindings #import bevy_pbr::skinning
#import bevy_pbr::morph
// NOTE: Bindings must come before functions that use them! #import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::mesh_functions #import bevy_pbr::mesh_vertex_output MeshVertexOutput
struct Vertex { struct Vertex {
#ifdef VERTEX_POSITIONS #ifdef VERTEX_POSITIONS
@ -29,26 +29,21 @@ struct Vertex {
#endif #endif
}; };
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
#import bevy_pbr::mesh_vertex_output
};
#ifdef MORPH_TARGETS #ifdef MORPH_TARGETS
fn morph_vertex(vertex_in: Vertex) -> Vertex { fn morph_vertex(vertex_in: Vertex) -> Vertex {
var vertex = vertex_in; var vertex = vertex_in;
let weight_count = layer_count(); let weight_count = bevy_pbr::morph::layer_count();
for (var i: u32 = 0u; i < weight_count; i ++) { for (var i: u32 = 0u; i < weight_count; i ++) {
let weight = weight_at(i); let weight = bevy_pbr::morph::weight_at(i);
if weight == 0.0 { if weight == 0.0 {
continue; continue;
} }
vertex.position += weight * morph(vertex.index, position_offset, i); vertex.position += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::position_offset, i);
#ifdef VERTEX_NORMALS #ifdef VERTEX_NORMALS
vertex.normal += weight * morph(vertex.index, normal_offset, i); vertex.normal += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::normal_offset, i);
#endif #endif
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
vertex.tangent += vec4(weight * morph(vertex.index, tangent_offset, i), 0.0); vertex.tangent += vec4(weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::tangent_offset, i), 0.0);
#endif #endif
} }
return vertex; return vertex;
@ -56,8 +51,8 @@ fn morph_vertex(vertex_in: Vertex) -> Vertex {
#endif #endif
@vertex @vertex
fn vertex(vertex_no_morph: Vertex) -> VertexOutput { fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput {
var out: VertexOutput; var out: MeshVertexOutput;
#ifdef MORPH_TARGETS #ifdef MORPH_TARGETS
var vertex = morph_vertex(vertex_no_morph); var vertex = morph_vertex(vertex_no_morph);
@ -66,22 +61,22 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#endif #endif
#ifdef SKINNED #ifdef SKINNED
var model = skin_model(vertex.joint_indices, vertex.joint_weights); var model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
#else #else
var model = mesh.model; var model = mesh.model;
#endif #endif
#ifdef VERTEX_NORMALS #ifdef VERTEX_NORMALS
#ifdef SKINNED #ifdef SKINNED
out.world_normal = skin_normals(model, vertex.normal); out.world_normal = bevy_pbr::skinning::skin_normals(model, vertex.normal);
#else #else
out.world_normal = mesh_normal_local_to_world(vertex.normal); out.world_normal = mesh_functions::mesh_normal_local_to_world(vertex.normal);
#endif #endif
#endif #endif
#ifdef VERTEX_POSITIONS #ifdef VERTEX_POSITIONS
out.world_position = mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0)); out.world_position = mesh_functions::mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
out.clip_position = mesh_position_world_to_clip(out.world_position); out.position = mesh_functions::mesh_position_world_to_clip(out.world_position);
#endif #endif
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
@ -89,7 +84,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#endif #endif
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
out.world_tangent = mesh_tangent_local_to_world(model, vertex.tangent); out.world_tangent = mesh_functions::mesh_tangent_local_to_world(model, vertex.tangent);
#endif #endif
#ifdef VERTEX_COLORS #ifdef VERTEX_COLORS
@ -99,14 +94,12 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
return out; return out;
} }
struct FragmentInput {
#import bevy_pbr::mesh_vertex_output
};
@fragment @fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> { fn fragment(
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
#ifdef VERTEX_COLORS #ifdef VERTEX_COLORS
return in.color; return mesh.color;
#else #else
return vec4<f32>(1.0, 0.0, 1.0, 1.0); return vec4<f32>(1.0, 0.0, 1.0, 1.0);
#endif #endif

View File

@ -1,20 +1,15 @@
#define_import_path bevy_pbr::mesh_bindings #define_import_path bevy_pbr::mesh_bindings
#import bevy_pbr::mesh_types #import bevy_pbr::mesh_types Mesh
#ifdef MESH_BINDGROUP_1
@group(1) @binding(0)
var<uniform> mesh: Mesh;
#else
@group(2) @binding(0) @group(2) @binding(0)
var<uniform> mesh: Mesh; var<uniform> mesh: Mesh;
#ifdef SKINNED
@group(2) @binding(1)
var<uniform> joint_matrices: SkinnedMesh;
#import bevy_pbr::skinning
#endif
#ifdef MORPH_TARGETS
@group(2) @binding(2)
var<uniform> morph_weights: MorphWeights;
@group(2) @binding(3)
var morph_targets: texture_3d<f32>;
#import bevy_pbr::morph
#endif #endif

View File

@ -1,5 +1,9 @@
#define_import_path bevy_pbr::mesh_functions #define_import_path bevy_pbr::mesh_functions
#import bevy_pbr::mesh_view_bindings view
#import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::mesh_types MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT
fn mesh_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> { fn mesh_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
return model * vertex_position; return model * vertex_position;
} }
@ -34,7 +38,7 @@ fn mesh_normal_local_to_world(vertex_normal: vec3<f32>) -> vec3<f32> {
// Calculates the sign of the determinant of the 3x3 model matrix based on a // Calculates the sign of the determinant of the 3x3 model matrix based on a
// mesh flag // mesh flag
fn sign_determinant_model_3x3() -> f32 { fn sign_determinant_model_3x3m() -> f32 {
// bool(u32) is false if 0u else true // bool(u32) is false if 0u else true
// f32(bool) is 1.0 if true else 0.0 // f32(bool) is 1.0 if true else 0.0
// * 2.0 - 1.0 remaps 0.0 or 1.0 to -1.0 or 1.0 respectively // * 2.0 - 1.0 remaps 0.0 or 1.0 to -1.0 or 1.0 respectively
@ -58,6 +62,6 @@ fn mesh_tangent_local_to_world(model: mat4x4<f32>, vertex_tangent: vec4<f32>) ->
), ),
// NOTE: Multiplying by the sign of the determinant of the 3x3 model matrix accounts for // NOTE: Multiplying by the sign of the determinant of the 3x3 model matrix accounts for
// situations such as negative scaling. // situations such as negative scaling.
vertex_tangent.w * sign_determinant_model_3x3() vertex_tangent.w * sign_determinant_model_3x3m()
); );
} }

View File

@ -1,13 +1,18 @@
#define_import_path bevy_pbr::mesh_vertex_output #define_import_path bevy_pbr::mesh_vertex_output
@location(0) world_position: vec4<f32>, struct MeshVertexOutput {
@location(1) world_normal: vec3<f32>, // this is `clip position` when the struct is used as a vertex stage output
#ifdef VERTEX_UVS // and `frag coord` when used as a fragment stage input
@location(2) uv: vec2<f32>, @builtin(position) position: vec4<f32>,
#endif @location(0) world_position: vec4<f32>,
#ifdef VERTEX_TANGENTS @location(1) world_normal: vec3<f32>,
@location(3) world_tangent: vec4<f32>, #ifdef VERTEX_UVS
#endif @location(2) uv: vec2<f32>,
#ifdef VERTEX_COLORS #endif
@location(4) color: vec4<f32>, #ifdef VERTEX_TANGENTS
#endif @location(3) world_tangent: vec4<f32>,
#endif
#ifdef VERTEX_COLORS
@location(4) color: vec4<f32>,
#endif
}

View File

@ -1,11 +1,13 @@
#define_import_path bevy_pbr::mesh_view_bindings #define_import_path bevy_pbr::mesh_view_bindings
#import bevy_pbr::mesh_view_types #import bevy_pbr::mesh_view_types as types
#import bevy_render::view View
#import bevy_render::globals Globals
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> view: View; var<uniform> view: View;
@group(0) @binding(1) @group(0) @binding(1)
var<uniform> lights: Lights; var<uniform> lights: types::Lights;
#ifdef NO_ARRAY_TEXTURES_SUPPORT #ifdef NO_ARRAY_TEXTURES_SUPPORT
@group(0) @binding(2) @group(0) @binding(2)
var point_shadow_textures: texture_depth_cube; var point_shadow_textures: texture_depth_cube;
@ -27,24 +29,24 @@ var directional_shadow_textures_sampler: sampler_comparison;
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3 #if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
@group(0) @binding(6) @group(0) @binding(6)
var<storage> point_lights: PointLights; var<storage> point_lights: types::PointLights;
@group(0) @binding(7) @group(0) @binding(7)
var<storage> cluster_light_index_lists: ClusterLightIndexLists; var<storage> cluster_light_index_lists: types::ClusterLightIndexLists;
@group(0) @binding(8) @group(0) @binding(8)
var<storage> cluster_offsets_and_counts: ClusterOffsetsAndCounts; var<storage> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts;
#else #else
@group(0) @binding(6) @group(0) @binding(6)
var<uniform> point_lights: PointLights; var<uniform> point_lights: types::PointLights;
@group(0) @binding(7) @group(0) @binding(7)
var<uniform> cluster_light_index_lists: ClusterLightIndexLists; var<uniform> cluster_light_index_lists: types::ClusterLightIndexLists;
@group(0) @binding(8) @group(0) @binding(8)
var<uniform> cluster_offsets_and_counts: ClusterOffsetsAndCounts; var<uniform> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts;
#endif #endif
@group(0) @binding(9) @group(0) @binding(9)
var<uniform> globals: Globals; var<uniform> globals: Globals;
@group(0) @binding(10) @group(0) @binding(10)
var<uniform> fog: Fog; var<uniform> fog: types::Fog;
@group(0) @binding(11) @group(0) @binding(11)
var screen_space_ambient_occlusion_texture: texture_2d<f32>; var screen_space_ambient_occlusion_texture: texture_2d<f32>;

View File

@ -7,6 +7,27 @@
#define_import_path bevy_pbr::morph #define_import_path bevy_pbr::morph
#ifdef MORPH_TARGETS
#import bevy_pbr::mesh_types MorphWeights
#ifdef MESH_BINDGROUP_1
@group(1) @binding(2)
var<uniform> morph_weights: MorphWeights;
@group(1) @binding(3)
var morph_targets: texture_3d<f32>;
#else
@group(2) @binding(2)
var<uniform> morph_weights: MorphWeights;
@group(2) @binding(3)
var morph_targets: texture_3d<f32>;
#endif
// NOTE: Those are the "hardcoded" values found in `MorphAttributes` struct // NOTE: Those are the "hardcoded" values found in `MorphAttributes` struct
// in crates/bevy_render/src/mesh/morph/visitors.rs // in crates/bevy_render/src/mesh/morph/visitors.rs
// In an ideal world, the offsets are established dynamically and passed as #defines // In an ideal world, the offsets are established dynamically and passed as #defines
@ -43,3 +64,5 @@ fn morph(vertex_index: u32, component_offset: u32, weight_index: u32) -> vec3<f3
morph_pixel(vertex_index, component_offset + 2u, weight_index), morph_pixel(vertex_index, component_offset + 2u, weight_index),
); );
} }
#endif // MORPH_TARGETS

View File

@ -1,5 +1,7 @@
#define_import_path bevy_pbr::parallax_mapping #define_import_path bevy_pbr::parallax_mapping
#import bevy_pbr::pbr_bindings depth_map_texture, depth_map_sampler
fn sample_depth_map(uv: vec2<f32>) -> f32 { fn sample_depth_map(uv: vec2<f32>) -> f32 {
// We use `textureSampleLevel` over `textureSample` because the wgpu DX12 // We use `textureSampleLevel` over `textureSample` because the wgpu DX12
// backend (Fxc) panics when using "gradient instructions" inside a loop. // backend (Fxc) panics when using "gradient instructions" inside a loop.

View File

@ -1,45 +1,45 @@
#import bevy_pbr::mesh_view_bindings #define_import_path bevy_pbr::fragment
#import bevy_pbr::pbr_bindings
#import bevy_pbr::mesh_bindings
#import bevy_pbr::utils #import bevy_pbr::pbr_functions as pbr_functions
#import bevy_pbr::clustered_forward #import bevy_pbr::pbr_bindings as pbr_bindings
#import bevy_pbr::lighting #import bevy_pbr::pbr_types as pbr_types
#import bevy_pbr::pbr_ambient #import bevy_pbr::prepass_utils
#import bevy_pbr::shadows
#import bevy_pbr::fog #import bevy_pbr::mesh_vertex_output MeshVertexOutput
#import bevy_pbr::pbr_functions #import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::parallax_mapping #import bevy_pbr::mesh_view_bindings view, fog, screen_space_ambient_occlusion_texture
#import bevy_pbr::mesh_view_types FOG_MODE_OFF
#import bevy_core_pipeline::tonemapping screen_space_dither, powsafe, tone_mapping
#import bevy_pbr::parallax_mapping parallaxed_uv
#import bevy_pbr::prepass_utils #import bevy_pbr::prepass_utils
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
#import bevy_pbr::gtao_utils #import bevy_pbr::gtao_utils gtao_multibounce
#endif #endif
struct FragmentInput {
@builtin(front_facing) is_front: bool,
@builtin(position) frag_coord: vec4<f32>,
#import bevy_pbr::mesh_vertex_output
};
@fragment @fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> { fn fragment(
in: MeshVertexOutput,
@builtin(front_facing) is_front: bool,
) -> @location(0) vec4<f32> {
var output_color: vec4<f32> = pbr_bindings::material.base_color;
let is_orthographic = view.projection[3].w == 1.0; let is_orthographic = view.projection[3].w == 1.0;
let V = calculate_view(in.world_position, is_orthographic); let V = pbr_functions::calculate_view(in.world_position, is_orthographic);
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
var uv = in.uv; var uv = in.uv;
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
if ((material.flags & STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) {
let N = in.world_normal; let N = in.world_normal;
let T = in.world_tangent.xyz; let T = in.world_tangent.xyz;
let B = in.world_tangent.w * cross(N, T); let B = in.world_tangent.w * cross(N, T);
// Transform V from fragment to camera in world space to tangent space. // Transform V from fragment to camera in world space to tangent space.
let Vt = vec3(dot(V, T), dot(V, B), dot(V, N)); let Vt = vec3(dot(V, T), dot(V, B), dot(V, N));
uv = parallaxed_uv( uv = parallaxed_uv(
material.parallax_depth_scale, pbr_bindings::material.parallax_depth_scale,
material.max_parallax_layer_count, pbr_bindings::material.max_parallax_layer_count,
material.max_relief_mapping_search_steps, pbr_bindings::material.max_relief_mapping_search_steps,
uv, uv,
// Flip the direction of Vt to go toward the surface to make the // Flip the direction of Vt to go toward the surface to make the
// parallax mapping algorithm easier to understand and reason // parallax mapping algorithm easier to understand and reason
@ -49,41 +49,41 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
} }
#endif #endif
#endif #endif
var output_color: vec4<f32> = material.base_color;
#ifdef VERTEX_COLORS #ifdef VERTEX_COLORS
output_color = output_color * in.color; output_color = output_color * in.color;
#endif #endif
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
output_color = output_color * textureSampleBias(base_color_texture, base_color_sampler, uv, view.mip_bias); output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, in.uv, view.mip_bias);
} }
#endif #endif
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
if ((material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) {
// Prepare a 'processed' StandardMaterial by sampling all textures to resolve // Prepare a 'processed' StandardMaterial by sampling all textures to resolve
// the material members // the material members
var pbr_input: PbrInput; var pbr_input: pbr_functions::PbrInput;
pbr_input.material.base_color = output_color; pbr_input.material.base_color = output_color;
pbr_input.material.reflectance = material.reflectance; pbr_input.material.reflectance = pbr_bindings::material.reflectance;
pbr_input.material.flags = material.flags; pbr_input.material.flags = pbr_bindings::material.flags;
pbr_input.material.alpha_cutoff = material.alpha_cutoff; pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff;
// TODO use .a for exposure compensation in HDR // TODO use .a for exposure compensation in HDR
var emissive: vec4<f32> = material.emissive; var emissive: vec4<f32> = pbr_bindings::material.emissive;
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
emissive = vec4<f32>(emissive.rgb * textureSampleBias(emissive_texture, emissive_sampler, uv, view.mip_bias).rgb, 1.0); emissive = vec4<f32>(emissive.rgb * textureSampleBias(pbr_bindings::emissive_texture, pbr_bindings::emissive_sampler, in.uv, view.mip_bias).rgb, 1.0);
} }
#endif #endif
pbr_input.material.emissive = emissive; pbr_input.material.emissive = emissive;
var metallic: f32 = material.metallic; var metallic: f32 = pbr_bindings::material.metallic;
var perceptual_roughness: f32 = material.perceptual_roughness; var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness;
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
let metallic_roughness = textureSampleBias(metallic_roughness_texture, metallic_roughness_sampler, uv, view.mip_bias); let metallic_roughness = textureSampleBias(pbr_bindings::metallic_roughness_texture, pbr_bindings::metallic_roughness_sampler, in.uv, view.mip_bias);
// Sampling from GLTF standard channels for now // Sampling from GLTF standard channels for now
metallic = metallic * metallic_roughness.b; metallic = metallic * metallic_roughness.b;
perceptual_roughness = perceptual_roughness * metallic_roughness.g; perceptual_roughness = perceptual_roughness * metallic_roughness.g;
@ -95,33 +95,33 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
// TODO: Split into diffuse/specular occlusion? // TODO: Split into diffuse/specular occlusion?
var occlusion: vec3<f32> = vec3(1.0); var occlusion: vec3<f32> = vec3(1.0);
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
occlusion = vec3(textureSampleBias(occlusion_texture, occlusion_sampler, in.uv, view.mip_bias).r); occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, in.uv, view.mip_bias).r);
} }
#endif #endif
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2<i32>(in.frag_coord.xy), 0i).r; let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2<i32>(in.position.xy), 0i).r;
let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb);
occlusion = min(occlusion, ssao_multibounce); occlusion = min(occlusion, ssao_multibounce);
#endif #endif
pbr_input.occlusion = occlusion; pbr_input.occlusion = occlusion;
pbr_input.frag_coord = in.frag_coord; pbr_input.frag_coord = in.position;
pbr_input.world_position = in.world_position; pbr_input.world_position = in.world_position;
pbr_input.world_normal = prepare_world_normal( pbr_input.world_normal = pbr_functions::prepare_world_normal(
in.world_normal, in.world_normal,
(material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u, (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
in.is_front, is_front,
); );
pbr_input.is_orthographic = is_orthographic; pbr_input.is_orthographic = is_orthographic;
#ifdef LOAD_PREPASS_NORMALS #ifdef LOAD_PREPASS_NORMALS
pbr_input.N = prepass_normal(in.frag_coord, 0u); pbr_input.N = bevy_pbr::prepass_utils::prepass_normal(in.position, 0u);
#else #else
pbr_input.N = apply_normal_mapping( pbr_input.N = pbr_functions::apply_normal_mapping(
material.flags, pbr_bindings::material.flags,
pbr_input.world_normal, pbr_input.world_normal,
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
#ifdef STANDARDMATERIAL_NORMAL_MAP #ifdef STANDARDMATERIAL_NORMAL_MAP
@ -139,22 +139,22 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
pbr_input.flags = mesh.flags; pbr_input.flags = mesh.flags;
output_color = pbr(pbr_input); output_color = pbr_functions::pbr(pbr_input);
} else { } else {
output_color = alpha_discard(material, output_color); output_color = pbr_functions::alpha_discard(pbr_bindings::material, output_color);
} }
// fog // fog
if (fog.mode != FOG_MODE_OFF && (material.flags & STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) { if (fog.mode != FOG_MODE_OFF && (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
output_color = apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz); output_color = pbr_functions::apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz);
} }
#ifdef TONEMAP_IN_SHADER #ifdef TONEMAP_IN_SHADER
output_color = tone_mapping(output_color); output_color = tone_mapping(output_color, view.color_grading);
#ifdef DEBAND_DITHER #ifdef DEBAND_DITHER
var output_rgb = output_color.rgb; var output_rgb = output_color.rgb;
output_rgb = powsafe(output_rgb, 1.0 / 2.2); output_rgb = powsafe(output_rgb, 1.0 / 2.2);
output_rgb = output_rgb + screen_space_dither(in.frag_coord.xy); output_rgb = output_rgb + screen_space_dither(in.position.xy);
// This conversion back to linear space is required because our output texture format is // This conversion back to linear space is required because our output texture format is
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion. // SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
output_rgb = powsafe(output_rgb, 2.2); output_rgb = powsafe(output_rgb, 2.2);
@ -162,7 +162,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
#endif #endif
#endif #endif
#ifdef PREMULTIPLY_ALPHA #ifdef PREMULTIPLY_ALPHA
output_color = premultiply_alpha(material.flags, output_color); output_color = pbr_functions::premultiply_alpha(pbr_bindings::material.flags, output_color);
#endif #endif
return output_color; return output_color;
} }

View File

@ -1,4 +1,7 @@
#define_import_path bevy_pbr::pbr_ambient #define_import_path bevy_pbr::ambient
#import bevy_pbr::lighting EnvBRDFApprox, F_AB
#import bevy_pbr::mesh_view_bindings lights
// A precomputed `NdotV` is provided because it is computed regardless, // A precomputed `NdotV` is provided because it is computed regardless,
// but `world_normal` and the view vector `V` are provided separately for more advanced uses. // but `world_normal` and the view vector `V` are provided separately for more advanced uses.
@ -12,8 +15,8 @@ fn ambient_light(
perceptual_roughness: f32, perceptual_roughness: f32,
occlusion: vec3<f32>, occlusion: vec3<f32>,
) -> vec3<f32> { ) -> vec3<f32> {
let diffuse_ambient = EnvBRDFApprox(diffuse_color, F_AB(1.0, NdotV)) * occlusion; let diffuse_ambient = EnvBRDFApprox(diffuse_color, F_AB(1.0, NdotV));
let specular_ambient = EnvBRDFApprox(specular_color, F_AB(perceptual_roughness, NdotV)); let specular_ambient = EnvBRDFApprox(specular_color, F_AB(perceptual_roughness, NdotV));
return (diffuse_ambient + specular_ambient) * lights.ambient_color.rgb; return (diffuse_ambient + specular_ambient) * lights.ambient_color.rgb * occlusion;
} }

View File

@ -1,6 +1,6 @@
#define_import_path bevy_pbr::pbr_bindings #define_import_path bevy_pbr::pbr_bindings
#import bevy_pbr::pbr_types #import bevy_pbr::pbr_types StandardMaterial
@group(1) @binding(0) @group(1) @binding(0)
var<uniform> material: StandardMaterial; var<uniform> material: StandardMaterial;

View File

@ -4,20 +4,32 @@
#import bevy_core_pipeline::tonemapping #import bevy_core_pipeline::tonemapping
#endif #endif
#import bevy_pbr::pbr_types as pbr_types
#import bevy_pbr::pbr_bindings as pbr_bindings
#import bevy_pbr::mesh_view_bindings as view_bindings
#import bevy_pbr::mesh_view_types as mesh_view_types
#import bevy_pbr::lighting as lighting
#import bevy_pbr::clustered_forward as clustering
#import bevy_pbr::shadows as shadows
#import bevy_pbr::fog as fog
#import bevy_pbr::ambient as ambient
#ifdef ENVIRONMENT_MAP #ifdef ENVIRONMENT_MAP
#import bevy_pbr::environment_map #import bevy_pbr::environment_map
#endif #endif
fn alpha_discard(material: StandardMaterial, output_color: vec4<f32>) -> vec4<f32> { #import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::mesh_types MESH_FLAGS_SHADOW_RECEIVER_BIT
fn alpha_discard(material: pbr_types::StandardMaterial, output_color: vec4<f32>) -> vec4<f32> {
var color = output_color; var color = output_color;
let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; let alpha_mode = material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE { if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
// NOTE: If rendering as opaque, alpha should be ignored so set to 1.0 // NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
color.a = 1.0; color.a = 1.0;
} }
#ifdef MAY_DISCARD #ifdef MAY_DISCARD
else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK { else if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
if color.a >= material.alpha_cutoff { if color.a >= material.alpha_cutoff {
// NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque // NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
color.a = 1.0; color.a = 1.0;
@ -82,8 +94,8 @@ fn apply_normal_mapping(
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
#ifdef STANDARDMATERIAL_NORMAL_MAP #ifdef STANDARDMATERIAL_NORMAL_MAP
// Nt is the tangent-space normal. // Nt is the tangent-space normal.
var Nt = textureSampleBias(normal_map_texture, normal_map_sampler, uv, view.mip_bias).rgb; var Nt = textureSampleBias(pbr_bindings::normal_map_texture, pbr_bindings::normal_map_sampler, uv, view_bindings::view.mip_bias).rgb;
if (standard_material_flags & STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u { if (standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u {
// Only use the xy components and derive z for 2-component normal maps. // Only use the xy components and derive z for 2-component normal maps.
Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0); Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0);
Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y); Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y);
@ -91,7 +103,7 @@ fn apply_normal_mapping(
Nt = Nt * 2.0 - 1.0; Nt = Nt * 2.0 - 1.0;
} }
// Normal maps authored for DirectX require flipping the y component // Normal maps authored for DirectX require flipping the y component
if (standard_material_flags & STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u { if (standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u {
Nt.y = -Nt.y; Nt.y = -Nt.y;
} }
// NOTE: The mikktspace method of normal mapping applies maps the tangent-space normal from // NOTE: The mikktspace method of normal mapping applies maps the tangent-space normal from
@ -116,16 +128,16 @@ fn calculate_view(
var V: vec3<f32>; var V: vec3<f32>;
if is_orthographic { if is_orthographic {
// Orthographic view vector // Orthographic view vector
V = normalize(vec3<f32>(view.view_proj[0].z, view.view_proj[1].z, view.view_proj[2].z)); V = normalize(vec3<f32>(view_bindings::view.view_proj[0].z, view_bindings::view.view_proj[1].z, view_bindings::view.view_proj[2].z));
} else { } else {
// Only valid for a perpective projection // Only valid for a perpective projection
V = normalize(view.world_position.xyz - world_position.xyz); V = normalize(view_bindings::view.world_position.xyz - world_position.xyz);
} }
return V; return V;
} }
struct PbrInput { struct PbrInput {
material: StandardMaterial, material: pbr_types::StandardMaterial,
occlusion: vec3<f32>, occlusion: vec3<f32>,
frag_coord: vec4<f32>, frag_coord: vec4<f32>,
world_position: vec4<f32>, world_position: vec4<f32>,
@ -145,7 +157,7 @@ struct PbrInput {
fn pbr_input_new() -> PbrInput { fn pbr_input_new() -> PbrInput {
var pbr_input: PbrInput; var pbr_input: PbrInput;
pbr_input.material = standard_material_new(); pbr_input.material = pbr_types::standard_material_new();
pbr_input.occlusion = vec3<f32>(1.0); pbr_input.occlusion = vec3<f32>(1.0);
pbr_input.frag_coord = vec4<f32>(0.0, 0.0, 0.0, 1.0); pbr_input.frag_coord = vec4<f32>(0.0, 0.0, 0.0, 1.0);
@ -174,7 +186,7 @@ fn pbr(
// calculate non-linear roughness from linear perceptualRoughness // calculate non-linear roughness from linear perceptualRoughness
let metallic = in.material.metallic; let metallic = in.material.metallic;
let perceptual_roughness = in.material.perceptual_roughness; let perceptual_roughness = in.material.perceptual_roughness;
let roughness = perceptualRoughnessToRoughness(perceptual_roughness); let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness);
let occlusion = in.occlusion; let occlusion = in.occlusion;
@ -193,64 +205,65 @@ fn pbr(
let R = reflect(-in.V, in.N); let R = reflect(-in.V, in.N);
let f_ab = F_AB(perceptual_roughness, NdotV); let f_ab = lighting::F_AB(perceptual_roughness, NdotV);
var direct_light: vec3<f32> = vec3<f32>(0.0); var direct_light: vec3<f32> = vec3<f32>(0.0);
let view_z = dot(vec4<f32>( let view_z = dot(vec4<f32>(
view.inverse_view[0].z, view_bindings::view.inverse_view[0].z,
view.inverse_view[1].z, view_bindings::view.inverse_view[1].z,
view.inverse_view[2].z, view_bindings::view.inverse_view[2].z,
view.inverse_view[3].z view_bindings::view.inverse_view[3].z
), in.world_position); ), in.world_position);
let cluster_index = fragment_cluster_index(in.frag_coord.xy, view_z, in.is_orthographic); let cluster_index = clustering::fragment_cluster_index(in.frag_coord.xy, view_z, in.is_orthographic);
let offset_and_counts = unpack_offset_and_counts(cluster_index); let offset_and_counts = clustering::unpack_offset_and_counts(cluster_index);
// Point lights (direct) // Point lights (direct)
for (var i: u32 = offset_and_counts[0]; i < offset_and_counts[0] + offset_and_counts[1]; i = i + 1u) { for (var i: u32 = offset_and_counts[0]; i < offset_and_counts[0] + offset_and_counts[1]; i = i + 1u) {
let light_id = get_light_id(i); let light_id = clustering::get_light_id(i);
var shadow: f32 = 1.0; var shadow: f32 = 1.0;
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
&& (point_lights.data[light_id].flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { && (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
shadow = fetch_point_shadow(light_id, in.world_position, in.world_normal); shadow = shadows::fetch_point_shadow(light_id, in.world_position, in.world_normal);
} }
let light_contrib = point_light(in.world_position.xyz, light_id, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color); let light_contrib = lighting::point_light(in.world_position.xyz, light_id, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color);
direct_light += light_contrib * shadow; direct_light += light_contrib * shadow;
} }
// Spot lights (direct) // Spot lights (direct)
for (var i: u32 = offset_and_counts[0] + offset_and_counts[1]; i < offset_and_counts[0] + offset_and_counts[1] + offset_and_counts[2]; i = i + 1u) { for (var i: u32 = offset_and_counts[0] + offset_and_counts[1]; i < offset_and_counts[0] + offset_and_counts[1] + offset_and_counts[2]; i = i + 1u) {
let light_id = get_light_id(i); let light_id = clustering::get_light_id(i);
var shadow: f32 = 1.0; var shadow: f32 = 1.0;
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
&& (point_lights.data[light_id].flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { && (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
shadow = fetch_spot_shadow(light_id, in.world_position, in.world_normal); shadow = shadows::fetch_spot_shadow(light_id, in.world_position, in.world_normal);
} }
let light_contrib = spot_light(in.world_position.xyz, light_id, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color); let light_contrib = lighting::spot_light(in.world_position.xyz, light_id, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color);
direct_light += light_contrib * shadow; direct_light += light_contrib * shadow;
} }
// Directional lights (direct) // directional lights (direct)
let n_directional_lights = lights.n_directional_lights; let n_directional_lights = view_bindings::lights.n_directional_lights;
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) { for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
var shadow: f32 = 1.0; var shadow: f32 = 1.0;
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
&& (lights.directional_lights[i].flags & DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { && (view_bindings::lights.directional_lights[i].flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
shadow = fetch_directional_shadow(i, in.world_position, in.world_normal, view_z); shadow = shadows::fetch_directional_shadow(i, in.world_position, in.world_normal, view_z);
} }
var light_contrib = directional_light(i, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color); var light_contrib = lighting::directional_light(i, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color);
#ifdef DIRECTIONAL_LIGHT_SHADOW_MAP_DEBUG_CASCADES #ifdef DIRECTIONAL_LIGHT_SHADOW_MAP_DEBUG_CASCADES
light_contrib = cascade_debug_visualization(light_contrib, i, view_z); light_contrib = shadows::cascade_debug_visualization(light_contrib, i, view_z);
#endif #endif
direct_light += light_contrib * shadow; direct_light += light_contrib * shadow;
} }
// Ambient light (indirect) // Ambient light (indirect)
var indirect_light = ambient_light(in.world_position, in.N, in.V, NdotV, diffuse_color, F0, perceptual_roughness, occlusion); var indirect_light = ambient::ambient_light(in.world_position, in.N, in.V, NdotV, diffuse_color, F0, perceptual_roughness, occlusion);
// Environment map light (indirect) // Environment map light (indirect)
#ifdef ENVIRONMENT_MAP #ifdef ENVIRONMENT_MAP
let environment_light = environment_map_light(perceptual_roughness, roughness, diffuse_color, NdotV, f_ab, in.N, R, F0); let environment_light = bevy_pbr::environment_map::environment_map_light(perceptual_roughness, roughness, diffuse_color, NdotV, f_ab, in.N, R, F0);
indirect_light += (environment_light.diffuse * occlusion) + environment_light.specular; indirect_light += (environment_light.diffuse * occlusion) + environment_light.specular;
#endif #endif
@ -262,7 +275,7 @@ fn pbr(
output_color.a output_color.a
); );
output_color = cluster_debug_visualization( output_color = clustering::cluster_debug_visualization(
output_color, output_color,
view_z, view_z,
in.is_orthographic, in.is_orthographic,
@ -275,7 +288,7 @@ fn pbr(
#endif // PREPASS_FRAGMENT #endif // PREPASS_FRAGMENT
#ifndef PREPASS_FRAGMENT #ifndef PREPASS_FRAGMENT
fn apply_fog(fog_params: Fog, input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_world_position: vec3<f32>) -> vec4<f32> { fn apply_fog(fog_params: mesh_view_types::Fog, input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_world_position: vec3<f32>) -> vec4<f32> {
let view_to_world = fragment_world_position.xyz - view_world_position.xyz; let view_to_world = fragment_world_position.xyz - view_world_position.xyz;
// `length()` is used here instead of just `view_to_world.z` since that produces more // `length()` is used here instead of just `view_to_world.z` since that produces more
@ -287,9 +300,9 @@ fn apply_fog(fog_params: Fog, input_color: vec4<f32>, fragment_world_position: v
var scattering = vec3<f32>(0.0); var scattering = vec3<f32>(0.0);
if fog_params.directional_light_color.a > 0.0 { if fog_params.directional_light_color.a > 0.0 {
let view_to_world_normalized = view_to_world / distance; let view_to_world_normalized = view_to_world / distance;
let n_directional_lights = lights.n_directional_lights; let n_directional_lights = view_bindings::lights.n_directional_lights;
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) { for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
let light = lights.directional_lights[i]; let light = view_bindings::lights.directional_lights[i];
scattering += pow( scattering += pow(
max( max(
dot(view_to_world_normalized, light.direction_to_light), dot(view_to_world_normalized, light.direction_to_light),
@ -300,14 +313,14 @@ fn apply_fog(fog_params: Fog, input_color: vec4<f32>, fragment_world_position: v
} }
} }
if fog_params.mode == FOG_MODE_LINEAR { if fog_params.mode == mesh_view_types::FOG_MODE_LINEAR {
return linear_fog(fog_params, input_color, distance, scattering); return fog::linear_fog(fog_params, input_color, distance, scattering);
} else if fog_params.mode == FOG_MODE_EXPONENTIAL { } else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL {
return exponential_fog(fog_params, input_color, distance, scattering); return fog::exponential_fog(fog_params, input_color, distance, scattering);
} else if fog_params.mode == FOG_MODE_EXPONENTIAL_SQUARED { } else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL_SQUARED {
return exponential_squared_fog(fog_params, input_color, distance, scattering); return fog::exponential_squared_fog(fog_params, input_color, distance, scattering);
} else if fog_params.mode == FOG_MODE_ATMOSPHERIC { } else if fog_params.mode == mesh_view_types::FOG_MODE_ATMOSPHERIC {
return atmospheric_fog(fog_params, input_color, distance, scattering); return fog::atmospheric_fog(fog_params, input_color, distance, scattering);
} else { } else {
return input_color; return input_color;
} }
@ -324,8 +337,8 @@ fn premultiply_alpha(standard_material_flags: u32, color: vec4<f32>) -> vec4<f32
// For `BlendState::PREMULTIPLIED_ALPHA_BLENDING` the blend function is: // For `BlendState::PREMULTIPLIED_ALPHA_BLENDING` the blend function is:
// //
// result = 1 * src_color + (1 - src_alpha) * dst_color // result = 1 * src_color + (1 - src_alpha) * dst_color
let alpha_mode = standard_material_flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; let alpha_mode = standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD { if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD {
// Here, we premultiply `src_color` by `src_alpha`, and replace `src_alpha` with 0.0: // Here, we premultiply `src_color` by `src_alpha`, and replace `src_alpha` with 0.0:
// //
// src_color *= src_alpha // src_color *= src_alpha

View File

@ -1,5 +1,9 @@
#define_import_path bevy_pbr::lighting #define_import_path bevy_pbr::lighting
#import bevy_pbr::utils PI
#import bevy_pbr::mesh_view_types as view_types
#import bevy_pbr::mesh_view_bindings as view_bindings
// From the Filament design doc // From the Filament design doc
// https://google.github.io/filament/Filament.html#table_symbols // https://google.github.io/filament/Filament.html#table_symbols
// Symbol Definition // Symbol Definition
@ -180,7 +184,7 @@ fn point_light(
f_ab: vec2<f32>, f_ab: vec2<f32>,
diffuseColor: vec3<f32> diffuseColor: vec3<f32>
) -> vec3<f32> { ) -> vec3<f32> {
let light = &point_lights.data[light_id]; let light = &view_bindings::point_lights.data[light_id];
let light_to_frag = (*light).position_radius.xyz - world_position.xyz; let light_to_frag = (*light).position_radius.xyz - world_position.xyz;
let distance_square = dot(light_to_frag, light_to_frag); let distance_square = dot(light_to_frag, light_to_frag);
let rangeAttenuation = getDistanceAttenuation(distance_square, (*light).color_inverse_square_range.w); let rangeAttenuation = getDistanceAttenuation(distance_square, (*light).color_inverse_square_range.w);
@ -244,12 +248,12 @@ fn spot_light(
// reuse the point light calculations // reuse the point light calculations
let point_light = point_light(world_position, light_id, roughness, NdotV, N, V, R, F0, f_ab, diffuseColor); let point_light = point_light(world_position, light_id, roughness, NdotV, N, V, R, F0, f_ab, diffuseColor);
let light = &point_lights.data[light_id]; let light = &view_bindings::point_lights.data[light_id];
// reconstruct spot dir from x/z and y-direction flag // reconstruct spot dir from x/z and y-direction flag
var spot_dir = vec3<f32>((*light).light_custom_data.x, 0.0, (*light).light_custom_data.y); var spot_dir = vec3<f32>((*light).light_custom_data.x, 0.0, (*light).light_custom_data.y);
spot_dir.y = sqrt(max(0.0, 1.0 - spot_dir.x * spot_dir.x - spot_dir.z * spot_dir.z)); spot_dir.y = sqrt(max(0.0, 1.0 - spot_dir.x * spot_dir.x - spot_dir.z * spot_dir.z));
if ((*light).flags & POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE) != 0u { if ((*light).flags & view_types::POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE) != 0u {
spot_dir.y = -spot_dir.y; spot_dir.y = -spot_dir.y;
} }
let light_to_frag = (*light).position_radius.xyz - world_position.xyz; let light_to_frag = (*light).position_radius.xyz - world_position.xyz;
@ -265,7 +269,7 @@ fn spot_light(
} }
fn directional_light(light_id: u32, roughness: f32, NdotV: f32, normal: vec3<f32>, view: vec3<f32>, R: vec3<f32>, F0: vec3<f32>, f_ab: vec2<f32>, diffuseColor: vec3<f32>) -> vec3<f32> { fn directional_light(light_id: u32, roughness: f32, NdotV: f32, normal: vec3<f32>, view: vec3<f32>, R: vec3<f32>, F0: vec3<f32>, f_ab: vec2<f32>, diffuseColor: vec3<f32>) -> vec3<f32> {
let light = &lights.directional_lights[light_id]; let light = &view_bindings::lights.directional_lights[light_id];
let incident_light = (*light).direction_to_light.xyz; let incident_light = (*light).direction_to_light.xyz;

View File

@ -1,5 +1,6 @@
#import bevy_pbr::prepass_bindings #import bevy_pbr::prepass_bindings
#import bevy_pbr::pbr_bindings #import bevy_pbr::pbr_bindings
#import bevy_pbr::pbr_types
#ifdef NORMAL_PREPASS #ifdef NORMAL_PREPASS
#import bevy_pbr::pbr_functions #import bevy_pbr::pbr_functions
#endif // NORMAL_PREPASS #endif // NORMAL_PREPASS
@ -35,24 +36,24 @@ const PREMULTIPLIED_ALPHA_CUTOFF = 0.05;
fn prepass_alpha_discard(in: FragmentInput) { fn prepass_alpha_discard(in: FragmentInput) {
#ifdef MAY_DISCARD #ifdef MAY_DISCARD
var output_color: vec4<f32> = material.base_color; var output_color: vec4<f32> = bevy_pbr::pbr_bindings::material.base_color;
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
if (material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u { if (bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u {
output_color = output_color * textureSampleBias(base_color_texture, base_color_sampler, in.uv, view.mip_bias); output_color = output_color * textureSampleBias(bevy_pbr::pbr_bindings::base_color_texture, bevy_pbr::pbr_bindings::base_color_sampler, in.uv, bevy_pbr::prepass_bindings::view.mip_bias);
} }
#endif // VERTEX_UVS #endif // VERTEX_UVS
let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; let alpha_mode = bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK { if alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
if output_color.a < material.alpha_cutoff { if output_color.a < bevy_pbr::pbr_bindings::material.alpha_cutoff {
discard; discard;
} }
} else if (alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD) { } else if (alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD) {
if output_color.a < PREMULTIPLIED_ALPHA_CUTOFF { if output_color.a < PREMULTIPLIED_ALPHA_CUTOFF {
discard; discard;
} }
} else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED { } else if alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED {
if all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) { if all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) {
discard; discard;
} }
@ -88,15 +89,15 @@ fn fragment(in: FragmentInput) -> FragmentOutput {
#ifdef NORMAL_PREPASS #ifdef NORMAL_PREPASS
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
if (material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u { if (bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u {
let world_normal = prepare_world_normal( let world_normal = bevy_pbr::pbr_functions::prepare_world_normal(
in.world_normal, in.world_normal,
(material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u, (bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
in.is_front, in.is_front,
); );
let normal = apply_normal_mapping( let normal = bevy_pbr::pbr_functions::apply_normal_mapping(
material.flags, bevy_pbr::pbr_bindings::material.flags,
world_normal, world_normal,
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
#ifdef STANDARDMATERIAL_NORMAL_MAP #ifdef STANDARDMATERIAL_NORMAL_MAP
@ -115,9 +116,9 @@ fn fragment(in: FragmentInput) -> FragmentOutput {
#endif // NORMAL_PREPASS #endif // NORMAL_PREPASS
#ifdef MOTION_VECTOR_PREPASS #ifdef MOTION_VECTOR_PREPASS
let clip_position_t = view.unjittered_view_proj * in.world_position; let clip_position_t = bevy_pbr::prepass_bindings::view.unjittered_view_proj * in.world_position;
let clip_position = clip_position_t.xy / clip_position_t.w; let clip_position = clip_position_t.xy / clip_position_t.w;
let previous_clip_position_t = previous_view_proj * in.previous_world_position; let previous_clip_position_t = bevy_pbr::prepass_bindings::previous_view_proj * in.previous_world_position;
let previous_clip_position = previous_clip_position_t.xy / previous_clip_position_t.w; let previous_clip_position = previous_clip_position_t.xy / previous_clip_position_t.w;
// These motion vectors are used as offsets to UV positions and are stored // These motion vectors are used as offsets to UV positions and are stored
// in the range -1,1 to allow offsetting from the one corner to the // in the range -1,1 to allow offsetting from the one corner to the

View File

@ -1,9 +1,12 @@
#define_import_path bevy_pbr::shadows #define_import_path bevy_pbr::shadows
#import bevy_pbr::mesh_view_types POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE
#import bevy_pbr::mesh_view_bindings as view_bindings
#import bevy_pbr::utils hsv2rgb
const flip_z: vec3<f32> = vec3<f32>(1.0, 1.0, -1.0); const flip_z: vec3<f32> = vec3<f32>(1.0, 1.0, -1.0);
fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 { fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
let light = &point_lights.data[light_id]; let light = &view_bindings::point_lights.data[light_id];
// because the shadow maps align with the axes and the frustum planes are at 45 degrees // because the shadow maps align with the axes and the frustum planes are at 45 degrees
// we can get the worldspace depth by taking the largest absolute axis // we can get the worldspace depth by taking the largest absolute axis
@ -38,14 +41,14 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: v
// mip-mapping functionality. The shadow maps have no mipmaps so Level just samples // mip-mapping functionality. The shadow maps have no mipmaps so Level just samples
// from LOD 0. // from LOD 0.
#ifdef NO_ARRAY_TEXTURES_SUPPORT #ifdef NO_ARRAY_TEXTURES_SUPPORT
return textureSampleCompare(point_shadow_textures, point_shadow_textures_sampler, frag_ls * flip_z, depth); return textureSampleCompare(view_bindings::point_shadow_textures, view_bindings::point_shadow_textures_sampler, frag_ls * flip_z, depth);
#else #else
return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls * flip_z, i32(light_id), depth); return textureSampleCompareLevel(view_bindings::point_shadow_textures, view_bindings::point_shadow_textures_sampler, frag_ls * flip_z, i32(light_id), depth);
#endif #endif
} }
fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 { fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
let light = &point_lights.data[light_id]; let light = &view_bindings::point_lights.data[light_id];
let surface_to_light = (*light).position_radius.xyz - frag_position.xyz; let surface_to_light = (*light).position_radius.xyz - frag_position.xyz;
@ -93,16 +96,16 @@ fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: ve
let depth = 0.1 / -projected_position.z; let depth = 0.1 / -projected_position.z;
#ifdef NO_ARRAY_TEXTURES_SUPPORT #ifdef NO_ARRAY_TEXTURES_SUPPORT
return textureSampleCompare(directional_shadow_textures, directional_shadow_textures_sampler, return textureSampleCompare(view_bindings::directional_shadow_textures, view_bindings::directional_shadow_textures_sampler,
shadow_uv, depth); shadow_uv, depth);
#else #else
return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, return textureSampleCompareLevel(view_bindings::directional_shadow_textures, view_bindings::directional_shadow_textures_sampler,
shadow_uv, i32(light_id) + lights.spot_light_shadowmap_offset, depth); shadow_uv, i32(light_id) + view_bindings::lights.spot_light_shadowmap_offset, depth);
#endif #endif
} }
fn get_cascade_index(light_id: u32, view_z: f32) -> u32 { fn get_cascade_index(light_id: u32, view_z: f32) -> u32 {
let light = &lights.directional_lights[light_id]; let light = &view_bindings::lights.directional_lights[light_id];
for (var i: u32 = 0u; i < (*light).num_cascades; i = i + 1u) { for (var i: u32 = 0u; i < (*light).num_cascades; i = i + 1u) {
if (-view_z < (*light).cascades[i].far_bound) { if (-view_z < (*light).cascades[i].far_bound) {
@ -113,7 +116,7 @@ fn get_cascade_index(light_id: u32, view_z: f32) -> u32 {
} }
fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 { fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
let light = &lights.directional_lights[light_id]; let light = &view_bindings::lights.directional_lights[light_id];
let cascade = &(*light).cascades[cascade_index]; let cascade = &(*light).cascades[cascade_index];
// The normal bias is scaled to the texel size. // The normal bias is scaled to the texel size.
@ -143,15 +146,15 @@ fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, s
// sampler to avoid use of implicit derivatives causing possible undefined behavior. // sampler to avoid use of implicit derivatives causing possible undefined behavior.
#ifdef NO_ARRAY_TEXTURES_SUPPORT #ifdef NO_ARRAY_TEXTURES_SUPPORT
return textureSampleCompareLevel( return textureSampleCompareLevel(
directional_shadow_textures, view_bindings::directional_shadow_textures,
directional_shadow_textures_sampler, view_bindings::directional_shadow_textures_sampler,
light_local, light_local,
depth depth
); );
#else #else
return textureSampleCompareLevel( return textureSampleCompareLevel(
directional_shadow_textures, view_bindings::directional_shadow_textures,
directional_shadow_textures_sampler, view_bindings::directional_shadow_textures_sampler,
light_local, light_local,
i32((*light).depth_texture_base_index + cascade_index), i32((*light).depth_texture_base_index + cascade_index),
depth depth
@ -160,7 +163,7 @@ fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, s
} }
fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>, view_z: f32) -> f32 { fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>, view_z: f32) -> f32 {
let light = &lights.directional_lights[light_id]; let light = &view_bindings::lights.directional_lights[light_id];
let cascade_index = get_cascade_index(light_id, view_z); let cascade_index = get_cascade_index(light_id, view_z);
if (cascade_index >= (*light).num_cascades) { if (cascade_index >= (*light).num_cascades) {

View File

@ -1,9 +1,22 @@
// If using this WGSL snippet as an #import, a dedicated
// "joint_matricies" uniform of type SkinnedMesh must be added in the
// main shader.
#define_import_path bevy_pbr::skinning #define_import_path bevy_pbr::skinning
#import bevy_pbr::mesh_types SkinnedMesh
#ifdef SKINNED
#ifdef MESH_BINDGROUP_1
@group(1) @binding(1)
var<uniform> joint_matrices: SkinnedMesh;
#else
@group(2) @binding(1)
var<uniform> joint_matrices: SkinnedMesh;
#endif
fn skin_model( fn skin_model(
indexes: vec4<u32>, indexes: vec4<u32>,
weights: vec4<f32>, weights: vec4<f32>,
@ -14,7 +27,7 @@ fn skin_model(
+ weights.w * joint_matrices.data[indexes.w]; + weights.w * joint_matrices.data[indexes.w];
} }
fn inverse_transpose_3x3(in: mat3x3<f32>) -> mat3x3<f32> { fn inverse_transpose_3x3m(in: mat3x3<f32>) -> mat3x3<f32> {
let x = cross(in[1], in[2]); let x = cross(in[1], in[2]);
let y = cross(in[2], in[0]); let y = cross(in[2], in[0]);
let z = cross(in[0], in[1]); let z = cross(in[0], in[1]);
@ -31,7 +44,7 @@ fn skin_normals(
normal: vec3<f32>, normal: vec3<f32>,
) -> vec3<f32> { ) -> vec3<f32> {
return normalize( return normalize(
inverse_transpose_3x3( inverse_transpose_3x3m(
mat3x3<f32>( mat3x3<f32>(
model[0].xyz, model[0].xyz,
model[1].xyz, model[1].xyz,
@ -40,3 +53,5 @@ fn skin_normals(
) * normal ) * normal
); );
} }
#endif

View File

@ -1,18 +1,10 @@
#import bevy_pbr::mesh_types #import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_functions mesh_position_local_to_clip
@group(1) @binding(0)
var<uniform> mesh: Mesh;
#ifdef SKINNED #ifdef SKINNED
@group(1) @binding(1) #import bevy_pbr::skinning
var<uniform> joint_matrices: SkinnedMesh;
#import bevy_pbr::skinning
#endif #endif
// NOTE: Bindings must come before functions that use them!
#import bevy_pbr::mesh_functions
struct Vertex { struct Vertex {
@location(0) position: vec3<f32>, @location(0) position: vec3<f32>,
#ifdef SKINNED #ifdef SKINNED
@ -28,7 +20,7 @@ struct VertexOutput {
@vertex @vertex
fn vertex(vertex: Vertex) -> VertexOutput { fn vertex(vertex: Vertex) -> VertexOutput {
#ifdef SKINNED #ifdef SKINNED
let model = skin_model(vertex.joint_indexes, vertex.joint_weights); let model = bevy_pbr::skinning::skin_model(vertex.joint_indexes, vertex.joint_weights);
#else #else
let model = mesh.model; let model = mesh.model;
#endif #endif

View File

@ -5,10 +5,10 @@
// Source code heavily based on XeGTAO v1.30 from Intel // Source code heavily based on XeGTAO v1.30 from Intel
// https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli // https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli
#import bevy_pbr::gtao_utils #import bevy_pbr::gtao_utils fast_acos
#import bevy_pbr::utils #import bevy_pbr::utils PI, HALF_PI
#import bevy_render::view #import bevy_render::view View
#import bevy_render::globals #import bevy_render::globals Globals
@group(0) @binding(0) var preprocessed_depth: texture_2d<f32>; @group(0) @binding(0) var preprocessed_depth: texture_2d<f32>;
@group(0) @binding(1) var normals: texture_2d<f32>; @group(0) @binding(1) var normals: texture_2d<f32>;

View File

@ -1,5 +1,7 @@
#define_import_path bevy_pbr::gtao_utils #define_import_path bevy_pbr::gtao_utils
#import bevy_pbr::utils PI, HALF_PI
// Approximates single-bounce ambient occlusion to multi-bounce ambient occlusion // Approximates single-bounce ambient occlusion to multi-bounce ambient occlusion
// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf#page=78 // https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf#page=78
fn gtao_multibounce(visibility: f32, base_color: vec3<f32>) -> vec3<f32> { fn gtao_multibounce(visibility: f32, base_color: vec3<f32>) -> vec3<f32> {

View File

@ -5,7 +5,7 @@
// Reference: https://research.nvidia.com/sites/default/files/pubs/2012-06_Scalable-Ambient-Obscurance/McGuire12SAO.pdf, section 2.2 // Reference: https://research.nvidia.com/sites/default/files/pubs/2012-06_Scalable-Ambient-Obscurance/McGuire12SAO.pdf, section 2.2
#import bevy_render::view #import bevy_render::view View
@group(0) @binding(0) var input_depth: texture_depth_2d; @group(0) @binding(0) var input_depth: texture_depth_2d;
@group(0) @binding(1) var preprocessed_depth_mip0: texture_storage_2d<r16float, write>; @group(0) @binding(1) var preprocessed_depth_mip0: texture_storage_2d<r16float, write>;

View File

@ -9,7 +9,7 @@
// XeGTAO does a 3x3 filter, on two pixels at a time per compute thread, applied twice // XeGTAO does a 3x3 filter, on two pixels at a time per compute thread, applied twice
// We do a 3x3 filter, on 1 pixel per compute thread, applied once // We do a 3x3 filter, on 1 pixel per compute thread, applied once
#import bevy_render::view #import bevy_render::view View
@group(0) @binding(0) var ambient_occlusion_noisy: texture_2d<f32>; @group(0) @binding(0) var ambient_occlusion_noisy: texture_2d<f32>;
@group(0) @binding(1) var depth_differences: texture_2d<u32>; @group(0) @binding(1) var depth_differences: texture_2d<u32>;

View File

@ -96,6 +96,10 @@ impl SpecializedMeshPipeline for WireframePipeline {
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?; let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
descriptor.vertex.shader = self.shader.clone_weak(); descriptor.vertex.shader = self.shader.clone_weak();
descriptor
.vertex
.shader_defs
.push("MESH_BINDGROUP_1".into());
descriptor.fragment.as_mut().unwrap().shader = self.shader.clone_weak(); descriptor.fragment.as_mut().unwrap().shader = self.shader.clone_weak();
descriptor.primitive.polygon_mode = PolygonMode::Line; descriptor.primitive.polygon_mode = PolygonMode::Line;
descriptor.depth_stencil.as_mut().unwrap().bias.slope_scale = 1.0; descriptor.depth_stencil.as_mut().unwrap().bias.slope_scale = 1.0;

View File

@ -57,7 +57,7 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }
image = { version = "0.24", default-features = false } image = { version = "0.24", default-features = false }
# misc # misc
wgpu = { version = "0.16.0" } wgpu = { version = "0.16.0", features=["naga"] }
wgpu-hal = "0.16.0" wgpu-hal = "0.16.0"
codespan-reporting = "0.11.0" codespan-reporting = "0.11.0"
naga = { version = "0.12.0", features = ["wgsl-in"] } naga = { version = "0.12.0", features = ["wgsl-in"] }
@ -76,6 +76,7 @@ parking_lot = "0.12.1"
regex = "1.5" regex = "1.5"
ddsfile = { version = "0.5.0", optional = true } ddsfile = { version = "0.5.0", optional = true }
ktx2 = { version = "0.3.0", optional = true } ktx2 = { version = "0.3.0", optional = true }
naga_oil = "0.8"
# For ktx2 supercompression # For ktx2 supercompression
flate2 = { version = "1.0.22", optional = true } flate2 = { version = "1.0.22", optional = true }
ruzstd = { version = "0.4.0", optional = true } ruzstd = { version = "0.4.0", optional = true }

View File

@ -1,10 +1,8 @@
use crate::{ use crate::{
render_resource::{ render_resource::{
AsModuleDescriptorError, BindGroupLayout, BindGroupLayoutId, ComputePipeline, BindGroupLayout, BindGroupLayoutId, ComputePipeline, ComputePipelineDescriptor,
ComputePipelineDescriptor, ProcessShaderError, ProcessedShader,
RawComputePipelineDescriptor, RawFragmentState, RawRenderPipelineDescriptor, RawComputePipelineDescriptor, RawFragmentState, RawRenderPipelineDescriptor,
RawVertexState, RenderPipeline, RenderPipelineDescriptor, Shader, ShaderImport, RawVertexState, RenderPipeline, RenderPipelineDescriptor, Shader, ShaderImport, Source,
ShaderProcessor, ShaderReflectError,
}, },
renderer::RenderDevice, renderer::RenderDevice,
Extract, Extract,
@ -17,11 +15,15 @@ use bevy_utils::{
tracing::{debug, error}, tracing::{debug, error},
Entry, HashMap, HashSet, Entry, HashMap, HashSet,
}; };
use naga::valid::Capabilities;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref}; use std::{borrow::Cow, hash::Hash, mem, ops::Deref};
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "shader_format_spirv")]
use wgpu::util::make_spirv;
use wgpu::{ use wgpu::{
PipelineLayoutDescriptor, PushConstantRange, VertexBufferLayout as RawVertexBufferLayout, Features, PipelineLayoutDescriptor, PushConstantRange, ShaderModuleDescriptor,
VertexBufferLayout as RawVertexBufferLayout,
}; };
use crate::render_resource::resource_macros::*; use crate::render_resource::resource_macros::*;
@ -123,13 +125,12 @@ struct ShaderData {
dependents: HashSet<Handle<Shader>>, dependents: HashSet<Handle<Shader>>,
} }
#[derive(Default)]
struct ShaderCache { struct ShaderCache {
data: HashMap<Handle<Shader>, ShaderData>, data: HashMap<Handle<Shader>, ShaderData>,
shaders: HashMap<Handle<Shader>, Shader>, shaders: HashMap<Handle<Shader>, Shader>,
import_path_shaders: HashMap<ShaderImport, Handle<Shader>>, import_path_shaders: HashMap<ShaderImport, Handle<Shader>>,
waiting_on_import: HashMap<ShaderImport, Vec<Handle<Shader>>>, waiting_on_import: HashMap<ShaderImport, Vec<Handle<Shader>>>,
processor: ShaderProcessor, composer: naga_oil::compose::Composer,
} }
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
@ -162,6 +163,78 @@ impl ShaderDefVal {
} }
impl ShaderCache { impl ShaderCache {
fn new(render_device: &RenderDevice) -> Self {
const CAPABILITIES: &[(Features, Capabilities)] = &[
(Features::PUSH_CONSTANTS, Capabilities::PUSH_CONSTANT),
(Features::SHADER_F64, Capabilities::FLOAT64),
(
Features::SHADER_PRIMITIVE_INDEX,
Capabilities::PRIMITIVE_INDEX,
),
(
Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
Capabilities::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
),
(
Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
Capabilities::SAMPLER_NON_UNIFORM_INDEXING,
),
(
Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
Capabilities::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
),
];
let features = render_device.features();
let mut capabilities = Capabilities::empty();
for (feature, capability) in CAPABILITIES {
if features.contains(*feature) {
capabilities |= *capability;
}
}
#[cfg(debug_assertions)]
let composer = naga_oil::compose::Composer::default();
#[cfg(not(debug_assertions))]
let composer = naga_oil::compose::Composer::non_validating();
let composer = composer.with_capabilities(capabilities);
Self {
composer,
data: Default::default(),
shaders: Default::default(),
import_path_shaders: Default::default(),
waiting_on_import: Default::default(),
}
}
fn add_import_to_composer(
composer: &mut naga_oil::compose::Composer,
import_path_shaders: &HashMap<ShaderImport, Handle<Shader>>,
shaders: &HashMap<Handle<Shader>, Shader>,
import: &ShaderImport,
) -> Result<(), PipelineCacheError> {
if !composer.contains_module(import.as_str()) {
if let Some(shader_handle) = import_path_shaders.get(import) {
if let Some(shader) = shaders.get(shader_handle) {
for import in &shader.imports {
Self::add_import_to_composer(
composer,
import_path_shaders,
shaders,
import,
)?;
}
composer.add_composable_module(shader.into())?;
}
}
// if we fail to add a module the composer will tell us what is missing
}
Ok(())
}
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
fn get( fn get(
&mut self, &mut self,
@ -210,19 +283,54 @@ impl ShaderCache {
"processing shader {:?}, with shader defs {:?}", "processing shader {:?}, with shader defs {:?}",
handle, shader_defs handle, shader_defs
); );
let processed = self.processor.process( let shader_source = match &shader.source {
shader, #[cfg(feature = "shader_format_spirv")]
&shader_defs, Source::SpirV(data) => make_spirv(data),
&self.shaders, #[cfg(not(feature = "shader_format_spirv"))]
&self.import_path_shaders, Source::SpirV(_) => {
)?; unimplemented!(
let module_descriptor = match processed "Enable feature \"shader_format_spirv\" to use SPIR-V shaders"
.get_module_descriptor(render_device.features()) )
{
Ok(module_descriptor) => module_descriptor,
Err(err) => {
return Err(PipelineCacheError::AsModuleDescriptorError(err, processed));
} }
_ => {
for import in shader.imports() {
Self::add_import_to_composer(
&mut self.composer,
&self.import_path_shaders,
&self.shaders,
import,
)?;
}
let shader_defs = shader_defs
.into_iter()
.map(|def| match def {
ShaderDefVal::Bool(k, v) => {
(k, naga_oil::compose::ShaderDefValue::Bool(v))
}
ShaderDefVal::Int(k, v) => {
(k, naga_oil::compose::ShaderDefValue::Int(v))
}
ShaderDefVal::UInt(k, v) => {
(k, naga_oil::compose::ShaderDefValue::UInt(v))
}
})
.collect::<std::collections::HashMap<_, _>>();
let naga = self.composer.make_naga_module(
naga_oil::compose::NagaModuleDescriptor {
shader_defs,
..shader.into()
},
)?;
wgpu::ShaderSource::Naga(Cow::Owned(naga))
}
};
let module_descriptor = ShaderModuleDescriptor {
label: None,
source: shader_source,
}; };
render_device render_device
@ -256,6 +364,10 @@ impl ShaderCache {
data.processed_shaders.clear(); data.processed_shaders.clear();
pipelines_to_queue.extend(data.pipelines.iter().cloned()); pipelines_to_queue.extend(data.pipelines.iter().cloned());
shaders_to_clear.extend(data.dependents.iter().map(|h| h.clone_weak())); shaders_to_clear.extend(data.dependents.iter().map(|h| h.clone_weak()));
if let Some(Shader { import_path, .. }) = self.shaders.get(&handle) {
self.composer.remove_composable_module(import_path.as_str());
}
} }
} }
@ -264,7 +376,7 @@ impl ShaderCache {
fn set_shader(&mut self, handle: &Handle<Shader>, shader: Shader) -> Vec<CachedPipelineId> { fn set_shader(&mut self, handle: &Handle<Shader>, shader: Shader) -> Vec<CachedPipelineId> {
let pipelines_to_queue = self.clear(handle); let pipelines_to_queue = self.clear(handle);
if let Some(path) = shader.import_path() { let path = shader.import_path();
self.import_path_shaders self.import_path_shaders
.insert(path.clone(), handle.clone_weak()); .insert(path.clone(), handle.clone_weak());
if let Some(waiting_shaders) = self.waiting_on_import.get_mut(path) { if let Some(waiting_shaders) = self.waiting_on_import.get_mut(path) {
@ -278,7 +390,6 @@ impl ShaderCache {
data.dependents.insert(waiting_shader.clone_weak()); data.dependents.insert(waiting_shader.clone_weak());
} }
} }
}
for import in shader.imports() { for import in shader.imports() {
if let Some(import_handle) = self.import_path_shaders.get(import) { if let Some(import_handle) = self.import_path_shaders.get(import) {
@ -302,9 +413,7 @@ impl ShaderCache {
fn remove(&mut self, handle: &Handle<Shader>) -> Vec<CachedPipelineId> { fn remove(&mut self, handle: &Handle<Shader>) -> Vec<CachedPipelineId> {
let pipelines_to_queue = self.clear(handle); let pipelines_to_queue = self.clear(handle);
if let Some(shader) = self.shaders.remove(handle) { if let Some(shader) = self.shaders.remove(handle) {
if let Some(import_path) = shader.import_path() { self.import_path_shaders.remove(shader.import_path());
self.import_path_shaders.remove(import_path);
}
} }
pipelines_to_queue pipelines_to_queue
@ -373,9 +482,9 @@ impl PipelineCache {
/// Create a new pipeline cache associated with the given render device. /// Create a new pipeline cache associated with the given render device.
pub fn new(device: RenderDevice) -> Self { pub fn new(device: RenderDevice) -> Self {
Self { Self {
shader_cache: ShaderCache::new(&device),
device, device,
layout_cache: default(), layout_cache: default(),
shader_cache: default(),
waiting_pipelines: default(), waiting_pipelines: default(),
new_pipelines: default(), new_pipelines: default(),
pipelines: default(), pipelines: default(),
@ -697,11 +806,8 @@ impl PipelineCache {
} }
// shader could not be processed ... retrying won't help // shader could not be processed ... retrying won't help
PipelineCacheError::ProcessShaderError(err) => { PipelineCacheError::ProcessShaderError(err) => {
error!("failed to process shader: {}", err); let error_detail = err.emit_to_string(&self.shader_cache.composer);
continue; error!("failed to process shader:\n{}", error_detail);
}
PipelineCacheError::AsModuleDescriptorError(err, source) => {
log_shader_error(source, err);
continue; continue;
} }
PipelineCacheError::CreateShaderModule(description) => { PipelineCacheError::CreateShaderModule(description) => {
@ -737,101 +843,6 @@ impl PipelineCache {
} }
} }
fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) {
use codespan_reporting::{
diagnostic::{Diagnostic, Label},
files::SimpleFile,
term,
};
match error {
AsModuleDescriptorError::ShaderReflectError(error) => match error {
ShaderReflectError::WgslParse(error) => {
let source = source
.get_wgsl_source()
.expect("non-wgsl source for wgsl error");
let msg = error.emit_to_string(source);
error!("failed to process shader:\n{}", msg);
}
#[cfg(feature = "shader_format_glsl")]
ShaderReflectError::GlslParse(errors) => {
let source = source
.get_glsl_source()
.expect("non-glsl source for glsl error");
let files = SimpleFile::new("glsl", source);
let config = codespan_reporting::term::Config::default();
let mut writer = term::termcolor::Ansi::new(Vec::new());
for err in errors {
let mut diagnostic = Diagnostic::error().with_message(err.kind.to_string());
if let Some(range) = err.meta.to_range() {
diagnostic = diagnostic.with_labels(vec![Label::primary((), range)]);
}
term::emit(&mut writer, &config, &files, &diagnostic)
.expect("cannot write error");
}
let msg = writer.into_inner();
let msg = String::from_utf8_lossy(&msg);
error!("failed to process shader: \n{}", msg);
}
#[cfg(feature = "shader_format_spirv")]
ShaderReflectError::SpirVParse(error) => {
error!("failed to process shader:\n{}", error);
}
ShaderReflectError::Validation(error) => {
let (filename, source) = match source {
ProcessedShader::Wgsl(source) => ("wgsl", source.as_ref()),
ProcessedShader::Glsl(source, _) => ("glsl", source.as_ref()),
ProcessedShader::SpirV(_) => {
error!("failed to process shader:\n{}", error);
return;
}
};
let files = SimpleFile::new(filename, source);
let config = term::Config::default();
let mut writer = term::termcolor::Ansi::new(Vec::new());
let diagnostic = Diagnostic::error()
.with_message(error.to_string())
.with_labels(
error
.spans()
.map(|(span, desc)| {
Label::primary((), span.to_range().unwrap())
.with_message(desc.to_owned())
})
.collect(),
)
.with_notes(
ErrorSources::of(error)
.map(|source| source.to_string())
.collect(),
);
term::emit(&mut writer, &config, &files, &diagnostic).expect("cannot write error");
let msg = writer.into_inner();
let msg = String::from_utf8_lossy(&msg);
error!("failed to process shader: \n{}", msg);
}
},
#[cfg(feature = "shader_format_glsl")]
AsModuleDescriptorError::WgslConversion(error) => {
error!("failed to convert shader to wgsl: \n{}", error);
}
#[cfg(feature = "shader_format_spirv")]
AsModuleDescriptorError::SpirVConversion(error) => {
error!("failed to convert shader to spirv: \n{}", error);
}
}
}
/// Type of error returned by a [`PipelineCache`] when the creation of a GPU pipeline object failed. /// Type of error returned by a [`PipelineCache`] when the creation of a GPU pipeline object failed.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum PipelineCacheError { pub enum PipelineCacheError {
@ -840,35 +851,9 @@ pub enum PipelineCacheError {
)] )]
ShaderNotLoaded(Handle<Shader>), ShaderNotLoaded(Handle<Shader>),
#[error(transparent)] #[error(transparent)]
ProcessShaderError(#[from] ProcessShaderError), ProcessShaderError(#[from] naga_oil::compose::ComposerError),
#[error("{0}")]
AsModuleDescriptorError(AsModuleDescriptorError, ProcessedShader),
#[error("Shader import not yet available.")] #[error("Shader import not yet available.")]
ShaderImportNotYetAvailable, ShaderImportNotYetAvailable,
#[error("Could not create shader module: {0}")] #[error("Could not create shader module: {0}")]
CreateShaderModule(String), CreateShaderModule(String),
} }
struct ErrorSources<'a> {
current: Option<&'a (dyn std::error::Error + 'static)>,
}
impl<'a> ErrorSources<'a> {
fn of(error: &'a dyn std::error::Error) -> Self {
Self {
current: error.source(),
}
}
}
impl<'a> Iterator for ErrorSources<'a> {
type Item = &'a (dyn std::error::Error + 'static);
fn next(&mut self) -> Option<Self::Item> {
let current = self.current;
self.current = self.current.and_then(std::error::Error::source);
current
}
}
impl<'a> FusedIterator for ErrorSources<'a> {}

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,7 @@ pub use texture_atlas::*;
pub use texture_atlas_builder::*; pub use texture_atlas_builder::*;
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::{AddAsset, Assets, Handle, HandleUntyped}; use bevy_asset::{load_internal_asset, AddAsset, Assets, Handle, HandleUntyped};
use bevy_core_pipeline::core_2d::Transparent2d; use bevy_core_pipeline::core_2d::Transparent2d;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
@ -56,9 +56,12 @@ pub enum SpriteSystem {
impl Plugin for SpritePlugin { impl Plugin for SpritePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
let mut shaders = app.world.resource_mut::<Assets<Shader>>(); load_internal_asset!(
let sprite_shader = Shader::from_wgsl(include_str!("render/sprite.wgsl")); app,
shaders.set_untracked(SPRITE_SHADER_HANDLE, sprite_shader); SPRITE_SHADER_HANDLE,
"render/sprite.wgsl",
Shader::from_wgsl
);
app.add_asset::<TextureAtlas>() app.add_asset::<TextureAtlas>()
.register_asset_reflect::<TextureAtlas>() .register_asset_reflect::<TextureAtlas>()
.register_type::<Sprite>() .register_type::<Sprite>()

View File

@ -1,5 +1,6 @@
#import bevy_sprite::mesh2d_types #import bevy_sprite::mesh2d_types Mesh2d
#import bevy_sprite::mesh2d_view_bindings #import bevy_sprite::mesh2d_vertex_output MeshVertexOutput
#import bevy_sprite::mesh2d_view_bindings view
#ifdef TONEMAP_IN_SHADER #ifdef TONEMAP_IN_SHADER
#import bevy_core_pipeline::tonemapping #import bevy_core_pipeline::tonemapping
@ -19,24 +20,19 @@ var texture: texture_2d<f32>;
@group(1) @binding(2) @group(1) @binding(2)
var texture_sampler: sampler; var texture_sampler: sampler;
@group(2) @binding(0)
var<uniform> mesh: Mesh2d;
struct FragmentInput {
#import bevy_sprite::mesh2d_vertex_output
};
@fragment @fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> { fn fragment(
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
var output_color: vec4<f32> = material.color; var output_color: vec4<f32> = material.color;
#ifdef VERTEX_COLORS #ifdef VERTEX_COLORS
output_color = output_color * in.color; output_color = output_color * mesh.color;
#endif #endif
if ((material.flags & COLOR_MATERIAL_FLAGS_TEXTURE_BIT) != 0u) { if ((material.flags & COLOR_MATERIAL_FLAGS_TEXTURE_BIT) != 0u) {
output_color = output_color * textureSample(texture, texture_sampler, in.uv); output_color = output_color * textureSample(texture, texture_sampler, mesh.uv);
} }
#ifdef TONEMAP_IN_SHADER #ifdef TONEMAP_IN_SHADER
output_color = tone_mapping(output_color); output_color = bevy_core_pipeline::tonemapping::tone_mapping(output_color, view.color_grading);
#endif #endif
return output_color; return output_color;
} }

View File

@ -1,8 +1,7 @@
#import bevy_sprite::mesh2d_view_bindings #import bevy_sprite::mesh2d_functions as mesh_functions
#import bevy_sprite::mesh2d_bindings #import bevy_sprite::mesh2d_bindings mesh
#import bevy_sprite::mesh2d_vertex_output MeshVertexOutput
// NOTE: Bindings must come before functions that use them! #import bevy_sprite::mesh2d_view_bindings view
#import bevy_sprite::mesh2d_functions
#ifdef TONEMAP_IN_SHADER #ifdef TONEMAP_IN_SHADER
#import bevy_core_pipeline::tonemapping #import bevy_core_pipeline::tonemapping
@ -26,30 +25,30 @@ struct Vertex {
#endif #endif
}; };
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
#import bevy_sprite::mesh2d_vertex_output
}
@vertex @vertex
fn vertex(vertex: Vertex) -> VertexOutput { fn vertex(vertex: Vertex) -> MeshVertexOutput {
var out: VertexOutput; var out: MeshVertexOutput;
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
out.uv = vertex.uv; out.uv = vertex.uv;
#endif #endif
#ifdef VERTEX_POSITIONS #ifdef VERTEX_POSITIONS
out.world_position = mesh2d_position_local_to_world(mesh.model, vec4<f32>(vertex.position, 1.0)); out.world_position = mesh_functions::mesh2d_position_local_to_world(
out.clip_position = mesh2d_position_world_to_clip(out.world_position); mesh.model,
vec4<f32>(vertex.position, 1.0)
);
out.position = mesh_functions::mesh2d_position_world_to_clip(out.world_position);
#endif #endif
#ifdef VERTEX_NORMALS #ifdef VERTEX_NORMALS
out.world_normal = mesh2d_normal_local_to_world(vertex.normal); out.world_normal = mesh_functions::mesh2d_normal_local_to_world(vertex.normal);
#endif #endif
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
out.world_tangent = mesh2d_tangent_local_to_world(mesh.model, vertex.tangent); out.world_tangent = mesh_functions::mesh2d_tangent_local_to_world(
mesh.model,
vertex.tangent
);
#endif #endif
#ifdef VERTEX_COLORS #ifdef VERTEX_COLORS
@ -58,16 +57,14 @@ fn vertex(vertex: Vertex) -> VertexOutput {
return out; return out;
} }
struct FragmentInput {
#import bevy_sprite::mesh2d_vertex_output
};
@fragment @fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> { fn fragment(
in: MeshVertexOutput,
) -> @location(0) vec4<f32> {
#ifdef VERTEX_COLORS #ifdef VERTEX_COLORS
var color = in.color; var color = in.color;
#ifdef TONEMAP_IN_SHADER #ifdef TONEMAP_IN_SHADER
color = tone_mapping(color); color = bevy_core_pipeline::tonemapping::tone_mapping(color, view.color_grading);
#endif #endif
return color; return color;
#else #else

View File

@ -3,4 +3,4 @@
#import bevy_sprite::mesh2d_types #import bevy_sprite::mesh2d_types
@group(2) @binding(0) @group(2) @binding(0)
var<uniform> mesh: Mesh2d; var<uniform> mesh: bevy_sprite::mesh2d_types::Mesh2d;

View File

@ -1,5 +1,8 @@
#define_import_path bevy_sprite::mesh2d_functions #define_import_path bevy_sprite::mesh2d_functions
#import bevy_sprite::mesh2d_view_bindings view
#import bevy_sprite::mesh2d_bindings mesh
fn mesh2d_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> { fn mesh2d_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
return model * vertex_position; return model * vertex_position;
} }

View File

@ -1,11 +1,16 @@
#define_import_path bevy_sprite::mesh2d_vertex_output #define_import_path bevy_sprite::mesh2d_vertex_output
@location(0) world_position: vec4<f32>, struct MeshVertexOutput {
@location(1) world_normal: vec3<f32>, // this is `clip position` when the struct is used as a vertex stage output
@location(2) uv: vec2<f32>, // and `frag coord` when used as a fragment stage input
#ifdef VERTEX_TANGENTS @builtin(position) position: vec4<f32>,
@location(3) world_tangent: vec4<f32>, @location(0) world_position: vec4<f32>,
#endif @location(1) world_normal: vec3<f32>,
#ifdef VERTEX_COLORS @location(2) uv: vec2<f32>,
@location(4) color: vec4<f32>, #ifdef VERTEX_TANGENTS
#endif @location(3) world_tangent: vec4<f32>,
#endif
#ifdef VERTEX_COLORS
@location(4) color: vec4<f32>,
#endif
}

View File

@ -1,6 +1,7 @@
#define_import_path bevy_sprite::mesh2d_view_bindings #define_import_path bevy_sprite::mesh2d_view_bindings
#import bevy_sprite::mesh2d_view_types #import bevy_render::view View
#import bevy_render::globals Globals
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> view: View; var<uniform> view: View;

View File

@ -2,7 +2,7 @@
#import bevy_core_pipeline::tonemapping #import bevy_core_pipeline::tonemapping
#endif #endif
#import bevy_render::view #import bevy_render::view View
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> view: View; var<uniform> view: View;
@ -45,7 +45,7 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
#endif #endif
#ifdef TONEMAP_IN_SHADER #ifdef TONEMAP_IN_SHADER
color = tone_mapping(color); color = bevy_core_pipeline::tonemapping::tone_mapping(color, view.color_grading);
#endif #endif
return color; return color;

View File

@ -111,7 +111,7 @@ impl Plugin for TextPlugin {
app, app,
DEFAULT_FONT_HANDLE, DEFAULT_FONT_HANDLE,
"FiraMono-subset.ttf", "FiraMono-subset.ttf",
|bytes: &[u8]| { Font::try_from_bytes(bytes.to_vec()).unwrap() } |bytes: &[u8], _path: String| { Font::try_from_bytes(bytes.to_vec()).unwrap() }
); );
} }
} }

View File

@ -1,4 +1,4 @@
#import bevy_render::view #import bevy_render::view View
const TEXTURED_QUAD: u32 = 0u; const TEXTURED_QUAD: u32 = 0u;

View File

@ -214,14 +214,11 @@ type DrawColoredMesh2d = (
// using `include_str!()`, or loaded like any other asset with `asset_server.load()`. // using `include_str!()`, or loaded like any other asset with `asset_server.load()`.
const COLORED_MESH2D_SHADER: &str = r" const COLORED_MESH2D_SHADER: &str = r"
// Import the standard 2d mesh uniforms and set their bind groups // Import the standard 2d mesh uniforms and set their bind groups
#import bevy_sprite::mesh2d_types #import bevy_sprite::mesh2d_types as MeshTypes
#import bevy_sprite::mesh2d_view_bindings #import bevy_sprite::mesh2d_functions as MeshFunctions
@group(1) @binding(0) @group(1) @binding(0)
var<uniform> mesh: Mesh2d; var<uniform> mesh: MeshTypes::Mesh2d;
// NOTE: Bindings must come before functions that use them!
#import bevy_sprite::mesh2d_functions
// The structure of the vertex buffer is as specified in `specialize()` // The structure of the vertex buffer is as specified in `specialize()`
struct Vertex { struct Vertex {
@ -241,7 +238,7 @@ struct VertexOutput {
fn vertex(vertex: Vertex) -> VertexOutput { fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
// Project the world position of the mesh into screen position // Project the world position of the mesh into screen position
out.clip_position = mesh2d_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0)); out.clip_position = MeshFunctions::mesh2d_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
// Unpack the `u32` from the vertex buffer into the `vec4<f32>` used by the fragment shader // Unpack the `u32` from the vertex buffer into the `vec4<f32>` used by the fragment shader
out.color = vec4<f32>((vec4<u32>(vertex.color) >> vec4<u32>(0u, 8u, 16u, 24u)) & vec4<u32>(255u)) / 255.0; out.color = vec4<f32>((vec4<u32>(vertex.color) >> vec4<u32>(0u, 8u, 16u, 24u)) & vec4<u32>(255u)) / 255.0;
return out; return out;
@ -273,7 +270,7 @@ impl Plugin for ColoredMesh2dPlugin {
let mut shaders = app.world.resource_mut::<Assets<Shader>>(); let mut shaders = app.world.resource_mut::<Assets<Shader>>();
shaders.set_untracked( shaders.set_untracked(
COLORED_MESH2D_SHADER_HANDLE, COLORED_MESH2D_SHADER_HANDLE,
Shader::from_wgsl(COLORED_MESH2D_SHADER), Shader::from_wgsl(COLORED_MESH2D_SHADER, file!()),
); );
// Register our custom draw function, and add our render systems // Register our custom draw function, and add our render systems

View File

@ -194,6 +194,15 @@ impl SpecializedMeshPipeline for CustomPipeline {
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayout,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?; let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
// meshes typically live in bind group 2. because we are using bindgroup 1
// we need to add MESH_BINDGROUP_1 shader def so that the bindings are correctly
// linked in the shader
descriptor
.vertex
.shader_defs
.push("MESH_BINDGROUP_1".into());
descriptor.vertex.shader = self.shader.clone(); descriptor.vertex.shader = self.shader.clone();
descriptor.vertex.buffers.push(VertexBufferLayout { descriptor.vertex.buffers.push(VertexBufferLayout {
array_stride: std::mem::size_of::<InstanceData>() as u64, array_stride: std::mem::size_of::<InstanceData>() as u64,