improve shader import model (#5703)

# Objective

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

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

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

## Solution

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

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

## Migration Guide

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

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

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

View file

@ -1,6 +1,6 @@
#import bevy_pbr::mesh_types
// The time since startup data is in the globals binding which is part of the mesh_view_bindings import
#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

View file

@ -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);
}

View file

@ -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,

View file

@ -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>,

View file

@ -10,7 +10,11 @@ layout(set = 1, binding = 0) uniform CustomMaterial {
layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture;
layout(set = 1, binding = 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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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>;

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -406,7 +406,9 @@ impl AddAsset for App {
}
}
/// Loads an internal asset.
/// Loads an internal asset from a project source file.
/// the file and its path are passed to the loader function, together with any additional parameters.
/// the resulting asset is stored in the app's asset server.
///
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
/// 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(),
),
);
}};
}

View file

@ -114,7 +114,7 @@ pub(crate) fn sync_debug_assets<T: Asset + Clone>(
/// If this feels a bit odd ... that's because it is. This was built to improve the UX of the
/// `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,

View file

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

View file

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

View file

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

View file

@ -1,6 +1,8 @@
#import bevy_core_pipeline::fullscreen_vertex_shader
#define TONEMAPPING_PASS
#import bevy_render::view
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
#import bevy_render::view View
#import bevy_core_pipeline::tonemapping tone_mapping, powsafe, screen_space_dither
@group(0) @binding(0)
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);

View file

@ -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);
}

View file

@ -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,

View file

@ -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());
}

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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/

View file

@ -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));

View file

@ -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

View file

@ -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

View file

@ -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()
);
}

View file

@ -1,5 +1,9 @@
#define_import_path bevy_pbr::mesh_vertex_output
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
@ -11,3 +15,4 @@
#ifdef VERTEX_COLORS
@location(4) color: vec4<f32>,
#endif
}

View file

@ -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>;

View file

@ -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

View file

@ -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.

View file

@ -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;
}

View file

@ -1,4 +1,7 @@
#define_import_path bevy_pbr::pbr_ambient
#define_import_path bevy_pbr::ambient
#import bevy_pbr::lighting EnvBRDFApprox, F_AB
#import bevy_pbr::mesh_view_bindings lights
// A precomputed `NdotV` is provided because it is computed regardless,
// 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;
}

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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) {

View file

@ -1,9 +1,22 @@
// If using this WGSL snippet as an #import, a dedicated
// "joint_matricies" uniform of type SkinnedMesh must be added in the
// main shader.
#define_import_path bevy_pbr::skinning
#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

View file

@ -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
#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

View file

@ -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>;

View file

@ -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> {

View file

@ -5,7 +5,7 @@
// Reference: https://research.nvidia.com/sites/default/files/pubs/2012-06_Scalable-Ambient-Obscurance/McGuire12SAO.pdf, section 2.2
#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>;

View file

@ -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>;

View file

@ -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;

View file

@ -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 }

View file

@ -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,7 +376,7 @@ 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() {
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) {
@ -278,7 +390,6 @@ impl ShaderCache {
data.dependents.insert(waiting_shader.clone_weak());
}
}
}
for import in shader.imports() {
if let Some(import_handle) = self.import_path_shaders.get(import) {
@ -302,9 +413,7 @@ impl ShaderCache {
fn remove(&mut self, handle: &Handle<Shader>) -> Vec<CachedPipelineId> {
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

View file

@ -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>()

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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;
}

View file

@ -1,5 +1,9 @@
#define_import_path bevy_sprite::mesh2d_vertex_output
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>,
@ -9,3 +13,4 @@
#ifdef VERTEX_COLORS
@location(4) color: vec4<f32>,
#endif
}

View file

@ -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;

View file

@ -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;

View file

@ -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() }
);
}
}

View file

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

View file

@ -214,14 +214,11 @@ type DrawColoredMesh2d = (
// using `include_str!()`, or loaded like any other asset with `asset_server.load()`.
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

View file

@ -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,