WebXR 是浏览器原生支持 VR/AR 的稳定 API,需 HTTPS/localhost、用户手势触发 requestSession,并通过 isSessionSupported 检测兼容性;渲染依赖 WebGL/Three.js,须正确配置 XRSession、renderState、referenceSpace 及控制器。
WebXR 是浏览器原生支持 VR/AR 应用的 API,不是第三方库,也不依赖插件;它已进入稳定阶段,Chrome、Edge、Firefox(部分支持)均可用,但必须通过 HTTPS 或 localhost 访问,否则 navigator.xr 为 undefined。
不能只靠 "xr" in navigator
判断——有些浏览器暴露 navigator.xr 但不支持任何 XRSessionMode。真实检测要发起一次 session 请求并监听拒绝原因。
navigator.xr 是否存在且为对象navigator.xr.isSessionSupported("immersive-vr")(返回 Promise),而不是直接 requestSession
SecurityError(非 HTTPS)、NotSupportedError(设备无 VR 硬件或浏览器未启用 WebXR)、NotAllowedError(用户未点击触发,如自动启动)if ("xr" in navigator) {
navigator.xr.isSessionSupported("immersive-vr").then(supported => {
if (supported) {
// 可安全调用 requestSession
}
}).catch(err => console.error("XR 检测失败:", err.name));
}
WebXR 不负责 3D 渲染,它只提供姿态、视图、投影矩阵;你得自己接 WebGL 或 Three.js。核心是绑定 XRSession 的 render 循环,并在每一帧中调用 getViewerPose 获取头部位置。
requestSession("immersive-vr") 必须由用户手势(如 click、touchstart)触发session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) }),否则 requestAnimationFrame 中无法获取有效视图session.requestAnimationFrame(不是 window.requestAnimationFrame),否则姿态更新会不同步button.addEventListener("click", async () => {
const session = await navigator.xr.requestSession("immersive-vr");
session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
function render(time, frame) {
const pose = frame.getViewerPose(referenceSpace);
if (pose) {
for (const view of pose.views) {
// 使用 view.transform、view.projectionMatrix 渲染左右眼
}
}
session.requestAnimationFrame(render);
}
session.requestAnimationFrame(render);
});
Three.js r125+ 内置 WebXR 支持,但默认不启用——必须手动开启渲染器的 xr.enabled 并添加 XRControllerModelFactory 才能有手柄模型。漏掉任一环节都会导致黑屏或无交互。
立即学习“Java免费学习笔记(深入)”;
renderer.xr.enabled = true
new THREE.XRController(0) 和 new THREE.XRController(1)(对应左右手),否则手柄输入不可用OrbitControls,需在进入 XR 时禁用,否则视角会被双重控制THREE.GLTFLoader 并启用 dracoLoader,否则复杂模型在 VR 中易卡顿const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.xr.enabled = true;
const controller1 = renderer.xr.getController(0);
const controller2 = renderer.xr.getController(1);
scene.add(controller1, controller2);
// 进入 VR 前
button.addEventListener("click", () => {
renderer.xr.setSession(session); // session 来自 requestSession
});
WebXR 的坑不在 API 复杂,而在环境链路太长:HTTPS → 浏览器标志位 → 设备驱动 → WebGL 上下文 → 三维引擎适配。任一环节断开,错误信息都极不明确,比如 getViewerPose 返回 null 可能是 reference space 未正确创建,也可能是 session 被意外 ended,调试时优先检查 session.visibilityState 和 console.log(frame) 的实际内容。