mirror of
https://github.com/bevyengine/bevy
synced 2024-12-21 02:23:08 +00:00
9b9d3d81cb
# Objective - Fixes #10909 - Fixes #8492 ## Solution - Name all matrices `x_from_y`, for example `world_from_view`. ## Testing - I've tested most of the 3D examples. The `lighting` example particularly should hit a lot of the changes and appears to run fine. --- ## Changelog - Renamed matrices across the engine to follow a `y_from_x` naming, making the space conversion more obvious. ## Migration Guide - `Frustum`'s `from_view_projection`, `from_view_projection_custom_far` and `from_view_projection_no_far` were renamed to `from_clip_from_world`, `from_clip_from_world_custom_far` and `from_clip_from_world_no_far`. - `ComputedCameraValues::projection_matrix` was renamed to `clip_from_view`. - `CameraProjection::get_projection_matrix` was renamed to `get_clip_from_view` (this affects implementations on `Projection`, `PerspectiveProjection` and `OrthographicProjection`). - `ViewRangefinder3d::from_view_matrix` was renamed to `from_world_from_view`. - `PreviousViewData`'s members were renamed to `view_from_world` and `clip_from_world`. - `ExtractedView`'s `projection`, `transform` and `view_projection` were renamed to `clip_from_view`, `world_from_view` and `clip_from_world`. - `ViewUniform`'s `view_proj`, `unjittered_view_proj`, `inverse_view_proj`, `view`, `inverse_view`, `projection` and `inverse_projection` were renamed to `clip_from_world`, `unjittered_clip_from_world`, `world_from_clip`, `world_from_view`, `view_from_world`, `clip_from_view` and `view_from_clip`. - `GpuDirectionalCascade::view_projection` was renamed to `clip_from_world`. - `MeshTransforms`' `transform` and `previous_transform` were renamed to `world_from_local` and `previous_world_from_local`. - `MeshUniform`'s `transform`, `previous_transform`, `inverse_transpose_model_a` and `inverse_transpose_model_b` were renamed to `world_from_local`, `previous_world_from_local`, `local_from_world_transpose_a` and `local_from_world_transpose_b` (the `Mesh` type in WGSL mirrors this, however `transform` and `previous_transform` were named `model` and `previous_model`). - `Mesh2dTransforms::transform` was renamed to `world_from_local`. - `Mesh2dUniform`'s `transform`, `inverse_transpose_model_a` and `inverse_transpose_model_b` were renamed to `world_from_local`, `local_from_world_transpose_a` and `local_from_world_transpose_b` (the `Mesh2d` type in WGSL mirrors this). - In WGSL, in `bevy_pbr::mesh_functions`, `get_model_matrix` and `get_previous_model_matrix` were renamed to `get_world_from_local` and `get_previous_world_from_local`. - In WGSL, `bevy_sprite::mesh2d_functions::get_model_matrix` was renamed to `get_world_from_local`.
189 lines
7.1 KiB
WebGPU Shading Language
189 lines
7.1 KiB
WebGPU Shading Language
// GPU mesh uniform building.
|
|
//
|
|
// This is a compute shader that expands each `MeshInputUniform` out to a full
|
|
// `MeshUniform` for each view before rendering. (Thus `MeshInputUniform`
|
|
// and `MeshUniform` are in a 1:N relationship.) It runs in parallel for all
|
|
// meshes for all views. As part of this process, the shader gathers each
|
|
// mesh's transform on the previous frame and writes it into the `MeshUniform`
|
|
// so that TAA works.
|
|
|
|
#import bevy_pbr::mesh_types::Mesh
|
|
#import bevy_render::maths
|
|
#import bevy_render::view::View
|
|
|
|
// Per-frame data that the CPU supplies to the GPU.
|
|
struct MeshInput {
|
|
// The model transform.
|
|
world_from_local: mat3x4<f32>,
|
|
// The lightmap UV rect, packed into 64 bits.
|
|
lightmap_uv_rect: vec2<u32>,
|
|
// Various flags.
|
|
flags: u32,
|
|
// The index of this mesh's `MeshInput` in the `previous_input` array, if
|
|
// applicable. If not present, this is `u32::MAX`.
|
|
previous_input_index: u32,
|
|
}
|
|
|
|
// Information about each mesh instance needed to cull it on GPU.
|
|
//
|
|
// At the moment, this just consists of its axis-aligned bounding box (AABB).
|
|
struct MeshCullingData {
|
|
// The 3D center of the AABB in model space, padded with an extra unused
|
|
// float value.
|
|
aabb_center: vec4<f32>,
|
|
// The 3D extents of the AABB in model space, divided by two, padded with
|
|
// an extra unused float value.
|
|
aabb_half_extents: vec4<f32>,
|
|
}
|
|
|
|
// One invocation of this compute shader: i.e. one mesh instance in a view.
|
|
struct PreprocessWorkItem {
|
|
// The index of the `MeshInput` in the `current_input` buffer that we read
|
|
// from.
|
|
input_index: u32,
|
|
// In direct mode, the index of the `Mesh` in `output` that we write to. In
|
|
// indirect mode, the index of the `IndirectParameters` in
|
|
// `indirect_parameters` that we write to.
|
|
output_index: u32,
|
|
}
|
|
|
|
// The `wgpu` indirect parameters structure. This is a union of two structures.
|
|
// For more information, see the corresponding comment in
|
|
// `gpu_preprocessing.rs`.
|
|
struct IndirectParameters {
|
|
// `vertex_count` or `index_count`.
|
|
data0: u32,
|
|
// `instance_count` in both structures.
|
|
instance_count: atomic<u32>,
|
|
// `first_vertex` in both structures.
|
|
first_vertex: u32,
|
|
// `first_instance` or `base_vertex`.
|
|
data1: u32,
|
|
// A read-only copy of `instance_index`.
|
|
instance_index: u32,
|
|
}
|
|
|
|
// The current frame's `MeshInput`.
|
|
@group(0) @binding(0) var<storage> current_input: array<MeshInput>;
|
|
// The `MeshInput` values from the previous frame.
|
|
@group(0) @binding(1) var<storage> previous_input: array<MeshInput>;
|
|
// Indices into the `MeshInput` buffer.
|
|
//
|
|
// There may be many indices that map to the same `MeshInput`.
|
|
@group(0) @binding(2) var<storage> work_items: array<PreprocessWorkItem>;
|
|
// The output array of `Mesh`es.
|
|
@group(0) @binding(3) var<storage, read_write> output: array<Mesh>;
|
|
|
|
#ifdef INDIRECT
|
|
// The array of indirect parameters for drawcalls.
|
|
@group(0) @binding(4) var<storage, read_write> indirect_parameters: array<IndirectParameters>;
|
|
#endif
|
|
|
|
#ifdef FRUSTUM_CULLING
|
|
// Data needed to cull the meshes.
|
|
//
|
|
// At the moment, this consists only of AABBs.
|
|
@group(0) @binding(5) var<storage> mesh_culling_data: array<MeshCullingData>;
|
|
|
|
// The view data, including the view matrix.
|
|
@group(0) @binding(6) var<uniform> view: View;
|
|
|
|
// Returns true if the view frustum intersects an oriented bounding box (OBB).
|
|
//
|
|
// `aabb_center.w` should be 1.0.
|
|
fn view_frustum_intersects_obb(
|
|
world_from_local: mat4x4<f32>,
|
|
aabb_center: vec4<f32>,
|
|
aabb_half_extents: vec3<f32>,
|
|
) -> bool {
|
|
|
|
for (var i = 0; i < 5; i += 1) {
|
|
// Calculate relative radius of the sphere associated with this plane.
|
|
let plane_normal = view.frustum[i];
|
|
let relative_radius = dot(
|
|
abs(
|
|
vec3(
|
|
dot(plane_normal, world_from_local[0]),
|
|
dot(plane_normal, world_from_local[1]),
|
|
dot(plane_normal, world_from_local[2]),
|
|
)
|
|
),
|
|
aabb_half_extents
|
|
);
|
|
|
|
// Check the frustum plane.
|
|
if (!maths::sphere_intersects_plane_half_space(
|
|
plane_normal, aabb_center, relative_radius)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
@compute
|
|
@workgroup_size(64)
|
|
fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
|
|
// Figure out our instance index. If this thread doesn't correspond to any
|
|
// index, bail.
|
|
let instance_index = global_invocation_id.x;
|
|
if (instance_index >= arrayLength(&work_items)) {
|
|
return;
|
|
}
|
|
|
|
// Unpack.
|
|
let input_index = work_items[instance_index].input_index;
|
|
let output_index = work_items[instance_index].output_index;
|
|
let world_from_local_affine_transpose = current_input[input_index].world_from_local;
|
|
let world_from_local = maths::affine3_to_square(world_from_local_affine_transpose);
|
|
|
|
// Cull if necessary.
|
|
#ifdef FRUSTUM_CULLING
|
|
let aabb_center = mesh_culling_data[input_index].aabb_center.xyz;
|
|
let aabb_half_extents = mesh_culling_data[input_index].aabb_half_extents.xyz;
|
|
|
|
// Do an OBB-based frustum cull.
|
|
let model_center = world_from_local * vec4(aabb_center, 1.0);
|
|
if (!view_frustum_intersects_obb(world_from_local, model_center, aabb_half_extents)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Calculate inverse transpose.
|
|
let local_from_world_transpose = transpose(maths::inverse_affine3(transpose(
|
|
world_from_local_affine_transpose)));
|
|
|
|
// Pack inverse transpose.
|
|
let local_from_world_transpose_a = mat2x4<f32>(
|
|
vec4<f32>(local_from_world_transpose[0].xyz, local_from_world_transpose[1].x),
|
|
vec4<f32>(local_from_world_transpose[1].yz, local_from_world_transpose[2].xy));
|
|
let local_from_world_transpose_b = local_from_world_transpose[2].z;
|
|
|
|
// Look up the previous model matrix.
|
|
let previous_input_index = current_input[input_index].previous_input_index;
|
|
var previous_world_from_local: mat3x4<f32>;
|
|
if (previous_input_index == 0xffffffff) {
|
|
previous_world_from_local = world_from_local_affine_transpose;
|
|
} else {
|
|
previous_world_from_local = previous_input[previous_input_index].world_from_local;
|
|
}
|
|
|
|
// Figure out the output index. In indirect mode, this involves bumping the
|
|
// instance index in the indirect parameters structure. Otherwise, this
|
|
// index was directly supplied to us.
|
|
#ifdef INDIRECT
|
|
let mesh_output_index = indirect_parameters[output_index].instance_index +
|
|
atomicAdd(&indirect_parameters[output_index].instance_count, 1u);
|
|
#else
|
|
let mesh_output_index = output_index;
|
|
#endif
|
|
|
|
// Write the output.
|
|
output[mesh_output_index].world_from_local = world_from_local_affine_transpose;
|
|
output[mesh_output_index].previous_world_from_local = previous_world_from_local;
|
|
output[mesh_output_index].local_from_world_transpose_a = local_from_world_transpose_a;
|
|
output[mesh_output_index].local_from_world_transpose_b = local_from_world_transpose_b;
|
|
output[mesh_output_index].flags = current_input[input_index].flags;
|
|
output[mesh_output_index].lightmap_uv_rect = current_input[input_index].lightmap_uv_rect;
|
|
}
|