From 396f97b7c522ac4a613af303be10aec246e6524b Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Wed, 1 Jun 2022 22:00:36 +0200 Subject: [PATCH] Add reusable shader functions for transforming position/normal/tangent --- assets/shaders/animate_shader.wgsl | 7 ++-- assets/shaders/custom_vertex_attribute.wgsl | 17 ++++----- assets/shaders/instancing.wgsl | 7 ++-- assets/shaders/shader_defs.wgsl | 7 ++-- crates/bevy_pbr/src/render/depth.wgsl | 5 ++- crates/bevy_pbr/src/render/mesh.rs | 8 +++++ crates/bevy_pbr/src/render/mesh.wgsl | 31 ++++++---------- .../bevy_pbr/src/render/mesh_functions.wgsl | 36 +++++++++++++++++++ crates/bevy_pbr/src/render/skinning.wgsl | 20 ++--------- crates/bevy_pbr/src/render/wireframe.wgsl | 25 ++++++------- crates/bevy_sprite/src/mesh2d/mesh.rs | 8 +++++ crates/bevy_sprite/src/mesh2d/mesh2d.wgsl | 24 ++++--------- .../src/mesh2d/mesh2d_functions.wgsl | 36 +++++++++++++++++++ crates/bevy_sprite/src/render/sprite.wgsl | 6 ++-- examples/2d/mesh2d_manual.rs | 5 ++- 15 files changed, 153 insertions(+), 89 deletions(-) create mode 100644 crates/bevy_pbr/src/render/mesh_functions.wgsl create mode 100644 crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl diff --git a/assets/shaders/animate_shader.wgsl b/assets/shaders/animate_shader.wgsl index 6dbe165db24f7..d71b30bfafe7c 100644 --- a/assets/shaders/animate_shader.wgsl +++ b/assets/shaders/animate_shader.wgsl @@ -4,6 +4,9 @@ [[group(1), binding(0)]] var mesh: Mesh; +// NOTE: Bindings must come before functions that use them! +#import bevy_pbr::mesh_functions + struct Vertex { [[location(0)]] position: vec3; [[location(1)]] normal: vec3; @@ -17,10 +20,8 @@ struct VertexOutput { [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { - let world_position = mesh.model * vec4(vertex.position, 1.0); - var out: VertexOutput; - out.clip_position = view.view_proj * world_position; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); out.uv = vertex.uv; return out; } diff --git a/assets/shaders/custom_vertex_attribute.wgsl b/assets/shaders/custom_vertex_attribute.wgsl index 52129f4acd39a..2def33a098602 100644 --- a/assets/shaders/custom_vertex_attribute.wgsl +++ b/assets/shaders/custom_vertex_attribute.wgsl @@ -1,17 +1,20 @@ #import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_bindings -struct Vertex { - [[location(0)]] position: vec3; - [[location(1)]] blend_color: vec4; -}; - struct CustomMaterial { color: vec4; }; [[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; +}; + struct VertexOutput { [[builtin(position)]] clip_position: vec4; [[location(0)]] blend_color: vec4; @@ -19,10 +22,8 @@ struct VertexOutput { [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { - let world_position = mesh.model * vec4(vertex.position, 1.0); - var out: VertexOutput; - out.clip_position = view.view_proj * world_position; + out.clip_position = mesh_position_local_to_clip(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 a6d0f7c2e05ba..c83a853b07842 100644 --- a/assets/shaders/instancing.wgsl +++ b/assets/shaders/instancing.wgsl @@ -4,6 +4,9 @@ [[group(1), binding(0)]] var mesh: Mesh; +// NOTE: Bindings must come before functions that use them! +#import bevy_pbr::mesh_functions + struct Vertex { [[location(0)]] position: vec3; [[location(1)]] normal: vec3; @@ -21,10 +24,8 @@ struct VertexOutput { [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz; - let world_position = mesh.model * vec4(position, 1.0); - var out: VertexOutput; - out.clip_position = view.view_proj * world_position; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(position, 1.0)); out.color = vertex.i_color; return out; } diff --git a/assets/shaders/shader_defs.wgsl b/assets/shaders/shader_defs.wgsl index ca63026b50958..a26e988ed951a 100644 --- a/assets/shaders/shader_defs.wgsl +++ b/assets/shaders/shader_defs.wgsl @@ -4,6 +4,9 @@ [[group(1), binding(0)]] var mesh: Mesh; +// NOTE: Bindings must come before functions that use them! +#import bevy_pbr::mesh_functions + struct Vertex { [[location(0)]] position: vec3; [[location(1)]] normal: vec3; @@ -16,10 +19,8 @@ struct VertexOutput { [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { - let world_position = mesh.model * vec4(vertex.position, 1.0); - var out: VertexOutput; - out.clip_position = view.view_proj * world_position; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); return out; } diff --git a/crates/bevy_pbr/src/render/depth.wgsl b/crates/bevy_pbr/src/render/depth.wgsl index f15c48c37eaff..23c797d9dfa69 100644 --- a/crates/bevy_pbr/src/render/depth.wgsl +++ b/crates/bevy_pbr/src/render/depth.wgsl @@ -13,6 +13,9 @@ 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 @@ -34,6 +37,6 @@ fn vertex(vertex: Vertex) -> VertexOutput { #endif var out: VertexOutput; - out.clip_position = view.view_proj * model * vec4(vertex.position, 1.0); + out.clip_position = mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); return out; } diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 204c44c3bd45a..1cc5fae9d81ab 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -43,6 +43,8 @@ pub const MESH_TYPES_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2506024101911992377); pub const MESH_BINDINGS_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 16831548636314682308); +pub const MESH_FUNCTIONS_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 6300874327833745635); pub const MESH_SHADER_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 3252377289100772450); pub const SKINNING_HANDLE: HandleUntyped = @@ -69,6 +71,12 @@ impl Plugin for MeshRenderPlugin { "mesh_bindings.wgsl", Shader::from_wgsl ); + load_internal_asset!( + app, + MESH_FUNCTIONS_HANDLE, + "mesh_functions.wgsl", + Shader::from_wgsl + ); load_internal_asset!(app, MESH_SHADER_HANDLE, "mesh.wgsl", Shader::from_wgsl); load_internal_asset!(app, SKINNING_HANDLE, "skinning.wgsl", Shader::from_wgsl); diff --git a/crates/bevy_pbr/src/render/mesh.wgsl b/crates/bevy_pbr/src/render/mesh.wgsl index f35ed2b92175b..82ad909e5ce21 100644 --- a/crates/bevy_pbr/src/render/mesh.wgsl +++ b/crates/bevy_pbr/src/render/mesh.wgsl @@ -1,6 +1,9 @@ #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 + struct Vertex { [[location(0)]] position: vec3; [[location(1)]] normal: vec3; @@ -35,35 +38,21 @@ fn vertex(vertex: Vertex) -> VertexOutput { var out: VertexOutput; #ifdef SKINNED var model = skin_model(vertex.joint_indices, vertex.joint_weights); - out.world_position = model * vec4(vertex.position, 1.0); out.world_normal = skin_normals(model, vertex.normal); -#ifdef VERTEX_TANGENTS - out.world_tangent = skin_tangents(model, vertex.tangent); -#endif #else - out.world_position = mesh.model * vec4(vertex.position, 1.0); - out.world_normal = mat3x3( - mesh.inverse_transpose_model[0].xyz, - mesh.inverse_transpose_model[1].xyz, - mesh.inverse_transpose_model[2].xyz - ) * vertex.normal; -#ifdef VERTEX_TANGENTS - out.world_tangent = vec4( - mat3x3( - mesh.model[0].xyz, - mesh.model[1].xyz, - mesh.model[2].xyz - ) * vertex.tangent.xyz, - vertex.tangent.w - ); + var model = mesh.model; #endif + out.world_position = mesh_position_local_to_world(model, vec4(vertex.position, 1.0)); + out.world_normal = mesh_normal_local_to_world(vertex.normal); + out.uv = vertex.uv; +#ifdef VERTEX_TANGENTS + out.world_tangent = mesh_tangent_local_to_world(model, vertex.tangent); #endif #ifdef VERTEX_COLORS out.color = vertex.color; #endif - out.uv = vertex.uv; - out.clip_position = view.view_proj * out.world_position; + out.clip_position = mesh_position_world_to_clip(out.world_position); return out; } diff --git a/crates/bevy_pbr/src/render/mesh_functions.wgsl b/crates/bevy_pbr/src/render/mesh_functions.wgsl new file mode 100644 index 0000000000000..20c763bd22d79 --- /dev/null +++ b/crates/bevy_pbr/src/render/mesh_functions.wgsl @@ -0,0 +1,36 @@ +#define_import_path bevy_pbr::mesh_functions + +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; +} + +// NOTE: The intermediate world_position assignment is important +// for precision purposes when using the 'equals' depth comparison +// function. +fn mesh_position_local_to_clip(model: mat4x4, vertex_position: vec4) -> vec4 { + let world_position = mesh_position_local_to_world(model, vertex_position); + return mesh_position_world_to_clip(world_position); +} + +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 + ) * vertex_normal; +} + +fn mesh_tangent_local_to_world(model: mat4x4, vertex_tangent: vec4) -> vec4 { + return vec4( + mat3x3( + model[0].xyz, + model[1].xyz, + model[2].xyz + ) * vertex_tangent.xyz, + vertex_tangent.w + ); +} diff --git a/crates/bevy_pbr/src/render/skinning.wgsl b/crates/bevy_pbr/src/render/skinning.wgsl index 56b35634a09cd..2a48556301254 100644 --- a/crates/bevy_pbr/src/render/skinning.wgsl +++ b/crates/bevy_pbr/src/render/skinning.wgsl @@ -1,10 +1,10 @@ -// If using this WGSL snippet as an #import, a dedicated +// 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 -/// HACK: This works around naga not supporting matrix addition in SPIR-V +/// HACK: This works around naga not supporting matrix addition in SPIR-V // translations. See https://github.com/gfx-rs/naga/issues/1527 fn add_matrix( a: mat4x4, @@ -30,7 +30,7 @@ fn skin_model( fn inverse_transpose_3x3(in: mat3x3) -> mat3x3 { let x = cross(in.y, in.z); - let y = cross(in.z, in.x); + let y = cross(in.z, in.x); let z = cross(in.x, in.y); let det = dot(in.z, z); return mat3x3( @@ -50,17 +50,3 @@ fn skin_normals( model[2].xyz )) * normal; } - -fn skin_tangents( - model: mat4x4, - tangent: vec4, -) -> vec4 { - return vec4( - mat3x3( - model[0].xyz, - model[1].xyz, - model[2].xyz - ) * tangent.xyz, - tangent.w - ); -} \ 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 f17123e5825b8..b3dc23c86fa27 100644 --- a/crates/bevy_pbr/src/render/wireframe.wgsl +++ b/crates/bevy_pbr/src/render/wireframe.wgsl @@ -1,6 +1,18 @@ #import bevy_pbr::mesh_types #import bevy_pbr::mesh_view_bindings +[[group(1), binding(0)]] +var mesh: Mesh; + +#ifdef SKINNED +[[group(1), 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 @@ -9,19 +21,10 @@ struct Vertex { #endif }; -[[group(1), binding(0)]] -var mesh: Mesh; - struct VertexOutput { [[builtin(position)]] clip_position: vec4; }; -#ifdef SKINNED -[[group(1), binding(1)]] -var joint_matrices: SkinnedMesh; -#import bevy_pbr::skinning -#endif - [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { #ifdef SKINNED @@ -30,10 +33,8 @@ fn vertex(vertex: Vertex) -> VertexOutput { let model = mesh.model; #endif - let world_position = model * vec4(vertex.position, 1.0); var out: VertexOutput; - out.clip_position = view.view_proj * world_position; - + out.clip_position = mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); return out; } diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index c0c3e3010c302..3bbdbd739c5a0 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -43,6 +43,8 @@ pub const MESH2D_TYPES_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 8994673400261890424); pub const MESH2D_BINDINGS_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 8983617858458862856); +pub const MESH2D_FUNCTIONS_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 4976379308250389413); pub const MESH2D_SHADER_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2971387252468633715); @@ -72,6 +74,12 @@ impl Plugin for Mesh2dRenderPlugin { "mesh2d_bindings.wgsl", Shader::from_wgsl ); + load_internal_asset!( + app, + MESH2D_FUNCTIONS_HANDLE, + "mesh2d_functions.wgsl", + Shader::from_wgsl + ); 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 2466a196e5a0f..c1d29aef33419 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d.wgsl @@ -1,6 +1,9 @@ #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 + struct Vertex { [[location(0)]] position: vec3; [[location(1)]] normal: vec3; @@ -28,26 +31,13 @@ struct VertexOutput { [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { - let world_position = mesh.model * vec4(vertex.position, 1.0); - var out: VertexOutput; out.uv = vertex.uv; - out.world_position = world_position; - out.clip_position = view.view_proj * world_position; - out.world_normal = mat3x3( - mesh.inverse_transpose_model[0].xyz, - mesh.inverse_transpose_model[1].xyz, - mesh.inverse_transpose_model[2].xyz - ) * vertex.normal; + 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); #ifdef VERTEX_TANGENTS - out.world_tangent = vec4( - mat3x3( - mesh.model[0].xyz, - mesh.model[1].xyz, - mesh.model[2].xyz - ) * vertex.tangent.xyz, - vertex.tangent.w - ); + out.world_tangent = mesh2d_tangent_local_to_world(vertex.tangent); #endif #ifdef VERTEX_COLORS out.colors = vertex.colors; diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl new file mode 100644 index 0000000000000..5342b638494de --- /dev/null +++ b/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl @@ -0,0 +1,36 @@ +#define_import_path bevy_sprite::mesh2d_functions + +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; +} + +// NOTE: The intermediate world_position assignment is important +// for precision purposes when using the 'equals' depth comparison +// function. +fn mesh2d_position_local_to_clip(model: mat4x4, vertex_position: vec4) -> vec4 { + let world_position = mesh2d_position_local_to_world(model, vertex_position); + return mesh2d_position_world_to_clip(world_position); +} + +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 + ) * vertex_normal; +} + +fn mesh2d_tangent_local_to_world(model: mat4x4, vertex_tangent: vec4) -> vec4 { + return vec4( + mat3x3( + model[0].xyz, + model[1].xyz, + model[2].xyz + ) * vertex_tangent.xyz, + vertex_tangent.w + ); +} diff --git a/crates/bevy_sprite/src/render/sprite.wgsl b/crates/bevy_sprite/src/render/sprite.wgsl index 99015d2f9eff1..e04559dd00412 100644 --- a/crates/bevy_sprite/src/render/sprite.wgsl +++ b/crates/bevy_sprite/src/render/sprite.wgsl @@ -28,7 +28,7 @@ fn vertex( out.color = vertex_color; #endif return out; -} +} [[group(1), binding(0)]] var sprite_texture: texture_2d; @@ -37,9 +37,9 @@ var sprite_sampler: sampler; [[stage(fragment)]] fn fragment(in: VertexOutput) -> [[location(0)]] vec4 { - var color = textureSample(sprite_texture, sprite_sampler, in.uv); + var color = textureSample(sprite_texture, sprite_sampler, in.uv); #ifdef COLORED color = in.color * color; #endif return color; -} \ No newline at end of file +} diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 4ab25f05f8a3b..2e6c9f6c479a7 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -215,6 +215,9 @@ const COLORED_MESH2D_SHADER: &str = r" [[group(1), binding(0)]] var mesh: Mesh2d; +// NOTE: Bindings must come before functions that use them! +#import bevy_sprite::mesh2d_functions + // The structure of the vertex buffer is as specified in `specialize()` struct Vertex { [[location(0)]] position: vec3; @@ -233,7 +236,7 @@ struct VertexOutput { fn vertex(vertex: Vertex) -> VertexOutput { var out: VertexOutput; // Project the world position of the mesh into screen position - out.clip_position = view.view_proj * mesh.model * vec4(vertex.position, 1.0); + out.clip_position = 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;