Skip to content

Commit b333386

Browse files
committed
Add reusable shader functions for transforming position/normal/tangent (#4901)
# Objective - Add reusable shader functions for transforming positions / normals / tangents between local and world / clip space for 2D and 3D so that they are done in a simple and correct way - The next step in #3969 so check there for more details. ## Solution - Add `bevy_pbr::mesh_functions` and `bevy_sprite::mesh2d_functions` shader imports - These contain `mesh_` and `mesh2d_` versions of the following functions: - `mesh_position_local_to_world` - `mesh_position_world_to_clip` - `mesh_position_local_to_clip` - `mesh_normal_local_to_world` - `mesh_tangent_local_to_world` - Use them everywhere where it is appropriate - Notably not in the sprite and UI shaders where `mesh2d_position_world_to_clip` could have been used, but including all the functions depends on the mesh binding so I chose to not use the function there - NOTE: The `mesh_` and `mesh2d_` functions are currently identical. However, if I had defined only `bevy_pbr::mesh_functions` and used that in bevy_sprite, then bevy_sprite would have a runtime dependency on bevy_pbr, which seems undesirable. I also expect that when we have a proper 2D rendering API, these functions will diverge between 2D and 3D. --- ## Changelog - Added: `bevy_pbr::mesh_functions` and `bevy_sprite::mesh2d_functions` shader imports containing `mesh_` and `mesh2d_` versions of the following functions: - `mesh_position_local_to_world` - `mesh_position_world_to_clip` - `mesh_position_local_to_clip` - `mesh_normal_local_to_world` - `mesh_tangent_local_to_world` ## Migration Guide - The `skin_tangents` function from the `bevy_pbr::skinning` shader import has been replaced with the `mesh_tangent_local_to_world` function from the `bevy_pbr::mesh_functions` shader import
1 parent 407c080 commit b333386

File tree

15 files changed

+153
-89
lines changed

15 files changed

+153
-89
lines changed

assets/shaders/animate_shader.wgsl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
[[group(1), binding(0)]]
55
var<uniform> mesh: Mesh;
66

7+
// NOTE: Bindings must come before functions that use them!
8+
#import bevy_pbr::mesh_functions
9+
710
struct Vertex {
811
[[location(0)]] position: vec3<f32>;
912
[[location(1)]] normal: vec3<f32>;
@@ -17,10 +20,8 @@ struct VertexOutput {
1720

1821
[[stage(vertex)]]
1922
fn vertex(vertex: Vertex) -> VertexOutput {
20-
let world_position = mesh.model * vec4<f32>(vertex.position, 1.0);
21-
2223
var out: VertexOutput;
23-
out.clip_position = view.view_proj * world_position;
24+
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
2425
out.uv = vertex.uv;
2526
return out;
2627
}

assets/shaders/custom_vertex_attribute.wgsl

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
#import bevy_pbr::mesh_view_bindings
22
#import bevy_pbr::mesh_bindings
33

4-
struct Vertex {
5-
[[location(0)]] position: vec3<f32>;
6-
[[location(1)]] blend_color: vec4<f32>;
7-
};
8-
94
struct CustomMaterial {
105
color: vec4<f32>;
116
};
127
[[group(1), binding(0)]]
138
var<uniform> material: CustomMaterial;
149

10+
// NOTE: Bindings must come before functions that use them!
11+
#import bevy_pbr::mesh_functions
12+
13+
struct Vertex {
14+
[[location(0)]] position: vec3<f32>;
15+
[[location(1)]] blend_color: vec4<f32>;
16+
};
17+
1518
struct VertexOutput {
1619
[[builtin(position)]] clip_position: vec4<f32>;
1720
[[location(0)]] blend_color: vec4<f32>;
1821
};
1922

2023
[[stage(vertex)]]
2124
fn vertex(vertex: Vertex) -> VertexOutput {
22-
let world_position = mesh.model * vec4<f32>(vertex.position, 1.0);
23-
2425
var out: VertexOutput;
25-
out.clip_position = view.view_proj * world_position;
26+
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
2627
out.blend_color = vertex.blend_color;
2728
return out;
2829
}

assets/shaders/instancing.wgsl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
[[group(1), binding(0)]]
55
var<uniform> mesh: Mesh;
66

7+
// NOTE: Bindings must come before functions that use them!
8+
#import bevy_pbr::mesh_functions
9+
710
struct Vertex {
811
[[location(0)]] position: vec3<f32>;
912
[[location(1)]] normal: vec3<f32>;
@@ -21,10 +24,8 @@ struct VertexOutput {
2124
[[stage(vertex)]]
2225
fn vertex(vertex: Vertex) -> VertexOutput {
2326
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
24-
let world_position = mesh.model * vec4<f32>(position, 1.0);
25-
2627
var out: VertexOutput;
27-
out.clip_position = view.view_proj * world_position;
28+
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(position, 1.0));
2829
out.color = vertex.i_color;
2930
return out;
3031
}

assets/shaders/shader_defs.wgsl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
[[group(1), binding(0)]]
55
var<uniform> mesh: Mesh;
66

7+
// NOTE: Bindings must come before functions that use them!
8+
#import bevy_pbr::mesh_functions
9+
710
struct Vertex {
811
[[location(0)]] position: vec3<f32>;
912
[[location(1)]] normal: vec3<f32>;
@@ -16,10 +19,8 @@ struct VertexOutput {
1619

1720
[[stage(vertex)]]
1821
fn vertex(vertex: Vertex) -> VertexOutput {
19-
let world_position = mesh.model * vec4<f32>(vertex.position, 1.0);
20-
2122
var out: VertexOutput;
22-
out.clip_position = view.view_proj * world_position;
23+
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
2324
return out;
2425
}
2526

crates/bevy_pbr/src/render/depth.wgsl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ var<uniform> joint_matrices: SkinnedMesh;
1313
#import bevy_pbr::skinning
1414
#endif
1515

16+
// NOTE: Bindings must come before functions that use them!
17+
#import bevy_pbr::mesh_functions
18+
1619
struct Vertex {
1720
[[location(0)]] position: vec3<f32>;
1821
#ifdef SKINNED
@@ -34,6 +37,6 @@ fn vertex(vertex: Vertex) -> VertexOutput {
3437
#endif
3538

3639
var out: VertexOutput;
37-
out.clip_position = view.view_proj * model * vec4<f32>(vertex.position, 1.0);
40+
out.clip_position = mesh_position_local_to_clip(model, vec4<f32>(vertex.position, 1.0));
3841
return out;
3942
}

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub const MESH_TYPES_HANDLE: HandleUntyped =
4545
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2506024101911992377);
4646
pub const MESH_BINDINGS_HANDLE: HandleUntyped =
4747
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 16831548636314682308);
48+
pub const MESH_FUNCTIONS_HANDLE: HandleUntyped =
49+
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 6300874327833745635);
4850
pub const MESH_SHADER_HANDLE: HandleUntyped =
4951
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 3252377289100772450);
5052
pub const SKINNING_HANDLE: HandleUntyped =
@@ -71,6 +73,12 @@ impl Plugin for MeshRenderPlugin {
7173
"mesh_bindings.wgsl",
7274
Shader::from_wgsl
7375
);
76+
load_internal_asset!(
77+
app,
78+
MESH_FUNCTIONS_HANDLE,
79+
"mesh_functions.wgsl",
80+
Shader::from_wgsl
81+
);
7482
load_internal_asset!(app, MESH_SHADER_HANDLE, "mesh.wgsl", Shader::from_wgsl);
7583
load_internal_asset!(app, SKINNING_HANDLE, "skinning.wgsl", Shader::from_wgsl);
7684

crates/bevy_pbr/src/render/mesh.wgsl

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#import bevy_pbr::mesh_view_bindings
22
#import bevy_pbr::mesh_bindings
33

4+
// NOTE: Bindings must come before functions that use them!
5+
#import bevy_pbr::mesh_functions
6+
47
struct Vertex {
58
[[location(0)]] position: vec3<f32>;
69
[[location(1)]] normal: vec3<f32>;
@@ -35,35 +38,21 @@ fn vertex(vertex: Vertex) -> VertexOutput {
3538
var out: VertexOutput;
3639
#ifdef SKINNED
3740
var model = skin_model(vertex.joint_indices, vertex.joint_weights);
38-
out.world_position = model * vec4<f32>(vertex.position, 1.0);
3941
out.world_normal = skin_normals(model, vertex.normal);
40-
#ifdef VERTEX_TANGENTS
41-
out.world_tangent = skin_tangents(model, vertex.tangent);
42-
#endif
4342
#else
44-
out.world_position = mesh.model * vec4<f32>(vertex.position, 1.0);
45-
out.world_normal = mat3x3<f32>(
46-
mesh.inverse_transpose_model[0].xyz,
47-
mesh.inverse_transpose_model[1].xyz,
48-
mesh.inverse_transpose_model[2].xyz
49-
) * vertex.normal;
50-
#ifdef VERTEX_TANGENTS
51-
out.world_tangent = vec4<f32>(
52-
mat3x3<f32>(
53-
mesh.model[0].xyz,
54-
mesh.model[1].xyz,
55-
mesh.model[2].xyz
56-
) * vertex.tangent.xyz,
57-
vertex.tangent.w
58-
);
43+
var model = mesh.model;
5944
#endif
45+
out.world_position = mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
46+
out.world_normal = mesh_normal_local_to_world(vertex.normal);
47+
out.uv = vertex.uv;
48+
#ifdef VERTEX_TANGENTS
49+
out.world_tangent = mesh_tangent_local_to_world(model, vertex.tangent);
6050
#endif
6151
#ifdef VERTEX_COLORS
6252
out.color = vertex.color;
6353
#endif
6454

65-
out.uv = vertex.uv;
66-
out.clip_position = view.view_proj * out.world_position;
55+
out.clip_position = mesh_position_world_to_clip(out.world_position);
6756
return out;
6857
}
6958

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#define_import_path bevy_pbr::mesh_functions
2+
3+
fn mesh_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
4+
return model * vertex_position;
5+
}
6+
7+
fn mesh_position_world_to_clip(world_position: vec4<f32>) -> vec4<f32> {
8+
return view.view_proj * world_position;
9+
}
10+
11+
// NOTE: The intermediate world_position assignment is important
12+
// for precision purposes when using the 'equals' depth comparison
13+
// function.
14+
fn mesh_position_local_to_clip(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
15+
let world_position = mesh_position_local_to_world(model, vertex_position);
16+
return mesh_position_world_to_clip(world_position);
17+
}
18+
19+
fn mesh_normal_local_to_world(vertex_normal: vec3<f32>) -> vec3<f32> {
20+
return mat3x3<f32>(
21+
mesh.inverse_transpose_model[0].xyz,
22+
mesh.inverse_transpose_model[1].xyz,
23+
mesh.inverse_transpose_model[2].xyz
24+
) * vertex_normal;
25+
}
26+
27+
fn mesh_tangent_local_to_world(model: mat4x4<f32>, vertex_tangent: vec4<f32>) -> vec4<f32> {
28+
return vec4<f32>(
29+
mat3x3<f32>(
30+
model[0].xyz,
31+
model[1].xyz,
32+
model[2].xyz
33+
) * vertex_tangent.xyz,
34+
vertex_tangent.w
35+
);
36+
}

crates/bevy_pbr/src/render/skinning.wgsl

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
// If using this WGSL snippet as an #import, a dedicated
1+
// If using this WGSL snippet as an #import, a dedicated
22
// "joint_matricies" uniform of type SkinnedMesh must be added in the
33
// main shader.
44

55
#define_import_path bevy_pbr::skinning
66

7-
/// HACK: This works around naga not supporting matrix addition in SPIR-V
7+
/// HACK: This works around naga not supporting matrix addition in SPIR-V
88
// translations. See https://github.com/gfx-rs/naga/issues/1527
99
fn add_matrix(
1010
a: mat4x4<f32>,
@@ -30,7 +30,7 @@ fn skin_model(
3030

3131
fn inverse_transpose_3x3(in: mat3x3<f32>) -> mat3x3<f32> {
3232
let x = cross(in.y, in.z);
33-
let y = cross(in.z, in.x);
33+
let y = cross(in.z, in.x);
3434
let z = cross(in.x, in.y);
3535
let det = dot(in.z, z);
3636
return mat3x3<f32>(
@@ -50,17 +50,3 @@ fn skin_normals(
5050
model[2].xyz
5151
)) * normal;
5252
}
53-
54-
fn skin_tangents(
55-
model: mat4x4<f32>,
56-
tangent: vec4<f32>,
57-
) -> vec4<f32> {
58-
return vec4<f32>(
59-
mat3x3<f32>(
60-
model[0].xyz,
61-
model[1].xyz,
62-
model[2].xyz
63-
) * tangent.xyz,
64-
tangent.w
65-
);
66-
}

crates/bevy_pbr/src/render/wireframe.wgsl

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
#import bevy_pbr::mesh_types
22
#import bevy_pbr::mesh_view_bindings
33

4+
[[group(1), binding(0)]]
5+
var<uniform> mesh: Mesh;
6+
7+
#ifdef SKINNED
8+
[[group(1), binding(1)]]
9+
var<uniform> joint_matrices: SkinnedMesh;
10+
#import bevy_pbr::skinning
11+
#endif
12+
13+
// NOTE: Bindings must come before functions that use them!
14+
#import bevy_pbr::mesh_functions
15+
416
struct Vertex {
517
[[location(0)]] position: vec3<f32>;
618
#ifdef SKINNED
@@ -9,19 +21,10 @@ struct Vertex {
921
#endif
1022
};
1123

12-
[[group(1), binding(0)]]
13-
var<uniform> mesh: Mesh;
14-
1524
struct VertexOutput {
1625
[[builtin(position)]] clip_position: vec4<f32>;
1726
};
1827

19-
#ifdef SKINNED
20-
[[group(1), binding(1)]]
21-
var<uniform> joint_matrices: SkinnedMesh;
22-
#import bevy_pbr::skinning
23-
#endif
24-
2528
[[stage(vertex)]]
2629
fn vertex(vertex: Vertex) -> VertexOutput {
2730
#ifdef SKINNED
@@ -30,10 +33,8 @@ fn vertex(vertex: Vertex) -> VertexOutput {
3033
let model = mesh.model;
3134
#endif
3235

33-
let world_position = model * vec4<f32>(vertex.position, 1.0);
3436
var out: VertexOutput;
35-
out.clip_position = view.view_proj * world_position;
36-
37+
out.clip_position = mesh_position_local_to_clip(model, vec4<f32>(vertex.position, 1.0));
3738
return out;
3839
}
3940

0 commit comments

Comments
 (0)