From 727a700f53949a2897ed2f0ea75ae47094778de4 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Mon, 15 Aug 2022 12:55:39 +0100 Subject: [PATCH 01/48] use naga_oil --- assets/shaders/animate_shader.wgsl | 15 +- assets/shaders/array_texture.wgsl | 52 +- assets/shaders/cubemap_unlit.wgsl | 6 +- assets/shaders/custom_material.frag | 4 +- assets/shaders/custom_material.wgsl | 6 +- .../custom_material_chromatic_aberration.wgsl | 7 +- .../custom_material_screenspace_texture.wgsl | 7 +- assets/shaders/custom_vertex_attribute.wgsl | 6 +- assets/shaders/instancing.wgsl | 13 +- assets/shaders/line_material.wgsl | 4 +- assets/shaders/shader_defs.wgsl | 4 +- crates/bevy_asset/src/assets.rs | 25 + crates/bevy_pbr/src/lib.rs | 44 +- .../src/render/clustered_forward.wgsl | 26 +- crates/bevy_pbr/src/render/depth.wgsl | 32 +- crates/bevy_pbr/src/render/light.rs | 2 +- crates/bevy_pbr/src/render/mesh.rs | 43 +- crates/bevy_pbr/src/render/mesh.wgsl | 47 +- crates/bevy_pbr/src/render/mesh_bindings.wgsl | 18 +- .../bevy_pbr/src/render/mesh_functions.wgsl | 11 +- .../src/render/mesh_vertex_output.wgsl | 24 +- .../src/render/mesh_view_bindings.wgsl | 18 +- crates/bevy_pbr/src/render/pbr.wgsl | 87 +- crates/bevy_pbr/src/render/pbr_bindings.wgsl | 4 +- crates/bevy_pbr/src/render/pbr_functions.wgsl | 90 +- crates/bevy_pbr/src/render/pbr_lighting.wgsl | 43 +- crates/bevy_pbr/src/render/shadows.wgsl | 25 +- crates/bevy_pbr/src/render/skinning.wgsl | 23 +- crates/bevy_pbr/src/render/wireframe.wgsl | 18 +- crates/bevy_pbr/src/wireframe.rs | 10 +- crates/bevy_render/Cargo.toml | 1 + .../src/render_resource/pipeline_cache.rs | 251 ++-- .../bevy_render/src/render_resource/shader.rs | 1178 ++--------------- .../bevy_sprite/src/mesh2d/color_material.rs | 6 +- .../src/mesh2d/color_material.wgsl | 20 +- crates/bevy_sprite/src/mesh2d/mesh.rs | 33 +- crates/bevy_sprite/src/mesh2d/mesh2d.wgsl | 41 +- .../src/mesh2d/mesh2d_bindings.wgsl | 4 +- .../src/mesh2d/mesh2d_functions.wgsl | 11 +- .../src/mesh2d/mesh2d_vertex_output.wgsl | 20 +- .../src/mesh2d/mesh2d_view_bindings.wgsl | 4 +- crates/bevy_ui/src/render/mod.rs | 9 +- examples/2d/mesh2d_manual.rs | 12 +- examples/shader/animate_shader.rs | 6 +- examples/shader/shader_instancing.rs | 9 + 45 files changed, 683 insertions(+), 1636 deletions(-) diff --git a/assets/shaders/animate_shader.wgsl b/assets/shaders/animate_shader.wgsl index 947c4a5420aff..812a1f1b72686 100644 --- a/assets/shaders/animate_shader.wgsl +++ b/assets/shaders/animate_shader.wgsl @@ -1,11 +1,6 @@ -#import bevy_pbr::mesh_types -#import bevy_pbr::mesh_view_bindings - -@group(1) @binding(0) -var mesh: Mesh; - -// NOTE: Bindings must come before functions that use them! -#import bevy_pbr::mesh_functions +#import bevy_pbr::mesh_types as MeshTypes +#import bevy_pbr::mesh_bindings as MeshBindings +#import bevy_pbr::mesh_functions as MeshFunctions struct Vertex { @location(0) position: vec3, @@ -21,7 +16,7 @@ struct VertexOutput { @vertex fn vertex(vertex: Vertex) -> VertexOutput { var out: VertexOutput; - out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); + out.clip_position = MeshFunctions::mesh_position_local_to_clip(MeshBindings::mesh.model, vec4(vertex.position, 1.0)); out.uv = vertex.uv; return out; } @@ -30,7 +25,7 @@ fn vertex(vertex: Vertex) -> VertexOutput { struct Time { time_since_startup: f32, }; -@group(2) @binding(0) +@group(1) @binding(0) var time: Time; diff --git a/assets/shaders/array_texture.wgsl b/assets/shaders/array_texture.wgsl index 91f4564e7fe27..3dca5f6a4c197 100644 --- a/assets/shaders/array_texture.wgsl +++ b/assets/shaders/array_texture.wgsl @@ -1,55 +1,47 @@ -#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::pbr_functions +#import bevy_pbr::mesh_vertex_output as OutputTypes +#import bevy_pbr::pbr_functions as PbrCore +#import bevy_pbr::mesh_view_bindings as ViewBindings @group(1) @binding(0) var my_array_texture: texture_2d_array; @group(1) @binding(1) var my_array_texture_sampler: sampler; -struct FragmentInput { +@fragment +fn fragment( @builtin(front_facing) is_front: bool, @builtin(position) frag_coord: vec4, - #import bevy_pbr::mesh_vertex_output -}; - -@fragment -fn fragment(in: FragmentInput) -> @location(0) vec4 { - let layer = i32(in.world_position.x) & 0x3; + mesh: OutputTypes::MeshVertexOutput, +) -> @location(0) vec4 { + 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: PbrCore::PbrInput = PbrCore::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 = in.world_normal; + pbr_input.frag_coord = frag_coord; + pbr_input.world_position = mesh.world_position; + pbr_input.world_normal = mesh.world_normal; - pbr_input.is_orthographic = view.projection[3].w == 1.0; + pbr_input.is_orthographic = ViewBindings::view.projection[3].w == 1.0; - pbr_input.N = prepare_normal( + pbr_input.N = PbrCore::prepare_normal( pbr_input.material.flags, - in.world_normal, + mesh.world_normal, #ifdef VERTEX_TANGENTS #ifdef STANDARDMATERIAL_NORMAL_MAP - in.world_tangent, + mesh.world_tangent, #endif #endif - in.uv, - in.is_front, + mesh.uv, + is_front, ); - pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); + pbr_input.V = PbrCore::calculate_view(mesh.world_position, pbr_input.is_orthographic); - return tone_mapping(pbr(pbr_input)); + return PbrCore::tone_mapping(PbrCore::pbr(pbr_input)); } diff --git a/assets/shaders/cubemap_unlit.wgsl b/assets/shaders/cubemap_unlit.wgsl index 6837384dea3ac..42adfea57b17c 100644 --- a/assets/shaders/cubemap_unlit.wgsl +++ b/assets/shaders/cubemap_unlit.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::mesh_vertex_output as OutputTypes #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: OutputTypes::MeshVertexOutput, ) -> @location(0) vec4 { - let fragment_position_view_lh = world_position.xyz * vec3(1.0, 1.0, -1.0); + let fragment_position_view_lh = mesh.world_position.xyz * vec3(1.0, 1.0, -1.0); return textureSample( base_color_texture, base_color_sampler, diff --git a/assets/shaders/custom_material.frag b/assets/shaders/custom_material.frag index bf46d1e5334fb..f4aeef2878b04 100644 --- a/assets/shaders/custom_material.frag +++ b/assets/shaders/custom_material.frag @@ -10,7 +10,9 @@ 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 +#import bevy_pbr::pbr_functions as PbrFuncs void main() { - o_Target = Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv); + o_Target = PbrFuncs::tone_mapping(Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv)); } diff --git a/assets/shaders/custom_material.wgsl b/assets/shaders/custom_material.wgsl index 95b1b7d26a196..0661f62e8e5db 100644 --- a/assets/shaders/custom_material.wgsl +++ b/assets/shaders/custom_material.wgsl @@ -1,3 +1,5 @@ +#import bevy_pbr::mesh_vertex_output as OutputTypes + struct CustomMaterial { color: vec4, }; @@ -11,7 +13,7 @@ var base_color_sampler: sampler; @fragment fn fragment( - #import bevy_pbr::mesh_vertex_output + mesh: OutputTypes::MeshVertexOutput, ) -> @location(0) vec4 { - return material.color * textureSample(base_color_texture, base_color_sampler, uv); + return material.color * textureSample(base_color_texture, base_color_sampler, mesh.uv); } diff --git a/assets/shaders/custom_material_chromatic_aberration.wgsl b/assets/shaders/custom_material_chromatic_aberration.wgsl index e8ccdcfb62513..5d0ad4f8a92ee 100644 --- a/assets/shaders/custom_material_chromatic_aberration.wgsl +++ b/assets/shaders/custom_material_chromatic_aberration.wgsl @@ -1,4 +1,5 @@ -#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::mesh_view_bindings as ViewBindings +#import bevy_pbr::mesh_vertex_output as OutputTypes @group(1) @binding(0) var texture: texture_2d; @@ -9,10 +10,10 @@ var our_sampler: sampler; @fragment fn fragment( @builtin(position) position: vec4, - #import bevy_sprite::mesh2d_vertex_output + mesh: OutputTypes::MeshVertexOutput ) -> @location(0) vec4 { // Get screen position with coordinates from 0 to 1 - let uv = position.xy / vec2(view.width, view.height); + let uv = position.xy / vec2(ViewBindings::view.width, ViewBindings::view.height); let offset_strength = 0.02; // Sample each color channel with an arbitrary shift diff --git a/assets/shaders/custom_material_screenspace_texture.wgsl b/assets/shaders/custom_material_screenspace_texture.wgsl index aad35920c093e..2461777b0d4de 100644 --- a/assets/shaders/custom_material_screenspace_texture.wgsl +++ b/assets/shaders/custom_material_screenspace_texture.wgsl @@ -1,4 +1,5 @@ -#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::mesh_view_bindings as ViewBindings +#import bevy_pbr::mesh_vertex_output as OutputTypes @group(1) @binding(0) var texture: texture_2d; @@ -8,9 +9,9 @@ var texture_sampler: sampler; @fragment fn fragment( @builtin(position) position: vec4, - #import bevy_pbr::mesh_vertex_output + mesh: OutputTypes::MeshVertexOutput, ) -> @location(0) vec4 { - let uv = position.xy / vec2(view.width, view.height); + let uv = position.xy / vec2(ViewBindings::view.width, ViewBindings::view.height); let color = textureSample(texture, texture_sampler, uv); return color; } diff --git a/assets/shaders/custom_vertex_attribute.wgsl b/assets/shaders/custom_vertex_attribute.wgsl index dd7cfc150308d..ab9a5e0fcce81 100644 --- a/assets/shaders/custom_vertex_attribute.wgsl +++ b/assets/shaders/custom_vertex_attribute.wgsl @@ -1,5 +1,5 @@ -#import bevy_pbr::mesh_view_bindings -#import bevy_pbr::mesh_bindings +#import bevy_pbr::mesh_bindings as MeshBindings +#import bevy_pbr::mesh_functions as MeshFunctions struct CustomMaterial { color: vec4, @@ -23,7 +23,7 @@ struct VertexOutput { @vertex fn vertex(vertex: Vertex) -> VertexOutput { var out: VertexOutput; - out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); + out.clip_position = MeshFunctions::mesh_position_local_to_clip(MeshBindings::mesh.model, vec4(vertex.position, 1.0)); out.blend_color = vertex.blend_color; return out; } diff --git a/assets/shaders/instancing.wgsl b/assets/shaders/instancing.wgsl index 7cb00b039a84a..14e4de09b393a 100644 --- a/assets/shaders/instancing.wgsl +++ b/assets/shaders/instancing.wgsl @@ -1,11 +1,6 @@ -#import bevy_pbr::mesh_types -#import bevy_pbr::mesh_view_bindings - -@group(1) @binding(0) -var mesh: Mesh; - -// NOTE: Bindings must come before functions that use them! -#import bevy_pbr::mesh_functions +#import bevy_pbr::mesh_types as MeshTypes +#import bevy_pbr::mesh_functions as MeshFunctions +#import bevy_pbr::mesh_bindings as MeshBindings struct Vertex { @location(0) position: vec3, @@ -25,7 +20,7 @@ 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(position, 1.0)); + out.clip_position = MeshFunctions::mesh_position_local_to_clip(MeshBindings::mesh.model, vec4(position, 1.0)); out.color = vertex.i_color; return out; } diff --git a/assets/shaders/line_material.wgsl b/assets/shaders/line_material.wgsl index e47ffe6e16acb..3bb38d7e43a80 100644 --- a/assets/shaders/line_material.wgsl +++ b/assets/shaders/line_material.wgsl @@ -1,3 +1,5 @@ +#import bevy_pbr::mesh_vertex_output as OutputTypes + struct LineMaterial { color: vec4, }; @@ -7,7 +9,7 @@ var material: LineMaterial; @fragment fn fragment( - #import bevy_pbr::mesh_vertex_output + mesh: OutputTypes::MeshVertexOutput, ) -> @location(0) vec4 { return material.color; } diff --git a/assets/shaders/shader_defs.wgsl b/assets/shaders/shader_defs.wgsl index 0efa91231a622..1dc1b54f4a46b 100644 --- a/assets/shaders/shader_defs.wgsl +++ b/assets/shaders/shader_defs.wgsl @@ -1,3 +1,5 @@ +#import bevy_pbr::mesh_vertex_output as OutputTypes + struct CustomMaterial { color: vec4, }; @@ -7,7 +9,7 @@ var material: CustomMaterial; @fragment fn fragment( - #import bevy_pbr::mesh_vertex_output + mesh: OutputTypes::MeshVertexOutput, ) -> @location(0) vec4 { #ifdef IS_RED return vec4(1.0, 0.0, 0.0, 1.0); diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 9ba30143dc964..c77693e29574e 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -417,6 +417,31 @@ macro_rules! load_internal_asset { }}; } +/// Loads an internal asset with its path. +/// +/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded +/// using the conventional API. See `DebugAssetServerPlugin`. +#[macro_export] +macro_rules! load_internal_asset_with_path { + ($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{ + let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); + assets.set_untracked( + $handle, + ($loader)( + include_str!($path_str), + format!( + "{}/{}", + std::path::Path::new(file!()) + .parent() + .unwrap() + .to_string_lossy(), + $path_str + ), + ), + ); + }}; +} + #[cfg(test)] mod tests { use bevy_app::App; diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 5d190540579ea..ce68e00cac359 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -38,7 +38,7 @@ pub mod draw_3d_graph { } use bevy_app::prelude::*; -use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped}; +use bevy_asset::{load_internal_asset_with_path, Assets, Handle, HandleUntyped}; use bevy_ecs::prelude::*; use bevy_reflect::TypeUuid; use bevy_render::{ @@ -78,49 +78,59 @@ pub struct PbrPlugin; impl Plugin for PbrPlugin { fn build(&self, app: &mut App) { - load_internal_asset!( + load_internal_asset_with_path!( app, PBR_TYPES_SHADER_HANDLE, "render/pbr_types.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, PBR_BINDINGS_SHADER_HANDLE, "render/pbr_bindings.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!(app, UTILS_HANDLE, "render/utils.wgsl", Shader::from_wgsl); - load_internal_asset!( + load_internal_asset_with_path!( + app, + UTILS_HANDLE, + "render/utils.wgsl", + Shader::from_wgsl_with_path + ); + load_internal_asset_with_path!( app, CLUSTERED_FORWARD_HANDLE, "render/clustered_forward.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, PBR_LIGHTING_HANDLE, "render/pbr_lighting.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, SHADOWS_HANDLE, "render/shadows.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, PBR_FUNCTIONS_HANDLE, "render/pbr_functions.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path + ); + load_internal_asset_with_path!( + app, + PBR_SHADER_HANDLE, + "render/pbr.wgsl", + Shader::from_wgsl_with_path ); - load_internal_asset!(app, PBR_SHADER_HANDLE, "render/pbr.wgsl", Shader::from_wgsl); - load_internal_asset!( + load_internal_asset_with_path!( app, SHADOW_SHADER_HANDLE, "render/depth.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); app.register_type::() diff --git a/crates/bevy_pbr/src/render/clustered_forward.wgsl b/crates/bevy_pbr/src/render/clustered_forward.wgsl index 46c54ad6f6390..ac2d468ef6052 100644 --- a/crates/bevy_pbr/src/render/clustered_forward.wgsl +++ b/crates/bevy_pbr/src/render/clustered_forward.wgsl @@ -1,28 +1,30 @@ #define_import_path bevy_pbr::clustered_forward +#import bevy_pbr::mesh_view_bindings as Bindings + // 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 closeer 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, view_z: f32, is_orthographic: bool) -> u32 { - let xy = vec2(floor(frag_coord * lights.cluster_factors.xy)); + let xy = vec2(floor(frag_coord * 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,7 +32,7 @@ fn fragment_cluster_index(frag_coord: vec2, view_z: f32, is_orthographic: b let CLUSTER_COUNT_SIZE = 9u; fn unpack_offset_and_counts(cluster_index: u32) -> vec3 { #ifdef NO_STORAGE_BUFFERS_SUPPORT - 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( @@ -39,7 +41,7 @@ fn unpack_offset_and_counts(cluster_index: u32) -> vec3 { offset_and_counts & ((1u << CLUSTER_COUNT_SIZE) - 1u), ); #else - return cluster_offsets_and_counts.data[cluster_index].xyz; + return Bindings::cluster_offsets_and_counts.data[cluster_index].xyz; #endif } @@ -47,11 +49,11 @@ fn get_light_id(index: u32) -> u32 { #ifdef NO_STORAGE_BUFFERS_SUPPORT // 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); #else - return cluster_light_index_lists.data[index]; + return Bindings::cluster_light_index_lists.data[index]; #endif } @@ -69,9 +71,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( (1.0 - cluster_overlay_alpha) * output_color.rgb + cluster_overlay_alpha * slice_color, output_color.a diff --git a/crates/bevy_pbr/src/render/depth.wgsl b/crates/bevy_pbr/src/render/depth.wgsl index 5a238cb1e5f21..ff11af37f814c 100644 --- a/crates/bevy_pbr/src/render/depth.wgsl +++ b/crates/bevy_pbr/src/render/depth.wgsl @@ -1,21 +1,17 @@ -#import bevy_pbr::mesh_view_types -#import bevy_pbr::mesh_types +#define_import_path depth -@group(0) @binding(0) -var view: View; +#import bevy_pbr::mesh_view_types as ViewTypes +#import bevy_pbr::mesh_types as MeshTypes +#import bevy_pbr::mesh_bindings as MeshBindings +#import bevy_pbr::mesh_functions as MeshFunctions -@group(1) @binding(0) -var mesh: Mesh; +@group(0) @binding(0) +var view: ViewTypes::View; #ifdef SKINNED -@group(1) @binding(1) -var joint_matrices: SkinnedMesh; -#import bevy_pbr::skinning +#import bevy_pbr::skinning as Skinning #endif -// NOTE: Bindings must come before functions that use them! -#import bevy_pbr::mesh_functions - struct Vertex { @location(0) position: vec3, #ifdef SKINNED @@ -31,12 +27,12 @@ struct VertexOutput { @vertex fn vertex(vertex: Vertex) -> VertexOutput { #ifdef SKINNED - let model = skin_model(vertex.joint_indices, vertex.joint_weights); + let model = Skinning::skin_model(vertex.joint_indices, vertex.joint_weights); #else - let model = mesh.model; + let model = MeshBindings::mesh.model; #endif - var out: VertexOutput; - out.clip_position = mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); - return out; -} + var out_depth_pipeline: VertexOutput; + out_depth_pipeline.clip_position = MeshFunctions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); + return out_depth_pipeline; +} \ No newline at end of file diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 5a9e75b0ea0e0..98d2f12170ee0 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -319,7 +319,7 @@ impl SpecializedMeshPipeline for ShadowPipeline { let mut vertex_attributes = vec![Mesh::ATTRIBUTE_POSITION.at_shader_location(0)]; let mut bind_group_layout = vec![self.view_layout.clone()]; - let mut shader_defs = Vec::new(); + let mut shader_defs = vec!["MESH_BINDGROUP_1".to_owned()]; if layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX) && layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT) diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 0bb456bcaa5fa..781c70f5502a6 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -4,7 +4,7 @@ use crate::{ CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, }; use bevy_app::Plugin; -use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped}; +use bevy_asset::{load_internal_asset_with_path, Assets, Handle, HandleUntyped}; use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem, SystemState}, @@ -56,39 +56,54 @@ pub const SKINNING_HANDLE: HandleUntyped = impl Plugin for MeshRenderPlugin { fn build(&self, app: &mut bevy_app::App) { - load_internal_asset!( + load_internal_asset_with_path!( app, MESH_VERTEX_OUTPUT, "mesh_vertex_output.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, MESH_VIEW_TYPES_HANDLE, "mesh_view_types.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, MESH_VIEW_BINDINGS_HANDLE, "mesh_view_bindings.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!(app, MESH_TYPES_HANDLE, "mesh_types.wgsl", Shader::from_wgsl); - load_internal_asset!( + load_internal_asset_with_path!( + app, + MESH_TYPES_HANDLE, + "mesh_types.wgsl", + Shader::from_wgsl_with_path + ); + load_internal_asset_with_path!( app, MESH_BINDINGS_HANDLE, "mesh_bindings.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, MESH_FUNCTIONS_HANDLE, "mesh_functions.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path + ); + load_internal_asset_with_path!( + app, + MESH_SHADER_HANDLE, + "mesh.wgsl", + Shader::from_wgsl_with_path + ); + load_internal_asset_with_path!( + app, + SKINNING_HANDLE, + "skinning.wgsl", + Shader::from_wgsl_with_path ); - load_internal_asset!(app, MESH_SHADER_HANDLE, "mesh.wgsl", Shader::from_wgsl); - load_internal_asset!(app, SKINNING_HANDLE, "skinning.wgsl", Shader::from_wgsl); app.add_plugin(UniformComponentPlugin::::default()); diff --git a/crates/bevy_pbr/src/render/mesh.wgsl b/crates/bevy_pbr/src/render/mesh.wgsl index 32cbe31c8d161..4f7a44853664d 100644 --- a/crates/bevy_pbr/src/render/mesh.wgsl +++ b/crates/bevy_pbr/src/render/mesh.wgsl @@ -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_bindings as MeshBindings +#import bevy_pbr::mesh_functions as MeshFunctions +#import bevy_pbr::skinning as Skinning +#import bevy_pbr::mesh_vertex_output as OutputTypes struct Vertex { @location(0) position: vec3, @@ -24,43 +24,52 @@ struct Vertex { struct VertexOutput { @builtin(position) clip_position: vec4, - #import bevy_pbr::mesh_vertex_output + // have to copy-paste here, we can't currently embed an unlocated struct in the vertex stage output + @location(0) world_position: vec4, + @location(1) world_normal: vec3, + #ifdef VERTEX_UVS + @location(2) uv: vec2, + #endif + #ifdef VERTEX_TANGENTS + @location(3) world_tangent: vec4, + #endif + #ifdef VERTEX_COLORS + @location(4) color: vec4, + #endif }; @vertex fn vertex(vertex: Vertex) -> VertexOutput { var out: VertexOutput; #ifdef SKINNED - var model = skin_model(vertex.joint_indices, vertex.joint_weights); - out.world_normal = skin_normals(model, vertex.normal); + var model = Skinning::skin_model(vertex.joint_indices, vertex.joint_weights); + out.world_normal = Skinning::skin_normals(model, vertex.normal); #else - var model = mesh.model; - out.world_normal = mesh_normal_local_to_world(vertex.normal); + var model = MeshBindings::mesh.model; + out.world_normal = MeshFunctions::mesh_normal_local_to_world(vertex.normal); #endif - out.world_position = mesh_position_local_to_world(model, vec4(vertex.position, 1.0)); + out.world_position = MeshFunctions::mesh_position_local_to_world(model, vec4(vertex.position, 1.0)); #ifdef VERTEX_UVS out.uv = vertex.uv; #endif #ifdef VERTEX_TANGENTS - out.world_tangent = mesh_tangent_local_to_world(model, vertex.tangent); + out.world_tangent = MeshFunctions::mesh_tangent_local_to_world(model, vertex.tangent); #endif #ifdef VERTEX_COLORS out.color = vertex.color; #endif - out.clip_position = mesh_position_world_to_clip(out.world_position); + out.clip_position = MeshFunctions::mesh_position_world_to_clip(out.world_position); return out; } -struct FragmentInput { - @builtin(front_facing) is_front: bool, - #import bevy_pbr::mesh_vertex_output -}; - @fragment -fn fragment(in: FragmentInput) -> @location(0) vec4 { +fn fragment( + @builtin(front_facing) is_front: bool, + mesh: OutputTypes::MeshVertexOutput, +) -> @location(0) vec4 { #ifdef VERTEX_COLORS - return in.color; + return mesh.color; #else return vec4(1.0, 0.0, 1.0, 1.0); #endif diff --git a/crates/bevy_pbr/src/render/mesh_bindings.wgsl b/crates/bevy_pbr/src/render/mesh_bindings.wgsl index d0781e149b420..d189fa1d8d083 100644 --- a/crates/bevy_pbr/src/render/mesh_bindings.wgsl +++ b/crates/bevy_pbr/src/render/mesh_bindings.wgsl @@ -1,11 +1,15 @@ #define_import_path bevy_pbr::mesh_bindings -#import bevy_pbr::mesh_types +#import bevy_pbr::mesh_types as Types + +#ifdef MESH_BINDGROUP_1 + +@group(1) @binding(0) +var mesh: Types::Mesh; + +#else @group(2) @binding(0) -var mesh: Mesh; -#ifdef SKINNED -@group(2) @binding(1) -var joint_matrices: SkinnedMesh; -#import bevy_pbr::skinning -#endif +var mesh: Types::Mesh; + +#endif \ No newline at end of file diff --git a/crates/bevy_pbr/src/render/mesh_functions.wgsl b/crates/bevy_pbr/src/render/mesh_functions.wgsl index 20c763bd22d79..9114a66285285 100644 --- a/crates/bevy_pbr/src/render/mesh_functions.wgsl +++ b/crates/bevy_pbr/src/render/mesh_functions.wgsl @@ -1,11 +1,14 @@ #define_import_path bevy_pbr::mesh_functions +#import bevy_pbr::mesh_view_bindings as ViewBindings +#import bevy_pbr::mesh_bindings as MeshBindings + fn mesh_position_local_to_world(model: mat4x4, vertex_position: vec4) -> vec4 { return model * vertex_position; } fn mesh_position_world_to_clip(world_position: vec4) -> vec4 { - return view.view_proj * world_position; + return ViewBindings::view.view_proj * world_position; } // NOTE: The intermediate world_position assignment is important @@ -18,9 +21,9 @@ fn mesh_position_local_to_clip(model: mat4x4, vertex_position: vec4) - fn mesh_normal_local_to_world(vertex_normal: vec3) -> vec3 { return mat3x3( - mesh.inverse_transpose_model[0].xyz, - mesh.inverse_transpose_model[1].xyz, - mesh.inverse_transpose_model[2].xyz + MeshBindings::mesh.inverse_transpose_model[0].xyz, + MeshBindings::mesh.inverse_transpose_model[1].xyz, + MeshBindings::mesh.inverse_transpose_model[2].xyz ) * vertex_normal; } diff --git a/crates/bevy_pbr/src/render/mesh_vertex_output.wgsl b/crates/bevy_pbr/src/render/mesh_vertex_output.wgsl index ba0c6b2237c0a..b52563560d5a4 100644 --- a/crates/bevy_pbr/src/render/mesh_vertex_output.wgsl +++ b/crates/bevy_pbr/src/render/mesh_vertex_output.wgsl @@ -1,13 +1,15 @@ #define_import_path bevy_pbr::mesh_vertex_output -@location(0) world_position: vec4, -@location(1) world_normal: vec3, -#ifdef VERTEX_UVS -@location(2) uv: vec2, -#endif -#ifdef VERTEX_TANGENTS -@location(3) world_tangent: vec4, -#endif -#ifdef VERTEX_COLORS -@location(4) color: vec4, -#endif +struct MeshVertexOutput { + @location(0) world_position: vec4, + @location(1) world_normal: vec3, + #ifdef VERTEX_UVS + @location(2) uv: vec2, + #endif + #ifdef VERTEX_TANGENTS + @location(3) world_tangent: vec4, + #endif + #ifdef VERTEX_COLORS + @location(4) color: vec4, + #endif +} diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl index 04780be3f0c96..69cfe48b81620 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl @@ -1,11 +1,11 @@ #define_import_path bevy_pbr::mesh_view_bindings -#import bevy_pbr::mesh_view_types +#import bevy_pbr::mesh_view_types as Types @group(0) @binding(0) -var view: View; +var view: Types::View; @group(0) @binding(1) -var lights: Lights; +var lights: Types::Lights; #ifdef NO_ARRAY_TEXTURES_SUPPORT @group(0) @binding(2) var point_shadow_textures: texture_depth_cube; @@ -27,16 +27,16 @@ var directional_shadow_textures_sampler: sampler_comparison; #ifdef NO_STORAGE_BUFFERS_SUPPORT @group(0) @binding(6) -var point_lights: PointLights; +var point_lights: Types::PointLights; @group(0) @binding(7) -var cluster_light_index_lists: ClusterLightIndexLists; +var cluster_light_index_lists: Types::ClusterLightIndexLists; @group(0) @binding(8) -var cluster_offsets_and_counts: ClusterOffsetsAndCounts; +var cluster_offsets_and_counts: Types::ClusterOffsetsAndCounts; #else @group(0) @binding(6) -var point_lights: PointLights; +var point_lights: Types::PointLights; @group(0) @binding(7) -var cluster_light_index_lists: ClusterLightIndexLists; +var cluster_light_index_lists: Types::ClusterLightIndexLists; @group(0) @binding(8) -var cluster_offsets_and_counts: ClusterOffsetsAndCounts; +var cluster_offsets_and_counts: Types::ClusterOffsetsAndCounts; #endif diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index b70b5b333943c..e9725b10f7571 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -1,56 +1,51 @@ -#import bevy_pbr::mesh_view_bindings -#import bevy_pbr::pbr_bindings -#import bevy_pbr::mesh_bindings +#import bevy_pbr::mesh_vertex_output as OutputTypes +#import bevy_pbr::pbr_functions as PbrCore +#import bevy_pbr::pbr_bindings as MaterialBindings +#import bevy_pbr::pbr_types as PbrTypes +#import bevy_pbr::mesh_view_bindings as ViewBindings -#import bevy_pbr::utils -#import bevy_pbr::clustered_forward -#import bevy_pbr::lighting -#import bevy_pbr::shadows -#import bevy_pbr::pbr_functions - -struct FragmentInput { +@fragment +fn fragment( + mesh: OutputTypes::MeshVertexOutput, @builtin(front_facing) is_front: bool, @builtin(position) frag_coord: vec4, - #import bevy_pbr::mesh_vertex_output -}; +) -> @location(0) vec4 { + var output_color: vec4 = MaterialBindings::material.base_color; -@fragment -fn fragment(in: FragmentInput) -> @location(0) vec4 { - var output_color: vec4 = material.base_color; #ifdef VERTEX_COLORS - output_color = output_color * in.color; + output_color = output_color * mesh.color; #endif #ifdef VERTEX_UVS - if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { - output_color = output_color * textureSample(base_color_texture, base_color_sampler, in.uv); + if ((MaterialBindings::material.flags & PbrTypes::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { + output_color = output_color * textureSample(MaterialBindings::base_color_texture, MaterialBindings::base_color_sampler, mesh.uv); } #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 ((MaterialBindings::material.flags & PbrTypes::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: PbrCore::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 = MaterialBindings::material.reflectance; + pbr_input.material.flags = MaterialBindings::material.flags; + pbr_input.material.alpha_cutoff = MaterialBindings::material.alpha_cutoff; // TODO use .a for exposure compensation in HDR - var emissive: vec4 = material.emissive; + var emissive: vec4 = MaterialBindings::material.emissive; #ifdef VERTEX_UVS - if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { - emissive = vec4(emissive.rgb * textureSample(emissive_texture, emissive_sampler, in.uv).rgb, 1.0); + if ((MaterialBindings::material.flags & PbrTypes::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { + emissive = vec4(emissive.rgb * textureSample(MaterialBindings::emissive_texture, MaterialBindings::emissive_sampler, mesh.uv).rgb, 1.0); } #endif pbr_input.material.emissive = emissive; - var metallic: f32 = material.metallic; - var perceptual_roughness: f32 = material.perceptual_roughness; + var metallic: f32 = MaterialBindings::material.metallic; + var perceptual_roughness: f32 = MaterialBindings::material.perceptual_roughness; #ifdef VERTEX_UVS - if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { - let metallic_roughness = textureSample(metallic_roughness_texture, metallic_roughness_sampler, in.uv); + if ((MaterialBindings::material.flags & PbrTypes::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { + let metallic_roughness = textureSample(MaterialBindings::metallic_roughness_texture, MaterialBindings::metallic_roughness_sampler, mesh.uv); // Sampling from GLTF standard channels for now metallic = metallic * metallic_roughness.b; perceptual_roughness = perceptual_roughness * metallic_roughness.g; @@ -61,34 +56,34 @@ fn fragment(in: FragmentInput) -> @location(0) vec4 { var occlusion: f32 = 1.0; #ifdef VERTEX_UVS - if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { - occlusion = textureSample(occlusion_texture, occlusion_sampler, in.uv).r; + if ((MaterialBindings::material.flags & PbrTypes::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { + occlusion = textureSample(MaterialBindings::occlusion_texture, MaterialBindings::occlusion_sampler, mesh.uv).r; } #endif pbr_input.occlusion = occlusion; - pbr_input.frag_coord = in.frag_coord; - pbr_input.world_position = in.world_position; - pbr_input.world_normal = in.world_normal; + pbr_input.frag_coord = frag_coord; + pbr_input.world_position = mesh.world_position; + pbr_input.world_normal = mesh.world_normal; - pbr_input.is_orthographic = view.projection[3].w == 1.0; + pbr_input.is_orthographic = ViewBindings::view.projection[3].w == 1.0; - pbr_input.N = prepare_normal( - material.flags, - in.world_normal, + pbr_input.N = PbrCore::prepare_normal( + MaterialBindings::material.flags, + mesh.world_normal, #ifdef VERTEX_TANGENTS -#ifdef STANDARDMATERIAL_NORMAL_MAP - in.world_tangent, -#endif + #ifdef STANDARDMATERIAL_NORMAL_MAP + mesh.world_tangent, + #endif #endif #ifdef VERTEX_UVS - in.uv, + mesh.uv, #endif - in.is_front, + is_front, ); - pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); + pbr_input.V = PbrCore::calculate_view(mesh.world_position, pbr_input.is_orthographic); - output_color = tone_mapping(pbr(pbr_input)); + output_color = PbrCore::tone_mapping(PbrCore::pbr(pbr_input)); } return output_color; diff --git a/crates/bevy_pbr/src/render/pbr_bindings.wgsl b/crates/bevy_pbr/src/render/pbr_bindings.wgsl index f4e4d34f3b4c2..1f00b5bf391b1 100644 --- a/crates/bevy_pbr/src/render/pbr_bindings.wgsl +++ b/crates/bevy_pbr/src/render/pbr_bindings.wgsl @@ -1,9 +1,9 @@ #define_import_path bevy_pbr::pbr_bindings -#import bevy_pbr::pbr_types +#import bevy_pbr::pbr_types as Types @group(1) @binding(0) -var material: StandardMaterial; +var material: Types::StandardMaterial; @group(1) @binding(1) var base_color_texture: texture_2d; @group(1) @binding(2) diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index cd922bf74412c..2b5dc731a34a9 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -1,5 +1,15 @@ #define_import_path bevy_pbr::pbr_functions +#import bevy_pbr::pbr_types as PbrTypes +#import bevy_pbr::pbr_bindings as PbrBindings +#import bevy_pbr::mesh_types as MeshTypes +#import bevy_pbr::mesh_bindings as MeshBindings +#import bevy_pbr::mesh_view_types as ViewTypes +#import bevy_pbr::mesh_view_bindings as ViewBindings +#import bevy_pbr::lighting as Lighting +#import bevy_pbr::clustered_forward as Clustering +#import bevy_pbr::shadows as Shadows + // NOTE: This ensures that the world_normal is normalized and if // vertex tangents and normal maps then normal mapping may be applied. fn prepare_normal( @@ -28,7 +38,7 @@ fn prepare_normal( #endif #endif - if ((standard_material_flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u) { + if ((standard_material_flags & PbrTypes::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u) { if (!is_front) { N = -N; #ifdef VERTEX_TANGENTS @@ -44,8 +54,8 @@ fn prepare_normal( #ifdef VERTEX_UVS #ifdef STANDARDMATERIAL_NORMAL_MAP // Nt is the tangent-space normal. - var Nt = textureSample(normal_map_texture, normal_map_sampler, uv).rgb; - if ((standard_material_flags & STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u) { + var Nt = textureSample(PbrBindings::normal_map_texture, PbrBindings::normal_map_sampler, uv).rgb; + if ((standard_material_flags & PbrTypes::STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u) { // Only use the xy components and derive z for 2-component normal maps. Nt = vec3(Nt.rg * 2.0 - 1.0, 0.0); Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y); @@ -53,7 +63,7 @@ fn prepare_normal( 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 & PbrTypes::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 @@ -78,16 +88,16 @@ fn calculate_view( var V: vec3; if (is_orthographic) { // Orthographic view vector - V = normalize(vec3(view.view_proj[0].z, view.view_proj[1].z, view.view_proj[2].z)); + V = normalize(vec3(ViewBindings::view.view_proj[0].z, ViewBindings::view.view_proj[1].z, ViewBindings::view.view_proj[2].z)); } else { // Only valid for a perpective projection - V = normalize(view.world_position.xyz - world_position.xyz); + V = normalize(ViewBindings::view.world_position.xyz - world_position.xyz); } return V; } struct PbrInput { - material: StandardMaterial, + material: PbrTypes::StandardMaterial, occlusion: f32, frag_coord: vec4, world_position: vec4, @@ -106,7 +116,7 @@ struct PbrInput { fn pbr_input_new() -> PbrInput { var pbr_input: PbrInput; - pbr_input.material = standard_material_new(); + pbr_input.material = PbrTypes::standard_material_new(); pbr_input.occlusion = 1.0; pbr_input.frag_coord = vec4(0.0, 0.0, 0.0, 1.0); @@ -132,14 +142,14 @@ 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; - if ((in.material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE) != 0u) { + if ((in.material.flags & PbrTypes::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE) != 0u) { // NOTE: If rendering as opaque, alpha should be ignored so set to 1.0 output_color.a = 1.0; - } else if ((in.material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u) { + } else if ((in.material.flags & PbrTypes::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u) { if (output_color.a >= in.material.alpha_cutoff) { // NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque output_color.a = 1.0; @@ -167,62 +177,62 @@ fn pbr( var light_accum: vec3 = vec3(0.0); let view_z = dot(vec4( - view.inverse_view[0].z, - view.inverse_view[1].z, - view.inverse_view[2].z, - view.inverse_view[3].z + ViewBindings::view.inverse_view[0].z, + ViewBindings::view.inverse_view[1].z, + ViewBindings::view.inverse_view[2].z, + ViewBindings::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 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 = point_lights.data[light_id]; + let light_id = Clustering::get_light_id(i); + let light = ViewBindings::point_lights.data[light_id]; var shadow: f32 = 1.0; - if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u - && (light.flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { - shadow = fetch_point_shadow(light_id, in.world_position, in.world_normal); + if ((MeshBindings::mesh.flags & MeshTypes::MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u + && (light.flags & ViewTypes::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, roughness, NdotV, in.N, in.V, R, F0, diffuse_color); + let light_contrib = Lighting::point_light(in.world_position.xyz, light, roughness, NdotV, in.N, in.V, R, F0, diffuse_color); light_accum = light_accum + light_contrib * shadow; } // spot lights 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 = point_lights.data[light_id]; + let light_id = Clustering::get_light_id(i); + let light = ViewBindings::point_lights.data[light_id]; var shadow: f32 = 1.0; - if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u - && (light.flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { - shadow = fetch_spot_shadow(light_id, in.world_position, in.world_normal); + if ((MeshBindings::mesh.flags & MeshTypes::MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u + && (light.flags & ViewTypes::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, roughness, NdotV, in.N, in.V, R, F0, diffuse_color); + let light_contrib = Lighting::spot_light(in.world_position.xyz, light, roughness, NdotV, in.N, in.V, R, F0, diffuse_color); light_accum = light_accum + light_contrib * shadow; } - let n_directional_lights = lights.n_directional_lights; + let n_directional_lights = ViewBindings::lights.n_directional_lights; for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) { - let light = lights.directional_lights[i]; + let light = ViewBindings::lights.directional_lights[i]; var shadow: f32 = 1.0; - if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u - && (light.flags & DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { - shadow = fetch_directional_shadow(i, in.world_position, in.world_normal); + if ((MeshBindings::mesh.flags & MeshTypes::MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u + && (light.flags & ViewTypes::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { + shadow = Shadows::fetch_directional_shadow(i, in.world_position, in.world_normal); } - let light_contrib = directional_light(light, roughness, NdotV, in.N, in.V, R, F0, diffuse_color); + let light_contrib = Lighting::directional_light(light, roughness, NdotV, in.N, in.V, R, F0, diffuse_color); light_accum = light_accum + light_contrib * shadow; } - let diffuse_ambient = EnvBRDFApprox(diffuse_color, 1.0, NdotV); - let specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV); + let diffuse_ambient = Lighting::EnvBRDFApprox(diffuse_color, 1.0, NdotV); + let specular_ambient = Lighting::EnvBRDFApprox(F0, perceptual_roughness, NdotV); output_color = vec4( light_accum + - (diffuse_ambient + specular_ambient) * lights.ambient_color.rgb * occlusion + + (diffuse_ambient + specular_ambient) * ViewBindings::lights.ambient_color.rgb * occlusion + emissive.rgb * output_color.a, output_color.a); - output_color = cluster_debug_visualization( + output_color = Clustering::cluster_debug_visualization( output_color, view_z, in.is_orthographic, @@ -235,7 +245,7 @@ fn pbr( fn tone_mapping(in: vec4) -> vec4 { // tone_mapping - return vec4(reinhard_luminance(in.rgb), in.a); + return vec4(Lighting::reinhard_luminance(in.rgb), in.a); // Gamma correction. // Not needed with sRGB buffer diff --git a/crates/bevy_pbr/src/render/pbr_lighting.wgsl b/crates/bevy_pbr/src/render/pbr_lighting.wgsl index ff9451ef302b4..f349f98b8742c 100644 --- a/crates/bevy_pbr/src/render/pbr_lighting.wgsl +++ b/crates/bevy_pbr/src/render/pbr_lighting.wgsl @@ -1,5 +1,8 @@ #define_import_path bevy_pbr::lighting +#import bevy_pbr::utils as Utils +#import bevy_pbr::mesh_view_types as ViewTypes + // From the Filament design doc // https://google.github.io/filament/Filament.html#table_symbols // Symbol Definition @@ -41,7 +44,7 @@ // because otherwise every light affects every fragment in the scene fn getDistanceAttenuation(distanceSquare: f32, inverseRangeSquared: f32) -> f32 { let factor = distanceSquare * inverseRangeSquared; - let smoothFactor = saturate(1.0 - factor * factor); + let smoothFactor = Utils::saturate(1.0 - factor * factor); let attenuation = smoothFactor * smoothFactor; return attenuation * 1.0 / max(distanceSquare, 0.0001); } @@ -57,7 +60,7 @@ fn D_GGX(roughness: f32, NoH: f32, h: vec3) -> f32 { let oneMinusNoHSquared = 1.0 - NoH * NoH; let a = NoH * roughness; let k = roughness / (oneMinusNoHSquared + a * a); - let d = k * k * (1.0 / PI); + let d = k * k * (1.0 / Utils::PI); return d; } @@ -92,7 +95,7 @@ fn F_Schlick(f0: f32, f90: f32, VoH: f32) -> f32 { fn fresnel(f0: vec3, LoH: f32) -> vec3 { // f_90 suitable for ambient occlusion // see https://google.github.io/filament/Filament.html#lighting/occlusion - let f90 = saturate(dot(f0, vec3(50.0 * 0.33))); + let f90 = Utils::saturate(dot(f0, vec3(50.0 * 0.33))); return F_Schlick_vec(f0, f90, LoH); } @@ -128,7 +131,7 @@ fn Fd_Burley(roughness: f32, NoV: f32, NoL: f32, LoH: f32) -> f32 { let f90 = 0.5 + 2.0 * roughness * LoH * LoH; let lightScatter = F_Schlick(1.0, f90, NoL); let viewScatter = F_Schlick(1.0, f90, NoV); - return lightScatter * viewScatter * (1.0 / PI); + return lightScatter * viewScatter * (1.0 / Utils::PI); } // From https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile @@ -185,7 +188,7 @@ fn reinhard_extended_luminance(color: vec3, max_white_l: f32) -> vec3 } fn point_light( - world_position: vec3, light: PointLight, roughness: f32, NdotV: f32, N: vec3, V: vec3, + world_position: vec3, light: ViewTypes::PointLight, roughness: f32, NdotV: f32, N: vec3, V: vec3, R: vec3, F0: vec3, diffuseColor: vec3 ) -> vec3 { let light_to_frag = light.position_radius.xyz - world_position.xyz; @@ -198,16 +201,16 @@ fn point_light( // see http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p14-16 let a = roughness; let centerToRay = dot(light_to_frag, R) * R - light_to_frag; - let closestPoint = light_to_frag + centerToRay * saturate(light.position_radius.w * inverseSqrt(dot(centerToRay, centerToRay))); + let closestPoint = light_to_frag + centerToRay * Utils::saturate(light.position_radius.w * inverseSqrt(dot(centerToRay, centerToRay))); let LspecLengthInverse = inverseSqrt(dot(closestPoint, closestPoint)); - let normalizationFactor = a / saturate(a + (light.position_radius.w * 0.5 * LspecLengthInverse)); + let normalizationFactor = a / Utils::saturate(a + (light.position_radius.w * 0.5 * LspecLengthInverse)); let specularIntensity = normalizationFactor * normalizationFactor; var L: vec3 = closestPoint * LspecLengthInverse; // normalize() equivalent? var H: vec3 = normalize(L + V); - var NoL: f32 = saturate(dot(N, L)); - var NoH: f32 = saturate(dot(N, H)); - var LoH: f32 = saturate(dot(L, H)); + var NoL: f32 = Utils::saturate(dot(N, L)); + var NoH: f32 = Utils::saturate(dot(N, H)); + var LoH: f32 = Utils::saturate(dot(L, H)); let specular_light = specular(F0, roughness, H, NdotV, NoL, NoH, LoH, specularIntensity); @@ -215,9 +218,9 @@ fn point_light( // Comes after specular since its NoL is used in the lighting equation. L = normalize(light_to_frag); H = normalize(L + V); - NoL = saturate(dot(N, L)); - NoH = saturate(dot(N, H)); - LoH = saturate(dot(L, H)); + NoL = Utils::saturate(dot(N, L)); + NoH = Utils::saturate(dot(N, H)); + LoH = Utils::saturate(dot(L, H)); let diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH); @@ -240,7 +243,7 @@ fn point_light( } fn spot_light( - world_position: vec3, light: PointLight, roughness: f32, NdotV: f32, N: vec3, V: vec3, + world_position: vec3, light: ViewTypes::PointLight, roughness: f32, NdotV: f32, N: vec3, V: vec3, R: vec3, F0: vec3, diffuseColor: vec3 ) -> vec3 { // reuse the point light calculations @@ -249,7 +252,7 @@ fn spot_light( // reconstruct spot dir from x/z and y-direction flag var spot_dir = vec3(light.light_custom_data.x, 0.0, light.light_custom_data.y); spot_dir.y = sqrt(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 & ViewTypes::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; @@ -258,19 +261,19 @@ fn spot_light( // spot_scale and spot_offset have been precomputed // note we normalize here to get "l" from the filament listing. spot_dir is already normalized let cd = dot(-spot_dir, normalize(light_to_frag)); - let attenuation = saturate(cd * light.light_custom_data.z + light.light_custom_data.w); + let attenuation = Utils::saturate(cd * light.light_custom_data.z + light.light_custom_data.w); let spot_attenuation = attenuation * attenuation; return point_light * spot_attenuation; } -fn directional_light(light: DirectionalLight, roughness: f32, NdotV: f32, normal: vec3, view: vec3, R: vec3, F0: vec3, diffuseColor: vec3) -> vec3 { +fn directional_light(light: ViewTypes::DirectionalLight, roughness: f32, NdotV: f32, normal: vec3, view: vec3, R: vec3, F0: vec3, diffuseColor: vec3) -> vec3 { let incident_light = light.direction_to_light.xyz; let half_vector = normalize(incident_light + view); - let NoL = saturate(dot(normal, incident_light)); - let NoH = saturate(dot(normal, half_vector)); - let LoH = saturate(dot(incident_light, half_vector)); + let NoL = Utils::saturate(dot(normal, incident_light)); + let NoH = Utils::saturate(dot(normal, half_vector)); + let LoH = Utils::saturate(dot(incident_light, half_vector)); let diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH); let specularIntensity = 1.0; diff --git a/crates/bevy_pbr/src/render/shadows.wgsl b/crates/bevy_pbr/src/render/shadows.wgsl index 3953edc041a10..a4283a74416da 100644 --- a/crates/bevy_pbr/src/render/shadows.wgsl +++ b/crates/bevy_pbr/src/render/shadows.wgsl @@ -1,7 +1,10 @@ #define_import_path bevy_pbr::shadows +#import bevy_pbr::mesh_view_types as Types +#import bevy_pbr::mesh_view_bindings as Bindings + fn fetch_point_shadow(light_id: u32, frag_position: vec4, surface_normal: vec3) -> f32 { - let light = point_lights.data[light_id]; + let light = 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 @@ -35,14 +38,14 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4, 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, depth); + return textureSampleCompare(Bindings::point_shadow_textures, Bindings::point_shadow_textures_sampler, frag_ls, depth); #else - return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth); + return textureSampleCompareLevel(Bindings::point_shadow_textures, Bindings::point_shadow_textures_sampler, frag_ls, i32(light_id), depth); #endif } fn fetch_spot_shadow(light_id: u32, frag_position: vec4, surface_normal: vec3) -> f32 { - let light = point_lights.data[light_id]; + let light = Bindings::point_lights.data[light_id]; let surface_to_light = light.position_radius.xyz - frag_position.xyz; @@ -50,7 +53,7 @@ fn fetch_spot_shadow(light_id: u32, frag_position: vec4, surface_normal: ve var spot_dir = vec3(light.light_custom_data.x, 0.0, light.light_custom_data.y); // reconstruct spot dir from x/z and y-direction flag spot_dir.y = sqrt(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 & Types::POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE) != 0u) { spot_dir.y = -spot_dir.y; } @@ -90,16 +93,16 @@ fn fetch_spot_shadow(light_id: u32, frag_position: vec4, 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(Bindings::directional_shadow_textures, 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(Bindings::directional_shadow_textures, Bindings::directional_shadow_textures_sampler, + shadow_uv, i32(light_id) + Bindings::lights.spot_light_shadowmap_offset, depth); #endif } fn fetch_directional_shadow(light_id: u32, frag_position: vec4, surface_normal: vec3) -> f32 { - let light = lights.directional_lights[light_id]; + let light = Bindings::lights.directional_lights[light_id]; // The normal bias is scaled to the texel size. let normal_offset = light.shadow_normal_bias * surface_normal.xyz; @@ -127,8 +130,8 @@ fn fetch_directional_shadow(light_id: u32, frag_position: vec4, surface_nor // NOTE: Due to non-uniform control flow above, we must use the level variant of the texture // 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, light_local, depth); + return textureSampleCompareLevel(Bindings::directional_shadow_textures, Bindings::directional_shadow_textures_sampler, light_local, depth); #else - return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), depth); + return textureSampleCompareLevel(Bindings::directional_shadow_textures, Bindings::directional_shadow_textures_sampler, light_local, i32(light_id), depth); #endif } diff --git a/crates/bevy_pbr/src/render/skinning.wgsl b/crates/bevy_pbr/src/render/skinning.wgsl index 9fe80c904163d..bc5b6ab375f73 100644 --- a/crates/bevy_pbr/src/render/skinning.wgsl +++ b/crates/bevy_pbr/src/render/skinning.wgsl @@ -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 as MeshTypes + +#ifdef SKINNED + +#ifdef MESH_BINDGROUP_1 + + @group(1) @binding(1) + var joint_matrices: MeshTypes::SkinnedMesh; + +#else + + @group(2) @binding(1) + var joint_matrices: MeshTypes::SkinnedMesh; + +#endif + + fn skin_model( indexes: vec4, weights: vec4, @@ -36,3 +49,5 @@ fn skin_normals( model[2].xyz )) * normal; } + +#endif \ No newline at end of file diff --git a/crates/bevy_pbr/src/render/wireframe.wgsl b/crates/bevy_pbr/src/render/wireframe.wgsl index 651a10d491b06..4dd3141a84c12 100644 --- a/crates/bevy_pbr/src/render/wireframe.wgsl +++ b/crates/bevy_pbr/src/render/wireframe.wgsl @@ -1,18 +1,12 @@ -#import bevy_pbr::mesh_types -#import bevy_pbr::mesh_view_bindings - -@group(1) @binding(0) -var mesh: Mesh; +#import bevy_pbr::mesh_bindings as MeshBindings +#import bevy_pbr::mesh_functions as MeshFunctions #ifdef SKINNED -@group(1) @binding(1) +@group(2) @binding(1) var 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, #ifdef SKINNED @@ -28,13 +22,13 @@ struct VertexOutput { @vertex fn vertex(vertex: Vertex) -> VertexOutput { #ifdef SKINNED - let model = skin_model(vertex.joint_indexes, vertex.joint_weights); + let model = MeshFunctions::skin_model(vertex.joint_indexes, vertex.joint_weights); #else - let model = mesh.model; + let model = MeshBindings::mesh.model; #endif var out: VertexOutput; - out.clip_position = mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); + out.clip_position = MeshFunctions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); return out; } diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index b194fa5983e54..7f0a1dd55cd30 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -1,7 +1,7 @@ use crate::MeshPipeline; use crate::{DrawMesh, MeshPipelineKey, MeshUniform, SetMeshBindGroup, SetMeshViewBindGroup}; use bevy_app::Plugin; -use bevy_asset::{load_internal_asset, Handle, HandleUntyped}; +use bevy_asset::{load_internal_asset_with_path, Handle, HandleUntyped}; use bevy_core_pipeline::core_3d::Opaque3d; use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_reflect::std_traits::ReflectDefault; @@ -29,11 +29,11 @@ pub struct WireframePlugin; impl Plugin for WireframePlugin { fn build(&self, app: &mut bevy_app::App) { - load_internal_asset!( + load_internal_asset_with_path!( app, WIREFRAME_SHADER_HANDLE, "render/wireframe.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); app.register_type::() @@ -92,6 +92,10 @@ impl SpecializedMeshPipeline for WireframePipeline { ) -> Result { let mut descriptor = self.mesh_pipeline.specialize(key, layout)?; descriptor.vertex.shader = self.shader.clone_weak(); + descriptor + .vertex + .shader_defs + .push("MESH_BINDGROUP_1".to_owned()); 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; diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index 2bd7342aec3f3..fc9804be85357 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -68,6 +68,7 @@ regex = "1.5" copyless = "0.1.5" ddsfile = { version = "0.5.0", optional = true } ktx2 = { version = "0.3.0", optional = true } +naga_oil = { git = "https://github.com/robtfm/naga_oil" } # For ktx2 supercompression flate2 = { version = "1.0.22", optional = true } ruzstd = { version = "0.2.4", optional = true } diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 341e3b21646ef..068043756c15e 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -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, @@ -13,11 +11,12 @@ use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_ecs::event::EventReader; use bevy_ecs::system::{Res, ResMut}; use bevy_utils::{default, tracing::error, Entry, HashMap, HashSet}; -use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref, sync::Arc}; +use naga::valid::Capabilities; +use std::{hash::Hash, mem, ops::Deref, sync::Arc}; use thiserror::Error; use wgpu::{ - BufferBindingType, PipelineLayoutDescriptor, ShaderModule, - VertexBufferLayout as RawVertexBufferLayout, + util::make_spirv, BufferBindingType, Features, PipelineLayoutDescriptor, ShaderModule, + ShaderModuleDescriptor, VertexBufferLayout as RawVertexBufferLayout, }; enum PipelineDescriptor { @@ -85,10 +84,44 @@ struct ShaderCache { shaders: HashMap, Shader>, import_path_shaders: HashMap>, waiting_on_import: HashMap>>, - processor: ShaderProcessor, + composers: HashMap, } impl ShaderCache { + fn get_or_init_composer<'a>( + render_device: &RenderDevice, + composers: &'a mut HashMap, + ) -> &'a mut naga_oil::compose::Composer { + let features = render_device.features(); + + match composers.entry(features) { + Entry::Occupied(e) => e.into_mut(), + Entry::Vacant(e) => { + const CAPABILITIES: &[(Features, Capabilities)] = &[ + (Features::PUSH_CONSTANTS, Capabilities::PUSH_CONSTANT), + (Features::SHADER_FLOAT64, Capabilities::FLOAT64), + ( + Features::SHADER_PRIMITIVE_INDEX, + Capabilities::PRIMITIVE_INDEX, + ), + ]; + let mut capabilities = Capabilities::empty(); + for (feature, capability) in CAPABILITIES { + if features.contains(*feature) { + capabilities |= *capability; + } + } + + #[cfg(debug)] + let composer = naga_oil::compose::Composer::default(); + #[cfg(not(debug))] + let composer = naga_oil::compose::Composer::non_validating(); + + e.insert(composer.with_capabilities(capabilities)) + } + } + } + fn get( &mut self, render_device: &RenderDevice, @@ -137,21 +170,59 @@ impl ShaderCache { shader_defs.push(String::from("NO_STORAGE_BUFFERS_SUPPORT")); } - 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 { + Source::SpirV(data) => make_spirv(data), + _ => { + let composer = + Self::get_or_init_composer(render_device, &mut self.composers); + + // todo clean this up + let mut to_check: Vec<_> = shader.imports().collect(); + let mut to_add: Vec<_> = Default::default(); + while let Some(import) = to_check.pop() { + if !composer.contains_module(import.as_str()) { + to_add.push(import); + let shader = self + .shaders + .get(self.import_path_shaders.get(import).unwrap_or_else( + || panic!("{:?} not found in imports", import), + )) + .unwrap_or_else(|| panic!("{:?} not found in shaders", import)); + to_check.extend(shader.imports.iter()); + } + } + for import in to_add.iter().rev() { + if !composer.contains_module(import.as_str()) { + let shader = self + .shaders + .get(self.import_path_shaders.get(import).unwrap_or_else( + || panic!("{:?} not found in imports", import), + )) + .unwrap_or_else(|| panic!("{:?} not found in shaders", import)); + composer.add_composable_module( + shader.source.as_str(), + shader.path.as_deref().unwrap_or(""), + (&shader.source).into(), + )?; + } + } + + let naga = composer.make_naga_module( + shader.source.as_str(), + shader.path.as_deref().unwrap_or(""), + (&shader.source).into(), + &shader_defs, + )?; + + wgpu::ShaderSource::Naga(naga) } }; + let module_descriptor = ShaderModuleDescriptor { + label: None, + source: shader_source, + }; + render_device .wgpu_device() .push_error_scope(wgpu::ErrorFilter::Validation); @@ -183,6 +254,16 @@ 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: Some(import_path), + .. + }) = self.shaders.get(&handle) + { + for composer in self.composers.values_mut() { + composer.remove_composable_module(import_path.as_str()); + } + } } } @@ -509,11 +590,12 @@ impl PipelineCache { | PipelineCacheError::ShaderImportNotYetAvailable => { /* retry */ } // 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(ShaderCache::get_or_init_composer( + &self.device, + &mut self.shader_cache.composers, + )); + error!("failed to process shader:\n{}", error_detail); continue; } PipelineCacheError::CreateShaderModule(description) => { @@ -563,97 +645,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); - } - 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); - } - 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); - } - }, - AsModuleDescriptorError::WgslConversion(error) => { - error!("failed to convert shader to wgsl: \n{}", error); - } - AsModuleDescriptorError::SpirVConversion(error) => { - error!("failed to convert shader to spirv: \n{}", error); - } - } -} - #[derive(Error, Debug)] pub enum PipelineCacheError { #[error( @@ -661,35 +652,9 @@ pub enum PipelineCacheError { )] ShaderNotLoaded(Handle), #[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 { - let current = self.current; - self.current = self.current.and_then(std::error::Error::source); - current - } -} - -impl<'a> FusedIterator for ErrorSources<'a> {} diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index 5913d44dd3aab..5f03ce525e2f4 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -1,17 +1,10 @@ use bevy_asset::{AssetLoader, AssetPath, Handle, LoadContext, LoadedAsset}; use bevy_reflect::{TypeUuid, Uuid}; -use bevy_utils::{tracing::error, BoxedFuture, HashMap}; -use naga::back::wgsl::WriterFlags; -use naga::valid::Capabilities; -use naga::{valid::ModuleInfo, Module}; +use bevy_utils::{tracing::error, BoxedFuture}; use once_cell::sync::Lazy; use regex::Regex; -use std::{ - borrow::Cow, collections::HashSet, marker::Copy, ops::Deref, path::PathBuf, str::FromStr, -}; +use std::{borrow::Cow, marker::Copy, path::PathBuf, str::FromStr}; use thiserror::Error; -use wgpu::Features; -use wgpu::{util::make_spirv, ShaderModuleDescriptor, ShaderSource}; #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] pub struct ShaderId(Uuid); @@ -34,31 +27,55 @@ pub enum ShaderReflectError { #[error(transparent)] Validation(#[from] naga::WithSpan), } -/// A shader, as defined by its [`ShaderSource`] and [`ShaderStage`](naga::ShaderStage) +/// A shader, as defined by its [`ShaderSource`](wgpu::ShaderSource) and [`ShaderStage`](naga::ShaderStage) /// This is an "unprocessed" shader. It can contain preprocessor directives. #[derive(Debug, Clone, TypeUuid)] #[uuid = "d95bc916-6c55-4de3-9622-37e7b6969fda"] pub struct Shader { - source: Source, - import_path: Option, - imports: Vec, + pub path: Option, + pub source: Source, + pub import_path: Option, + pub imports: Vec, } impl Shader { + pub fn from_wgsl_with_path( + source: impl Into>, + path: impl Into, + ) -> Shader { + Self { + path: Some(path.into()), + ..Self::from_wgsl(source) + } + } + pub fn from_wgsl(source: impl Into>) -> Shader { let source = source.into(); let shader_imports = SHADER_IMPORT_PROCESSOR.get_imports_from_str(&source); Shader { + path: None, imports: shader_imports.imports, import_path: shader_imports.import_path, source: Source::Wgsl(source), } } + pub fn from_glsl_with_path( + source: impl Into>, + stage: naga::ShaderStage, + path: impl Into, + ) -> Shader { + Self { + path: Some(path.into()), + ..Self::from_glsl(source, stage) + } + } + pub fn from_glsl(source: impl Into>, stage: naga::ShaderStage) -> Shader { let source = source.into(); let shader_imports = SHADER_IMPORT_PROCESSOR.get_imports_from_str(&source); Shader { + path: None, imports: shader_imports.imports, import_path: shader_imports.import_path, source: Source::Glsl(source, stage), @@ -67,6 +84,7 @@ impl Shader { pub fn from_spirv(source: impl Into>) -> Shader { Shader { + path: None, imports: Vec::new(), import_path: None, source: Source::SpirV(source.into()), @@ -103,131 +121,38 @@ pub enum Source { // NagaModule(Module) ... Module impls Serialize/Deserialize } -/// A processed [Shader]. This cannot contain preprocessor directions. It must be "ready to compile" -#[derive(PartialEq, Eq, Debug)] -pub enum ProcessedShader { - Wgsl(Cow<'static, str>), - Glsl(Cow<'static, str>, naga::ShaderStage), - SpirV(Cow<'static, [u8]>), -} - -impl ProcessedShader { - pub fn get_wgsl_source(&self) -> Option<&str> { - if let ProcessedShader::Wgsl(source) = self { - Some(source) - } else { - None +impl Source { + pub fn as_str(&self) -> &str { + match self { + Source::Wgsl(s) | Source::Glsl(s, _) => s, + Source::SpirV(_) => panic!("spirv not yet implemented"), } } - pub fn get_glsl_source(&self) -> Option<&str> { - if let ProcessedShader::Glsl(source, _stage) = self { - Some(source) - } else { - None +} + +impl From<&Source> for naga_oil::compose::ShaderLanguage { + fn from(value: &Source) -> Self { + match value { + Source::Wgsl(_) => naga_oil::compose::ShaderLanguage::Wgsl, + Source::Glsl(_, _) => naga_oil::compose::ShaderLanguage::Glsl, + Source::SpirV(_) => panic!("spirv not yet implemented"), } } +} - pub fn reflect(&self, features: Features) -> Result { - let module = match &self { - // TODO: process macros here - ProcessedShader::Wgsl(source) => naga::front::wgsl::parse_str(source)?, - ProcessedShader::Glsl(source, shader_stage) => { - let mut parser = naga::front::glsl::Parser::default(); - parser - .parse(&naga::front::glsl::Options::from(*shader_stage), source) - .map_err(ShaderReflectError::GlslParse)? +impl From<&Source> for naga_oil::compose::ShaderType { + fn from(value: &Source) -> Self { + match value { + Source::Wgsl(_) => naga_oil::compose::ShaderType::Wgsl, + Source::Glsl(_, naga::ShaderStage::Vertex) => naga_oil::compose::ShaderType::GlslVertex, + Source::Glsl(_, naga::ShaderStage::Fragment) => { + naga_oil::compose::ShaderType::GlslFragment } - ProcessedShader::SpirV(source) => naga::front::spv::parse_u8_slice( - source, - &naga::front::spv::Options { - adjust_coordinate_space: false, - ..naga::front::spv::Options::default() - }, - )?, - }; - const CAPABILITIES: &[(Features, Capabilities)] = &[ - (Features::PUSH_CONSTANTS, Capabilities::PUSH_CONSTANT), - (Features::SHADER_FLOAT64, Capabilities::FLOAT64), - ( - Features::SHADER_PRIMITIVE_INDEX, - Capabilities::PRIMITIVE_INDEX, - ), - ]; - let mut capabilities = Capabilities::empty(); - for (feature, capability) in CAPABILITIES { - if features.contains(*feature) { - capabilities |= *capability; + Source::Glsl(_, naga::ShaderStage::Compute) => { + panic!("glsl compute not yet implemented") } + Source::SpirV(_) => panic!("spirv not yet implemented"), } - let module_info = - naga::valid::Validator::new(naga::valid::ValidationFlags::default(), capabilities) - .validate(&module)?; - - Ok(ShaderReflection { - module, - module_info, - }) - } - - pub fn get_module_descriptor( - &self, - features: Features, - ) -> Result { - Ok(ShaderModuleDescriptor { - label: None, - source: match self { - ProcessedShader::Wgsl(source) => { - #[cfg(debug_assertions)] - // Parse and validate the shader early, so that (e.g. while hot reloading) we can - // display nicely formatted error messages instead of relying on just displaying the error string - // returned by wgpu upon creating the shader module. - let _ = self.reflect(features)?; - - ShaderSource::Wgsl(source.clone()) - } - ProcessedShader::Glsl(_source, _stage) => { - let reflection = self.reflect(features)?; - // TODO: it probably makes more sense to convert this to spirv, but as of writing - // this comment, naga's spirv conversion is broken - let wgsl = reflection.get_wgsl()?; - ShaderSource::Wgsl(wgsl.into()) - } - ProcessedShader::SpirV(source) => make_spirv(source), - }, - }) - } -} - -#[derive(Error, Debug)] -pub enum AsModuleDescriptorError { - #[error(transparent)] - ShaderReflectError(#[from] ShaderReflectError), - #[error(transparent)] - WgslConversion(#[from] naga::back::wgsl::Error), - #[error(transparent)] - SpirVConversion(#[from] naga::back::spv::Error), -} - -pub struct ShaderReflection { - pub module: Module, - pub module_info: ModuleInfo, -} - -impl ShaderReflection { - pub fn get_spirv(&self) -> Result, naga::back::spv::Error> { - naga::back::spv::write_vec( - &self.module, - &self.module_info, - &naga::back::spv::Options { - flags: naga::back::spv::WriterFlags::empty(), - ..naga::back::spv::Options::default() - }, - None, - ) - } - - pub fn get_wgsl(&self) -> Result { - naga::back::wgsl::write_string(&self.module, &self.module_info, WriterFlags::EXPLICIT_TYPES) } } @@ -245,14 +170,19 @@ impl AssetLoader for ShaderLoader { let mut shader = match ext { "spv" => Shader::from_spirv(Vec::from(bytes)), - "wgsl" => Shader::from_wgsl(String::from_utf8(Vec::from(bytes))?), - "vert" => Shader::from_glsl( + "wgsl" => Shader::from_wgsl_with_path( + String::from_utf8(Vec::from(bytes))?, + load_context.path().to_string_lossy(), + ), + "vert" => Shader::from_glsl_with_path( String::from_utf8(Vec::from(bytes))?, naga::ShaderStage::Vertex, + load_context.path().to_string_lossy(), ), - "frag" => Shader::from_glsl( + "frag" => Shader::from_glsl_with_path( String::from_utf8(Vec::from(bytes))?, naga::ShaderStage::Fragment, + load_context.path().to_string_lossy(), ), _ => panic!("unhandled extension: {}", ext), }; @@ -283,24 +213,6 @@ impl AssetLoader for ShaderLoader { } } -#[derive(Error, Debug, PartialEq, Eq)] -pub enum ProcessShaderError { - #[error("Too many '# endif' lines. Each endif should be preceded by an if statement.")] - TooManyEndIfs, - #[error( - "Not enough '# endif' lines. Each if statement should be followed by an endif statement." - )] - NotEnoughEndIfs, - #[error("This Shader's format does not support processing shader defs.")] - ShaderFormatDoesNotSupportShaderDefs, - #[error("This Shader's formatdoes not support imports.")] - ShaderFormatDoesNotSupportImports, - #[error("Unresolved import: {0:?}.")] - UnresolvedImport(ShaderImport), - #[error("The shader import {0:?} does not match the source file type. Support for this might be added in the future.")] - MismatchedImportFormat(ShaderImport), -} - pub struct ShaderImportProcessor { import_asset_path_regex: Regex, import_custom_path_regex: Regex, @@ -313,12 +225,20 @@ pub enum ShaderImport { Custom(String), } +impl ShaderImport { + pub fn as_str(&self) -> &str { + match self { + ShaderImport::AssetPath(s) | ShaderImport::Custom(s) => s, + } + } +} + impl Default for ShaderImportProcessor { fn default() -> Self { Self { - import_asset_path_regex: Regex::new(r#"^\s*#\s*import\s+"(.+)""#).unwrap(), - import_custom_path_regex: Regex::new(r"^\s*#\s*import\s+(.+)").unwrap(), - define_import_path_regex: Regex::new(r"^\s*#\s*define_import_path\s+(.+)").unwrap(), + import_asset_path_regex: Regex::new(r#"^\s*#\s*import\s+"([^\s]+)""#).unwrap(), + import_custom_path_regex: Regex::new(r"^\s*#\s*import\s+([^\s]+)").unwrap(), + define_import_path_regex: Regex::new(r"^\s*#\s*define_import_path\s+([^\s]+)").unwrap(), } } } @@ -364,160 +284,6 @@ impl ShaderImportProcessor { pub static SHADER_IMPORT_PROCESSOR: Lazy = Lazy::new(ShaderImportProcessor::default); -pub struct ShaderProcessor { - ifdef_regex: Regex, - ifndef_regex: Regex, - else_regex: Regex, - endif_regex: Regex, -} - -impl Default for ShaderProcessor { - fn default() -> Self { - Self { - ifdef_regex: Regex::new(r"^\s*#\s*ifdef\s*([\w|\d|_]+)").unwrap(), - ifndef_regex: Regex::new(r"^\s*#\s*ifndef\s*([\w|\d|_]+)").unwrap(), - else_regex: Regex::new(r"^\s*#\s*else").unwrap(), - endif_regex: Regex::new(r"^\s*#\s*endif").unwrap(), - } - } -} - -impl ShaderProcessor { - pub fn process( - &self, - shader: &Shader, - shader_defs: &[String], - shaders: &HashMap, Shader>, - import_handles: &HashMap>, - ) -> Result { - let shader_str = match &shader.source { - Source::Wgsl(source) => source.deref(), - Source::Glsl(source, _stage) => source.deref(), - Source::SpirV(source) => { - if shader_defs.is_empty() { - return Ok(ProcessedShader::SpirV(source.clone())); - } - return Err(ProcessShaderError::ShaderFormatDoesNotSupportShaderDefs); - } - }; - - let shader_defs_unique = HashSet::::from_iter(shader_defs.iter().cloned()); - let mut scopes = vec![true]; - let mut final_string = String::new(); - for line in shader_str.lines() { - if let Some(cap) = self.ifdef_regex.captures(line) { - let def = cap.get(1).unwrap(); - scopes.push(*scopes.last().unwrap() && shader_defs_unique.contains(def.as_str())); - } else if let Some(cap) = self.ifndef_regex.captures(line) { - let def = cap.get(1).unwrap(); - scopes.push(*scopes.last().unwrap() && !shader_defs_unique.contains(def.as_str())); - } else if self.else_regex.is_match(line) { - let mut is_parent_scope_truthy = true; - if scopes.len() > 1 { - is_parent_scope_truthy = scopes[scopes.len() - 2]; - } - if let Some(last) = scopes.last_mut() { - *last = is_parent_scope_truthy && !*last; - } - } else if self.endif_regex.is_match(line) { - scopes.pop(); - if scopes.is_empty() { - return Err(ProcessShaderError::TooManyEndIfs); - } - } else if *scopes.last().unwrap() { - if let Some(cap) = SHADER_IMPORT_PROCESSOR - .import_asset_path_regex - .captures(line) - { - let import = ShaderImport::AssetPath(cap.get(1).unwrap().as_str().to_string()); - self.apply_import( - import_handles, - shaders, - &import, - shader, - shader_defs, - &mut final_string, - )?; - } else if let Some(cap) = SHADER_IMPORT_PROCESSOR - .import_custom_path_regex - .captures(line) - { - let import = ShaderImport::Custom(cap.get(1).unwrap().as_str().to_string()); - self.apply_import( - import_handles, - shaders, - &import, - shader, - shader_defs, - &mut final_string, - )?; - } else if SHADER_IMPORT_PROCESSOR - .define_import_path_regex - .is_match(line) - { - // ignore import path lines - } else { - final_string.push_str(line); - final_string.push('\n'); - } - } - } - - if scopes.len() != 1 { - return Err(ProcessShaderError::NotEnoughEndIfs); - } - - let processed_source = Cow::from(final_string); - - match &shader.source { - Source::Wgsl(_source) => Ok(ProcessedShader::Wgsl(processed_source)), - Source::Glsl(_source, stage) => Ok(ProcessedShader::Glsl(processed_source, *stage)), - Source::SpirV(_source) => { - unreachable!("SpirV has early return"); - } - } - } - - fn apply_import( - &self, - import_handles: &HashMap>, - shaders: &HashMap, Shader>, - import: &ShaderImport, - shader: &Shader, - shader_defs: &[String], - final_string: &mut String, - ) -> Result<(), ProcessShaderError> { - let imported_shader = import_handles - .get(import) - .and_then(|handle| shaders.get(handle)) - .ok_or_else(|| ProcessShaderError::UnresolvedImport(import.clone()))?; - let imported_processed = - self.process(imported_shader, shader_defs, shaders, import_handles)?; - - match &shader.source { - Source::Wgsl(_) => { - if let ProcessedShader::Wgsl(import_source) = &imported_processed { - final_string.push_str(import_source); - } else { - return Err(ProcessShaderError::MismatchedImportFormat(import.clone())); - } - } - Source::Glsl(_, _) => { - if let ProcessedShader::Glsl(import_source, _) = &imported_processed { - final_string.push_str(import_source); - } else { - return Err(ProcessShaderError::MismatchedImportFormat(import.clone())); - } - } - Source::SpirV(_) => { - return Err(ProcessShaderError::ShaderFormatDoesNotSupportImports); - } - } - - Ok(()) - } -} - /// A reference to a shader asset. pub enum ShaderRef { /// Use the "default" shader for the current context. @@ -545,799 +311,3 @@ impl From<&'static str> for ShaderRef { Self::Path(AssetPath::from(path)) } } - -#[cfg(test)] -mod tests { - use bevy_asset::{Handle, HandleUntyped}; - use bevy_reflect::TypeUuid; - use bevy_utils::HashMap; - use naga::ShaderStage; - - use crate::render_resource::{ProcessShaderError, Shader, ShaderImport, ShaderProcessor}; - #[rustfmt::skip] -const WGSL: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - -#ifdef TEXTURE -@group(1) @binding(0) -var sprite_texture: texture_2d; -#endif - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - - const WGSL_ELSE: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - -#ifdef TEXTURE -@group(1) @binding(0) -var sprite_texture: texture_2d; -#else -@group(1) @binding(0) -var sprite_texture: texture_2d_array; -#endif - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - - const WGSL_NESTED_IFDEF: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - -# ifdef TEXTURE -# ifdef ATTRIBUTE -@group(1) @binding(0) -var sprite_texture: texture_2d; -# endif -# endif - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - - const WGSL_NESTED_IFDEF_ELSE: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - -# ifdef TEXTURE -# ifdef ATTRIBUTE -@group(1) @binding(0) -var sprite_texture: texture_2d; -#else -@group(1) @binding(0) -var sprite_texture: texture_2d_array; -# endif -# endif - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - - #[test] - fn process_shader_def_defined() { - #[rustfmt::skip] - const EXPECTED: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - -@group(1) @binding(0) -var sprite_texture: texture_2d; - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(WGSL), - &["TEXTURE".to_string()], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_shader_def_not_defined() { - #[rustfmt::skip] - const EXPECTED: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(WGSL), - &[], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_shader_def_else() { - #[rustfmt::skip] - const EXPECTED: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - -@group(1) @binding(0) -var sprite_texture: texture_2d_array; - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(WGSL_ELSE), - &[], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_shader_def_unclosed() { - #[rustfmt::skip] - const INPUT: &str = r" -#ifdef FOO -"; - let processor = ShaderProcessor::default(); - let result = processor.process( - &Shader::from_wgsl(INPUT), - &[], - &HashMap::default(), - &HashMap::default(), - ); - assert_eq!(result, Err(ProcessShaderError::NotEnoughEndIfs)); - } - - #[test] - fn process_shader_def_too_closed() { - #[rustfmt::skip] - const INPUT: &str = r" -#endif -"; - let processor = ShaderProcessor::default(); - let result = processor.process( - &Shader::from_wgsl(INPUT), - &[], - &HashMap::default(), - &HashMap::default(), - ); - assert_eq!(result, Err(ProcessShaderError::TooManyEndIfs)); - } - - #[test] - fn process_shader_def_commented() { - #[rustfmt::skip] - const INPUT: &str = r" -// #ifdef FOO -fn foo() { } -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(INPUT), - &[], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), INPUT); - } - - #[test] - fn process_import_wgsl() { - #[rustfmt::skip] - const FOO: &str = r" -fn foo() { } -"; - #[rustfmt::skip] - const INPUT: &str = r" -#import FOO -fn bar() { } -"; - #[rustfmt::skip] - const EXPECTED: &str = r" - -fn foo() { } -fn bar() { } -"; - let processor = ShaderProcessor::default(); - let mut shaders = HashMap::default(); - let mut import_handles = HashMap::default(); - let foo_handle = Handle::::default(); - shaders.insert(foo_handle.clone_weak(), Shader::from_wgsl(FOO)); - import_handles.insert( - ShaderImport::Custom("FOO".to_string()), - foo_handle.clone_weak(), - ); - let result = processor - .process(&Shader::from_wgsl(INPUT), &[], &shaders, &import_handles) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_import_glsl() { - #[rustfmt::skip] - const FOO: &str = r" -void foo() { } -"; - #[rustfmt::skip] - const INPUT: &str = r" -#import FOO -void bar() { } -"; - #[rustfmt::skip] - const EXPECTED: &str = r" - -void foo() { } -void bar() { } -"; - let processor = ShaderProcessor::default(); - let mut shaders = HashMap::default(); - let mut import_handles = HashMap::default(); - let foo_handle = Handle::::default(); - shaders.insert( - foo_handle.clone_weak(), - Shader::from_glsl(FOO, ShaderStage::Vertex), - ); - import_handles.insert( - ShaderImport::Custom("FOO".to_string()), - foo_handle.clone_weak(), - ); - let result = processor - .process( - &Shader::from_glsl(INPUT, ShaderStage::Vertex), - &[], - &shaders, - &import_handles, - ) - .unwrap(); - assert_eq!(result.get_glsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_nested_shader_def_outer_defined_inner_not() { - #[rustfmt::skip] - const EXPECTED: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(WGSL_NESTED_IFDEF), - &["TEXTURE".to_string()], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_nested_shader_def_outer_defined_inner_else() { - #[rustfmt::skip] - const EXPECTED: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - -@group(1) @binding(0) -var sprite_texture: texture_2d_array; - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(WGSL_NESTED_IFDEF_ELSE), - &["TEXTURE".to_string()], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_nested_shader_def_neither_defined() { - #[rustfmt::skip] - const EXPECTED: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(WGSL_NESTED_IFDEF), - &[], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_nested_shader_def_neither_defined_else() { - #[rustfmt::skip] - const EXPECTED: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(WGSL_NESTED_IFDEF_ELSE), - &[], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_nested_shader_def_inner_defined_outer_not() { - #[rustfmt::skip] - const EXPECTED: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(WGSL_NESTED_IFDEF), - &["ATTRIBUTE".to_string()], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_nested_shader_def_both_defined() { - #[rustfmt::skip] - const EXPECTED: &str = r" -struct View { - view_proj: mat4x4, - world_position: vec3, -}; -@group(0) @binding(0) -var view: View; - -@group(1) @binding(0) -var sprite_texture: texture_2d; - -struct VertexOutput { - @location(0) uv: vec2, - @builtin(position) position: vec4, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3, - @location(1) vertex_uv: vec2 -) -> VertexOutput { - var out: VertexOutput; - out.uv = vertex_uv; - out.position = view.view_proj * vec4(vertex_position, 1.0); - return out; -} -"; - let processor = ShaderProcessor::default(); - let result = processor - .process( - &Shader::from_wgsl(WGSL_NESTED_IFDEF), - &["TEXTURE".to_string(), "ATTRIBUTE".to_string()], - &HashMap::default(), - &HashMap::default(), - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_import_ifdef() { - #[rustfmt::skip] - const FOO: &str = r" -#ifdef IMPORT_MISSING -fn in_import_missing() { } -#endif -#ifdef IMPORT_PRESENT -fn in_import_present() { } -#endif -"; - #[rustfmt::skip] - const INPUT: &str = r" -#import FOO -#ifdef MAIN_MISSING -fn in_main_missing() { } -#endif -#ifdef MAIN_PRESENT -fn in_main_present() { } -#endif -"; - #[rustfmt::skip] - const EXPECTED: &str = r" - -fn in_import_present() { } -fn in_main_present() { } -"; - let processor = ShaderProcessor::default(); - let mut shaders = HashMap::default(); - let mut import_handles = HashMap::default(); - let foo_handle = Handle::::default(); - shaders.insert(foo_handle.clone_weak(), Shader::from_wgsl(FOO)); - import_handles.insert( - ShaderImport::Custom("FOO".to_string()), - foo_handle.clone_weak(), - ); - let result = processor - .process( - &Shader::from_wgsl(INPUT), - &["MAIN_PRESENT".to_string(), "IMPORT_PRESENT".to_string()], - &shaders, - &import_handles, - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_import_in_import() { - #[rustfmt::skip] - const BAR: &str = r" -#ifdef DEEP -fn inner_import() { } -#endif -"; - const FOO: &str = r" -#import BAR -fn import() { } -"; - #[rustfmt::skip] - const INPUT: &str = r" -#import FOO -fn in_main() { } -"; - #[rustfmt::skip] - const EXPECTED: &str = r" - - -fn inner_import() { } -fn import() { } -fn in_main() { } -"; - let processor = ShaderProcessor::default(); - let mut shaders = HashMap::default(); - let mut import_handles = HashMap::default(); - { - let bar_handle = Handle::::default(); - shaders.insert(bar_handle.clone_weak(), Shader::from_wgsl(BAR)); - import_handles.insert( - ShaderImport::Custom("BAR".to_string()), - bar_handle.clone_weak(), - ); - } - { - let foo_handle = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1).typed(); - shaders.insert(foo_handle.clone_weak(), Shader::from_wgsl(FOO)); - import_handles.insert( - ShaderImport::Custom("FOO".to_string()), - foo_handle.clone_weak(), - ); - } - let result = processor - .process( - &Shader::from_wgsl(INPUT), - &["DEEP".to_string()], - &shaders, - &import_handles, - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } - - #[test] - fn process_import_in_ifdef() { - #[rustfmt::skip] - const BAR: &str = r" -fn bar() { } -"; - #[rustfmt::skip] - const BAZ: &str = r" -fn baz() { } -"; - #[rustfmt::skip] - const INPUT: &str = r" -#ifdef FOO - #import BAR -#else - #import BAZ -#endif -"; - #[rustfmt::skip] - const EXPECTED_FOO: &str = r" - -fn bar() { } -"; - #[rustfmt::skip] - const EXPECTED: &str = r" - -fn baz() { } -"; - let processor = ShaderProcessor::default(); - let mut shaders = HashMap::default(); - let mut import_handles = HashMap::default(); - { - let bar_handle = Handle::::default(); - shaders.insert(bar_handle.clone_weak(), Shader::from_wgsl(BAR)); - import_handles.insert( - ShaderImport::Custom("BAR".to_string()), - bar_handle.clone_weak(), - ); - } - { - let baz_handle = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1).typed(); - shaders.insert(baz_handle.clone_weak(), Shader::from_wgsl(BAZ)); - import_handles.insert( - ShaderImport::Custom("BAZ".to_string()), - baz_handle.clone_weak(), - ); - } - let result = processor - .process( - &Shader::from_wgsl(INPUT), - &["FOO".to_string()], - &shaders, - &import_handles, - ) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED_FOO); - - let result = processor - .process(&Shader::from_wgsl(INPUT), &[], &shaders, &import_handles) - .unwrap(); - assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); - } -} diff --git a/crates/bevy_sprite/src/mesh2d/color_material.rs b/crates/bevy_sprite/src/mesh2d/color_material.rs index b0f68db9795e4..f5aad0f0d9ce8 100644 --- a/crates/bevy_sprite/src/mesh2d/color_material.rs +++ b/crates/bevy_sprite/src/mesh2d/color_material.rs @@ -1,5 +1,5 @@ use bevy_app::{App, Plugin}; -use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped}; +use bevy_asset::{load_internal_asset_with_path, Assets, Handle, HandleUntyped}; use bevy_math::Vec4; use bevy_reflect::TypeUuid; use bevy_render::{ @@ -16,11 +16,11 @@ pub struct ColorMaterialPlugin; impl Plugin for ColorMaterialPlugin { fn build(&self, app: &mut App) { - load_internal_asset!( + load_internal_asset_with_path!( app, COLOR_MATERIAL_SHADER_HANDLE, "color_material.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); app.add_plugin(Material2dPlugin::::default()); diff --git a/crates/bevy_sprite/src/mesh2d/color_material.wgsl b/crates/bevy_sprite/src/mesh2d/color_material.wgsl index 3b6a2b5217176..51ec3395b3550 100644 --- a/crates/bevy_sprite/src/mesh2d/color_material.wgsl +++ b/crates/bevy_sprite/src/mesh2d/color_material.wgsl @@ -1,5 +1,5 @@ -#import bevy_sprite::mesh2d_types -#import bevy_sprite::mesh2d_view_bindings +#import bevy_sprite::mesh2d_types as MeshTypes +#import bevy_sprite::mesh2d_vertex_output as OutputTypes struct ColorMaterial { color: vec4, @@ -16,21 +16,19 @@ var texture: texture_2d; var texture_sampler: sampler; @group(2) @binding(0) -var mesh: Mesh2d; - -struct FragmentInput { - @builtin(front_facing) is_front: bool, - #import bevy_sprite::mesh2d_vertex_output -}; +var mesh: MeshTypes::Mesh2d; @fragment -fn fragment(in: FragmentInput) -> @location(0) vec4 { +fn fragment( + @builtin(front_facing) is_front: bool, + mesh: OutputTypes::MeshVertexOutput, +) -> @location(0) vec4 { var output_color: vec4 = material.color; if ((material.flags & COLOR_MATERIAL_FLAGS_TEXTURE_BIT) != 0u) { #ifdef VERTEX_COLORS - output_color = output_color * textureSample(texture, texture_sampler, in.uv) * in.color; + output_color = output_color * textureSample(texture, texture_sampler, mesh.uv) * mesh.color; #else - output_color = output_color * textureSample(texture, texture_sampler, in.uv); + output_color = output_color * textureSample(texture, texture_sampler, mesh.uv); #endif } return output_color; diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 41188cd48679f..ba400ff56c13c 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -1,5 +1,5 @@ use bevy_app::Plugin; -use bevy_asset::{load_internal_asset, Handle, HandleUntyped}; +use bevy_asset::{load_internal_asset_with_path, Handle, HandleUntyped}; use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem, SystemState}, @@ -54,43 +54,48 @@ pub const MESH2D_SHADER_HANDLE: HandleUntyped = impl Plugin for Mesh2dRenderPlugin { fn build(&self, app: &mut bevy_app::App) { - load_internal_asset!( + load_internal_asset_with_path!( app, MESH2D_VERTEX_OUTPUT, "mesh2d_vertex_output.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, MESH2D_VIEW_TYPES_HANDLE, "mesh2d_view_types.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, MESH2D_VIEW_BINDINGS_HANDLE, "mesh2d_view_bindings.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, MESH2D_TYPES_HANDLE, "mesh2d_types.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, MESH2D_BINDINGS_HANDLE, "mesh2d_bindings.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path ); - load_internal_asset!( + load_internal_asset_with_path!( app, MESH2D_FUNCTIONS_HANDLE, "mesh2d_functions.wgsl", - Shader::from_wgsl + Shader::from_wgsl_with_path + ); + load_internal_asset_with_path!( + app, + MESH2D_SHADER_HANDLE, + "mesh2d.wgsl", + Shader::from_wgsl_with_path ); - load_internal_asset!(app, MESH2D_SHADER_HANDLE, "mesh2d.wgsl", Shader::from_wgsl); app.add_plugin(UniformComponentPlugin::::default()); diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl index 00c3a9ab74416..596bcd9fe5ec6 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl @@ -1,8 +1,6 @@ -#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_bindings as MeshBindings +#import bevy_sprite::mesh2d_functions as MeshFunctions +#import bevy_sprite::mesh2d_vertex_output as OutputTypes struct Vertex { @location(0) position: vec3, @@ -18,18 +16,31 @@ struct Vertex { struct VertexOutput { @builtin(position) clip_position: vec4, - #import bevy_sprite::mesh2d_vertex_output + // have to copy-paste here, we can't currently embed an unlocated struct in the vertex stage output + @location(0) world_position: vec4, + @location(1) world_normal: vec3, + #ifdef VERTEX_UVS + @location(2) uv: vec2, + #endif + #ifdef VERTEX_TANGENTS + @location(3) world_tangent: vec4, + #endif + #ifdef VERTEX_COLORS + @location(4) color: vec4, + #endif } @vertex fn vertex(vertex: Vertex) -> VertexOutput { var out: VertexOutput; +#ifdef VERTEX_UVS out.uv = vertex.uv; - out.world_position = mesh2d_position_local_to_world(mesh.model, vec4(vertex.position, 1.0)); - out.clip_position = mesh2d_position_world_to_clip(out.world_position); - out.world_normal = mesh2d_normal_local_to_world(vertex.normal); +#endif + out.world_position = MeshFunctions::mesh2d_position_local_to_world(MeshBindings::mesh.model, vec4(vertex.position, 1.0)); + out.clip_position = MeshFunctions::mesh2d_position_world_to_clip(out.world_position); + out.world_normal = MeshFunctions::mesh2d_normal_local_to_world(vertex.normal); #ifdef VERTEX_TANGENTS - out.world_tangent = mesh2d_tangent_local_to_world(vertex.tangent); + out.world_tangent = MeshFunctions::mesh2d_tangent_local_to_world(vertex.tangent); #endif #ifdef VERTEX_COLORS out.color = vertex.color; @@ -37,12 +48,10 @@ fn vertex(vertex: Vertex) -> VertexOutput { return out; } -struct FragmentInput { - @builtin(front_facing) is_front: bool, - #import bevy_sprite::mesh2d_vertex_output -}; - @fragment -fn fragment(in: FragmentInput) -> @location(0) vec4 { +fn fragment( + @builtin(front_facing) is_front: bool, + mesh: OutputTypes::MeshVertexOutput, +) -> @location(0) vec4 { return vec4(1.0, 0.0, 1.0, 1.0); } diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d_bindings.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d_bindings.wgsl index f26a0442c95db..b0ac35b69d6ea 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d_bindings.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d_bindings.wgsl @@ -1,6 +1,6 @@ #define_import_path bevy_sprite::mesh2d_bindings -#import bevy_sprite::mesh2d_types +#import bevy_sprite::mesh2d_types as MeshTypes @group(2) @binding(0) -var mesh: Mesh2d; +var mesh: MeshTypes::Mesh2d; diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl index 5342b638494de..ffca006f0e04b 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl @@ -1,11 +1,14 @@ #define_import_path bevy_sprite::mesh2d_functions +#import bevy_sprite::mesh2d_view_bindings as ViewBindings +#import bevy_sprite::mesh2d_bindings as MeshBindings + fn mesh2d_position_local_to_world(model: mat4x4, vertex_position: vec4) -> vec4 { return model * vertex_position; } fn mesh2d_position_world_to_clip(world_position: vec4) -> vec4 { - return view.view_proj * world_position; + return ViewBindings::view.view_proj * world_position; } // NOTE: The intermediate world_position assignment is important @@ -18,9 +21,9 @@ fn mesh2d_position_local_to_clip(model: mat4x4, vertex_position: vec4) fn mesh2d_normal_local_to_world(vertex_normal: vec3) -> vec3 { return mat3x3( - mesh.inverse_transpose_model[0].xyz, - mesh.inverse_transpose_model[1].xyz, - mesh.inverse_transpose_model[2].xyz + MeshBindings::mesh.inverse_transpose_model[0].xyz, + MeshBindings::mesh.inverse_transpose_model[1].xyz, + MeshBindings::mesh.inverse_transpose_model[2].xyz ) * vertex_normal; } diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d_vertex_output.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d_vertex_output.wgsl index cd0c2e8f42f44..b3f1df61e3f76 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d_vertex_output.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d_vertex_output.wgsl @@ -1,11 +1,13 @@ #define_import_path bevy_sprite::mesh2d_vertex_output -@location(0) world_position: vec4, -@location(1) world_normal: vec3, -@location(2) uv: vec2, -#ifdef VERTEX_TANGENTS -@location(3) world_tangent: vec4, -#endif -#ifdef VERTEX_COLORS -@location(4) color: vec4, -#endif +struct MeshVertexOutput { + @location(0) world_position: vec4, + @location(1) world_normal: vec3, + @location(2) uv: vec2, + #ifdef VERTEX_TANGENTS + @location(3) world_tangent: vec4, + #endif + #ifdef VERTEX_COLORS + @location(4) color: vec4, + #endif +} diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d_view_bindings.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d_view_bindings.wgsl index cc138d28facb7..940405f78440e 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d_view_bindings.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d_view_bindings.wgsl @@ -1,6 +1,6 @@ #define_import_path bevy_sprite::mesh2d_view_bindings -#import bevy_sprite::mesh2d_view_types +#import bevy_sprite::mesh2d_view_types as ViewTypes @group(0) @binding(0) -var view: View; +var view: ViewTypes::View; diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index e19d7b1731667..eac33d6c42114 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -7,7 +7,7 @@ pub use render_pass::*; use crate::{prelude::UiCameraConfig, CalculatedClip, Node, UiColor, UiImage}; use bevy_app::prelude::*; -use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped}; +use bevy_asset::{load_internal_asset_with_path, AssetEvent, Assets, Handle, HandleUntyped}; use bevy_ecs::prelude::*; use bevy_math::{Mat4, Vec2, Vec3, Vec4Swizzles}; use bevy_reflect::TypeUuid; @@ -55,7 +55,12 @@ pub enum RenderUiSystem { } pub fn build_ui_render(app: &mut App) { - load_internal_asset!(app, UI_SHADER_HANDLE, "ui.wgsl", Shader::from_wgsl); + load_internal_asset_with_path!( + app, + UI_SHADER_HANDLE, + "ui.wgsl", + Shader::from_wgsl_with_path + ); let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index d5c245f7bd728..9bfa1830ae675 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -209,14 +209,12 @@ 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_view_bindings +#import bevy_sprite::mesh2d_functions as MeshFunctions @group(1) @binding(0) -var mesh: Mesh2d; - -// NOTE: Bindings must come before functions that use them! -#import bevy_sprite::mesh2d_functions +var mesh: MeshTypes::Mesh2d; // The structure of the vertex buffer is as specified in `specialize()` struct Vertex { @@ -236,7 +234,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(vertex.position, 1.0)); + out.clip_position = MeshFunctions::mesh2d_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); // Unpack the `u32` from the vertex buffer into the `vec4` used by the fragment shader out.color = vec4((vec4(vertex.color) >> vec4(0u, 8u, 16u, 24u)) & vec4(255u)) / 255.0; return out; diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index 6d983277765a0..aa63b516ea6f0 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -234,8 +234,8 @@ impl SpecializedMeshPipeline for CustomPipeline { descriptor.fragment.as_mut().unwrap().shader = self.shader.clone(); descriptor.layout = Some(vec![ self.mesh_pipeline.view_layout.clone(), - self.mesh_pipeline.mesh_layout.clone(), self.time_bind_group_layout.clone(), + self.mesh_pipeline.mesh_layout.clone(), ]); Ok(descriptor) } @@ -244,8 +244,8 @@ impl SpecializedMeshPipeline for CustomPipeline { type DrawCustom = ( SetItemPipeline, SetMeshViewBindGroup<0>, - SetMeshBindGroup<1>, - SetTimeBindGroup<2>, + SetTimeBindGroup<1>, + SetMeshBindGroup<2>, DrawMesh, ); diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 3f90ee14220a8..4d614a583209f 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -189,6 +189,15 @@ impl SpecializedMeshPipeline for CustomPipeline { layout: &MeshVertexBufferLayout, ) -> Result { 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".to_owned()); + descriptor.vertex.shader = self.shader.clone(); descriptor.vertex.buffers.push(VertexBufferLayout { array_stride: std::mem::size_of::() as u64, From bdadb446729a9af9332a1e76cea9b0c548b157e1 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Tue, 16 Aug 2022 15:34:59 +0100 Subject: [PATCH 02/48] remove a few unused shader imports --- assets/shaders/animate_shader.wgsl | 1 - assets/shaders/custom_vertex_attribute.wgsl | 3 --- assets/shaders/instancing.wgsl | 1 - crates/bevy_pbr/src/render/depth.wgsl | 1 - crates/bevy_pbr/src/render/mesh.wgsl | 1 - crates/bevy_pbr/src/render/wireframe.wgsl | 6 ++---- examples/2d/mesh2d_manual.rs | 1 - 7 files changed, 2 insertions(+), 12 deletions(-) diff --git a/assets/shaders/animate_shader.wgsl b/assets/shaders/animate_shader.wgsl index 812a1f1b72686..6dfcb217ce4f6 100644 --- a/assets/shaders/animate_shader.wgsl +++ b/assets/shaders/animate_shader.wgsl @@ -1,4 +1,3 @@ -#import bevy_pbr::mesh_types as MeshTypes #import bevy_pbr::mesh_bindings as MeshBindings #import bevy_pbr::mesh_functions as MeshFunctions diff --git a/assets/shaders/custom_vertex_attribute.wgsl b/assets/shaders/custom_vertex_attribute.wgsl index ab9a5e0fcce81..1c1c134299490 100644 --- a/assets/shaders/custom_vertex_attribute.wgsl +++ b/assets/shaders/custom_vertex_attribute.wgsl @@ -7,9 +7,6 @@ struct CustomMaterial { @group(1) @binding(0) var material: CustomMaterial; -// NOTE: Bindings must come before functions that use them! -#import bevy_pbr::mesh_functions - struct Vertex { @location(0) position: vec3, @location(1) blend_color: vec4, diff --git a/assets/shaders/instancing.wgsl b/assets/shaders/instancing.wgsl index 14e4de09b393a..bf578fbd3e8e3 100644 --- a/assets/shaders/instancing.wgsl +++ b/assets/shaders/instancing.wgsl @@ -1,4 +1,3 @@ -#import bevy_pbr::mesh_types as MeshTypes #import bevy_pbr::mesh_functions as MeshFunctions #import bevy_pbr::mesh_bindings as MeshBindings diff --git a/crates/bevy_pbr/src/render/depth.wgsl b/crates/bevy_pbr/src/render/depth.wgsl index ff11af37f814c..d579e9ef7ec44 100644 --- a/crates/bevy_pbr/src/render/depth.wgsl +++ b/crates/bevy_pbr/src/render/depth.wgsl @@ -1,7 +1,6 @@ #define_import_path depth #import bevy_pbr::mesh_view_types as ViewTypes -#import bevy_pbr::mesh_types as MeshTypes #import bevy_pbr::mesh_bindings as MeshBindings #import bevy_pbr::mesh_functions as MeshFunctions diff --git a/crates/bevy_pbr/src/render/mesh.wgsl b/crates/bevy_pbr/src/render/mesh.wgsl index 4f7a44853664d..78d51e296ff91 100644 --- a/crates/bevy_pbr/src/render/mesh.wgsl +++ b/crates/bevy_pbr/src/render/mesh.wgsl @@ -1,4 +1,3 @@ -#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_bindings as MeshBindings #import bevy_pbr::mesh_functions as MeshFunctions #import bevy_pbr::skinning as Skinning diff --git a/crates/bevy_pbr/src/render/wireframe.wgsl b/crates/bevy_pbr/src/render/wireframe.wgsl index 4dd3141a84c12..b6ac4be5bb388 100644 --- a/crates/bevy_pbr/src/render/wireframe.wgsl +++ b/crates/bevy_pbr/src/render/wireframe.wgsl @@ -2,9 +2,7 @@ #import bevy_pbr::mesh_functions as MeshFunctions #ifdef SKINNED -@group(2) @binding(1) -var joint_matrices: SkinnedMesh; -#import bevy_pbr::skinning + #import bevy_pbr::skinning as Skinning #endif struct Vertex { @@ -22,7 +20,7 @@ struct VertexOutput { @vertex fn vertex(vertex: Vertex) -> VertexOutput { #ifdef SKINNED - let model = MeshFunctions::skin_model(vertex.joint_indexes, vertex.joint_weights); + let model = Skinning::skin_model(vertex.joint_indexes, vertex.joint_weights); #else let model = MeshBindings::mesh.model; #endif diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 9bfa1830ae675..3d04fddae09e4 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -210,7 +210,6 @@ type DrawColoredMesh2d = ( const COLORED_MESH2D_SHADER: &str = r" // Import the standard 2d mesh uniforms and set their bind groups #import bevy_sprite::mesh2d_types as MeshTypes -#import bevy_sprite::mesh2d_view_bindings #import bevy_sprite::mesh2d_functions as MeshFunctions @group(1) @binding(0) From 2813f779a170a609074ba7bf624f9ba1a3ea3fd0 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Fri, 19 Aug 2022 11:30:54 +0100 Subject: [PATCH 03/48] add override example --- Cargo.toml | 10 ++ assets/shaders/custom_material_override.wgsl | 36 ++++++ crates/bevy_pbr/src/render/pbr.wgsl | 2 + .../src/render_resource/pipeline_cache.rs | 5 +- examples/shader/shader_material_override.rs | 121 ++++++++++++++++++ 5 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 assets/shaders/custom_material_override.wgsl create mode 100644 examples/shader/shader_material_override.rs diff --git a/Cargo.toml b/Cargo.toml index b0920d9ecd0fa..cfd339ec1b498 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1196,6 +1196,16 @@ description = "A shader that uses the GLSL shading language" category = "Shaders" wasm = true +[[example]] +name = "shader_material_override" +path = "examples/shader/shader_material_override.rs" + +[package.metadata.example.shader_material_override] +name = "Material with fore function override" +description = "A shader that uses the default pipeline with a function override" +category = "Shaders" +wasm = true + [[example]] name = "shader_instancing" path = "examples/shader/shader_instancing.rs" diff --git a/assets/shaders/custom_material_override.wgsl b/assets/shaders/custom_material_override.wgsl new file mode 100644 index 0000000000000..96d382a9c1978 --- /dev/null +++ b/assets/shaders/custom_material_override.wgsl @@ -0,0 +1,36 @@ +#import bevy_pbr::lighting as Lighting +#import bevy_pbr::mesh_view_types as ViewTypes +#import bevy_pbr::mesh_vertex_output as OutputTypes +#import bevy_pbr::fragment as Pbr + +fn quantize_steps() -> f32 { + return 2.0; +} + +override fn Lighting::point_light ( + world_position: vec3, + light: ViewTypes::PointLight, + roughness: f32, + NdotV: f32, + N: vec3, + V: vec3, + R: vec3, + F0: vec3, + diffuseColor: vec3 +) -> vec3 { + // call original function + let original = + Lighting::point_light(world_position, light, roughness, NdotV, N, V, R, F0, diffuseColor); + // quantize + let quantized = vec3(original * quantize_steps() + 0.5); + return clamp(vec3(quantized) / quantize_steps(), vec3(0.0), vec3(1.0)); +} + +@fragment +fn fragment( + mesh: OutputTypes::MeshVertexOutput, + @builtin(front_facing) is_front: bool, + @builtin(position) frag_coord: vec4, +) -> @location(0) vec4 { + return Pbr::fragment(mesh, is_front, frag_coord); +} diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index e9725b10f7571..22689f5789f6c 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -1,3 +1,5 @@ +#define_import_path bevy_pbr::fragment + #import bevy_pbr::mesh_vertex_output as OutputTypes #import bevy_pbr::pbr_functions as PbrCore #import bevy_pbr::pbr_bindings as MaterialBindings diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 068043756c15e..55ed5b221d1cc 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -112,9 +112,9 @@ impl ShaderCache { } } - #[cfg(debug)] + #[cfg(debug_assertions)] let composer = naga_oil::compose::Composer::default(); - #[cfg(not(debug))] + #[cfg(not(debug_assertions))] let composer = naga_oil::compose::Composer::non_validating(); e.insert(composer.with_capabilities(capabilities)) @@ -203,6 +203,7 @@ impl ShaderCache { shader.source.as_str(), shader.path.as_deref().unwrap_or(""), (&shader.source).into(), + None, )?; } } diff --git a/examples/shader/shader_material_override.rs b/examples/shader/shader_material_override.rs new file mode 100644 index 0000000000000..eec9d45d83b92 --- /dev/null +++ b/examples/shader/shader_material_override.rs @@ -0,0 +1,121 @@ +//! A shader and a material that uses it. + +use bevy::{ + prelude::*, + reflect::TypeUuid, + render::{ + render_asset::RenderAsset, + render_resource::{ + AsBindGroup, AsBindGroupError, BindGroupLayout, PreparedBindGroup, ShaderRef, + }, + renderer::RenderDevice, + texture::FallbackImage, + }, + utils::HashMap, +}; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_plugin(MaterialPlugin::::default()) + .add_startup_system(setup) + .add_system(move_light) + .run(); +} + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // cube + commands.spawn().insert_bundle(MaterialMeshBundle { + mesh: meshes.add(Mesh::from(shape::Icosphere { + radius: 0.5, + subdivisions: 5, + })), + transform: Transform::from_xyz(0.0, 0.5, 0.0), + material: materials.add(CustomMaterial { + inner: StandardMaterial { + base_color: Color::LIME_GREEN, + reflectance: 0.2, + perceptual_roughness: 0.5, + ..Default::default() + }, + }), + ..default() + }); + + // light + commands + .spawn_bundle(PointLightBundle { + point_light: PointLight { + color: Color::WHITE, + intensity: 300.0, + ..Default::default() + }, + transform: Transform::from_xyz(-1.0, 0.25, 1.0), + ..Default::default() + }) + .insert(Move); + + // camera + commands.spawn_bundle(Camera3dBundle { + transform: Transform::from_xyz(-1.0, 1.25, 2.5).looking_at(Vec3::Y * 0.25, Vec3::Y), + ..default() + }); +} + +#[derive(Component)] +struct Move; + +fn move_light(mut q: Query<&mut Transform, With>, time: Res