mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
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:
parent
0f4d16aa3c
commit
10f5c92068
69 changed files with 950 additions and 3144 deletions
|
@ -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
|
||||
#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> {
|
||||
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
|
||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
||||
fn fragment(in: MeshVertexOutput) -> @location(0) vec4<f32> {
|
||||
let speed = 2.0;
|
||||
// The globals binding contains various global values like time
|
||||
// which is the time since startup in seconds
|
||||
|
|
|
@ -1,60 +1,51 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_bindings
|
||||
|
||||
#import bevy_pbr::pbr_types
|
||||
#import bevy_pbr::utils
|
||||
#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
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
#import bevy_pbr::mesh_view_bindings view
|
||||
#import bevy_pbr::pbr_types STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT
|
||||
#import bevy_core_pipeline::tonemapping tone_mapping
|
||||
#import bevy_pbr::pbr_functions as fns
|
||||
|
||||
@group(1) @binding(0)
|
||||
var my_array_texture: texture_2d_array<f32>;
|
||||
@group(1) @binding(1)
|
||||
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
|
||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
||||
let layer = i32(in.world_position.x) & 0x3;
|
||||
fn fragment(
|
||||
@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
|
||||
// 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
|
||||
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
|
||||
|
||||
pbr_input.frag_coord = in.frag_coord;
|
||||
pbr_input.world_position = in.world_position;
|
||||
pbr_input.world_normal = prepare_world_normal(
|
||||
in.world_normal,
|
||||
pbr_input.frag_coord = mesh.position;
|
||||
pbr_input.world_position = mesh.world_position;
|
||||
pbr_input.world_normal = fns::prepare_world_normal(
|
||||
mesh.world_normal,
|
||||
(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.N = apply_normal_mapping(
|
||||
pbr_input.N = fns::apply_normal_mapping(
|
||||
pbr_input.material.flags,
|
||||
pbr_input.world_normal,
|
||||
mesh.world_normal,
|
||||
#ifdef VERTEX_TANGENTS
|
||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||
in.world_tangent,
|
||||
mesh.world_tangent,
|
||||
#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);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
#ifdef CUBEMAP_ARRAY
|
||||
@group(1) @binding(0)
|
||||
|
@ -13,9 +13,9 @@ var base_color_sampler: sampler;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @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(
|
||||
base_color_texture,
|
||||
base_color_sampler,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#import bevy_sprite::mesh2d_view_bindings
|
||||
#import bevy_sprite::mesh2d_bindings
|
||||
#import bevy_sprite::mesh2d_functions
|
||||
#import bevy_sprite::mesh2d_view_bindings globals
|
||||
#import bevy_sprite::mesh2d_bindings mesh
|
||||
#import bevy_sprite::mesh2d_functions mesh2d_position_local_to_clip
|
||||
|
||||
struct Vertex {
|
||||
@location(0) position: vec3<f32>,
|
||||
|
|
|
@ -10,7 +10,11 @@ layout(set = 1, binding = 0) uniform CustomMaterial {
|
|||
layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture;
|
||||
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() {
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
struct CustomMaterial {
|
||||
color: vec4<f32>,
|
||||
};
|
||||
|
@ -11,7 +13,7 @@ var base_color_sampler: sampler;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @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);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::utils
|
||||
#import bevy_pbr::mesh_view_bindings view
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
#import bevy_pbr::utils coords_to_viewport_uv
|
||||
|
||||
@group(1) @binding(0)
|
||||
var texture: texture_2d<f32>;
|
||||
|
@ -8,10 +9,9 @@ var texture_sampler: sampler;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
@builtin(position) position: vec4<f32>,
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @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);
|
||||
return color;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_bindings
|
||||
#import bevy_pbr::mesh_bindings mesh
|
||||
#import bevy_pbr::mesh_functions mesh_position_local_to_clip
|
||||
|
||||
struct CustomMaterial {
|
||||
color: vec4<f32>,
|
||||
|
@ -7,9 +7,6 @@ struct CustomMaterial {
|
|||
@group(1) @binding(0)
|
||||
var<uniform> material: CustomMaterial;
|
||||
|
||||
// NOTE: Bindings must come before functions that use them!
|
||||
#import bevy_pbr::mesh_functions
|
||||
|
||||
struct Vertex {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) blend_color: vec4<f32>,
|
||||
|
@ -23,7 +20,10 @@ struct VertexOutput {
|
|||
@vertex
|
||||
fn vertex(vertex: Vertex) -> 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;
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
#import bevy_pbr::mesh_types
|
||||
#import bevy_pbr::mesh_view_bindings
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> mesh: Mesh;
|
||||
|
||||
// NOTE: Bindings must come before functions that use them!
|
||||
#import bevy_pbr::mesh_functions
|
||||
#import bevy_pbr::mesh_functions mesh_position_local_to_clip
|
||||
#import bevy_pbr::mesh_bindings mesh
|
||||
|
||||
struct Vertex {
|
||||
@location(0) position: vec3<f32>,
|
||||
|
@ -25,7 +19,10 @@ struct VertexOutput {
|
|||
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
|
||||
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;
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
struct LineMaterial {
|
||||
color: vec4<f32>,
|
||||
};
|
||||
|
@ -7,7 +9,7 @@ var<uniform> material: LineMaterial;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
return material.color;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
// 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.
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||
|
||||
@group(0) @binding(0)
|
||||
var screen_texture: texture_2d<f32>;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
struct CustomMaterial {
|
||||
color: vec4<f32>,
|
||||
};
|
||||
|
@ -7,7 +9,7 @@ var<uniform> material: CustomMaterial;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
#ifdef IS_RED
|
||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#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::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
struct ShowPrepassSettings {
|
||||
show_depth: u32,
|
||||
|
@ -14,23 +15,22 @@ var<uniform> settings: ShowPrepassSettings;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
@builtin(position) frag_coord: vec4<f32>,
|
||||
#ifdef MULTISAMPLED
|
||||
@builtin(sample_index) sample_index: u32,
|
||||
#endif
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
#ifndef MULTISAMPLED
|
||||
let sample_index = 0u;
|
||||
#endif
|
||||
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);
|
||||
} 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);
|
||||
} 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
@group(1) @binding(0)
|
||||
var textures: binding_array<texture_2d<f32>>;
|
||||
@group(1) @binding(1)
|
||||
|
@ -7,11 +9,11 @@ var nearest_sampler: sampler;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
// 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 inner_uv = fract(uv * 4.0);
|
||||
let inner_uv = fract(mesh.uv * 4.0);
|
||||
return textureSample(textures[index], nearest_sampler, inner_uv);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
#import bevy_pbr::mesh_view_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
|
||||
#import bevy_core_pipeline::tonemapping
|
||||
#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
|
||||
// quantized into 24 steps for both axis.
|
||||
fn color_sweep(uv: vec2<f32>) -> vec3<f32> {
|
||||
|
@ -47,7 +42,9 @@ fn continuous_hue(uv: vec2<f32>) -> vec3<f32> {
|
|||
}
|
||||
|
||||
@fragment
|
||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
||||
fn fragment(
|
||||
in: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
var uv = in.uv;
|
||||
var out = vec3(0.0);
|
||||
if uv.y > 0.5 {
|
||||
|
@ -58,7 +55,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||
}
|
||||
var color = vec4(out, 1.0);
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
color = tone_mapping(color);
|
||||
color = tone_mapping(color, bevy_pbr::mesh_view_bindings::view.color_grading);
|
||||
#endif
|
||||
return color;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
/// 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<_>>();
|
||||
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
|
||||
/// using the conventional API. See `DebugAssetServerPlugin`.
|
||||
#[cfg(not(feature = "debug_asset_server"))]
|
||||
#[macro_export]
|
||||
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<_>>();
|
||||
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<_>>();
|
||||
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 {
|
||||
($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{
|
||||
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(),
|
||||
),
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
/// `load_internal_asset` macro.
|
||||
pub fn register_handle_with_loader<A: Asset, T>(
|
||||
_loader: fn(T) -> A,
|
||||
_loader: fn(T, String) -> A,
|
||||
app: &mut DebugAssetApp,
|
||||
handle: HandleUntyped,
|
||||
file_path: &str,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||
|
||||
@group(0) @binding(0)
|
||||
var in_texture: texture_2d<f32>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// 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)
|
||||
var screenTexture: texture_2d<f32>;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#import bevy_render::view
|
||||
#import bevy_render::view View
|
||||
|
||||
@group(0) @binding(0)
|
||||
var skybox: texture_cube<f32>;
|
||||
|
|
|
@ -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)
|
||||
var<uniform> view: View;
|
||||
|
@ -20,11 +22,11 @@ var dt_lut_sampler: sampler;
|
|||
fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
|
||||
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
|
||||
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
|
||||
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
|
||||
output_rgb = powsafe(output_rgb.rgb, 2.2);
|
||||
|
|
|
@ -1,5 +1,19 @@
|
|||
#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> {
|
||||
// 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
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -65,7 +79,7 @@ fn somewhat_boring_display_transform(col: vec3<f32>) -> vec3<f32> {
|
|||
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 final_mult = 0.97;
|
||||
let tm1 = tonemap_curve3(desat_col);
|
||||
let tm1 = tonemap_curve3_(desat_col);
|
||||
|
||||
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.
|
||||
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;
|
||||
|
||||
// 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));
|
||||
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));
|
||||
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> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -270,7 +284,7 @@ fn screen_space_dither(frag_coord: vec2<f32>) -> vec3<f32> {
|
|||
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));
|
||||
|
||||
// Possible future grading:
|
||||
|
@ -282,9 +296,9 @@ fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
|||
// color += color * luma.xxx * 1.0;
|
||||
|
||||
// Linear pre tonemapping grading
|
||||
color = saturation(color, view.color_grading.pre_saturation);
|
||||
color = powsafe(color, view.color_grading.gamma);
|
||||
color = color * powsafe(vec3(2.0), view.color_grading.exposure);
|
||||
color = saturation(color, color_grading.pre_saturation);
|
||||
color = powsafe(color, color_grading.gamma);
|
||||
color = color * powsafe(vec3(2.0), color_grading.exposure);
|
||||
color = max(color, vec3(0.0));
|
||||
|
||||
// tone_mapping
|
||||
|
@ -308,7 +322,7 @@ fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
|||
#endif
|
||||
|
||||
// Perceptual post tonemapping grading
|
||||
color = saturation(color, view.color_grading.post_saturation);
|
||||
color = saturation(color, color_grading.post_saturation);
|
||||
|
||||
return vec4(color, in.a);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#ifdef GIZMO_3D
|
||||
#import bevy_pbr::mesh_view_bindings
|
||||
#else
|
||||
#import bevy_sprite::mesh2d_view_bindings
|
||||
#endif
|
||||
// TODO use common view binding
|
||||
#import bevy_render::view View
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
|
||||
|
||||
struct LineGizmoUniform {
|
||||
line_width: f32,
|
||||
|
|
|
@ -12,10 +12,7 @@ use bevy_ecs::{
|
|||
system::{Query, Res, ResMut, Resource},
|
||||
world::{FromWorld, World},
|
||||
};
|
||||
use bevy_pbr::{
|
||||
MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup, MAX_CASCADES_PER_LIGHT,
|
||||
MAX_DIRECTIONAL_LIGHTS,
|
||||
};
|
||||
use bevy_pbr::{MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup};
|
||||
use bevy_render::{
|
||||
render_asset::RenderAssets,
|
||||
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
|
||||
|
@ -78,15 +75,6 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
|
|||
"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 {
|
||||
shader_defs.push("PERSPECTIVE".into());
|
||||
}
|
||||
|
|
|
@ -30,3 +30,4 @@ bitflags = "2.3"
|
|||
# direct dependency required for derive macro
|
||||
bytemuck = { version = "1", features = ["derive"] }
|
||||
radsort = "0.1"
|
||||
naga_oil = "0.8"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#define_import_path bevy_pbr::environment_map
|
||||
|
||||
#import bevy_pbr::mesh_view_bindings as bindings
|
||||
|
||||
struct EnvironmentMapLight {
|
||||
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
|
||||
// Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform
|
||||
// because textureNumLevels() does not work on WebGL2
|
||||
let radiance_level = perceptual_roughness * f32(lights.environment_map_smallest_specular_mip_level);
|
||||
let irradiance = textureSample(environment_map_diffuse, 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_level = perceptual_roughness * f32(bindings::lights.environment_map_smallest_specular_mip_level);
|
||||
let irradiance = textureSample(bindings::environment_map_diffuse, bindings::environment_map_sampler, vec3(N.xy, -N.z)).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
|
||||
// Useful reference: https://bruop.github.io/ibl
|
||||
|
|
|
@ -31,8 +31,8 @@ use bevy_render::{
|
|||
BindGroupLayoutEntry, BindingResource, BindingType, BlendState, BufferBindingType,
|
||||
ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
|
||||
DynamicUniformBuffer, FragmentState, FrontFace, MultisampleState, PipelineCache,
|
||||
PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderDefVal, ShaderRef,
|
||||
ShaderStages, ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
|
||||
PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderRef, ShaderStages,
|
||||
ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
|
||||
SpecializedMeshPipelines, StencilFaceState, StencilState, TextureSampleType,
|
||||
TextureViewDimension, VertexState,
|
||||
},
|
||||
|
@ -47,7 +47,7 @@ use bevy_utils::tracing::error;
|
|||
use crate::{
|
||||
prepare_lights, setup_morph_and_skinning_defs, AlphaMode, DrawMesh, Material, MaterialPipeline,
|
||||
MaterialPipelineKey, MeshLayouts, MeshPipeline, MeshPipelineKey, MeshUniform, RenderMaterials,
|
||||
SetMaterialBindGroup, SetMeshBindGroup, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS,
|
||||
SetMaterialBindGroup, SetMeshBindGroup,
|
||||
};
|
||||
|
||||
use std::{hash::Hash, marker::PhantomData};
|
||||
|
@ -373,14 +373,6 @@ where
|
|||
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) {
|
||||
shader_defs.push("DEPTH_CLAMP_ORTHO".into());
|
||||
// PERF: This line forces the "prepass fragment shader" to always run in
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#import bevy_pbr::prepass_bindings
|
||||
#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
|
||||
// pass them to custom prepass shaders like pbr_prepass.wgsl.
|
||||
|
@ -83,12 +86,12 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
|||
#endif
|
||||
|
||||
#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
|
||||
var model = mesh.model;
|
||||
#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
|
||||
out.clip_position_unclamped = out.clip_position;
|
||||
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 SKINNED
|
||||
out.world_normal = skin_normals(model, vertex.normal);
|
||||
out.world_normal = bevy_pbr::skinning::skin_normals(model, vertex.normal);
|
||||
#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
|
||||
|
||||
#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 // NORMAL_PREPASS
|
||||
|
||||
#ifdef MOTION_VECTOR_PREPASS
|
||||
out.world_position = 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.world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(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
|
||||
|
||||
return out;
|
||||
|
@ -165,9 +168,9 @@ fn fragment(in: FragmentInput) -> FragmentOutput {
|
|||
#endif // DEPTH_CLAMP_ORTHO
|
||||
|
||||
#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 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;
|
||||
// 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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#define_import_path bevy_pbr::prepass_bindings
|
||||
|
||||
#import bevy_pbr::mesh_view_types
|
||||
#import bevy_render::view View
|
||||
#import bevy_render::globals Globals
|
||||
#import bevy_pbr::mesh_types
|
||||
|
||||
@group(0) @binding(0)
|
||||
|
@ -16,18 +16,4 @@ var<uniform> previous_view_proj: mat4x4<f32>;
|
|||
// Material bindings will be in @group(1)
|
||||
|
||||
@group(2) @binding(0)
|
||||
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
|
||||
var<uniform> mesh: bevy_pbr::mesh_types::Mesh;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#define_import_path bevy_pbr::prepass_utils
|
||||
|
||||
#import bevy_pbr::mesh_view_bindings as view_bindings
|
||||
|
||||
#ifndef DEPTH_PREPASS
|
||||
fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 {
|
||||
#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
|
||||
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
|
||||
return depth_sample;
|
||||
}
|
||||
|
@ -14,9 +16,9 @@ fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 {
|
|||
#ifndef NORMAL_PREPASS
|
||||
fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> {
|
||||
#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
|
||||
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
|
||||
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
|
||||
fn prepass_motion_vector(frag_coord: vec4<f32>, sample_index: u32) -> vec2<f32> {
|
||||
#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
|
||||
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
|
||||
return motion_vector_sample.rg;
|
||||
}
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
#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
|
||||
fn view_z_to_z_slice(view_z: f32, is_orthographic: bool) -> u32 {
|
||||
var z_slice: u32 = 0u;
|
||||
if (is_orthographic) {
|
||||
// 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 {
|
||||
// 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
|
||||
// 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 {
|
||||
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);
|
||||
// NOTE: Restricting cluster index to avoid undefined behavior when accessing uniform buffer
|
||||
// arrays based on the cluster index.
|
||||
return min(
|
||||
(xy.y * lights.cluster_dimensions.x + xy.x) * lights.cluster_dimensions.z + z_slice,
|
||||
lights.cluster_dimensions.w - 1u
|
||||
(xy.y * bindings::lights.cluster_dimensions.x + xy.x) * bindings::lights.cluster_dimensions.z + z_slice,
|
||||
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;
|
||||
fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> {
|
||||
#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
|
||||
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 ]
|
||||
// [ offset | point light count | spot light count ]
|
||||
return vec3<u32>(
|
||||
|
@ -45,11 +48,11 @@ fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> {
|
|||
|
||||
fn get_light_id(index: u32) -> u32 {
|
||||
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
||||
return cluster_light_index_lists.data[index];
|
||||
return bindings::cluster_light_index_lists.data[index];
|
||||
#else
|
||||
// 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
|
||||
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
|
||||
return (indices >> (8u * (index & ((1u << 2u) - 1u)))) & ((1u << 8u) - 1u);
|
||||
#endif
|
||||
|
@ -69,9 +72,9 @@ fn cluster_debug_visualization(
|
|||
var z_slice: u32 = view_z_to_z_slice(view_z, is_orthographic);
|
||||
// A hack to make the colors alternate a bit more
|
||||
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>(
|
||||
(1.0 - cluster_overlay_alpha) * output_color.rgb + cluster_overlay_alpha * slice_color,
|
||||
output_color.a
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#define_import_path bevy_pbr::fog
|
||||
|
||||
#import bevy_pbr::mesh_view_bindings fog
|
||||
#import bevy_pbr::mesh_view_types Fog
|
||||
|
||||
// Fog formulas adapted from:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas
|
||||
// https://catlikecoding.com/unity/tutorials/rendering/part-14/
|
||||
|
|
|
@ -86,7 +86,17 @@ impl Plugin for MeshRenderPlugin {
|
|||
app,
|
||||
MESH_VIEW_TYPES_HANDLE,
|
||||
"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!(
|
||||
app,
|
||||
|
@ -709,15 +719,6 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
|||
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) {
|
||||
shader_defs.push("VERTEX_UVS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_bindings
|
||||
|
||||
// NOTE: Bindings must come before functions that use them!
|
||||
#import bevy_pbr::mesh_functions
|
||||
#import bevy_pbr::mesh_functions as mesh_functions
|
||||
#import bevy_pbr::skinning
|
||||
#import bevy_pbr::morph
|
||||
#import bevy_pbr::mesh_bindings mesh
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
struct Vertex {
|
||||
#ifdef VERTEX_POSITIONS
|
||||
|
@ -29,26 +29,21 @@ struct Vertex {
|
|||
#endif
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
};
|
||||
|
||||
#ifdef MORPH_TARGETS
|
||||
fn morph_vertex(vertex_in: Vertex) -> Vertex {
|
||||
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 ++) {
|
||||
let weight = weight_at(i);
|
||||
let weight = bevy_pbr::morph::weight_at(i);
|
||||
if weight == 0.0 {
|
||||
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
|
||||
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
|
||||
#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
|
||||
}
|
||||
return vertex;
|
||||
|
@ -56,8 +51,8 @@ fn morph_vertex(vertex_in: Vertex) -> Vertex {
|
|||
#endif
|
||||
|
||||
@vertex
|
||||
fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput {
|
||||
var out: MeshVertexOutput;
|
||||
|
||||
#ifdef MORPH_TARGETS
|
||||
var vertex = morph_vertex(vertex_no_morph);
|
||||
|
@ -66,22 +61,22 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
|||
#endif
|
||||
|
||||
#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
|
||||
var model = mesh.model;
|
||||
#endif
|
||||
|
||||
#ifdef VERTEX_NORMALS
|
||||
#ifdef SKINNED
|
||||
out.world_normal = skin_normals(model, vertex.normal);
|
||||
out.world_normal = bevy_pbr::skinning::skin_normals(model, vertex.normal);
|
||||
#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
|
||||
|
||||
#ifdef VERTEX_POSITIONS
|
||||
out.world_position = mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
|
||||
out.clip_position = mesh_position_world_to_clip(out.world_position);
|
||||
out.world_position = mesh_functions::mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
|
||||
out.position = mesh_functions::mesh_position_world_to_clip(out.world_position);
|
||||
#endif
|
||||
|
||||
#ifdef VERTEX_UVS
|
||||
|
@ -89,7 +84,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
|||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#ifdef VERTEX_COLORS
|
||||
|
@ -99,14 +94,12 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
|||
return out;
|
||||
}
|
||||
|
||||
struct FragmentInput {
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
};
|
||||
|
||||
@fragment
|
||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
||||
fn fragment(
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
#ifdef VERTEX_COLORS
|
||||
return in.color;
|
||||
return mesh.color;
|
||||
#else
|
||||
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
|
||||
#endif
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
#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)
|
||||
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
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#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> {
|
||||
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
|
||||
// mesh flag
|
||||
fn sign_determinant_model_3x3() -> f32 {
|
||||
fn sign_determinant_model_3x3m() -> f32 {
|
||||
// bool(u32) is false if 0u else true
|
||||
// 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
|
||||
|
@ -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
|
||||
// situations such as negative scaling.
|
||||
vertex_tangent.w * sign_determinant_model_3x3()
|
||||
vertex_tangent.w * sign_determinant_model_3x3m()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
#define_import_path bevy_pbr::mesh_vertex_output
|
||||
|
||||
@location(0) world_position: vec4<f32>,
|
||||
@location(1) world_normal: vec3<f32>,
|
||||
#ifdef VERTEX_UVS
|
||||
@location(2) uv: vec2<f32>,
|
||||
#endif
|
||||
#ifdef VERTEX_TANGENTS
|
||||
@location(3) world_tangent: vec4<f32>,
|
||||
#endif
|
||||
#ifdef VERTEX_COLORS
|
||||
@location(4) color: vec4<f32>,
|
||||
#endif
|
||||
struct MeshVertexOutput {
|
||||
// this is `clip position` when the struct is used as a vertex stage output
|
||||
// and `frag coord` when used as a fragment stage input
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) world_position: vec4<f32>,
|
||||
@location(1) world_normal: vec3<f32>,
|
||||
#ifdef VERTEX_UVS
|
||||
@location(2) uv: vec2<f32>,
|
||||
#endif
|
||||
#ifdef VERTEX_TANGENTS
|
||||
@location(3) world_tangent: vec4<f32>,
|
||||
#endif
|
||||
#ifdef VERTEX_COLORS
|
||||
@location(4) color: vec4<f32>,
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#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)
|
||||
var<uniform> view: View;
|
||||
@group(0) @binding(1)
|
||||
var<uniform> lights: Lights;
|
||||
var<uniform> lights: types::Lights;
|
||||
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
||||
@group(0) @binding(2)
|
||||
var point_shadow_textures: texture_depth_cube;
|
||||
|
@ -27,24 +29,24 @@ var directional_shadow_textures_sampler: sampler_comparison;
|
|||
|
||||
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
||||
@group(0) @binding(6)
|
||||
var<storage> point_lights: PointLights;
|
||||
var<storage> point_lights: types::PointLights;
|
||||
@group(0) @binding(7)
|
||||
var<storage> cluster_light_index_lists: ClusterLightIndexLists;
|
||||
var<storage> cluster_light_index_lists: types::ClusterLightIndexLists;
|
||||
@group(0) @binding(8)
|
||||
var<storage> cluster_offsets_and_counts: ClusterOffsetsAndCounts;
|
||||
var<storage> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts;
|
||||
#else
|
||||
@group(0) @binding(6)
|
||||
var<uniform> point_lights: PointLights;
|
||||
var<uniform> point_lights: types::PointLights;
|
||||
@group(0) @binding(7)
|
||||
var<uniform> cluster_light_index_lists: ClusterLightIndexLists;
|
||||
var<uniform> cluster_light_index_lists: types::ClusterLightIndexLists;
|
||||
@group(0) @binding(8)
|
||||
var<uniform> cluster_offsets_and_counts: ClusterOffsetsAndCounts;
|
||||
var<uniform> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts;
|
||||
#endif
|
||||
|
||||
@group(0) @binding(9)
|
||||
var<uniform> globals: Globals;
|
||||
@group(0) @binding(10)
|
||||
var<uniform> fog: Fog;
|
||||
var<uniform> fog: types::Fog;
|
||||
|
||||
@group(0) @binding(11)
|
||||
var screen_space_ambient_occlusion_texture: texture_2d<f32>;
|
||||
|
|
|
@ -7,6 +7,27 @@
|
|||
|
||||
#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
|
||||
// in crates/bevy_render/src/mesh/morph/visitors.rs
|
||||
// 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),
|
||||
);
|
||||
}
|
||||
|
||||
#endif // MORPH_TARGETS
|
|
@ -1,5 +1,7 @@
|
|||
#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 {
|
||||
// We use `textureSampleLevel` over `textureSample` because the wgpu DX12
|
||||
// backend (Fxc) panics when using "gradient instructions" inside a loop.
|
||||
|
|
|
@ -1,45 +1,45 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::pbr_bindings
|
||||
#import bevy_pbr::mesh_bindings
|
||||
#define_import_path bevy_pbr::fragment
|
||||
|
||||
#import bevy_pbr::utils
|
||||
#import bevy_pbr::clustered_forward
|
||||
#import bevy_pbr::lighting
|
||||
#import bevy_pbr::pbr_ambient
|
||||
#import bevy_pbr::shadows
|
||||
#import bevy_pbr::fog
|
||||
#import bevy_pbr::pbr_functions
|
||||
#import bevy_pbr::parallax_mapping
|
||||
#import bevy_pbr::pbr_functions as pbr_functions
|
||||
#import bevy_pbr::pbr_bindings as pbr_bindings
|
||||
#import bevy_pbr::pbr_types as pbr_types
|
||||
#import bevy_pbr::prepass_utils
|
||||
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
#import bevy_pbr::mesh_bindings mesh
|
||||
#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
|
||||
|
||||
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
|
||||
#import bevy_pbr::gtao_utils
|
||||
#import bevy_pbr::gtao_utils gtao_multibounce
|
||||
#endif
|
||||
|
||||
struct FragmentInput {
|
||||
@builtin(front_facing) is_front: bool,
|
||||
@builtin(position) frag_coord: vec4<f32>,
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
};
|
||||
|
||||
@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 V = calculate_view(in.world_position, is_orthographic);
|
||||
let V = pbr_functions::calculate_view(in.world_position, is_orthographic);
|
||||
#ifdef VERTEX_UVS
|
||||
var uv = in.uv;
|
||||
#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 T = in.world_tangent.xyz;
|
||||
let B = in.world_tangent.w * cross(N, T);
|
||||
// Transform V from fragment to camera in world space to tangent space.
|
||||
let Vt = vec3(dot(V, T), dot(V, B), dot(V, N));
|
||||
uv = parallaxed_uv(
|
||||
material.parallax_depth_scale,
|
||||
material.max_parallax_layer_count,
|
||||
material.max_relief_mapping_search_steps,
|
||||
pbr_bindings::material.parallax_depth_scale,
|
||||
pbr_bindings::material.max_parallax_layer_count,
|
||||
pbr_bindings::material.max_relief_mapping_search_steps,
|
||||
uv,
|
||||
// Flip the direction of Vt to go toward the surface to make the
|
||||
// parallax mapping algorithm easier to understand and reason
|
||||
|
@ -49,41 +49,41 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||
}
|
||||
#endif
|
||||
#endif
|
||||
var output_color: vec4<f32> = material.base_color;
|
||||
|
||||
#ifdef VERTEX_COLORS
|
||||
output_color = output_color * in.color;
|
||||
#endif
|
||||
#ifdef VERTEX_UVS
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
|
||||
output_color = output_color * textureSampleBias(base_color_texture, base_color_sampler, uv, view.mip_bias);
|
||||
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
|
||||
output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, in.uv, view.mip_bias);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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
|
||||
// the material members
|
||||
var pbr_input: PbrInput;
|
||||
var pbr_input: pbr_functions::PbrInput;
|
||||
|
||||
pbr_input.material.base_color = output_color;
|
||||
pbr_input.material.reflectance = material.reflectance;
|
||||
pbr_input.material.flags = material.flags;
|
||||
pbr_input.material.alpha_cutoff = material.alpha_cutoff;
|
||||
pbr_input.material.reflectance = pbr_bindings::material.reflectance;
|
||||
pbr_input.material.flags = pbr_bindings::material.flags;
|
||||
pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff;
|
||||
|
||||
// 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
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
|
||||
emissive = vec4<f32>(emissive.rgb * textureSampleBias(emissive_texture, emissive_sampler, uv, view.mip_bias).rgb, 1.0);
|
||||
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
|
||||
emissive = vec4<f32>(emissive.rgb * textureSampleBias(pbr_bindings::emissive_texture, pbr_bindings::emissive_sampler, in.uv, view.mip_bias).rgb, 1.0);
|
||||
}
|
||||
#endif
|
||||
pbr_input.material.emissive = emissive;
|
||||
|
||||
var metallic: f32 = material.metallic;
|
||||
var perceptual_roughness: f32 = material.perceptual_roughness;
|
||||
var metallic: f32 = pbr_bindings::material.metallic;
|
||||
var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness;
|
||||
#ifdef VERTEX_UVS
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
|
||||
let metallic_roughness = textureSampleBias(metallic_roughness_texture, metallic_roughness_sampler, uv, view.mip_bias);
|
||||
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
|
||||
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
|
||||
metallic = metallic * metallic_roughness.b;
|
||||
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?
|
||||
var occlusion: vec3<f32> = vec3(1.0);
|
||||
#ifdef VERTEX_UVS
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
|
||||
occlusion = vec3(textureSampleBias(occlusion_texture, occlusion_sampler, in.uv, view.mip_bias).r);
|
||||
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
|
||||
occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, in.uv, view.mip_bias).r);
|
||||
}
|
||||
#endif
|
||||
#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);
|
||||
occlusion = min(occlusion, ssao_multibounce);
|
||||
#endif
|
||||
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_normal = prepare_world_normal(
|
||||
pbr_input.world_normal = pbr_functions::prepare_world_normal(
|
||||
in.world_normal,
|
||||
(material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
|
||||
in.is_front,
|
||||
(pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
|
||||
is_front,
|
||||
);
|
||||
|
||||
pbr_input.is_orthographic = is_orthographic;
|
||||
|
||||
#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
|
||||
pbr_input.N = apply_normal_mapping(
|
||||
material.flags,
|
||||
pbr_input.N = pbr_functions::apply_normal_mapping(
|
||||
pbr_bindings::material.flags,
|
||||
pbr_input.world_normal,
|
||||
#ifdef VERTEX_TANGENTS
|
||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||
|
@ -139,22 +139,22 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||
|
||||
pbr_input.flags = mesh.flags;
|
||||
|
||||
output_color = pbr(pbr_input);
|
||||
output_color = pbr_functions::pbr(pbr_input);
|
||||
} else {
|
||||
output_color = alpha_discard(material, output_color);
|
||||
output_color = pbr_functions::alpha_discard(pbr_bindings::material, output_color);
|
||||
}
|
||||
|
||||
// fog
|
||||
if (fog.mode != FOG_MODE_OFF && (material.flags & STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
|
||||
output_color = apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz);
|
||||
if (fog.mode != FOG_MODE_OFF && (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
|
||||
output_color = pbr_functions::apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz);
|
||||
}
|
||||
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
output_color = tone_mapping(output_color);
|
||||
output_color = tone_mapping(output_color, view.color_grading);
|
||||
#ifdef DEBAND_DITHER
|
||||
var output_rgb = output_color.rgb;
|
||||
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
|
||||
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
|
||||
output_rgb = powsafe(output_rgb, 2.2);
|
||||
|
@ -162,7 +162,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||
#endif
|
||||
#endif
|
||||
#ifdef PREMULTIPLY_ALPHA
|
||||
output_color = premultiply_alpha(material.flags, output_color);
|
||||
output_color = pbr_functions::premultiply_alpha(pbr_bindings::material.flags, output_color);
|
||||
#endif
|
||||
return output_color;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
// 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,
|
||||
occlusion: 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));
|
||||
|
||||
return (diffuse_ambient + specular_ambient) * lights.ambient_color.rgb;
|
||||
return (diffuse_ambient + specular_ambient) * lights.ambient_color.rgb * occlusion;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#define_import_path bevy_pbr::pbr_bindings
|
||||
|
||||
#import bevy_pbr::pbr_types
|
||||
#import bevy_pbr::pbr_types StandardMaterial
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> material: StandardMaterial;
|
||||
|
|
|
@ -4,20 +4,32 @@
|
|||
#import bevy_core_pipeline::tonemapping
|
||||
#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
|
||||
#import bevy_pbr::environment_map
|
||||
#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;
|
||||
let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
|
||||
let alpha_mode = material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||
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
|
||||
color.a = 1.0;
|
||||
}
|
||||
|
||||
#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 {
|
||||
// NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
|
||||
color.a = 1.0;
|
||||
|
@ -82,8 +94,8 @@ fn apply_normal_mapping(
|
|||
#ifdef VERTEX_UVS
|
||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||
// Nt is the tangent-space normal.
|
||||
var Nt = textureSampleBias(normal_map_texture, normal_map_sampler, uv, view.mip_bias).rgb;
|
||||
if (standard_material_flags & STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u {
|
||||
var Nt = textureSampleBias(pbr_bindings::normal_map_texture, pbr_bindings::normal_map_sampler, uv, view_bindings::view.mip_bias).rgb;
|
||||
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.
|
||||
Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0);
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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>;
|
||||
if is_orthographic {
|
||||
// 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 {
|
||||
// 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;
|
||||
}
|
||||
|
||||
struct PbrInput {
|
||||
material: StandardMaterial,
|
||||
material: pbr_types::StandardMaterial,
|
||||
occlusion: vec3<f32>,
|
||||
frag_coord: vec4<f32>,
|
||||
world_position: vec4<f32>,
|
||||
|
@ -145,7 +157,7 @@ struct PbrInput {
|
|||
fn pbr_input_new() -> 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.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
|
||||
let metallic = in.material.metallic;
|
||||
let perceptual_roughness = in.material.perceptual_roughness;
|
||||
let roughness = perceptualRoughnessToRoughness(perceptual_roughness);
|
||||
let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness);
|
||||
|
||||
let occlusion = in.occlusion;
|
||||
|
||||
|
@ -193,64 +205,65 @@ fn pbr(
|
|||
|
||||
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);
|
||||
|
||||
let view_z = dot(vec4<f32>(
|
||||
view.inverse_view[0].z,
|
||||
view.inverse_view[1].z,
|
||||
view.inverse_view[2].z,
|
||||
view.inverse_view[3].z
|
||||
view_bindings::view.inverse_view[0].z,
|
||||
view_bindings::view.inverse_view[1].z,
|
||||
view_bindings::view.inverse_view[2].z,
|
||||
view_bindings::view.inverse_view[3].z
|
||||
), in.world_position);
|
||||
let cluster_index = fragment_cluster_index(in.frag_coord.xy, view_z, in.is_orthographic);
|
||||
let offset_and_counts = unpack_offset_and_counts(cluster_index);
|
||||
let cluster_index = clustering::fragment_cluster_index(in.frag_coord.xy, view_z, in.is_orthographic);
|
||||
let offset_and_counts = clustering::unpack_offset_and_counts(cluster_index);
|
||||
|
||||
// Point lights (direct)
|
||||
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;
|
||||
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||
&& (point_lights.data[light_id].flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||
shadow = fetch_point_shadow(light_id, in.world_position, in.world_normal);
|
||||
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||
&& (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
let light_id = get_light_id(i);
|
||||
let light_id = clustering::get_light_id(i);
|
||||
|
||||
var shadow: f32 = 1.0;
|
||||
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||
&& (point_lights.data[light_id].flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||
shadow = fetch_spot_shadow(light_id, in.world_position, in.world_normal);
|
||||
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||
&& (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Directional lights (direct)
|
||||
let n_directional_lights = lights.n_directional_lights;
|
||||
// directional lights (direct)
|
||||
let n_directional_lights = view_bindings::lights.n_directional_lights;
|
||||
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
|
||||
var shadow: f32 = 1.0;
|
||||
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||
&& (lights.directional_lights[i].flags & DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||
shadow = fetch_directional_shadow(i, in.world_position, in.world_normal, view_z);
|
||||
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||
&& (view_bindings::lights.directional_lights[i].flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||
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
|
||||
light_contrib = cascade_debug_visualization(light_contrib, i, view_z);
|
||||
light_contrib = shadows::cascade_debug_visualization(light_contrib, i, view_z);
|
||||
#endif
|
||||
direct_light += light_contrib * shadow;
|
||||
}
|
||||
|
||||
// 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)
|
||||
#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;
|
||||
#endif
|
||||
|
||||
|
@ -262,7 +275,7 @@ fn pbr(
|
|||
output_color.a
|
||||
);
|
||||
|
||||
output_color = cluster_debug_visualization(
|
||||
output_color = clustering::cluster_debug_visualization(
|
||||
output_color,
|
||||
view_z,
|
||||
in.is_orthographic,
|
||||
|
@ -275,7 +288,7 @@ fn pbr(
|
|||
#endif // 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;
|
||||
|
||||
// `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);
|
||||
if fog_params.directional_light_color.a > 0.0 {
|
||||
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) {
|
||||
let light = lights.directional_lights[i];
|
||||
let light = view_bindings::lights.directional_lights[i];
|
||||
scattering += pow(
|
||||
max(
|
||||
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 {
|
||||
return linear_fog(fog_params, input_color, distance, scattering);
|
||||
} else if fog_params.mode == FOG_MODE_EXPONENTIAL {
|
||||
return exponential_fog(fog_params, input_color, distance, scattering);
|
||||
} else if fog_params.mode == FOG_MODE_EXPONENTIAL_SQUARED {
|
||||
return exponential_squared_fog(fog_params, input_color, distance, scattering);
|
||||
} else if fog_params.mode == FOG_MODE_ATMOSPHERIC {
|
||||
return atmospheric_fog(fog_params, input_color, distance, scattering);
|
||||
if fog_params.mode == mesh_view_types::FOG_MODE_LINEAR {
|
||||
return fog::linear_fog(fog_params, input_color, distance, scattering);
|
||||
} else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL {
|
||||
return fog::exponential_fog(fog_params, input_color, distance, scattering);
|
||||
} else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL_SQUARED {
|
||||
return fog::exponential_squared_fog(fog_params, input_color, distance, scattering);
|
||||
} else if fog_params.mode == mesh_view_types::FOG_MODE_ATMOSPHERIC {
|
||||
return fog::atmospheric_fog(fog_params, input_color, distance, scattering);
|
||||
} else {
|
||||
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:
|
||||
//
|
||||
// result = 1 * src_color + (1 - src_alpha) * dst_color
|
||||
let alpha_mode = standard_material_flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD {
|
||||
let alpha_mode = standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||
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:
|
||||
//
|
||||
// src_color *= src_alpha
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#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
|
||||
// https://google.github.io/filament/Filament.html#table_symbols
|
||||
// Symbol Definition
|
||||
|
@ -180,7 +184,7 @@ fn point_light(
|
|||
f_ab: vec2<f32>,
|
||||
diffuseColor: 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 distance_square = dot(light_to_frag, light_to_frag);
|
||||
let rangeAttenuation = getDistanceAttenuation(distance_square, (*light).color_inverse_square_range.w);
|
||||
|
@ -244,12 +248,12 @@ fn spot_light(
|
|||
// reuse the point light calculations
|
||||
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
|
||||
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));
|
||||
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;
|
||||
}
|
||||
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> {
|
||||
let light = &lights.directional_lights[light_id];
|
||||
let light = &view_bindings::lights.directional_lights[light_id];
|
||||
|
||||
let incident_light = (*light).direction_to_light.xyz;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import bevy_pbr::prepass_bindings
|
||||
#import bevy_pbr::pbr_bindings
|
||||
#import bevy_pbr::pbr_types
|
||||
#ifdef NORMAL_PREPASS
|
||||
#import bevy_pbr::pbr_functions
|
||||
#endif // NORMAL_PREPASS
|
||||
|
@ -35,24 +36,24 @@ const PREMULTIPLIED_ALPHA_CUTOFF = 0.05;
|
|||
fn prepass_alpha_discard(in: FragmentInput) {
|
||||
|
||||
#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
|
||||
if (material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u {
|
||||
output_color = output_color * textureSampleBias(base_color_texture, base_color_sampler, in.uv, view.mip_bias);
|
||||
if (bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u {
|
||||
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
|
||||
|
||||
let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
||||
if output_color.a < material.alpha_cutoff {
|
||||
let alpha_mode = bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||
if alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
||||
if output_color.a < bevy_pbr::pbr_bindings::material.alpha_cutoff {
|
||||
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 {
|
||||
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)) {
|
||||
discard;
|
||||
}
|
||||
|
@ -88,15 +89,15 @@ fn fragment(in: FragmentInput) -> FragmentOutput {
|
|||
|
||||
#ifdef NORMAL_PREPASS
|
||||
// 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 {
|
||||
let world_normal = prepare_world_normal(
|
||||
if (bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u {
|
||||
let world_normal = bevy_pbr::pbr_functions::prepare_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,
|
||||
);
|
||||
|
||||
let normal = apply_normal_mapping(
|
||||
material.flags,
|
||||
let normal = bevy_pbr::pbr_functions::apply_normal_mapping(
|
||||
bevy_pbr::pbr_bindings::material.flags,
|
||||
world_normal,
|
||||
#ifdef VERTEX_TANGENTS
|
||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||
|
@ -115,9 +116,9 @@ fn fragment(in: FragmentInput) -> FragmentOutput {
|
|||
#endif // NORMAL_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 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;
|
||||
// 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
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#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);
|
||||
|
||||
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
|
||||
// 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
|
||||
// from LOD 0.
|
||||
#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
|
||||
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
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
#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);
|
||||
#else
|
||||
return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler,
|
||||
shadow_uv, i32(light_id) + lights.spot_light_shadowmap_offset, depth);
|
||||
return textureSampleCompareLevel(view_bindings::directional_shadow_textures, view_bindings::directional_shadow_textures_sampler,
|
||||
shadow_uv, i32(light_id) + view_bindings::lights.spot_light_shadowmap_offset, depth);
|
||||
#endif
|
||||
}
|
||||
|
||||
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) {
|
||||
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 {
|
||||
let light = &lights.directional_lights[light_id];
|
||||
let light = &view_bindings::lights.directional_lights[light_id];
|
||||
let cascade = &(*light).cascades[cascade_index];
|
||||
|
||||
// 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.
|
||||
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
||||
return textureSampleCompareLevel(
|
||||
directional_shadow_textures,
|
||||
directional_shadow_textures_sampler,
|
||||
view_bindings::directional_shadow_textures,
|
||||
view_bindings::directional_shadow_textures_sampler,
|
||||
light_local,
|
||||
depth
|
||||
);
|
||||
#else
|
||||
return textureSampleCompareLevel(
|
||||
directional_shadow_textures,
|
||||
directional_shadow_textures_sampler,
|
||||
view_bindings::directional_shadow_textures,
|
||||
view_bindings::directional_shadow_textures_sampler,
|
||||
light_local,
|
||||
i32((*light).depth_texture_base_index + cascade_index),
|
||||
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 {
|
||||
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);
|
||||
|
||||
if (cascade_index >= (*light).num_cascades) {
|
||||
|
|
|
@ -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
|
||||
|
||||
#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(
|
||||
indexes: vec4<u32>,
|
||||
weights: vec4<f32>,
|
||||
|
@ -14,7 +27,7 @@ fn skin_model(
|
|||
+ 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 y = cross(in[2], in[0]);
|
||||
let z = cross(in[0], in[1]);
|
||||
|
@ -31,7 +44,7 @@ fn skin_normals(
|
|||
normal: vec3<f32>,
|
||||
) -> vec3<f32> {
|
||||
return normalize(
|
||||
inverse_transpose_3x3(
|
||||
inverse_transpose_3x3m(
|
||||
mat3x3<f32>(
|
||||
model[0].xyz,
|
||||
model[1].xyz,
|
||||
|
@ -40,3 +53,5 @@ fn skin_normals(
|
|||
) * normal
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
#import bevy_pbr::mesh_types
|
||||
#import bevy_pbr::mesh_view_bindings
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> mesh: Mesh;
|
||||
#import bevy_pbr::mesh_bindings mesh
|
||||
#import bevy_pbr::mesh_functions mesh_position_local_to_clip
|
||||
|
||||
#ifdef SKINNED
|
||||
@group(1) @binding(1)
|
||||
var<uniform> joint_matrices: SkinnedMesh;
|
||||
#import bevy_pbr::skinning
|
||||
#import bevy_pbr::skinning
|
||||
#endif
|
||||
|
||||
// NOTE: Bindings must come before functions that use them!
|
||||
#import bevy_pbr::mesh_functions
|
||||
|
||||
struct Vertex {
|
||||
@location(0) position: vec3<f32>,
|
||||
#ifdef SKINNED
|
||||
|
@ -28,7 +20,7 @@ struct VertexOutput {
|
|||
@vertex
|
||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||
#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
|
||||
let model = mesh.model;
|
||||
#endif
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
// Source code heavily based on XeGTAO v1.30 from Intel
|
||||
// https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli
|
||||
|
||||
#import bevy_pbr::gtao_utils
|
||||
#import bevy_pbr::utils
|
||||
#import bevy_render::view
|
||||
#import bevy_render::globals
|
||||
#import bevy_pbr::gtao_utils fast_acos
|
||||
#import bevy_pbr::utils PI, HALF_PI
|
||||
#import bevy_render::view View
|
||||
#import bevy_render::globals Globals
|
||||
|
||||
@group(0) @binding(0) var preprocessed_depth: texture_2d<f32>;
|
||||
@group(0) @binding(1) var normals: texture_2d<f32>;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#define_import_path bevy_pbr::gtao_utils
|
||||
|
||||
#import bevy_pbr::utils PI, HALF_PI
|
||||
|
||||
// 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
|
||||
fn gtao_multibounce(visibility: f32, base_color: vec3<f32>) -> vec3<f32> {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
// 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(1) var preprocessed_depth_mip0: texture_storage_2d<r16float, write>;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
// 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
|
||||
|
||||
#import bevy_render::view
|
||||
#import bevy_render::view View
|
||||
|
||||
@group(0) @binding(0) var ambient_occlusion_noisy: texture_2d<f32>;
|
||||
@group(0) @binding(1) var depth_differences: texture_2d<u32>;
|
||||
|
|
|
@ -96,6 +96,10 @@ impl SpecializedMeshPipeline for WireframePipeline {
|
|||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
|
||||
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.primitive.polygon_mode = PolygonMode::Line;
|
||||
descriptor.depth_stencil.as_mut().unwrap().bias.slope_scale = 1.0;
|
||||
|
|
|
@ -57,7 +57,7 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }
|
|||
image = { version = "0.24", default-features = false }
|
||||
|
||||
# misc
|
||||
wgpu = { version = "0.16.0" }
|
||||
wgpu = { version = "0.16.0", features=["naga"] }
|
||||
wgpu-hal = "0.16.0"
|
||||
codespan-reporting = "0.11.0"
|
||||
naga = { version = "0.12.0", features = ["wgsl-in"] }
|
||||
|
@ -76,6 +76,7 @@ parking_lot = "0.12.1"
|
|||
regex = "1.5"
|
||||
ddsfile = { version = "0.5.0", optional = true }
|
||||
ktx2 = { version = "0.3.0", optional = true }
|
||||
naga_oil = "0.8"
|
||||
# For ktx2 supercompression
|
||||
flate2 = { version = "1.0.22", optional = true }
|
||||
ruzstd = { version = "0.4.0", optional = true }
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use crate::{
|
||||
render_resource::{
|
||||
AsModuleDescriptorError, BindGroupLayout, BindGroupLayoutId, ComputePipeline,
|
||||
ComputePipelineDescriptor, ProcessShaderError, ProcessedShader,
|
||||
BindGroupLayout, BindGroupLayoutId, ComputePipeline, ComputePipelineDescriptor,
|
||||
RawComputePipelineDescriptor, RawFragmentState, RawRenderPipelineDescriptor,
|
||||
RawVertexState, RenderPipeline, RenderPipelineDescriptor, Shader, ShaderImport,
|
||||
ShaderProcessor, ShaderReflectError,
|
||||
RawVertexState, RenderPipeline, RenderPipelineDescriptor, Shader, ShaderImport, Source,
|
||||
},
|
||||
renderer::RenderDevice,
|
||||
Extract,
|
||||
|
@ -17,11 +15,15 @@ use bevy_utils::{
|
|||
tracing::{debug, error},
|
||||
Entry, HashMap, HashSet,
|
||||
};
|
||||
use naga::valid::Capabilities;
|
||||
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;
|
||||
#[cfg(feature = "shader_format_spirv")]
|
||||
use wgpu::util::make_spirv;
|
||||
use wgpu::{
|
||||
PipelineLayoutDescriptor, PushConstantRange, VertexBufferLayout as RawVertexBufferLayout,
|
||||
Features, PipelineLayoutDescriptor, PushConstantRange, ShaderModuleDescriptor,
|
||||
VertexBufferLayout as RawVertexBufferLayout,
|
||||
};
|
||||
|
||||
use crate::render_resource::resource_macros::*;
|
||||
|
@ -123,13 +125,12 @@ struct ShaderData {
|
|||
dependents: HashSet<Handle<Shader>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ShaderCache {
|
||||
data: HashMap<Handle<Shader>, ShaderData>,
|
||||
shaders: HashMap<Handle<Shader>, Shader>,
|
||||
import_path_shaders: HashMap<ShaderImport, Handle<Shader>>,
|
||||
waiting_on_import: HashMap<ShaderImport, Vec<Handle<Shader>>>,
|
||||
processor: ShaderProcessor,
|
||||
composer: naga_oil::compose::Composer,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
|
@ -162,6 +163,78 @@ impl ShaderDefVal {
|
|||
}
|
||||
|
||||
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)]
|
||||
fn get(
|
||||
&mut self,
|
||||
|
@ -210,19 +283,54 @@ impl ShaderCache {
|
|||
"processing shader {:?}, with shader defs {:?}",
|
||||
handle, shader_defs
|
||||
);
|
||||
let processed = self.processor.process(
|
||||
shader,
|
||||
&shader_defs,
|
||||
&self.shaders,
|
||||
&self.import_path_shaders,
|
||||
)?;
|
||||
let module_descriptor = match processed
|
||||
.get_module_descriptor(render_device.features())
|
||||
{
|
||||
Ok(module_descriptor) => module_descriptor,
|
||||
Err(err) => {
|
||||
return Err(PipelineCacheError::AsModuleDescriptorError(err, processed));
|
||||
let shader_source = match &shader.source {
|
||||
#[cfg(feature = "shader_format_spirv")]
|
||||
Source::SpirV(data) => make_spirv(data),
|
||||
#[cfg(not(feature = "shader_format_spirv"))]
|
||||
Source::SpirV(_) => {
|
||||
unimplemented!(
|
||||
"Enable feature \"shader_format_spirv\" to use SPIR-V shaders"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
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
|
||||
|
@ -256,6 +364,10 @@ impl ShaderCache {
|
|||
data.processed_shaders.clear();
|
||||
pipelines_to_queue.extend(data.pipelines.iter().cloned());
|
||||
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,19 +376,18 @@ impl ShaderCache {
|
|||
|
||||
fn set_shader(&mut self, handle: &Handle<Shader>, shader: Shader) -> Vec<CachedPipelineId> {
|
||||
let pipelines_to_queue = self.clear(handle);
|
||||
if let Some(path) = shader.import_path() {
|
||||
self.import_path_shaders
|
||||
.insert(path.clone(), handle.clone_weak());
|
||||
if let Some(waiting_shaders) = self.waiting_on_import.get_mut(path) {
|
||||
for waiting_shader in waiting_shaders.drain(..) {
|
||||
// resolve waiting shader import
|
||||
let data = self.data.entry(waiting_shader.clone_weak()).or_default();
|
||||
data.resolved_imports
|
||||
.insert(path.clone(), handle.clone_weak());
|
||||
// add waiting shader as dependent of this shader
|
||||
let data = self.data.entry(handle.clone_weak()).or_default();
|
||||
data.dependents.insert(waiting_shader.clone_weak());
|
||||
}
|
||||
let path = shader.import_path();
|
||||
self.import_path_shaders
|
||||
.insert(path.clone(), handle.clone_weak());
|
||||
if let Some(waiting_shaders) = self.waiting_on_import.get_mut(path) {
|
||||
for waiting_shader in waiting_shaders.drain(..) {
|
||||
// resolve waiting shader import
|
||||
let data = self.data.entry(waiting_shader.clone_weak()).or_default();
|
||||
data.resolved_imports
|
||||
.insert(path.clone(), handle.clone_weak());
|
||||
// add waiting shader as dependent of this shader
|
||||
let data = self.data.entry(handle.clone_weak()).or_default();
|
||||
data.dependents.insert(waiting_shader.clone_weak());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,9 +413,7 @@ impl ShaderCache {
|
|||
fn remove(&mut self, handle: &Handle<Shader>) -> Vec<CachedPipelineId> {
|
||||
let pipelines_to_queue = self.clear(handle);
|
||||
if let Some(shader) = self.shaders.remove(handle) {
|
||||
if let Some(import_path) = shader.import_path() {
|
||||
self.import_path_shaders.remove(import_path);
|
||||
}
|
||||
self.import_path_shaders.remove(shader.import_path());
|
||||
}
|
||||
|
||||
pipelines_to_queue
|
||||
|
@ -373,9 +482,9 @@ impl PipelineCache {
|
|||
/// Create a new pipeline cache associated with the given render device.
|
||||
pub fn new(device: RenderDevice) -> Self {
|
||||
Self {
|
||||
shader_cache: ShaderCache::new(&device),
|
||||
device,
|
||||
layout_cache: default(),
|
||||
shader_cache: default(),
|
||||
waiting_pipelines: default(),
|
||||
new_pipelines: default(),
|
||||
pipelines: default(),
|
||||
|
@ -697,11 +806,8 @@ impl PipelineCache {
|
|||
}
|
||||
// shader could not be processed ... retrying won't help
|
||||
PipelineCacheError::ProcessShaderError(err) => {
|
||||
error!("failed to process shader: {}", err);
|
||||
continue;
|
||||
}
|
||||
PipelineCacheError::AsModuleDescriptorError(err, source) => {
|
||||
log_shader_error(source, err);
|
||||
let error_detail = err.emit_to_string(&self.shader_cache.composer);
|
||||
error!("failed to process shader:\n{}", error_detail);
|
||||
continue;
|
||||
}
|
||||
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.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PipelineCacheError {
|
||||
|
@ -840,35 +851,9 @@ pub enum PipelineCacheError {
|
|||
)]
|
||||
ShaderNotLoaded(Handle<Shader>),
|
||||
#[error(transparent)]
|
||||
ProcessShaderError(#[from] ProcessShaderError),
|
||||
#[error("{0}")]
|
||||
AsModuleDescriptorError(AsModuleDescriptorError, ProcessedShader),
|
||||
ProcessShaderError(#[from] naga_oil::compose::ComposerError),
|
||||
#[error("Shader import not yet available.")]
|
||||
ShaderImportNotYetAvailable,
|
||||
#[error("Could not create shader module: {0}")]
|
||||
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
|
@ -29,7 +29,7 @@ pub use texture_atlas::*;
|
|||
pub use texture_atlas_builder::*;
|
||||
|
||||
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_ecs::prelude::*;
|
||||
use bevy_reflect::TypeUuid;
|
||||
|
@ -56,9 +56,12 @@ pub enum SpriteSystem {
|
|||
|
||||
impl Plugin for SpritePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let mut shaders = app.world.resource_mut::<Assets<Shader>>();
|
||||
let sprite_shader = Shader::from_wgsl(include_str!("render/sprite.wgsl"));
|
||||
shaders.set_untracked(SPRITE_SHADER_HANDLE, sprite_shader);
|
||||
load_internal_asset!(
|
||||
app,
|
||||
SPRITE_SHADER_HANDLE,
|
||||
"render/sprite.wgsl",
|
||||
Shader::from_wgsl
|
||||
);
|
||||
app.add_asset::<TextureAtlas>()
|
||||
.register_asset_reflect::<TextureAtlas>()
|
||||
.register_type::<Sprite>()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import bevy_sprite::mesh2d_types
|
||||
#import bevy_sprite::mesh2d_view_bindings
|
||||
#import bevy_sprite::mesh2d_types Mesh2d
|
||||
#import bevy_sprite::mesh2d_vertex_output MeshVertexOutput
|
||||
#import bevy_sprite::mesh2d_view_bindings view
|
||||
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
#import bevy_core_pipeline::tonemapping
|
||||
|
@ -19,24 +20,19 @@ var texture: texture_2d<f32>;
|
|||
@group(1) @binding(2)
|
||||
var texture_sampler: sampler;
|
||||
|
||||
@group(2) @binding(0)
|
||||
var<uniform> mesh: Mesh2d;
|
||||
|
||||
struct FragmentInput {
|
||||
#import bevy_sprite::mesh2d_vertex_output
|
||||
};
|
||||
|
||||
@fragment
|
||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
||||
fn fragment(
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
var output_color: vec4<f32> = material.color;
|
||||
#ifdef VERTEX_COLORS
|
||||
output_color = output_color * in.color;
|
||||
output_color = output_color * mesh.color;
|
||||
#endif
|
||||
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
|
||||
output_color = tone_mapping(output_color);
|
||||
output_color = bevy_core_pipeline::tonemapping::tone_mapping(output_color, view.color_grading);
|
||||
#endif
|
||||
return output_color;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#import bevy_sprite::mesh2d_view_bindings
|
||||
#import bevy_sprite::mesh2d_bindings
|
||||
|
||||
// NOTE: Bindings must come before functions that use them!
|
||||
#import bevy_sprite::mesh2d_functions
|
||||
#import bevy_sprite::mesh2d_functions as mesh_functions
|
||||
#import bevy_sprite::mesh2d_bindings mesh
|
||||
#import bevy_sprite::mesh2d_vertex_output MeshVertexOutput
|
||||
#import bevy_sprite::mesh2d_view_bindings view
|
||||
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
#import bevy_core_pipeline::tonemapping
|
||||
|
@ -26,30 +25,30 @@ struct Vertex {
|
|||
#endif
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
#import bevy_sprite::mesh2d_vertex_output
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
|
||||
fn vertex(vertex: Vertex) -> MeshVertexOutput {
|
||||
var out: MeshVertexOutput;
|
||||
#ifdef VERTEX_UVS
|
||||
out.uv = vertex.uv;
|
||||
#endif
|
||||
|
||||
#ifdef VERTEX_POSITIONS
|
||||
out.world_position = mesh2d_position_local_to_world(mesh.model, vec4<f32>(vertex.position, 1.0));
|
||||
out.clip_position = mesh2d_position_world_to_clip(out.world_position);
|
||||
out.world_position = mesh_functions::mesh2d_position_local_to_world(
|
||||
mesh.model,
|
||||
vec4<f32>(vertex.position, 1.0)
|
||||
);
|
||||
out.position = mesh_functions::mesh2d_position_world_to_clip(out.world_position);
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#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
|
||||
|
||||
#ifdef VERTEX_COLORS
|
||||
|
@ -58,16 +57,14 @@ fn vertex(vertex: Vertex) -> VertexOutput {
|
|||
return out;
|
||||
}
|
||||
|
||||
struct FragmentInput {
|
||||
#import bevy_sprite::mesh2d_vertex_output
|
||||
};
|
||||
|
||||
@fragment
|
||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
||||
fn fragment(
|
||||
in: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
#ifdef VERTEX_COLORS
|
||||
var color = in.color;
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
color = tone_mapping(color);
|
||||
color = bevy_core_pipeline::tonemapping::tone_mapping(color, view.color_grading);
|
||||
#endif
|
||||
return color;
|
||||
#else
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
#import bevy_sprite::mesh2d_types
|
||||
|
||||
@group(2) @binding(0)
|
||||
var<uniform> mesh: Mesh2d;
|
||||
var<uniform> mesh: bevy_sprite::mesh2d_types::Mesh2d;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#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> {
|
||||
return model * vertex_position;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
#define_import_path bevy_sprite::mesh2d_vertex_output
|
||||
|
||||
@location(0) world_position: vec4<f32>,
|
||||
@location(1) world_normal: vec3<f32>,
|
||||
@location(2) uv: vec2<f32>,
|
||||
#ifdef VERTEX_TANGENTS
|
||||
@location(3) world_tangent: vec4<f32>,
|
||||
#endif
|
||||
#ifdef VERTEX_COLORS
|
||||
@location(4) color: vec4<f32>,
|
||||
#endif
|
||||
struct MeshVertexOutput {
|
||||
// this is `clip position` when the struct is used as a vertex stage output
|
||||
// and `frag coord` when used as a fragment stage input
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) world_position: vec4<f32>,
|
||||
@location(1) world_normal: vec3<f32>,
|
||||
@location(2) uv: vec2<f32>,
|
||||
#ifdef VERTEX_TANGENTS
|
||||
@location(3) world_tangent: vec4<f32>,
|
||||
#endif
|
||||
#ifdef VERTEX_COLORS
|
||||
@location(4) color: vec4<f32>,
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#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)
|
||||
var<uniform> view: View;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#import bevy_core_pipeline::tonemapping
|
||||
#endif
|
||||
|
||||
#import bevy_render::view
|
||||
#import bevy_render::view View
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
|
@ -45,7 +45,7 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||
#endif
|
||||
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
color = tone_mapping(color);
|
||||
color = bevy_core_pipeline::tonemapping::tone_mapping(color, view.color_grading);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
|
|
|
@ -111,7 +111,7 @@ impl Plugin for TextPlugin {
|
|||
app,
|
||||
DEFAULT_FONT_HANDLE,
|
||||
"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() }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#import bevy_render::view
|
||||
#import bevy_render::view View
|
||||
|
||||
const TEXTURED_QUAD: u32 = 0u;
|
||||
|
||||
|
|
|
@ -214,14 +214,11 @@ type DrawColoredMesh2d = (
|
|||
// using `include_str!()`, or loaded like any other asset with `asset_server.load()`.
|
||||
const COLORED_MESH2D_SHADER: &str = r"
|
||||
// Import the standard 2d mesh uniforms and set their bind groups
|
||||
#import bevy_sprite::mesh2d_types
|
||||
#import bevy_sprite::mesh2d_view_bindings
|
||||
#import bevy_sprite::mesh2d_types as MeshTypes
|
||||
#import bevy_sprite::mesh2d_functions as MeshFunctions
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> mesh: Mesh2d;
|
||||
|
||||
// NOTE: Bindings must come before functions that use them!
|
||||
#import bevy_sprite::mesh2d_functions
|
||||
var<uniform> mesh: MeshTypes::Mesh2d;
|
||||
|
||||
// The structure of the vertex buffer is as specified in `specialize()`
|
||||
struct Vertex {
|
||||
|
@ -241,7 +238,7 @@ struct VertexOutput {
|
|||
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
// 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
|
||||
out.color = vec4<f32>((vec4<u32>(vertex.color) >> vec4<u32>(0u, 8u, 16u, 24u)) & vec4<u32>(255u)) / 255.0;
|
||||
return out;
|
||||
|
@ -273,7 +270,7 @@ impl Plugin for ColoredMesh2dPlugin {
|
|||
let mut shaders = app.world.resource_mut::<Assets<Shader>>();
|
||||
shaders.set_untracked(
|
||||
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
|
||||
|
|
|
@ -194,6 +194,15 @@ impl SpecializedMeshPipeline for CustomPipeline {
|
|||
layout: &MeshVertexBufferLayout,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
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.buffers.push(VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<InstanceData>() as u64,
|
||||
|
|
Loading…
Reference in a new issue