1
0

Fix stereo rendering glitches on Meta Quest browsers

Replace camera reference comparison with view matrix eye detection
for more reliable left/right eye identification. Recent Quest Browser
updates (Chromium 138/140, Horizon OS v83) changed WebXR multiview
behavior, causing the previous xrCamera.cameras[0]/[1] comparison
to fail intermittently on the left eye.

The new approach uses activeCamera.matrixWorldInverse.elements[12]
(the X translation in the view matrix) which reliably indicates:
- Negative value = left eye
- Positive value = right eye

This method is consistent across both Quest Browser and Safari/VisionOS.

Also adds explicit video texture synchronization in the render loop
to prevent timing-related glitches.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Verdi
2026-01-27 00:16:54 +00:00
parent d869f75e1e
commit 4183ae2530

View File

@@ -428,25 +428,20 @@ function init() {
return; return;
} }
const xrCamera = renderer.xr.getCamera(); // Use view matrix eye offset for reliable stereo detection
// This works consistently across Quest Browser updates and Safari/VisionOS
// Left eye has negative X offset, right eye has positive X offset
const viewMatrix = activeCamera.matrixWorldInverse;
const eyeOffsetX = viewMatrix.elements[12];
if (xrCamera && xrCamera.cameras && xrCamera.cameras.length >= 2) { if (eyeOffsetX < 0) {
if (activeCamera === xrCamera.cameras[0]) { // Left eye - show left half of SBS video
material.map.offset.x = 0; material.map.offset.x = 0;
} else if (activeCamera === xrCamera.cameras[1]) {
material.map.offset.x = 0.5;
} else { } else {
material.map.offset.x = 0; // Right eye - show right half of SBS video
material.map.offset.x = 0.5;
} }
material.map.repeat.x = 0.5; material.map.repeat.x = 0.5;
} else {
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;
}
}
}; };
// Initialize 2D camera // Initialize 2D camera
@@ -1731,6 +1726,12 @@ function renderXR(timestamp, frame) {
} }
} }
try { try {
// Ensure video texture is synchronized with render loop
// This prevents glitches from texture update timing issues on Quest browsers
if (videoTexture && video && !video.paused && !video.ended) {
videoTexture.needsUpdate = true;
}
handleControllerInteractions(); handleControllerInteractions();
renderer.render(scene, camera); renderer.render(scene, camera);
} catch (error) { } catch (error) {