From bebcb3d355b149cea2ad7d9d28c3dfeb3817f443 Mon Sep 17 00:00:00 2001 From: Verdi Date: Mon, 26 Jan 2026 23:27:45 -0600 Subject: [PATCH] Fix stereo eye detection with multiple fallback methods - Replace onBeforeRender stereo detection to use cascading fallbacks: 1. Direct xrCamera.cameras[0]/[1] comparison 2. View matrix matrixWorldInverse.elements[12] with 0.001 threshold 3. Projection matrix elements[8] as last resort - Add video texture sync in renderXR before render call Co-Authored-By: Claude Opus 4.5 --- vr180player/vr180-player.js | 39 ++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/vr180player/vr180-player.js b/vr180player/vr180-player.js index cdb098a..fd2971e 100644 --- a/vr180player/vr180-player.js +++ b/vr180player/vr180-player.js @@ -429,24 +429,41 @@ function init() { } const xrCamera = renderer.xr.getCamera(); + let isLeftEye = true; // Default to left eye if (xrCamera && xrCamera.cameras && xrCamera.cameras.length >= 2) { + // Method 1: Direct camera reference comparison if (activeCamera === xrCamera.cameras[0]) { - material.map.offset.x = 0; + isLeftEye = true; } else if (activeCamera === xrCamera.cameras[1]) { - material.map.offset.x = 0.5; + isLeftEye = false; } else { - material.map.offset.x = 0; + // Method 2: View matrix position (matrixWorldInverse.elements[12] is the X translation) + const viewMatrixX = activeCamera.matrixWorldInverse.elements[12]; + const leftCamX = xrCamera.cameras[0].matrixWorldInverse.elements[12]; + const rightCamX = xrCamera.cameras[1].matrixWorldInverse.elements[12]; + + const diffToLeft = Math.abs(viewMatrixX - leftCamX); + const diffToRight = Math.abs(viewMatrixX - rightCamX); + + if (diffToLeft < 0.001 || diffToLeft < diffToRight) { + isLeftEye = true; + } else if (diffToRight < 0.001) { + isLeftEye = false; + } else { + // Method 3: Projection matrix asymmetry (elements[8] indicates eye offset) + const projMatrixEl8 = activeCamera.projectionMatrix.elements[8]; + isLeftEye = projMatrixEl8 <= 0; + } } - material.map.repeat.x = 0.5; } else { + // Fallback when xrCamera.cameras is not available const projMatrixEl8 = activeCamera.projectionMatrix.elements[8]; - if (projMatrixEl8 < -0.0001) { - material.map.offset.x = 0; material.map.repeat.x = 0.5; - } else if (projMatrixEl8 > 0.0001) { - material.map.offset.x = 0.5; material.map.repeat.x = 0.5; - } + isLeftEye = projMatrixEl8 <= 0; } + + material.map.offset.x = isLeftEye ? 0 : 0.5; + material.map.repeat.x = 0.5; }; // Initialize 2D camera @@ -1731,6 +1748,10 @@ function renderXR(timestamp, frame) { } } try { + // Sync video texture before render to ensure frame consistency + if (videoTexture && video && !video.paused && !video.ended) { + videoTexture.needsUpdate = true; + } handleControllerInteractions(); renderer.render(scene, camera); } catch (error) {