Merge pull request #110865 from brycehutchings/openxr_d3d12_near_far_fix

Fix OpenXR with D3D12 using the wrong clip space projection matrix
This commit is contained in:
Thaddeus Crews 2025-09-25 12:13:55 -05:00
commit 5aa220efee
No known key found for this signature in database
GPG key ID: 8C6E5FEB5FC03CCC
6 changed files with 17 additions and 32 deletions

View file

@ -273,7 +273,7 @@ bool OpenXRD3D12Extension::get_swapchain_image_data(XrSwapchain p_swapchain, int
bool OpenXRD3D12Extension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) { bool OpenXRD3D12Extension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
OpenXRUtil::XrMatrix4x4f matrix; OpenXRUtil::XrMatrix4x4f matrix;
OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_D3D, p_fov, (float)p_z_near, (float)p_z_far); OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {

View file

@ -298,9 +298,8 @@ void OpenXRMetalExtension::cleanup_swapchain_graphics_data(void **p_swapchain_gr
} }
bool OpenXRMetalExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) { bool OpenXRMetalExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
// Even though this is a Metal renderer we're using OpenGL coordinate systems.
OpenXRUtil::XrMatrix4x4f matrix; OpenXRUtil::XrMatrix4x4f matrix;
OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far); OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {

View file

@ -288,7 +288,7 @@ bool OpenXROpenGLExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
bool OpenXROpenGLExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) { bool OpenXROpenGLExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
OpenXRUtil::XrMatrix4x4f matrix; OpenXRUtil::XrMatrix4x4f matrix;
OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far); OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {

View file

@ -417,9 +417,8 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
} }
bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) { bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
// Even though this is a Vulkan renderer we're using OpenGL coordinate systems.
OpenXRUtil::XrMatrix4x4f matrix; OpenXRUtil::XrMatrix4x4f matrix;
OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far); OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {

View file

@ -78,7 +78,7 @@ String OpenXRUtil::make_xr_version_string(XrVersion p_version) {
return version; return version;
} }
// Copied from OpenXR xr_linear.h private header, so we can still link against // Based on the OpenXR xr_linear.h private header, so we can still link against
// system-provided packages without relying on our `thirdparty` code. // system-provided packages without relying on our `thirdparty` code.
// Copyright (c) 2017 The Khronos Group Inc. // Copyright (c) 2017 The Khronos Group Inc.
@ -87,25 +87,22 @@ String OpenXRUtil::make_xr_version_string(XrVersion p_version) {
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Creates a projection matrix based on the specified dimensions. // Creates a projection matrix based on the specified dimensions.
// The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for the graphics API. // The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for Godot (OpenGL convention).
// The far plane is placed at infinity if farZ <= nearZ. // The far plane is placed at infinity if farZ <= nearZ.
// An infinite projection matrix is preferred for rasterization because, except for // An infinite projection matrix is preferred for rasterization because, except for
// things *right* up against the near plane, it always provides better precision: // things *right* up against the near plane, it always provides better precision:
// "Tightening the Precision of Perspective Rendering" // "Tightening the Precision of Perspective Rendering"
// Paul Upchurch, Mathieu Desbrun // Paul Upchurch, Mathieu Desbrun
// Journal of Graphics Tools, Volume 16, Issue 1, 2012 // Journal of Graphics Tools, Volume 16, Issue 1, 2012
void OpenXRUtil::XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const float tanAngleLeft, void OpenXRUtil::XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, const float tanAngleLeft, const float tanAngleRight,
const float tanAngleRight, const float tanAngleUp, float const tanAngleDown, const float tanAngleUp, float const tanAngleDown, const float nearZ, const float farZ) {
const float nearZ, const float farZ) {
const float tanAngleWidth = tanAngleRight - tanAngleLeft; const float tanAngleWidth = tanAngleRight - tanAngleLeft;
// Set to tanAngleDown - tanAngleUp for a clip space with positive Y down (Vulkan). // Set to tanAngleUp - tanAngleDown for a clip space with positive Y up.
// Set to tanAngleUp - tanAngleDown for a clip space with positive Y up (OpenGL / D3D / Metal). const float tanAngleHeight = (tanAngleUp - tanAngleDown);
const float tanAngleHeight = graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown);
// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES). // Set to nearZ for a [-1,1] Z clip space.
// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal). const float offsetZ = nearZ;
const float offsetZ = (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0;
if (farZ <= nearZ) { if (farZ <= nearZ) {
// place the far plane at infinity // place the far plane at infinity
@ -153,13 +150,12 @@ void OpenXRUtil::XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI
} }
// Creates a projection matrix based on the specified FOV. // Creates a projection matrix based on the specified FOV.
void OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const XrFovf fov, void OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, const XrFovf fov, const float nearZ, const float farZ) {
const float nearZ, const float farZ) {
const float tanLeft = std::tan(fov.angleLeft); const float tanLeft = std::tan(fov.angleLeft);
const float tanRight = std::tan(fov.angleRight); const float tanRight = std::tan(fov.angleRight);
const float tanDown = std::tan(fov.angleDown); const float tanDown = std::tan(fov.angleDown);
const float tanUp = std::tan(fov.angleUp); const float tanUp = std::tan(fov.angleUp);
XrMatrix4x4f_CreateProjection(result, graphicsApi, tanLeft, tanRight, tanUp, tanDown, nearZ, farZ); XrMatrix4x4f_CreateProjection(result, tanLeft, tanRight, tanUp, tanDown, nearZ, farZ);
} }

View file

@ -52,16 +52,7 @@ public:
float m[16]; float m[16];
} XrMatrix4x4f; } XrMatrix4x4f;
typedef enum GraphicsAPI { static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, const float tanAngleLeft, const float tanAngleRight,
GRAPHICS_VULKAN, const float tanAngleUp, float const tanAngleDown, const float nearZ, const float farZ);
GRAPHICS_OPENGL, static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, const XrFovf fov, const float nearZ, const float farZ);
GRAPHICS_OPENGL_ES,
GRAPHICS_D3D
} GraphicsAPI;
static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const float tanAngleLeft,
const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,
const float nearZ, const float farZ);
static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const XrFovf fov,
const float nearZ, const float farZ);
}; };