You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 17:45:47