Three.js 中 VR/AR 相机配置主要通过 WebXR API 实现。以下是详细的配置指南:
javascript
import * as THREE from 'three';
import { VRButton } from 'three/examples/jsm/webxr/VRButton.js';
import { ARButton } from 'three/examples/jsm/webxr/ARButton.js';
// 创建渲染器时启用 XR
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.xr.enabled = true;
// 添加到页面
document.body.appendChild(renderer.domElement);
javascript
// 创建场景和相机
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);
// 添加 VR 按钮
document.body.appendChild(VRButton.createButton(renderer));
// 设置渲染循环
renderer.setAnimationLoop(function () {
renderer.render(scene, camera);
});
// 处理窗口大小变化
window.addEventListener('resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
javascript
// 启用 AR(需要 HTTPS)
renderer.xr.enabled = true;
// 添加 AR 按钮
document.body.appendChild(ARButton.createButton(renderer));
// 可选:添加 AR 特定功能
const sessionInit = {
optionalFeatures: [
'hit-test', // 命中测试
'dom-overlay', // DOM 叠加
'light-estimation' // 光线估计
],
domOverlay: {
root: document.getElementById('overlay') // DOM 覆盖元素
}
};
javascript
// 添加控制器
const controller1 = renderer.xr.getController(0);
const controller2 = renderer.xr.getController(1);
// 创建控制器可视化
const controllerModelFactory = new XRControllerModelFactory();
const controllerGrip1 = renderer.xr.getControllerGrip(0);
const controllerGrip2 = renderer.xr.getControllerGrip(1);
controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1));
controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2));
scene.add(controller1);
scene.add(controller2);
scene.add(controllerGrip1);
scene.add(controllerGrip2);
// 射线可视化(用于交互)
const geometry = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0, 0, -1)
]);
const line = new THREE.Line(geometry);
line.name = 'line';
line.scale.z = 5;
controller1.add(line.clone());
controller2.add(line.clone());
javascript
// 选择事件
controller1.addEventListener('selectstart', onSelectStart);
controller1.addEventListener('selectend', onSelectEnd);
function onSelectStart() {
this.userData.isSelecting = true;
}
function onSelectEnd() {
this.userData.isSelecting = false;
}
javascript
let hitTestSource = null;
let hitTestSourceRequested = false;
function onSessionStarted(session) {
session.addEventListener('end', onSessionEnded);
renderer.xr.setSession(session);
hitTestSourceRequested = false;
}
function requestHitTestSource() {
const session = renderer.xr.getSession();
session.requestReferenceSpace('viewer').then(function (referenceSpace) {
session.requestHitTestSource({ space: referenceSpace }).then(function (source) {
hitTestSource = source;
});
});
session.requestHitTestSourceForTransientInput({
profile: 'generic-touchscreen',
offsetRay: new XRRay()
}).then(function (source) {
transientHitTestSource = source;
});
}
javascript
let lightProbe = new THREE.LightProbe();
scene.add(lightProbe);
renderer.xr.addEventListener('sessionstart', function (event) {
const session = event.session;
if ('requestLightProbe' in XRWebGLBinding.prototype) {
session.requestLightProbe().then(function (lightProbe) {
// 使用环境光信息
});
}
});
javascript
import * as THREE from 'three';
import { VRButton } from 'three/examples/jsm/webxr/VRButton.js';
import { ARButton } from 'three/examples/jsm/webxr/ARButton.js';
import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerModelFactory.js';
class XRApplication {
constructor() {
this.init();
}
init() {
// 1. 创建场景
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x444444);
// 2. 创建相机
this.camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(0, 1.6, 3);
// 3. 创建渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.xr.enabled = true;
document.body.appendChild(this.renderer.domElement);
// 4. 添加 VR/AR 按钮
document.body.appendChild(VRButton.createButton(this.renderer));
// document.body.appendChild(ARButton.createButton(this.renderer)); // AR
// 5. 添加控制器
this.setupControllers();
// 6. 添加内容
this.setupScene();
// 7. 启动动画循环
this.animate();
// 8. 窗口大小调整
window.addEventListener('resize', this.onWindowResize.bind(this));
}
setupControllers() {
const controllerModelFactory = new XRControllerModelFactory();
for (let i = 0; i < 2; i++) {
const controller = this.renderer.xr.getController(i);
const controllerGrip = this.renderer.xr.getControllerGrip(i);
controllerGrip.add(controllerModelFactory.createControllerModel(controllerGrip));
this.scene.add(controller);
this.scene.add(controllerGrip);
// 添加射线
const geometry = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0, 0, -1)
]);
const line = new THREE.Line(geometry);
line.scale.z = 5;
controller.add(line);
}
}
setupScene() {
// 添加环境光
const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1);
light.position.set(0.5, 1, 0.25);
this.scene.add(light);
// 添加一个立方体
const geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
this.cube = new THREE.Mesh(geometry, material);
this.cube.position.set(0, 1.5, -2);
this.scene.add(this.cube);
// 添加地板
const floorGeometry = new THREE.PlaneGeometry(20, 20);
const floorMaterial = new THREE.MeshStandardMaterial({
color: 0x808080,
side: THREE.DoubleSide
});
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
this.scene.add(floor);
}
animate() {
this.renderer.setAnimationLoop((time) => {
// 更新立方体旋转
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
// 渲染场景
this.renderer.render(this.scene, this.camera);
});
}
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
}
// 启动应用
new XRApplication();
HTTPS 要求:AR 功能必须在 HTTPS 环境下运行
设备支持:检查设备是否支持 WebXR
javascript
if ('xr' in navigator) {
navigator.xr.isSessionSupported('immersive-vr')
.then(supported => console.log('VR supported:', supported));
navigator.xr.isSessionSupported('immersive-ar')
.then(supported => console.log('AR supported:', supported));
}
性能优化:保持稳定的 60/90/120 FPS
用户交互:提供清晰的进入/退出 XR 的 UI
javascript
// 检查 WebXR 支持
if (!navigator.xr) {
document.body.innerHTML = '<p>WebXR not supported in this browser</p>';
} else {
// 初始化应用
}
这个配置提供了完整的 VR/AR 相机设置,包括控制器交互、命中测试等高级功能。根据具体需求调整配置参数。