Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

View Transformations #9726

Merged
merged 16 commits into from
Oct 24, 2023
Merged
32 changes: 1 addition & 31 deletions crates/bevy_pbr/src/deferred/pbr_deferred_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,7 @@
#import bevy_pbr::mesh_view_bindings as view_bindings
#import bevy_pbr::mesh_view_bindings view
#import bevy_pbr::utils octahedral_encode, octahedral_decode

// ---------------------------
// from https://github.com/DGriffin91/bevy_coordinate_systems/blob/main/src/transformations.wgsl
// ---------------------------

/// Convert a ndc space position to world space
fn position_ndc_to_world(ndc_pos: vec3<f32>) -> vec3<f32> {
let world_pos = view.inverse_view_proj * vec4(ndc_pos, 1.0);
return world_pos.xyz / world_pos.w;
}

/// Convert ndc space xy coordinate [-1.0 .. 1.0] to uv [0.0 .. 1.0]
fn ndc_to_uv(ndc: vec2<f32>) -> vec2<f32> {
return ndc * vec2(0.5, -0.5) + vec2(0.5);
}

/// Convert uv [0.0 .. 1.0] coordinate to ndc space xy [-1.0 .. 1.0]
fn uv_to_ndc(uv: vec2<f32>) -> vec2<f32> {
return uv * vec2(2.0, -2.0) + vec2(-1.0, 1.0);
}

/// Returns the (0.0, 0.0) .. (1.0, 1.0) position within the viewport for the current render target.
/// [0 .. render target viewport size] eg. [(0.0, 0.0) .. (1280.0, 720.0)] to [(0.0, 0.0) .. (1.0, 1.0)]
fn frag_coord_to_uv(frag_coord: vec2<f32>) -> vec2<f32> {
return (frag_coord - view.viewport.xy) / view.viewport.zw;
}

/// Convert frag coord to ndc.
fn frag_coord_to_ndc(frag_coord: vec4<f32>) -> vec3<f32> {
return vec3(uv_to_ndc(frag_coord_to_uv(frag_coord.xy)), frag_coord.z);
}
#import bevy_pbr::view_transformations position_ndc_to_world, frag_coord_to_ndc

// Creates the deferred gbuffer from a PbrInput.
fn deferred_gbuffer_from_pbr_input(in: PbrInput) -> vec4<u32> {
Expand Down
8 changes: 8 additions & 0 deletions crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ pub const PBR_FUNCTIONS_HANDLE: Handle<Shader> = Handle::weak_from_u128(16550102
pub const PBR_AMBIENT_HANDLE: Handle<Shader> = Handle::weak_from_u128(2441520459096337034);
pub const PARALLAX_MAPPING_SHADER_HANDLE: Handle<Shader> =
Handle::weak_from_u128(17035894873630133905);
pub const VIEW_TRANSFORMATIONS_SHADER_HANDLE: Handle<Shader> =
Handle::weak_from_u128(2098345702398750291);
pub const PBR_PREPASS_FUNCTIONS_SHADER_HANDLE: Handle<Shader> =
Handle::weak_from_u128(73204817249182637);
pub const PBR_DEFERRED_TYPES_HANDLE: Handle<Shader> = Handle::weak_from_u128(3221241127431430599);
Expand Down Expand Up @@ -191,6 +193,12 @@ impl Plugin for PbrPlugin {
"render/parallax_mapping.wgsl",
Shader::from_wgsl
);
load_internal_asset!(
app,
VIEW_TRANSFORMATIONS_SHADER_HANDLE,
"render/view_transformations.wgsl",
superdump marked this conversation as resolved.
Show resolved Hide resolved
Shader::from_wgsl
);

app.register_asset_reflect::<StandardMaterial>()
.register_type::<AlphaMode>()
Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use bevy_ecs::{
};
use bevy_reflect::Reflect;
use bevy_render::{
camera::Projection,
extract_instances::{ExtractInstancesPlugin, ExtractedInstances},
extract_resource::ExtractResource,
mesh::{Mesh, MeshVertexBufferLayout},
Expand Down Expand Up @@ -447,6 +448,7 @@ pub fn queue_material_meshes<M: Material>(
Option<&NormalPrepass>,
Option<&DeferredPrepass>,
Option<&TemporalAntiAliasSettings>,
Option<&Projection>,
&mut RenderPhase<Opaque3d>,
&mut RenderPhase<AlphaMask3d>,
&mut RenderPhase<Transparent3d>,
Expand All @@ -465,6 +467,7 @@ pub fn queue_material_meshes<M: Material>(
normal_prepass,
deferred_prepass,
taa_settings,
projection,
mut opaque_phase,
mut alpha_mask_phase,
mut transparent_phase,
Expand Down Expand Up @@ -494,6 +497,13 @@ pub fn queue_material_meshes<M: Material>(
view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
}

if let Some(projection) = projection {
view_key |= match projection {
Projection::Perspective(_) => MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE,
Projection::Orthographic(_) => MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC,
};
}

match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
ShadowFilteringMethod::Hardware2x2 => {
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
Expand Down
18 changes: 18 additions & 0 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,11 @@ bitflags::bitflags! {
const SHADOW_FILTER_METHOD_HARDWARE_2X2 = 0 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
const SHADOW_FILTER_METHOD_CASTANO_13 = 1 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
const SHADOW_FILTER_METHOD_JIMENEZ_14 = 2 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
const VIEW_PROJECTION_RESERVED_BITS = Self::VIEW_PROJECTION_MASK_BITS << Self::VIEW_PROJECTION_SHIFT_BITS;
const VIEW_PROJECTION_NONSTANDARD = 0 << Self::VIEW_PROJECTION_SHIFT_BITS;
const VIEW_PROJECTION_PERSPECTIVE = 1 << Self::VIEW_PROJECTION_SHIFT_BITS;
const VIEW_PROJECTION_ORTHOGRAPHIC = 2 << Self::VIEW_PROJECTION_SHIFT_BITS;
const VIEW_PROJECTION_RESERVED = 3 << Self::VIEW_PROJECTION_SHIFT_BITS;
}
}

Expand All @@ -687,6 +692,10 @@ impl MeshPipelineKey {
const SHADOW_FILTER_METHOD_SHIFT_BITS: u32 =
Self::TONEMAP_METHOD_SHIFT_BITS - Self::SHADOW_FILTER_METHOD_MASK_BITS.count_ones();

const VIEW_PROJECTION_MASK_BITS: u32 = 0b11;
const VIEW_PROJECTION_SHIFT_BITS: u32 =
Self::SHADOW_FILTER_METHOD_SHIFT_BITS - Self::VIEW_PROJECTION_MASK_BITS.count_ones();

pub fn from_msaa_samples(msaa_samples: u32) -> Self {
let msaa_bits =
(msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
Expand Down Expand Up @@ -874,6 +883,15 @@ impl SpecializedMeshPipeline for MeshPipeline {
shader_defs.push("LOAD_PREPASS_NORMALS".into());
}

let view_projection = key.intersection(MeshPipelineKey::VIEW_PROJECTION_RESERVED_BITS);
if view_projection == MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD {
shader_defs.push("VIEW_PROJECTION_NONSTANDARD".into());
} else if view_projection == MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE {
shader_defs.push("VIEW_PROJECTION_PERSPECTIVE".into());
} else if view_projection == MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC {
shader_defs.push("VIEW_PROJECTION_ORTHOGRAPHIC".into());
}

#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
shader_defs.push("WEBGL2".into());

Expand Down
9 changes: 5 additions & 4 deletions crates/bevy_pbr/src/render/mesh.wgsl
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#import bevy_pbr::mesh_functions as mesh_functions
#import bevy_pbr::skinning
#import bevy_pbr::morph
#import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::forward_io Vertex, VertexOutput
#import bevy_render::instance_index get_instance_index
#import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::forward_io Vertex, VertexOutput
#import bevy_render::instance_index get_instance_index
#import bevy_pbr::view_transformations position_world_to_clip

#ifdef MORPH_TARGETS
fn morph_vertex(vertex_in: Vertex) -> Vertex {
Expand Down Expand Up @@ -59,7 +60,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {

#ifdef VERTEX_POSITIONS
out.world_position = mesh_functions::mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
out.position = mesh_functions::mesh_position_world_to_clip(out.world_position);
out.position = position_world_to_clip(out.world_position.xyz);
#endif

#ifdef VERTEX_UVS
Expand Down
17 changes: 7 additions & 10 deletions crates/bevy_pbr/src/render/mesh_functions.wgsl
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#define_import_path bevy_pbr::mesh_functions

#import bevy_pbr::mesh_view_bindings view
#import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::mesh_types MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT
#import bevy_render::instance_index get_instance_index
#import bevy_render::maths affine_to_square, mat2x4_f32_to_mat3x3_unpack
#import bevy_pbr::mesh_view_bindings view
#import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::mesh_types MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT
#import bevy_render::instance_index get_instance_index
#import bevy_render::maths affine_to_square, mat2x4_f32_to_mat3x3_unpack
#import bevy_pbr::view_transformations position_world_to_clip

fn get_model_matrix(instance_index: u32) -> mat4x4<f32> {
return affine_to_square(mesh[get_instance_index(instance_index)].model);
Expand All @@ -18,16 +19,12 @@ fn mesh_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>)
return model * vertex_position;
}

fn mesh_position_world_to_clip(world_position: vec4<f32>) -> vec4<f32> {
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<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
let world_position = mesh_position_local_to_world(model, vertex_position);
return mesh_position_world_to_clip(world_position);
return position_world_to_clip(world_position.xyz);
}

fn mesh_normal_local_to_world(vertex_normal: vec3<f32>, instance_index: u32) -> vec3<f32> {
Expand Down
198 changes: 198 additions & 0 deletions crates/bevy_pbr/src/render/view_transformations.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#define_import_path bevy_pbr::view_transformations

#import bevy_pbr::mesh_view_bindings as view_bindings
DGriffin91 marked this conversation as resolved.
Show resolved Hide resolved

/// World space:
/// +y is up

/// View space:
/// -z is forward, +x is right, +y is up
DGriffin91 marked this conversation as resolved.
Show resolved Hide resolved
/// Forward is from the camera position into the scene.
/// (0.0, 0.0, -1.0) is linear distance of 1.0 in front of the camera's view relative to the camera's rotation
/// (0.0, 1.0, 0.0) is linear distance of 1.0 above the camera's view relative to the camera's rotation
DGriffin91 marked this conversation as resolved.
Show resolved Hide resolved

/// NDC (normalized device coordinate):
/// https://www.w3.org/TR/webgpu/#coordinate-systems
/// (-1.0, -1.0) in NDC is located at the bottom-left corner of NDC
/// (1.0, 1.0) in NDC is located at the top-right corner of NDC
/// Z is depth where:
/// 1.0 is near clipping plane
/// Perspective projection: 0.0 is inf far away
/// Orthographic projection: 0.0 is far clipping plane

/// UV space:
/// 0.0, 0.0 is the top left
/// 1.0, 1.0 is the bottom right


// -----------------
// TO WORLD --------
// -----------------

/// Convert a view space position to world space
fn position_view_to_world(view_pos: vec3<f32>) -> vec3<f32> {
let world_pos = view_bindings::view.view * vec4(view_pos, 1.0);
return world_pos.xyz;
}

/// Convert a clip space position to world space
DGriffin91 marked this conversation as resolved.
Show resolved Hide resolved
fn position_clip_to_world(clip_pos: vec4<f32>) -> vec3<f32> {
let world_pos = view_bindings::view.inverse_view_proj * clip_pos;
return world_pos.xyz;
}

/// Convert a ndc space position to world space
fn position_ndc_to_world(ndc_pos: vec3<f32>) -> vec3<f32> {
let world_pos = view_bindings::view.inverse_view_proj * vec4(ndc_pos, 1.0);
return world_pos.xyz / world_pos.w;
}

/// Convert a view space direction to world space
fn direction_view_to_world(view_dir: vec3<f32>) -> vec3<f32> {
let world_dir = view_bindings::view.view * vec4(view_dir, 0.0);
return world_dir.xyz;
}

/// Convert a clip space direction to world space
fn direction_clip_to_world(clip_dir: vec4<f32>) -> vec3<f32> {
let world_dir = view_bindings::view.inverse_view_proj * clip_dir;
return world_dir.xyz;
DGriffin91 marked this conversation as resolved.
Show resolved Hide resolved
}

// -----------------
// TO VIEW ---------
// -----------------

/// Convert a world space position to view space
fn position_world_to_view(world_pos: vec3<f32>) -> vec3<f32> {
let view_pos = view_bindings::view.inverse_view * vec4(world_pos, 1.0);
return view_pos.xyz;
}

/// Convert a clip space position to view space
fn position_clip_to_view(clip_pos: vec4<f32>) -> vec3<f32> {
let view_pos = view_bindings::view.inverse_projection * clip_pos;
return view_pos.xyz / view_pos.w;
}
DGriffin91 marked this conversation as resolved.
Show resolved Hide resolved

/// Convert a ndc space position to view space
fn position_ndc_to_view(ndc_pos: vec3<f32>) -> vec3<f32> {
let view_pos = view_bindings::view.inverse_projection * vec4(ndc_pos, 1.0);
return view_pos.xyz / view_pos.w;
}

/// Convert a world space direction to view space
fn direction_world_to_view(world_dir: vec3<f32>) -> vec3<f32> {
let view_dir = view_bindings::view.inverse_view * vec4(world_dir, 0.0);
return view_dir.xyz;
}

/// Convert a clip space direction to view space
fn direction_clip_to_view(clip_dir: vec4<f32>) -> vec3<f32> {
let view_dir = view_bindings::view.inverse_projection * clip_dir;
return view_dir.xyz;
}

// -----------------
// TO CLIP ---------
// -----------------

/// Convert a world space position to clip space
fn position_world_to_clip(world_pos: vec3<f32>) -> vec4<f32> {
let clip_pos = view_bindings::view.view_proj * vec4(world_pos, 1.0);
return clip_pos;
}
DGriffin91 marked this conversation as resolved.
Show resolved Hide resolved

/// Convert a view space position to clip space
fn position_view_to_clip(view_pos: vec3<f32>) -> vec4<f32> {
let clip_pos = view_bindings::view.projection * vec4(view_pos, 1.0);
return clip_pos;
}

/// Convert a world space direction to clip space
fn direction_world_to_clip(world_dir: vec3<f32>) -> vec4<f32> {
let clip_dir = view_bindings::view.view_proj * vec4(world_dir, 0.0);
return clip_dir;
}

/// Convert a view space direction to clip space
fn direction_view_to_clip(view_dir: vec3<f32>) -> vec4<f32> {
let clip_dir = view_bindings::view.projection * vec4(view_dir, 0.0);
return clip_dir;
}

// -----------------
// TO NDC ----------
// -----------------

/// Convert a world space position to ndc space
fn position_world_to_ndc(world_pos: vec3<f32>) -> vec3<f32> {
let ndc_pos = view_bindings::view.view_proj * vec4(world_pos, 1.0);
return ndc_pos.xyz / ndc_pos.w;
}

/// Convert a view space position to ndc space
fn position_view_to_ndc(view_pos: vec3<f32>) -> vec3<f32> {
let ndc_pos = view_bindings::view.projection * vec4(view_pos, 1.0);
return ndc_pos.xyz / ndc_pos.w;
}

// -----------------
// DEPTH -----------
// -----------------

/// Retrieve the perspective camera near clipping plane
fn perspective_camera_near() -> f32 {
return view_bindings::view.projection[3][2];
DGriffin91 marked this conversation as resolved.
Show resolved Hide resolved
}

/// Convert ndc depth to linear view z.
/// Note: Depth values in front of the camera will be negative as -z is forward
fn depth_ndc_to_view_z(ndc_depth: f32) -> f32 {
#ifdef VIEW_PROJECTION_PERSPECTIVE
return -perspective_camera_near() / ndc_depth;
#else ifdef VIEW_PROJECTION_ORTHOGRAPHIC
return -(view_bindings::view.projection[3][2] - ndc_depth) / view_bindings::view.projection[2][2];
#else
let view_pos = view_bindings::view.inverse_projection * vec4(0.0, 0.0, ndc_depth, 1.0);
return view_pos.z / view_pos.w;
#endif
}

/// Convert linear view z to ndc depth.
/// Note: View z input should be negative for values in front of the camera as -z is forward
fn view_z_to_depth_ndc(view_z: f32) -> f32 {
#ifdef VIEW_PROJECTION_PERSPECTIVE
return -perspective_camera_near() / view_z;
#else ifdef VIEW_PROJECTION_ORTHOGRAPHIC
return view_bindings::view.projection[3][2] + view_z * view_bindings::view.projection[2][2];
#else
let ndc_pos = view_bindings::view.projection * vec4(0.0, 0.0, view_z, 1.0);
return ndc_pos.z / ndc_pos.w;
#endif
}

// -----------------
// UV --------------
// -----------------

/// Convert ndc space xy coordinate [-1.0 .. 1.0] to uv [0.0 .. 1.0]
fn ndc_to_uv(ndc: vec2<f32>) -> vec2<f32> {
return ndc * vec2(0.5, -0.5) + vec2(0.5);
}

/// Convert uv [0.0 .. 1.0] coordinate to ndc space xy [-1.0 .. 1.0]
fn uv_to_ndc(uv: vec2<f32>) -> vec2<f32> {
return uv * vec2(2.0, -2.0) + vec2(-1.0, 1.0);
}

/// returns the (0.0, 0.0) .. (1.0, 1.0) position within the viewport for the current render target
/// [0 .. render target viewport size] eg. [(0.0, 0.0) .. (1280.0, 720.0)] to [(0.0, 0.0) .. (1.0, 1.0)]
fn frag_coord_to_uv(frag_coord: vec2<f32>) -> vec2<f32> {
return (frag_coord - view_bindings::view.viewport.xy) / view_bindings::view.viewport.zw;
}

/// Convert frag coord to ndc
fn frag_coord_to_ndc(frag_coord: vec4<f32>) -> vec3<f32> {
return vec3(uv_to_ndc(frag_coord_to_uv(frag_coord.xy)), frag_coord.z);
}
Loading