From 57f3956cd6be9729f79b8ba5138beeeb1b523ee3 Mon Sep 17 00:00:00 2001 From: karma404 Date: Tue, 18 Nov 2025 23:14:02 +0530 Subject: [PATCH] Fix WebXR video texture position transform (#32268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add videoTextureCorrection flag to apply -90° Y-axis rotation correction for video textures in WebXR projection layers. - Add videoTextureCorrection flag (default: false, opt-in) to WebXRManager - Add videoTextureCorrection flag (default: false, opt-in) to XRManager - Apply -90° Y-axis rotation correction when flag is enabled - Works for both WebGL and WebGPU renderers - Zero performance overhead - simple quaternion multiplication Usage: renderer.xr.videoTextureCorrection = true; Note: This rotates the entire camera, affecting all rendered content. --- src/renderers/common/XRManager.js | 19 +++++++++++++++++++ src/renderers/webxr/WebXRManager.js | 21 ++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/renderers/common/XRManager.js b/src/renderers/common/XRManager.js index 23bb15b1708845..81c51ac44f0ec6 100644 --- a/src/renderers/common/XRManager.js +++ b/src/renderers/common/XRManager.js @@ -20,6 +20,7 @@ import { warn } from '../../utils.js'; const _cameraLPos = /*@__PURE__*/ new Vector3(); const _cameraRPos = /*@__PURE__*/ new Vector3(); +const _correctionQuaternion = /*@__PURE__*/ new Quaternion().setFromAxisAngle( new Vector3( 0, 1, 0 ), - Math.PI / 2 ); /** * The XR manager is built on top of the WebXR Device API to @@ -66,6 +67,16 @@ class XRManager extends EventDispatcher { */ this.cameraAutoUpdate = true; + /** + * Whether to apply a -90° Y-axis rotation correction to the XR camera for video textures. + * This corrects the orientation of video textures in WebXR projection layers. + * When enabled, this rotates the entire camera, affecting all rendered content. + * + * @type {boolean} + * @default false + */ + this.videoTextureCorrection = false; + /** * The renderer. * @@ -1624,6 +1635,14 @@ function onAnimationFrame( time, frame ) { camera.matrix.fromArray( view.transform.matrix ); camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); + + if ( this.videoTextureCorrection ) { + + camera.quaternion.premultiply( _correctionQuaternion ); + camera.matrix.compose( camera.position, camera.quaternion, camera.scale ); + + } + camera.projectionMatrix.fromArray( view.projectionMatrix ); camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); diff --git a/src/renderers/webxr/WebXRManager.js b/src/renderers/webxr/WebXRManager.js index 7d9b999db7339a..fdbc9c91894189 100644 --- a/src/renderers/webxr/WebXRManager.js +++ b/src/renderers/webxr/WebXRManager.js @@ -1,6 +1,7 @@ import { ArrayCamera } from '../../cameras/ArrayCamera.js'; import { EventDispatcher } from '../../core/EventDispatcher.js'; import { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js'; +import { Quaternion } from '../../math/Quaternion.js'; import { Vector2 } from '../../math/Vector2.js'; import { Vector3 } from '../../math/Vector3.js'; import { Vector4 } from '../../math/Vector4.js'; @@ -102,6 +103,16 @@ class WebXRManager extends EventDispatcher { */ this.enabled = false; + /** + * Whether to apply a -90° Y-axis rotation correction to the XR camera for video textures. + * This corrects the orientation of video textures in WebXR projection layers. + * When enabled, this rotates the entire camera, affecting all rendered content. + * + * @type {boolean} + * @default false + */ + this.videoTextureCorrection = false; + /** * Whether XR presentation is active or not. * @@ -912,7 +923,7 @@ class WebXRManager extends EventDispatcher { // Animation Loop let onAnimationFrameCallback = null; - + const correctionQuaternion = new Quaternion().setFromAxisAngle( new Vector3( 0, 1, 0 ), - Math.PI / 2 ); function onAnimationFrame( time, frame ) { pose = frame.getViewerPose( customReferenceSpace || referenceSpace ); @@ -982,6 +993,14 @@ class WebXRManager extends EventDispatcher { camera.matrix.fromArray( view.transform.matrix ); camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); + + if ( scope.videoTextureCorrection ) { + + camera.quaternion.premultiply( correctionQuaternion ); + camera.matrix.compose( camera.position, camera.quaternion, camera.scale ); + + } + camera.projectionMatrix.fromArray( view.projectionMatrix ); camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );