Three.js中进入XR模式时如何管理宽高比?
解决Three.js XR模式下场景拉伸问题
问题核心
XR模式下,Three.js会生成独立的左右眼XR相机,这些相机的投影参数由WebXR API根据设备视图配置自动生成,直接修改原PerspectiveCamera的aspect不会同步到XR相机。同时XR渲染会占满设备全屏,忽略原画布的宽高比,导致场景拉伸变形。
解决方案:在XR渲染循环中调整视口
通过在XR的动画循环里,为每个XR视图(左右眼)计算适配原宽高比的视口,让场景居中显示并保持比例,避免拉伸。
步骤1:保存原画布的宽高比
在初始化时记录原画布的宽高比:
const originalAspectRatio = root.clientWidth / root.clientHeight;
步骤2:替换动画循环为XR兼容版本
使用renderer.setAnimationLoop替代普通的requestAnimationFrame,在循环中处理XR和非XR场景的渲染:
// 替换原有的渲染逻辑,使用XR兼容的动画循环 renderer.setAnimationLoop(function(timestamp, frame) { if (frame) { // XR模式下的渲染逻辑 const referenceSpace = renderer.xr.getReferenceSpace(); const pose = frame.getViewerPose(referenceSpace); if (pose) { for (const view of pose.views) { const baseViewport = renderer.xr.getViewport(referenceSpace, view); // 计算适配原宽高比的视口尺寸 let targetWidth = baseViewport.width; let targetHeight = targetWidth / originalAspectRatio; // 如果计算出的高度超过设备视图高度,按高度适配 if (targetHeight > baseViewport.height) { targetHeight = baseViewport.height; targetWidth = targetHeight * originalAspectRatio; } // 计算视口偏移,让场景居中 const xOffset = (baseViewport.width - targetWidth) / 2; const yOffset = (baseViewport.height - targetHeight) / 2; // 设置当前视图的视口 renderer.setViewport( baseViewport.x + xOffset, baseViewport.y + yOffset, targetWidth, targetHeight ); // 渲染当前视图的场景 renderer.render(scene, view.camera); // 重置视口,避免影响后续渲染 renderer.setViewport(baseViewport.x, baseViewport.y, baseViewport.width, baseViewport.height); } } } else { // 非XR模式下的正常渲染 controls.update(); renderer.render(scene, camera); } });
原理说明
XR模式下每个视图(左右眼)默认使用全屏视口,通过计算原宽高比,调整视口的大小和偏移量,让场景在设备屏幕中居中显示,保持原比例。如果设备宽高比与原画布比例不同,会在屏幕两侧或上下出现黑边,但场景不会拉伸变形。
额外注意事项
- 不要直接修改XR相机的
aspect或投影矩阵,这会破坏WebXR的立体视觉参数,导致显示异常。 - 确保响应式逻辑在非XR模式下正常工作:监听窗口 resize 事件,更新原相机的
aspect并更新投影矩阵:
window.addEventListener('resize', () => { if (!renderer.xr.isPresenting) { camera.aspect = root.clientWidth / root.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(root.clientWidth, root.clientHeight); } });
内容的提问来源于stack exchange,提问作者AndyMGar
相关产品推荐
相关产品推荐

