1. VR/AR相机配置

Three.js 中 VR/AR 相机配置主要通过 WebXR API 实现。以下是详细的配置指南:

1.1. 基础 WebXR 设置

1.1.1. 启用 WebXR

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);

1.2. VR 相机配置

1.2.1. 基础 VR 设置

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);
});

1.3. AR 相机配置

1.3.1. 基础 AR 设置

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 覆盖元素
    }
};

1.4. 相机控制与交互

1.4.1. 添加控制器

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());

1.4.2. 手柄交互事件

javascript

// 选择事件
controller1.addEventListener('selectstart', onSelectStart);
controller1.addEventListener('selectend', onSelectEnd);

function onSelectStart() {
    this.userData.isSelecting = true;
}

function onSelectEnd() {
    this.userData.isSelecting = false;
}

1.5. 高级功能配置

1.5.1. 命中测试(AR 平面检测)

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;
    });
}

1.5.2. 光线估计(AR 环境光)

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) {
            // 使用环境光信息
        });
    }
});

1.6. 完整示例代码

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();

1.7. 重要注意事项

  1. HTTPS 要求:AR 功能必须在 HTTPS 环境下运行

  2. 设备支持:检查设备是否支持 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));
   }
  1. 性能优化:保持稳定的 60/90/120 FPS

  2. 用户交互:提供清晰的进入/退出 XR 的 UI

1.8. 兼容性处理

javascript

// 检查 WebXR 支持
if (!navigator.xr) {
    document.body.innerHTML = '<p>WebXR not supported in this browser</p>';
} else {
    // 初始化应用
}

这个配置提供了完整的 VR/AR 相机设置,包括控制器交互、命中测试等高级功能。根据具体需求调整配置参数。