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) {
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 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) {
// Even though this is a Metal renderer we're using OpenGL coordinate systems.
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 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) {
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 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) {
// Even though this is a Vulkan renderer we're using OpenGL coordinate systems.
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 i = 0; i < 4; i++) {

View file

@ -78,7 +78,7 @@ String OpenXRUtil::make_xr_version_string(XrVersion p_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.
// 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
// 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.
// An infinite projection matrix is preferred for rasterization because, except for
// things *right* up against the near plane, it always provides better precision:
// "Tightening the Precision of Perspective Rendering"
// Paul Upchurch, Mathieu Desbrun
// Journal of Graphics Tools, Volume 16, Issue 1, 2012
void OpenXRUtil::XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const float tanAngleLeft,
const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,
const float nearZ, const float farZ) {
void OpenXRUtil::XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, const float tanAngleLeft, const float tanAngleRight,
const float tanAngleUp, float const tanAngleDown, const float nearZ, const float farZ) {
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 (OpenGL / D3D / Metal).
const float tanAngleHeight = graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown);
// Set to tanAngleUp - tanAngleDown for a clip space with positive Y up.
const float tanAngleHeight = (tanAngleUp - tanAngleDown);
// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
const float offsetZ = (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0;
// Set to nearZ for a [-1,1] Z clip space.
const float offsetZ = nearZ;
if (farZ <= nearZ) {
// 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.
void OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const XrFovf fov,
const float nearZ, const float farZ) {
void OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, const XrFovf fov, const float nearZ, const float farZ) {
const float tanLeft = std::tan(fov.angleLeft);
const float tanRight = std::tan(fov.angleRight);
const float tanDown = std::tan(fov.angleDown);
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];
} XrMatrix4x4f;
typedef enum GraphicsAPI {
GRAPHICS_VULKAN,
GRAPHICS_OPENGL,
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);
static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, const float tanAngleLeft, const float tanAngleRight,
const float tanAngleUp, float const tanAngleDown, const float nearZ, const float farZ);
static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, const XrFovf fov, const float nearZ, const float farZ);
};