WebXR / AR / VR
概述
WebXR Device API 是 W3C 制定的 Web 扩展现实标准,旨在为浏览器带来虚拟现实(VR)和增强现实(AR)设备的访问能力。作为 WebVR 标准的继承者,WebXR 统一了 VR 和 AR 的 API 设计,让开发者可以用一套接口同时支持沉浸式虚拟现实和增强现实体验。
WebXR 的核心愿景是让 XR 应用像普通 Web 应用一样易于访问和分发——用户无需安装应用,只需点击链接即可进入沉浸式体验。随着 Meta Quest、Apple Vision Pro、PICO 等设备的普及,WebXR 正逐渐成为 XR 内容分发的重要渠道。
WebXR API 架构
XRSession
XRSession 是 WebXR 的核心接口,代表一个持续运行的 XR 会话。它管理着 XR 设备的状态、输入源和帧循环:
// 检查 WebXR 支持
if (navigator.xr) {
const supported = await navigator.xr.isSessionSupported('immersive-vr')
if (supported) {
const session = await navigator.xr.requestSession('immersive-vr', {
requiredFeatures: ['local-floor'],
optionalFeatures: ['hand-tracking']
})
// 开始 XR 会话
}
}会话模式有三种类型:
| 模式 | 描述 | 典型设备 |
|---|---|---|
inline | 内嵌模式,在浏览器页面中显示,无沉浸感 | 所有设备 |
immersive-vr | 沉浸式 VR,完全占据显示设备 | Quest、Vive、PICO |
immersive-ar | 沉浸式 AR,叠加在真实世界上 | HoloLens、Magic Leap、手机 AR |
XRFrame
XRFrame 表示 XR 会话中的一帧数据,包含所有空间关系和输入状态:
function onXRFrame(time, frame) {
const pose = frame.getViewerPose(referenceSpace)
if (pose) {
// 处理每帧的渲染
for (const view of pose.views) {
// 获取左右眼的投影矩阵和视图矩阵
const projectionMatrix = view.projectionMatrix
const viewMatrix = view.transform.inverse.matrix
}
}
session.requestAnimationFrame(onXRFrame)
}XRFrame 从 XRViewerPose 中获取观察者的位置和朝向,以及左右两个眼睛的视图参数。开发者需要根据这些参数分别渲染左右眼的画面,以产生立体效果。
XRSpace
XRSpace 表示空间中的坐标系,是 XR 空间定位的基础:
- XRReferenceSpace:参考空间,定义坐标系的基准
local:以用户初始位置为原点(适合坐着或站立)local-floor:以地面为 Y=0 平面(适合房间尺度)bounded-floor:有边界的地面参考空间(适合 Room-scale)unbounded:无边界的空间(适合大范围 AR)
- XRJointSpace:基于手部关节的空间
- XRInputSource:手柄等输入设备的空间
// 创建不同层次的参考空间
const localSpace = await session.requestReferenceSpace('local')
const floorSpace = await session.requestReferenceSpace('local-floor')Three.js WebXR 集成
Three.js 对 WebXR 提供了官方集成支持,通过 THREE.WebXRManager 管理和渲染 XR 会话。
基本集成
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.xr.enabled = true
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
// 请求 XR 会话
document.getElementById('enterVR').addEventListener('click', () => {
renderer.xr.getSessionManager().requestSession('immersive-vr')
.then((session) => {
renderer.xr.setSession(session)
})
})
// 使用 Three.js 的动画循环(自动处理 XR 帧)
renderer.setAnimationLoop((time, frame) => {
// 自动处理左右眼渲染
renderer.render(scene, camera)
})Three.js 的 setAnimationLoop 会自动处理 XR 会话中的双眼渲染、帧循环和控制器更新,开发者只需要关注场景内容的逻辑即可。
XR 手柄交互
Three.js 通过 XRControllerModelFactory 加载手柄模型,通过 XRController 处理按钮事件:
import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'
const controllerModelFactory = new XRControllerModelFactory()
// 创建左右手柄
const controller1 = renderer.xr.getControllerGrip(0)
controller1.add(controllerModelFactory.createControllerModel(controller1))
scene.add(controller1)
const controller2 = renderer.xr.getControllerGrip(1)
controller2.add(controllerModelFactory.createControllerModel(controller2))
scene.add(controller2)
// 监听手柄事件
controller1.addEventListener('selectstart', () => {
console.log('Trigger pressed')
})
controller1.addEventListener('selectend', () => {
console.log('Trigger released')
})射线交互
控制器射线的实现通常使用 THREE.Raycaster 来检测手柄射线与场景中物体的相交:
const raycaster = new THREE.Raycaster()
const tempMatrix = new THREE.Matrix4()
function handleController(controller) {
tempMatrix.identity().extractRotation(controller.matrixWorld)
raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld)
raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix)
const intersects = raycaster.intersectObjects(scene.children)
if (intersects.length > 0) {
// 高亮选中的物体
intersects[0].object.material.emissive.setHex(0x444444)
}
}A-Frame 框架
A-Frame 是 Mozilla 推出的声明式 WebVR 框架,基于 Three.js 构建,通过 HTML 自定义元素的方式简化 VR 场景的开发:
<html>
<head>
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
</head>
<body>
<a-scene>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<a-sphere position="0 1 -3" radius="0.5" color="#EF2D5E"></a-sphere>
<a-cylinder position="1 0.5 -3" radius="0.5" height="1" color="#FFC65D"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-sky color="#FAFAFA"></a-sky>
<a-light type="ambient" color="#445566"></a-light>
</a-scene>
</body>
</html>Components 系统
A-Frame 的 Components 系统是其扩展性的核心,类似于 Cocos Creator 的组件模式:
AFRAME.registerComponent('color-changer', {
schema: { color: { type: 'color', default: '#EF2D5E' } },
init: function() {
this.el.addEventListener('click', () => {
this.el.setAttribute('color', this.data.color)
})
},
tick: function(time, delta) {
// 每帧更新逻辑
}
})控制器与交互
A-Frame 内置了对主流 VR 控制器的支持,包括 Oculus Touch、HTC Vive、Windows MR 等:
<a-scene>
<!-- 自动加载对应设备的手柄模型 -->
<a-entity id="leftHand" oculus-touch-controls="hand: left"></a-entity>
<a-entity id="rightHand" oculus-touch-controls="hand: right"
raycaster="far: 20; objects: .clickable"
laser-controls></a-entity>
</a-scene>A-Frame 的优势在于极低的上手门槛——前端开发者可以用熟悉的 HTML 标签组合 VR 场景,无需深入掌握 Three.js 或 WebGL 的细节。
8th Wall WebAR 方案
8th Wall 是目前最成熟的 WebAR 商业解决方案,它提供了完整的 SLAM(即时定位与地图构建)和世界锚点(Anchor)能力。
核心能力
- SLAM 追踪:通过手机摄像头实时追踪环境特征点,实现 6-DoF(六自由度)定位
- 世界锚点:将虚拟内容固定在真实世界的特定位置
- 图像识别:识别特定图片并叠加 AR 内容
- 面部特效:面部追踪和 AR 滤镜
- 云锚点:跨会话持久化的锚点
基本使用
8th Wall 通过 HTML 标签和 JavaScript 接口提供 AR 体验:
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.8thwall.com/web/aframe/8wall-aframe.js"></script>
</head>
<body>
<a-scene background-color="transparent"
8thwall="camera: rear"
arjs="sourceType: webcam; sourceWidth: 1280; sourceHeight: 720;">
<!-- AR 内容将叠加在摄像头画面之上 -->
<a-box position="0 0 -2" color="#00d2ff" shadow></a-box>
</a-scene>
</body>
</html>适用场景
8th Wall 广泛用于品牌营销、产品展示、互动游戏等领域。其商业许可模式对于企业级 AR 应用是成熟的选择,但对于个人开发者和学习目的,成本较高。
WebXR 性能与兼容性
性能优化
WebXR 应用的性能直接决定了用户体验的舒适度。XR 应用需要维持 72-120 FPS 的帧率,任何掉帧都会导致晕动症。
渲染优化:
- 降低 Draw Call:合并几何体、使用实例化渲染、减少材质切换
- 纹理压缩:使用 ASTC(Android)或 PVRTC(iOS)格式
- LOD(层次细节):根据物体与用户的距离切换不同精度的模型
- 遮挡剔除:不渲染被遮挡或不在视野内的物体
- Fixed Foveated Rendering(FFR):降低边缘区域的分辨率
性能检测工具:
// 检测 XR 设备的性能能力
const supported = await session.requestFeature('foveation')
if (supported) {
renderer.xr.setFoveation(0.5) // 0 = 无, 1 = 最大
}兼容性矩阵
| 浏览器 | VR 支持 | AR 支持 | 备注 |
|---|---|---|---|
| Chrome Android | ✅ | ✅ | ARCore 设备 |
| Safari iOS | ❌ | ✅ | 通过 AR Quick Look |
| Meta Quest 浏览器 | ✅ | ✅ | 完整 WebXR 支持 |
| Chrome Desktop | ✅ | ❌ | 需连接 VR 头显 |
| Firefox | ✅ | ❌ | 需开启 WebXR 配置 |
| Edge | ✅ | ❌ | 需开启 WebXR 配置 |
渐进增强策略
由于 WebXR 设备和浏览器的支持程度差异较大,推荐采用渐进增强策略:
async function initXRExperience() {
if (!navigator.xr) {
// 降级方案:使用 Three.js 普通 3D 场景
showFallback3D()
return
}
try {
const vrSupported = await navigator.xr.isSessionSupported('immersive-vr')
const arSupported = await navigator.xr.isSessionSupported('immersive-ar')
if (arSupported) {
initAR()
} else if (vrSupported) {
initVR()
} else {
showFallback3D()
}
} catch {
showFallback3D()
}
}降级方案的设计确保了应用在普通浏览器上也能提供基本的 3D 查看体验,而在支持 XR 的设备上则可以解锁沉浸式功能。
实战案例:WebAR/VR 模型查看器
以下 Demo 展示了一个基于 Three.js 的 WebAR/VR 模型查看器,虽然不依赖真实的 XR 硬件,但模拟了 WebXR 应用的核心交互模式:
3D 查看模式:
- 环面结(TorusKnot)、二十面体(Icosahedron)、球体(Sphere)、立方体(Cube)组合展示
- OrbitControls 提供旋转/缩放/平移操作
- 环境光 + 方向光 + 点光源的多光源系统
- 浮动粒子环绕效果
全景展厅模式:
- 大球体内表面视角模拟 360° 全景空间
- Canvas 绘制的纹理包含星空、星云和画廊画框
- 自动缓慢旋转营造沉浸感
- VR 标签提示
设备检测:
- 模拟 WebXR 设备检测结果
- 显示 VR 和 AR 支持状态
总结
WebXR 正在重塑 Web 与真实世界的交互方式。从基础的 XRSession 和 XRFrame 的 API 架构,到 Three.js 和 A-Frame 提供的开发框架支持,再到 8th Wall 等商业 AR 平台,WebXR 的生态正在快速成熟。
对于前端开发者来说,WebXR 的学习路径相对清晰:从 Three.js 的 3D 场景构建入手,逐步掌握 XR 会话管理、控制器交互和空间定位技术。随着 Apple Vision Pro 等混合现实设备的推出,WebXR 的应用场景和市场需求将持续增长,掌握 WebXR 技术将成为前端开发者的一项差异化竞争力。