From 20101647c114e59e8c95dd0514f316a67c307e2d Mon Sep 17 00:00:00 2001 From: Daniel Chia Date: Sat, 18 Mar 2023 19:06:53 -0400 Subject: [PATCH] Left-handed y-up cubemap coordinates (#8122) Co-authored-by: Robert Swain Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com> --- crates/bevy_pbr/src/render/light.rs | 38 ++++++++++++++----------- crates/bevy_pbr/src/render/shadows.wgsl | 11 ++++--- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index cdf1e7ab15..b68b25ccef 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -472,37 +472,43 @@ pub(crate) struct CubeMapFace { pub(crate) up: Vec3, } -// see https://www.khronos.org/opengl/wiki/Cubemap_Texture +// Cubemap faces are [+X, -X, +Y, -Y, +Z, -Z], per https://www.w3.org/TR/webgpu/#texture-view-creation +// Note: Cubemap coordinates are left-handed y-up, unlike the rest of Bevy. +// See https://registry.khronos.org/vulkan/specs/1.2/html/chap16.html#_cube_map_face_selection +// +// For each cubemap face, we take care to specify the appropriate target/up axis such that the rendered +// texture using Bevy's right-handed y-up coordinate space matches the expected cubemap face in +// left-handed y-up cubemap coordinates. pub(crate) const CUBE_MAP_FACES: [CubeMapFace; 6] = [ - // 0 GL_TEXTURE_CUBE_MAP_POSITIVE_X - CubeMapFace { - target: Vec3::NEG_X, - up: Vec3::NEG_Y, - }, - // 1 GL_TEXTURE_CUBE_MAP_NEGATIVE_X + // +X CubeMapFace { target: Vec3::X, - up: Vec3::NEG_Y, + up: Vec3::Y, }, - // 2 GL_TEXTURE_CUBE_MAP_POSITIVE_Y + // -X CubeMapFace { - target: Vec3::NEG_Y, - up: Vec3::Z, + target: Vec3::NEG_X, + up: Vec3::Y, }, - // 3 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y + // +Y CubeMapFace { target: Vec3::Y, + up: Vec3::Z, + }, + // -Y + CubeMapFace { + target: Vec3::NEG_Y, up: Vec3::NEG_Z, }, - // 4 GL_TEXTURE_CUBE_MAP_POSITIVE_Z + // +Z (with left-handed conventions, pointing forwards) CubeMapFace { target: Vec3::NEG_Z, - up: Vec3::NEG_Y, + up: Vec3::Y, }, - // 5 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z + // -Z (with left-handed conventions, pointing backwards) CubeMapFace { target: Vec3::Z, - up: Vec3::NEG_Y, + up: Vec3::Y, }, ]; diff --git a/crates/bevy_pbr/src/render/shadows.wgsl b/crates/bevy_pbr/src/render/shadows.wgsl index f74733c900..8da216803b 100644 --- a/crates/bevy_pbr/src/render/shadows.wgsl +++ b/crates/bevy_pbr/src/render/shadows.wgsl @@ -1,5 +1,7 @@ #define_import_path bevy_pbr::shadows +const flip_z: vec3 = vec3(1.0, 1.0, -1.0); + fn fetch_point_shadow(light_id: u32, frag_position: vec4, surface_normal: vec3) -> f32 { let light = &point_lights.data[light_id]; @@ -17,7 +19,7 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4, surface_normal: v let offset_position = frag_position.xyz + normal_offset + depth_offset; // similar largest-absolute-axis trick as above, but now with the offset fragment position - let frag_ls = (*light).position_radius.xyz - offset_position.xyz; + let frag_ls = offset_position.xyz - (*light).position_radius.xyz ; let abs_position_ls = abs(frag_ls); let major_axis_magnitude = max(abs_position_ls.x, max(abs_position_ls.y, abs_position_ls.z)); @@ -28,16 +30,17 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4, surface_normal: v let zw = -major_axis_magnitude * (*light).light_custom_data.xy + (*light).light_custom_data.zw; let depth = zw.x / zw.y; - // do the lookup, using HW PCF and comparison + // Do the lookup, using HW PCF and comparison. Cubemaps assume a left-handed coordinate space, + // so we have to flip the z-axis when sampling. // NOTE: Due to the non-uniform control flow above, we must use the Level variant of // textureSampleCompare to avoid undefined behaviour due to some of the fragments in // a quad (2x2 fragments) being processed not being sampled, and this messing with // mip-mapping functionality. The shadow maps have no mipmaps so Level just samples // from LOD 0. #ifdef NO_ARRAY_TEXTURES_SUPPORT - return textureSampleCompare(point_shadow_textures, point_shadow_textures_sampler, frag_ls, depth); + return textureSampleCompare(point_shadow_textures, point_shadow_textures_sampler, frag_ls * flip_z, depth); #else - return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth); + return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls * flip_z, i32(light_id), depth); #endif }