mirror of
https://github.com/bevyengine/bevy
synced 2025-01-02 00:08:53 +00:00
fd30e0c67d
# Objective - Mikktspace requires that we normalize world normals/tangents _before_ interpolation across vertices, and then do _not_ normalize after. I had it backwards. - We do not (am not supposed to?) need a second set of barycentrics for motion vectors. If you think about the typical raster pipeline, in the vertex shader we calculate previous_world_position, and then it gets interpolated using the current triangle's barycentrics. ## Solution - Fix normal/tangent processing - Reuse barycentrics for motion vector calculations - Not implementing this for 0.14, but long term I aim to remove explicit vertex tangents and calculate them in the shader on the fly. ## Testing - I tested out some of the normal maps we have in repo. Didn't seem to make a difference, but mikktspace is all about correctness across various baking tools. I probably just didn't have any of the ones that would cause it to break. - Didn't test motion vectors as there's a known bug with the depth buffer and meshlets that I'm waiting on the render graph rewrite to fix.
123 lines
5.5 KiB
WebGPU Shading Language
123 lines
5.5 KiB
WebGPU Shading Language
#define_import_path bevy_pbr::mesh_functions
|
|
|
|
#import bevy_pbr::{
|
|
mesh_view_bindings::{
|
|
view,
|
|
visibility_ranges,
|
|
VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE
|
|
},
|
|
mesh_bindings::mesh,
|
|
mesh_types::MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT,
|
|
view_transformations::position_world_to_clip,
|
|
}
|
|
#import bevy_render::maths::{affine3_to_square, mat2x4_f32_to_mat3x3_unpack}
|
|
|
|
|
|
fn get_world_from_local(instance_index: u32) -> mat4x4<f32> {
|
|
return affine3_to_square(mesh[instance_index].world_from_local);
|
|
}
|
|
|
|
fn get_previous_world_from_local(instance_index: u32) -> mat4x4<f32> {
|
|
return affine3_to_square(mesh[instance_index].previous_world_from_local);
|
|
}
|
|
|
|
fn mesh_position_local_to_world(world_from_local: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
|
|
return world_from_local * vertex_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(world_from_local: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
|
|
let world_position = mesh_position_local_to_world(world_from_local, vertex_position);
|
|
return position_world_to_clip(world_position.xyz);
|
|
}
|
|
|
|
fn mesh_normal_local_to_world(vertex_normal: vec3<f32>, instance_index: u32) -> vec3<f32> {
|
|
// NOTE: The mikktspace method of normal mapping requires that the world normal is
|
|
// re-normalized in the vertex shader to match the way mikktspace bakes vertex tangents
|
|
// and normal maps so that the exact inverse process is applied when shading. Blender, Unity,
|
|
// Unreal Engine, Godot, and more all use the mikktspace method.
|
|
// We only skip normalization for invalid normals so that they don't become NaN.
|
|
// Do not change this code unless you really know what you are doing.
|
|
// http://www.mikktspace.com/
|
|
if any(vertex_normal != vec3<f32>(0.0)) {
|
|
return normalize(
|
|
mat2x4_f32_to_mat3x3_unpack(
|
|
mesh[instance_index].local_from_world_transpose_a,
|
|
mesh[instance_index].local_from_world_transpose_b,
|
|
) * vertex_normal
|
|
);
|
|
} else {
|
|
return vertex_normal;
|
|
}
|
|
}
|
|
|
|
// Calculates the sign of the determinant of the 3x3 model matrix based on a
|
|
// mesh flag
|
|
fn sign_determinant_model_3x3m(mesh_flags: u32) -> f32 {
|
|
// bool(u32) is false if 0u else true
|
|
// f32(bool) is 1.0 if true else 0.0
|
|
// * 2.0 - 1.0 remaps 0.0 or 1.0 to -1.0 or 1.0 respectively
|
|
return f32(bool(mesh_flags & MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT)) * 2.0 - 1.0;
|
|
}
|
|
|
|
fn mesh_tangent_local_to_world(world_from_local: mat4x4<f32>, vertex_tangent: vec4<f32>, instance_index: u32) -> vec4<f32> {
|
|
// NOTE: The mikktspace method of normal mapping requires that the world tangent is
|
|
// re-normalized in the vertex shader to match the way mikktspace bakes vertex tangents
|
|
// and normal maps so that the exact inverse process is applied when shading. Blender, Unity,
|
|
// Unreal Engine, Godot, and more all use the mikktspace method.
|
|
// We only skip normalization for invalid tangents so that they don't become NaN.
|
|
// Do not change this code unless you really know what you are doing.
|
|
// http://www.mikktspace.com/
|
|
if any(vertex_tangent != vec4<f32>(0.0)) {
|
|
return vec4<f32>(
|
|
normalize(
|
|
mat3x3<f32>(
|
|
world_from_local[0].xyz,
|
|
world_from_local[1].xyz,
|
|
world_from_local[2].xyz,
|
|
) * vertex_tangent.xyz
|
|
),
|
|
// NOTE: Multiplying by the sign of the determinant of the 3x3 model matrix accounts for
|
|
// situations such as negative scaling.
|
|
vertex_tangent.w * sign_determinant_model_3x3m(mesh[instance_index].flags)
|
|
);
|
|
} else {
|
|
return vertex_tangent;
|
|
}
|
|
}
|
|
|
|
// Returns an appropriate dither level for the current mesh instance.
|
|
//
|
|
// This looks up the LOD range in the `visibility_ranges` table and compares the
|
|
// camera distance to determine the dithering level.
|
|
#ifdef VISIBILITY_RANGE_DITHER
|
|
fn get_visibility_range_dither_level(instance_index: u32, world_position: vec4<f32>) -> i32 {
|
|
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 6
|
|
// If we're using a storage buffer, then the length is variable.
|
|
let visibility_buffer_array_len = arrayLength(&visibility_ranges);
|
|
#else // AVAILABLE_STORAGE_BUFFER_BINDINGS >= 6
|
|
// If we're using a uniform buffer, then the length is constant
|
|
let visibility_buffer_array_len = VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE;
|
|
#endif // AVAILABLE_STORAGE_BUFFER_BINDINGS >= 6
|
|
|
|
let visibility_buffer_index = mesh[instance_index].flags & 0xffffu;
|
|
if (visibility_buffer_index > visibility_buffer_array_len) {
|
|
return -16;
|
|
}
|
|
|
|
let lod_range = visibility_ranges[visibility_buffer_index];
|
|
let camera_distance = length(view.world_position.xyz - world_position.xyz);
|
|
|
|
// This encodes the following mapping:
|
|
//
|
|
// `lod_range.` x y z w camera distance
|
|
// ←───────┼────────┼────────┼────────┼────────→
|
|
// LOD level -16 -16 0 0 16 16 LOD level
|
|
let offset = select(-16, 0, camera_distance >= lod_range.z);
|
|
let bounds = select(lod_range.xy, lod_range.zw, camera_distance >= lod_range.z);
|
|
let level = i32(round((camera_distance - bounds.x) / (bounds.y - bounds.x) * 16.0));
|
|
return offset + clamp(level, 0, 16);
|
|
}
|
|
#endif
|