From 979c4094d4ea8540334697bf19a8086728c8f592 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:12:40 +0100 Subject: [PATCH] pbr shader cleanup (#10105) # Objective cleanup some pbr shader code. improve shader stage io consistency and make pbr.wgsl (probably many people's first foray into bevy shader code) a little more human-readable. also fix a couple of small issues with deferred rendering. ## Solution mesh_vertex_output: - rename to forward_io (to align with prepass_io) - rename `MeshVertexOutput` to `VertexOutput` (to align with prepass_io) - move `Vertex` from mesh.wgsl into here (to align with prepass_io) prepass_io: - remove `FragmentInput`, use `VertexOutput` directly (to align with forward_io) - rename `VertexOutput::clip_position` to `position` (to align with forward_io) pbr.wgsl: - restructure so we don't need `#ifdefs` on the actual entrypoint, use VertexOutput and FragmentOutput in all cases and use #ifdefs to import the right struct definitions. - rearrange to make the flow clearer - move alpha_discard up from `pbr_functions::pbr` to avoid needing to call it on some branches and not others - add a bunch of comments deferred_lighting: - move ssao into the `!unlit` block to reflect forward behaviour correctly - fix compile error with deferred + premultiply_alpha ## Migration Guide in custom material shaders: - `pbr_functions::pbr` no longer calls to `pbr_functions::alpha_discard`. if you were using the `pbr` function in a custom shader with alpha mask mode you now also need to call alpha_discard manually - rename imports of `bevy_pbr::mesh_vertex_output` to `bevy_pbr::forward_io` - rename instances of `MeshVertexOutput` to `VertexOutput` in custom material prepass shaders: - rename instances of `VertexOutput::clip_position` to `VertexOutput::position` --- assets/shaders/animate_shader.wgsl | 4 +- assets/shaders/array_texture.wgsl | 4 +- assets/shaders/cubemap_unlit.wgsl | 4 +- assets/shaders/custom_material.wgsl | 4 +- .../custom_material_screenspace_texture.wgsl | 4 +- assets/shaders/fallback_image_test.wgsl | 4 +- assets/shaders/line_material.wgsl | 4 +- assets/shaders/shader_defs.wgsl | 4 +- assets/shaders/show_prepass.wgsl | 4 +- assets/shaders/texture_binding_array.wgsl | 4 +- assets/shaders/tonemapping_test_patterns.wgsl | 4 +- .../src/deferred/deferred_lighting.wgsl | 16 +-- crates/bevy_pbr/src/prepass/prepass.wgsl | 10 +- crates/bevy_pbr/src/prepass/prepass_io.wgsl | 34 +---- crates/bevy_pbr/src/render/forward_io.wgsl | 52 ++++++++ crates/bevy_pbr/src/render/mesh.rs | 9 +- crates/bevy_pbr/src/render/mesh.wgsl | 35 +---- .../src/render/mesh_vertex_output.wgsl | 21 --- crates/bevy_pbr/src/render/pbr.wgsl | 122 +++++++++--------- crates/bevy_pbr/src/render/pbr_functions.wgsl | 2 - crates/bevy_pbr/src/render/pbr_prepass.wgsl | 4 +- .../src/render/pbr_prepass_functions.wgsl | 6 +- crates/bevy_pbr/src/render/wireframe.wgsl | 6 +- .../src/mesh2d/color_material.wgsl | 4 +- crates/bevy_sprite/src/mesh2d/mesh2d.wgsl | 8 +- .../src/mesh2d/mesh2d_vertex_output.wgsl | 2 +- 26 files changed, 173 insertions(+), 202 deletions(-) create mode 100644 crates/bevy_pbr/src/render/forward_io.wgsl delete mode 100644 crates/bevy_pbr/src/render/mesh_vertex_output.wgsl diff --git a/assets/shaders/animate_shader.wgsl b/assets/shaders/animate_shader.wgsl index 76112e147776b..addb1dee89b34 100644 --- a/assets/shaders/animate_shader.wgsl +++ b/assets/shaders/animate_shader.wgsl @@ -1,6 +1,6 @@ // The time since startup data is in the globals binding which is part of the mesh_view_bindings import #import bevy_pbr::mesh_view_bindings globals -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput fn oklab_to_linear_srgb(c: vec3) -> vec3 { let L = c.x; @@ -23,7 +23,7 @@ fn oklab_to_linear_srgb(c: vec3) -> vec3 { } @fragment -fn fragment(in: MeshVertexOutput) -> @location(0) vec4 { +fn fragment(in: VertexOutput) -> @location(0) vec4 { let speed = 2.0; // The globals binding contains various global values like time // which is the time since startup in seconds diff --git a/assets/shaders/array_texture.wgsl b/assets/shaders/array_texture.wgsl index 7945baf3f8a98..05b0b855313e6 100644 --- a/assets/shaders/array_texture.wgsl +++ b/assets/shaders/array_texture.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput #import bevy_pbr::mesh_view_bindings view #import bevy_pbr::pbr_types STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT, PbrInput, pbr_input_new #import bevy_core_pipeline::tonemapping tone_mapping @@ -10,7 +10,7 @@ @fragment fn fragment( @builtin(front_facing) is_front: bool, - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { let layer = i32(mesh.world_position.x) & 0x3; diff --git a/assets/shaders/cubemap_unlit.wgsl b/assets/shaders/cubemap_unlit.wgsl index 2e05147b15865..56a5b005008e6 100644 --- a/assets/shaders/cubemap_unlit.wgsl +++ b/assets/shaders/cubemap_unlit.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput #ifdef CUBEMAP_ARRAY @group(1) @binding(0) var base_color_texture: texture_cube_array; @@ -10,7 +10,7 @@ @fragment fn fragment( - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { let fragment_position_view_lh = mesh.world_position.xyz * vec3(1.0, 1.0, -1.0); return textureSample( diff --git a/assets/shaders/custom_material.wgsl b/assets/shaders/custom_material.wgsl index 34c2fc3f28f9e..b1c8d75ed9ff5 100644 --- a/assets/shaders/custom_material.wgsl +++ b/assets/shaders/custom_material.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput // we can import items from shader modules in the assets folder with a quoted path #import "shaders/custom_material_import.wgsl" COLOR_MULTIPLIER @@ -12,7 +12,7 @@ struct CustomMaterial { @fragment fn fragment( - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { return material.color * textureSample(base_color_texture, base_color_sampler, mesh.uv) * COLOR_MULTIPLIER; } diff --git a/assets/shaders/custom_material_screenspace_texture.wgsl b/assets/shaders/custom_material_screenspace_texture.wgsl index c285b7587aa82..50fdfdd2b10c3 100644 --- a/assets/shaders/custom_material_screenspace_texture.wgsl +++ b/assets/shaders/custom_material_screenspace_texture.wgsl @@ -1,5 +1,5 @@ #import bevy_pbr::mesh_view_bindings view -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput #import bevy_pbr::utils coords_to_viewport_uv @group(1) @binding(0) var texture: texture_2d; @@ -7,7 +7,7 @@ @fragment fn fragment( - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { let viewport_uv = coords_to_viewport_uv(mesh.position.xy, view.viewport); let color = textureSample(texture, texture_sampler, viewport_uv); diff --git a/assets/shaders/fallback_image_test.wgsl b/assets/shaders/fallback_image_test.wgsl index 175d3d08da592..f51d9961cde7a 100644 --- a/assets/shaders/fallback_image_test.wgsl +++ b/assets/shaders/fallback_image_test.wgsl @@ -1,6 +1,6 @@ #import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_bindings -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput @group(1) @binding(0) var test_texture_1d: texture_1d; @group(1) @binding(1) var test_texture_1d_sampler: sampler; @@ -21,4 +21,4 @@ @group(1) @binding(11) var test_texture_3d_sampler: sampler; @fragment -fn fragment(in: MeshVertexOutput) {} +fn fragment(in: VertexOutput) {} diff --git a/assets/shaders/line_material.wgsl b/assets/shaders/line_material.wgsl index 7e897e6cc1fb3..ed06a27cd069f 100644 --- a/assets/shaders/line_material.wgsl +++ b/assets/shaders/line_material.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput struct LineMaterial { color: vec4, @@ -8,7 +8,7 @@ struct LineMaterial { @fragment fn fragment( - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { return material.color; } diff --git a/assets/shaders/shader_defs.wgsl b/assets/shaders/shader_defs.wgsl index 10495abf62ba3..0586b560c78ff 100644 --- a/assets/shaders/shader_defs.wgsl +++ b/assets/shaders/shader_defs.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput struct CustomMaterial { color: vec4, @@ -8,7 +8,7 @@ struct CustomMaterial { @fragment fn fragment( - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { #ifdef IS_RED return vec4(1.0, 0.0, 0.0, 1.0); diff --git a/assets/shaders/show_prepass.wgsl b/assets/shaders/show_prepass.wgsl index 34f1f814576ed..881c5724d27b4 100644 --- a/assets/shaders/show_prepass.wgsl +++ b/assets/shaders/show_prepass.wgsl @@ -1,7 +1,7 @@ #import bevy_pbr::mesh_types #import bevy_pbr::mesh_view_bindings globals #import bevy_pbr::prepass_utils -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput struct ShowPrepassSettings { show_depth: u32, @@ -17,7 +17,7 @@ fn fragment( #ifdef MULTISAMPLED @builtin(sample_index) sample_index: u32, #endif - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { #ifndef MULTISAMPLED let sample_index = 0u; diff --git a/assets/shaders/texture_binding_array.wgsl b/assets/shaders/texture_binding_array.wgsl index cd1045f8bf882..ee92b85b53231 100644 --- a/assets/shaders/texture_binding_array.wgsl +++ b/assets/shaders/texture_binding_array.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput @group(1) @binding(0) var textures: binding_array>; @group(1) @binding(1) var nearest_sampler: sampler; @@ -7,7 +7,7 @@ @fragment fn fragment( - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { // Select the texture to sample from using non-uniform uv coordinates let coords = clamp(vec2(mesh.uv * 4.0), vec2(0u), vec2(3u)); diff --git a/assets/shaders/tonemapping_test_patterns.wgsl b/assets/shaders/tonemapping_test_patterns.wgsl index 8c5ba99ec4cd5..7e64d699bef22 100644 --- a/assets/shaders/tonemapping_test_patterns.wgsl +++ b/assets/shaders/tonemapping_test_patterns.wgsl @@ -1,6 +1,6 @@ #import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_bindings -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput #import bevy_pbr::utils PI #ifdef TONEMAP_IN_SHADER @@ -43,7 +43,7 @@ fn continuous_hue(uv: vec2) -> vec3 { @fragment fn fragment( - in: MeshVertexOutput, + in: VertexOutput, ) -> @location(0) vec4 { var uv = in.uv; var out = vec3(0.0); diff --git a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl index a8b046e5b4159..d9c2f3e4e08f8 100644 --- a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl @@ -56,14 +56,15 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { var pbr_input = pbr_input_from_deferred_gbuffer(frag_coord, deferred_data); var output_color = vec4(0.0); -#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION - let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; - let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); - pbr_input.occlusion = min(pbr_input.occlusion, ssao_multibounce); -#endif // SCREEN_SPACE_AMBIENT_OCCLUSION - // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit if ((pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { + +#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION + let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; + let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); + pbr_input.occlusion = min(pbr_input.occlusion, ssao_multibounce); +#endif // SCREEN_SPACE_AMBIENT_OCCLUSION + output_color = pbr_functions::pbr(pbr_input); } else { output_color = pbr_input.material.base_color; @@ -85,9 +86,6 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { output_rgb = powsafe(output_rgb, 2.2); output_color = vec4(output_rgb, output_color.a); #endif -#endif -#ifdef PREMULTIPLY_ALPHA - output_color = pbr_functions::premultiply_alpha(material.flags, output_color); #endif return output_color; diff --git a/crates/bevy_pbr/src/prepass/prepass.wgsl b/crates/bevy_pbr/src/prepass/prepass.wgsl index fd2021e9eee1e..ac4f7683b3325 100644 --- a/crates/bevy_pbr/src/prepass/prepass.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass.wgsl @@ -1,6 +1,6 @@ #import bevy_pbr::prepass_bindings #import bevy_pbr::mesh_functions -#import bevy_pbr::prepass_io Vertex, VertexOutput, FragmentInput, FragmentOutput +#import bevy_pbr::prepass_io Vertex, VertexOutput, FragmentOutput #import bevy_pbr::skinning #import bevy_pbr::morph #import bevy_pbr::mesh_bindings mesh @@ -50,10 +50,10 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { var model = bevy_pbr::mesh_functions::get_model_matrix(vertex_no_morph.instance_index); #endif // SKINNED - out.clip_position = bevy_pbr::mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); + out.position = bevy_pbr::mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); #ifdef DEPTH_CLAMP_ORTHO - out.clip_position_unclamped = out.clip_position; - out.clip_position.z = min(out.clip_position.z, 1.0); + out.clip_position_unclamped = out.position; + out.position.z = min(out.position.z, 1.0); #endif // DEPTH_CLAMP_ORTHO #ifdef VERTEX_UVS @@ -111,7 +111,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { #ifdef PREPASS_FRAGMENT @fragment -fn fragment(in: FragmentInput) -> FragmentOutput { +fn fragment(in: VertexOutput) -> FragmentOutput { var out: FragmentOutput; #ifdef NORMAL_PREPASS diff --git a/crates/bevy_pbr/src/prepass/prepass_io.wgsl b/crates/bevy_pbr/src/prepass/prepass_io.wgsl index ca49da07e1c0f..b66952f5bda84 100644 --- a/crates/bevy_pbr/src/prepass/prepass_io.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass_io.wgsl @@ -32,7 +32,9 @@ struct Vertex { } struct VertexOutput { - @builtin(position) clip_position: vec4, + // This is `clip position` when the struct is used as a vertex stage output + // and `frag coord` when used as a fragment stage input + @builtin(position) position: vec4, #ifdef VERTEX_UVS @location(0) uv: vec2, @@ -62,36 +64,6 @@ struct VertexOutput { #endif } -struct FragmentInput { - @builtin(position) position: vec4, -#ifdef VERTEX_UVS - @location(0) uv: vec2, -#endif // VERTEX_UVS - -#ifdef NORMAL_PREPASS_OR_DEFERRED_PREPASS - @location(1) world_normal: vec3, -#ifdef VERTEX_TANGENTS - @location(2) world_tangent: vec4, -#endif -#endif // NORMAL_PREPASS_OR_DEFERRED_PREPASS - - @location(3) world_position: vec4, -#ifdef MOTION_VECTOR_PREPASS - @location(4) previous_world_position: vec4, -#endif // MOTION_VECTOR_PREPASS - -#ifdef DEPTH_CLAMP_ORTHO - @location(5) clip_position_unclamped: vec4, -#endif // DEPTH_CLAMP_ORTHO -#ifdef VERTEX_OUTPUT_INSTANCE_INDEX - @location(6) instance_index: u32, -#endif - -#ifdef VERTEX_COLORS - @location(7) color: vec4, -#endif -}; - #ifdef PREPASS_FRAGMENT struct FragmentOutput { #ifdef NORMAL_PREPASS diff --git a/crates/bevy_pbr/src/render/forward_io.wgsl b/crates/bevy_pbr/src/render/forward_io.wgsl new file mode 100644 index 0000000000000..97567486de444 --- /dev/null +++ b/crates/bevy_pbr/src/render/forward_io.wgsl @@ -0,0 +1,52 @@ +#define_import_path bevy_pbr::forward_io + +struct Vertex { + @builtin(instance_index) instance_index: u32, +#ifdef VERTEX_POSITIONS + @location(0) position: vec3, +#endif +#ifdef VERTEX_NORMALS + @location(1) normal: vec3, +#endif +#ifdef VERTEX_UVS + @location(2) uv: vec2, +#endif +// (Alternate UVs are at location 3, but they're currently unused here.) +#ifdef VERTEX_TANGENTS + @location(4) tangent: vec4, +#endif +#ifdef VERTEX_COLORS + @location(5) color: vec4, +#endif +#ifdef SKINNED + @location(6) joint_indices: vec4, + @location(7) joint_weights: vec4, +#endif +#ifdef MORPH_TARGETS + @builtin(vertex_index) index: u32, +#endif +}; + +struct VertexOutput { + // This is `clip position` when the struct is used as a vertex stage output + // and `frag coord` when used as a fragment stage input + @builtin(position) position: vec4, + @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 +#ifdef VERTEX_OUTPUT_INSTANCE_INDEX + @location(5) @interpolate(flat) instance_index: u32, +#endif +} + +struct FragmentOutput { + @location(0) color: vec4, +} diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index a883b56555697..1675997707efc 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -62,7 +62,7 @@ use super::skin::SkinIndices; #[derive(Default)] pub struct MeshRenderPlugin; -pub const MESH_VERTEX_OUTPUT: Handle = Handle::weak_from_u128(2645551199423808407); +pub const FORWARD_IO_HANDLE: Handle = Handle::weak_from_u128(2645551199423808407); pub const MESH_VIEW_TYPES_HANDLE: Handle = Handle::weak_from_u128(8140454348013264787); pub const MESH_VIEW_BINDINGS_HANDLE: Handle = Handle::weak_from_u128(9076678235888822571); pub const MESH_TYPES_HANDLE: Handle = Handle::weak_from_u128(2506024101911992377); @@ -74,12 +74,7 @@ pub const MORPH_HANDLE: Handle = Handle::weak_from_u128(9709828135876073 impl Plugin for MeshRenderPlugin { fn build(&self, app: &mut bevy_app::App) { - load_internal_asset!( - app, - MESH_VERTEX_OUTPUT, - "mesh_vertex_output.wgsl", - Shader::from_wgsl - ); + load_internal_asset!(app, FORWARD_IO_HANDLE, "forward_io.wgsl", Shader::from_wgsl); load_internal_asset!( app, MESH_VIEW_TYPES_HANDLE, diff --git a/crates/bevy_pbr/src/render/mesh.wgsl b/crates/bevy_pbr/src/render/mesh.wgsl index 84ad95dd361db..641bbeaf91eb1 100644 --- a/crates/bevy_pbr/src/render/mesh.wgsl +++ b/crates/bevy_pbr/src/render/mesh.wgsl @@ -2,36 +2,9 @@ #import bevy_pbr::skinning #import bevy_pbr::morph #import bevy_pbr::mesh_bindings mesh -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io Vertex, VertexOutput #import bevy_render::instance_index get_instance_index -struct Vertex { - @builtin(instance_index) instance_index: u32, -#ifdef VERTEX_POSITIONS - @location(0) position: vec3, -#endif -#ifdef VERTEX_NORMALS - @location(1) normal: vec3, -#endif -#ifdef VERTEX_UVS - @location(2) uv: vec2, -#endif -// (Alternate UVs are at location 3, but they're currently unused here.) -#ifdef VERTEX_TANGENTS - @location(4) tangent: vec4, -#endif -#ifdef VERTEX_COLORS - @location(5) color: vec4, -#endif -#ifdef SKINNED - @location(6) joint_indices: vec4, - @location(7) joint_weights: vec4, -#endif -#ifdef MORPH_TARGETS - @builtin(vertex_index) index: u32, -#endif -}; - #ifdef MORPH_TARGETS fn morph_vertex(vertex_in: Vertex) -> Vertex { var vertex = vertex_in; @@ -54,8 +27,8 @@ fn morph_vertex(vertex_in: Vertex) -> Vertex { #endif @vertex -fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput { - var out: MeshVertexOutput; +fn vertex(vertex_no_morph: Vertex) -> VertexOutput { + var out: VertexOutput; #ifdef MORPH_TARGETS var vertex = morph_vertex(vertex_no_morph); @@ -118,7 +91,7 @@ fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput { @fragment fn fragment( - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { #ifdef VERTEX_COLORS return mesh.color; diff --git a/crates/bevy_pbr/src/render/mesh_vertex_output.wgsl b/crates/bevy_pbr/src/render/mesh_vertex_output.wgsl deleted file mode 100644 index 6cf70d2d17450..0000000000000 --- a/crates/bevy_pbr/src/render/mesh_vertex_output.wgsl +++ /dev/null @@ -1,21 +0,0 @@ -#define_import_path bevy_pbr::mesh_vertex_output - -struct MeshVertexOutput { - // this is `clip position` when the struct is used as a vertex stage output - // and `frag coord` when used as a fragment stage input - @builtin(position) position: vec4, - @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 - #ifdef VERTEX_OUTPUT_INSTANCE_INDEX - @location(5) @interpolate(flat) instance_index: u32, - #endif -} diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index 125d07d0e2a01..dc84ee30dfa5f 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -17,12 +17,11 @@ #endif #ifdef DEFERRED_PREPASS -#import bevy_pbr::pbr_deferred_types as pbr_deferred_types -#import bevy_pbr::pbr_deferred_functions as pbr_deferred_functions -#import bevy_pbr::pbr_prepass_functions as pbr_prepass_functions -#import bevy_pbr::prepass_io as prepass_io +#import bevy_pbr::pbr_deferred_functions deferred_gbuffer_from_pbr_input +#import bevy_pbr::pbr_prepass_functions calculate_motion_vector +#import bevy_pbr::prepass_io VertexOutput, FragmentOutput #else // DEFERRED_PREPASS -#import bevy_pbr::mesh_vertex_output as mesh_vertex_output +#import bevy_pbr::forward_io VertexOutput, FragmentOutput #endif // DEFERRED_PREPASS #ifdef MOTION_VECTOR_PREPASS @@ -30,21 +29,16 @@ var previous_view_proj: mat4x4; #endif // MOTION_VECTOR_PREPASS - @fragment -#ifdef DEFERRED_PREPASS -fn fragment( - in: prepass_io::FragmentInput, - @builtin(front_facing) is_front: bool, - ) -> prepass_io::FragmentOutput { - var out: prepass_io::FragmentOutput; -#else // DEFERRED_PREPASS fn fragment( - in: mesh_vertex_output::MeshVertexOutput, - @builtin(front_facing) is_front: bool, - ) -> @location(0) vec4 { -#endif // DEFERRED_PREPASS - var output_color: vec4 = pbr_bindings::material.base_color; + in: VertexOutput, + @builtin(front_facing) is_front: bool, +) -> FragmentOutput { + var out: FragmentOutput; + + // calculate unlit color + // --------------------- + var unlit_color: vec4 = pbr_bindings::material.base_color; let is_orthographic = view.projection[3].w == 1.0; let V = pbr_functions::calculate_view(in.world_position, is_orthographic); @@ -72,25 +66,31 @@ fn fragment( #endif #ifdef VERTEX_COLORS - output_color = output_color * in.color; + unlit_color = unlit_color * in.color; #endif #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { - output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, view.mip_bias); + unlit_color = unlit_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, view.mip_bias); } -#endif // VERTEX_UVS +#endif + // gather pbr lighting data + // ------------------ + var pbr_input: pbr_types::PbrInput; // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { // Prepare a 'processed' StandardMaterial by sampling all textures to resolve // the material members - var pbr_input: pbr_types::PbrInput; - pbr_input.material.base_color = output_color; pbr_input.material.reflectance = pbr_bindings::material.reflectance; pbr_input.material.flags = pbr_bindings::material.flags; pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff; + pbr_input.frag_coord = in.position; + pbr_input.world_position = in.world_position; + pbr_input.is_orthographic = is_orthographic; + pbr_input.flags = mesh[in.instance_index].flags; + // emmissive // TODO use .a for exposure compensation in HDR var emissive: vec4 = pbr_bindings::material.emissive; #ifdef VERTEX_UVS @@ -100,6 +100,7 @@ fn fragment( #endif pbr_input.material.emissive = emissive; + // metallic and perceptual roughness var metallic: f32 = pbr_bindings::material.metallic; var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness; #ifdef VERTEX_UVS @@ -113,6 +114,7 @@ fn fragment( pbr_input.material.metallic = metallic; pbr_input.material.perceptual_roughness = perceptual_roughness; + // occlusion // TODO: Split into diffuse/specular occlusion? var occlusion: vec3 = vec3(1.0); #ifdef VERTEX_UVS @@ -120,26 +122,21 @@ fn fragment( occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, uv, view.mip_bias).r); } #endif -#ifndef DEFERRED_PREPASS #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); occlusion = min(occlusion, ssao_multibounce); -#endif // SCREEN_SPACE_AMBIENT_OCCLUSION -#endif // DEFERRED_PREPASS +#endif pbr_input.occlusion = occlusion; - pbr_input.frag_coord = in.position; - pbr_input.world_position = in.world_position; - + // world normal pbr_input.world_normal = pbr_functions::prepare_world_normal( in.world_normal, (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u, is_front, ); - pbr_input.is_orthographic = is_orthographic; - + // N (normal vector) #ifdef LOAD_PREPASS_NORMALS pbr_input.N = bevy_pbr::prepass_utils::prepass_normal(in.position, 0u); #else @@ -158,45 +155,48 @@ fn fragment( ); #endif + // V (view vector) pbr_input.V = V; - pbr_input.occlusion = occlusion; - pbr_input.flags = mesh[in.instance_index].flags; -#ifdef DEFERRED_PREPASS - pbr_functions::alpha_discard(pbr_bindings::material, output_color); - out.deferred = pbr_deferred_functions::deferred_gbuffer_from_pbr_input(pbr_input); - out.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id; -#ifdef NORMAL_PREPASS - out.normal = vec4(pbr_input.N * 0.5 + vec3(0.5), 1.0); -#endif // NORMAL_PREPASS -#else // DEFERRED_PREPASS - output_color = pbr_functions::pbr(pbr_input); -#endif // DEFERRED_PREPASS } else { // if UNLIT_BIT != 0 - pbr_functions::alpha_discard(pbr_bindings::material, output_color); -#ifdef DEFERRED_PREPASS - var pbr_input = pbr_types::pbr_input_new(); +#ifdef DEFERRED_PREPASS + // in deferred mode, we need to fill some of the pbr input data even for unlit materials + // to pass through the gbuffer to the deferred lighting shader + pbr_input = pbr_types::pbr_input_new(); pbr_input.flags = mesh[in.instance_index].flags; pbr_input.material.flags = pbr_bindings::material.flags; - pbr_input.material.base_color = output_color; pbr_input.world_position = in.world_position; - pbr_input.world_normal = in.world_normal; - pbr_input.frag_coord = in.position; - out.deferred = pbr_deferred_functions::deferred_gbuffer_from_pbr_input(pbr_input); - out.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id; -#ifdef NORMAL_PREPASS - out.normal = vec4(in.world_normal * 0.5 + vec3(0.5), 1.0); #endif -#endif // DEFERRED_PREPASS } + // apply alpha discard + // ------------------- + // note even though this is based on the unlit color, it must be done after all texture samples for uniform control flow + unlit_color = pbr_functions::alpha_discard(pbr_bindings::material, unlit_color); + pbr_input.material.base_color = unlit_color; + + // generate output + // --------------- #ifdef DEFERRED_PREPASS + // write the gbuffer + out.deferred = deferred_gbuffer_from_pbr_input(pbr_input); + out.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id; +#ifdef NORMAL_PREPASS + out.normal = vec4(in.world_normal * 0.5 + vec3(0.5), 1.0); +#endif #ifdef MOTION_VECTOR_PREPASS - out.motion_vector = pbr_prepass_functions::calculate_motion_vector(in.world_position, in.previous_world_position); + out.motion_vector = calculate_motion_vector(in.world_position, in.previous_world_position); #endif // MOTION_VECTOR_PREPASS - return out; -#else //DEFERRED_PREPASS - // fog + +#else // DEFERRED_PREPASS + + // in forward mode, we calculate the lit color immediately, and then apply some post-lighting effects here. + // in deferred mode the lit color and these effects will be calculated in the deferred lighting shader + var output_color = unlit_color; + if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { + output_color = pbr_functions::pbr(pbr_input); + } + if (fog.mode != FOG_MODE_OFF && (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) { output_color = pbr_functions::apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz); } @@ -217,6 +217,10 @@ fn fragment( output_color = pbr_functions::premultiply_alpha(pbr_bindings::material.flags, output_color); #endif - return output_color; + // write the final pixel color + out.color = output_color; + #endif //DEFERRED_PREPASS + + return out; } diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index a17457c5058f6..e587f242a1437 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -152,8 +152,6 @@ fn pbr( let occlusion = in.occlusion; - output_color = alpha_discard(in.material, output_color); - // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" let NdotV = max(dot(in.N, in.V), 0.0001); diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index c1c05df25ecd7..479d64ce236c3 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -11,7 +11,7 @@ #ifdef PREPASS_FRAGMENT @fragment fn fragment( - in: prepass_io::FragmentInput, + in: prepass_io::VertexOutput, @builtin(front_facing) is_front: bool, ) -> prepass_io::FragmentOutput { bevy_pbr::pbr_prepass_functions::prepass_alpha_discard(in); @@ -59,7 +59,7 @@ fn fragment( } #else @fragment -fn fragment(in: prepass_io::FragmentInput) { +fn fragment(in: prepass_io::VertexOutput) { bevy_pbr::pbr_prepass_functions::prepass_alpha_discard(in); } #endif // PREPASS_FRAGMENT diff --git a/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl b/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl index 88fb2316e89da..50afd8380c2a4 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl @@ -1,5 +1,5 @@ #define_import_path bevy_pbr::pbr_prepass_functions -#import bevy_pbr::prepass_io as prepass_io +#import bevy_pbr::prepass_io VertexOutput #import bevy_pbr::prepass_bindings previous_view_proj #import bevy_pbr::mesh_view_bindings view @@ -11,7 +11,7 @@ const PREMULTIPLIED_ALPHA_CUTOFF = 0.05; // We can use a simplified version of alpha_discard() here since we only need to handle the alpha_cutoff -fn prepass_alpha_discard(in: prepass_io::FragmentInput) { +fn prepass_alpha_discard(in: VertexOutput) { #ifdef MAY_DISCARD var output_color: vec4 = pbr_bindings::material.base_color; @@ -54,4 +54,4 @@ fn calculate_motion_vector(world_position: vec4, previous_world_position: v // down where clip space y goes up, so y needs to be flipped. return (clip_position - previous_clip_position) * vec2(0.5, -0.5); } -#endif // MOTION_VECTOR_PREPASS \ No newline at end of file +#endif // MOTION_VECTOR_PREPASS diff --git a/crates/bevy_pbr/src/render/wireframe.wgsl b/crates/bevy_pbr/src/render/wireframe.wgsl index ae9a1ffef57dc..b15b25175e565 100644 --- a/crates/bevy_pbr/src/render/wireframe.wgsl +++ b/crates/bevy_pbr/src/render/wireframe.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::forward_io VertexOutput struct WireframeMaterial { color: vec4, }; @@ -6,6 +6,6 @@ struct WireframeMaterial { @group(1) @binding(0) var material: WireframeMaterial; @fragment -fn fragment(in: MeshVertexOutput) -> @location(0) vec4 { +fn fragment(in: VertexOutput) -> @location(0) vec4 { return material.color; -} \ No newline at end of file +} diff --git a/crates/bevy_sprite/src/mesh2d/color_material.wgsl b/crates/bevy_sprite/src/mesh2d/color_material.wgsl index 8c6212d5da804..6f125a83b18ba 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 Mesh2d -#import bevy_sprite::mesh2d_vertex_output MeshVertexOutput +#import bevy_sprite::mesh2d_vertex_output VertexOutput #import bevy_sprite::mesh2d_view_bindings view #ifdef TONEMAP_IN_SHADER @@ -19,7 +19,7 @@ const COLOR_MATERIAL_FLAGS_TEXTURE_BIT: u32 = 1u; @fragment fn fragment( - mesh: MeshVertexOutput, + mesh: VertexOutput, ) -> @location(0) vec4 { var output_color: vec4 = material.color; #ifdef VERTEX_COLORS diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl index 003f7dda13af9..a3a8d118c199d 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl @@ -1,6 +1,6 @@ #import bevy_sprite::mesh2d_functions as mesh_functions #import bevy_sprite::mesh2d_bindings mesh -#import bevy_sprite::mesh2d_vertex_output MeshVertexOutput +#import bevy_sprite::mesh2d_vertex_output VertexOutput #import bevy_sprite::mesh2d_view_bindings view #ifdef TONEMAP_IN_SHADER @@ -27,8 +27,8 @@ struct Vertex { }; @vertex -fn vertex(vertex: Vertex) -> MeshVertexOutput { - var out: MeshVertexOutput; +fn vertex(vertex: Vertex) -> VertexOutput { + var out: VertexOutput; #ifdef VERTEX_UVS out.uv = vertex.uv; #endif @@ -61,7 +61,7 @@ fn vertex(vertex: Vertex) -> MeshVertexOutput { @fragment fn fragment( - in: MeshVertexOutput, + in: VertexOutput, ) -> @location(0) vec4 { #ifdef VERTEX_COLORS var color = in.color; diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d_vertex_output.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d_vertex_output.wgsl index 607cb00c5c571..c7839caffeebb 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d_vertex_output.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d_vertex_output.wgsl @@ -1,6 +1,6 @@ #define_import_path bevy_sprite::mesh2d_vertex_output -struct MeshVertexOutput { +struct VertexOutput { // this is `clip position` when the struct is used as a vertex stage output // and `frag coord` when used as a fragment stage input @builtin(position) position: vec4,