Files
english/.opencode/skills/threejs/references/14-physics-vr.md
2026-04-12 01:06:31 +07:00

305 lines
6.7 KiB
Markdown

# Physics & VR/XR
Integrate physics simulations and virtual reality.
## Physics Integration
Three.js doesn't include physics - use external libraries:
### Rapier Physics (Recommended)
Rust-based, high-performance:
```javascript
import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js';
// Initialize
const physics = await RapierPhysics();
// Create physics body
const box = new THREE.Mesh(
new THREE.BoxGeometry(),
new THREE.MeshStandardMaterial()
);
scene.add(box);
// Add physics (mass > 0 = dynamic)
physics.addMesh(box, 1); // mass = 1
// Static ground
const ground = new THREE.Mesh(
new THREE.BoxGeometry(10, 0.5, 10),
new THREE.MeshStandardMaterial()
);
ground.position.y = -2;
scene.add(ground);
physics.addMesh(ground); // no mass = static
// Update in animation loop
function animate() {
physics.step();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
```
### Ammo Physics
Port of Bullet physics engine:
```javascript
import { AmmoPhysics } from 'three/addons/physics/AmmoPhysics.js';
const physics = await AmmoPhysics();
// Same API as Rapier
physics.addMesh(mesh, mass);
function animate() {
physics.step();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
```
### Jolt Physics
High-performance alternative:
```javascript
import { JoltPhysics } from 'three/addons/physics/JoltPhysics.js';
const physics = await JoltPhysics();
physics.addMesh(mesh, mass);
```
### Physics Constraints
```javascript
// After initialization
const physics = await RapierPhysics();
// Point-to-point constraint
physics.addConstraint(meshA, meshB, 'fixed');
physics.addConstraint(meshA, meshB, 'spring', { stiffness: 100 });
// Remove constraint
physics.removeConstraint(constraint);
```
## VR/XR Setup
### Basic WebXR
```javascript
import { VRButton } from 'three/addons/webxr/VRButton.js';
// Enable XR
renderer.xr.enabled = true;
// Add VR button to page
document.body.appendChild(VRButton.createButton(renderer));
// Animation loop for VR
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
// Stop using requestAnimationFrame, use setAnimationLoop instead
```
### AR Mode
```javascript
import { ARButton } from 'three/addons/webxr/ARButton.js';
renderer.xr.enabled = true;
document.body.appendChild(ARButton.createButton(renderer));
// AR-specific features
const session = renderer.xr.getSession();
session.requestHitTestSource({ space: viewerSpace }).then((hitTestSource) => {
// Use hit testing for placing objects
});
```
### VR Controllers
```javascript
// Get controllers
const controller1 = renderer.xr.getController(0);
const controller2 = renderer.xr.getController(1);
scene.add(controller1);
scene.add(controller2);
// Controller events
controller1.addEventListener('selectstart', () => {
console.log('Trigger pressed');
});
controller1.addEventListener('selectend', () => {
console.log('Trigger released');
});
// Add visual controller models
import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js';
const controllerModelFactory = new XRControllerModelFactory();
const grip1 = renderer.xr.getControllerGrip(0);
grip1.add(controllerModelFactory.createControllerModel(grip1));
scene.add(grip1);
const grip2 = renderer.xr.getControllerGrip(1);
grip2.add(controllerModelFactory.createControllerModel(grip2));
scene.add(grip2);
```
### Hand Tracking
```javascript
import { OculusHandModel } from 'three/addons/webxr/OculusHandModel.js';
const hand1 = renderer.xr.getHand(0);
const handModel1 = new OculusHandModel(hand1);
hand1.add(handModel1);
scene.add(hand1);
const hand2 = renderer.xr.getHand(1);
const handModel2 = new OculusHandModel(hand2);
hand2.add(handModel2);
scene.add(hand2);
```
### Teleportation
```javascript
const raycaster = new THREE.Raycaster();
const tempMatrix = new THREE.Matrix4();
function handleController(controller) {
const intersections = getIntersections(controller);
if (intersections.length > 0) {
const intersection = intersections[0];
// Teleport on button release
controller.addEventListener('selectend', () => {
const offset = intersection.point.y;
camera.position.y += offset;
});
}
}
function getIntersections(controller) {
tempMatrix.identity().extractRotation(controller.matrixWorld);
raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
return raycaster.intersectObjects(scene.children, true);
}
```
### Spatial Audio for VR
```javascript
const listener = new THREE.AudioListener();
camera.add(listener);
const sound = new THREE.PositionalAudio(listener);
const audioLoader = new THREE.AudioLoader();
audioLoader.load('sound.ogg', (buffer) => {
sound.setBuffer(buffer);
sound.setRefDistance(1);
sound.setLoop(true);
sound.play();
});
// Attach to object
object.add(sound);
// Update listener in VR
renderer.setAnimationLoop(() => {
// Listener automatically updates with camera in VR
renderer.render(scene, camera);
});
```
### Room-Scale VR
```javascript
// Request room-scale experience
navigator.xr.requestSession('immersive-vr', {
requiredFeatures: ['local-floor']
}).then((session) => {
// Session setup
});
// Get play area bounds
session.requestReferenceSpace('bounded-floor').then((space) => {
const bounds = space.boundsGeometry;
// Create visual boundary
});
```
### Performance Tips for VR
- Target 90 FPS (11.1ms per frame)
- Use lower polygon counts
- Reduce shadow quality
- Limit post-processing
- Use instancing for repeated objects
- Enable foveated rendering if available
- Test on actual VR hardware
```javascript
// Foveated rendering (Quest 2+)
const gl = renderer.getContext();
const ext = gl.getExtension('WEBGL_foveated_rendering');
if (ext) {
ext.foveatedRenderingModeWEBGL(gl.FOVEATED_RENDERING_MODE_ENABLE_WEBGL);
}
```
## Mixed Reality (MR)
```javascript
import { XRButton } from 'three/addons/webxr/XRButton.js';
// Request MR features
document.body.appendChild(
XRButton.createButton(renderer, {
requiredFeatures: ['hand-tracking', 'layers'],
optionalFeatures: ['local-floor', 'bounded-floor']
})
);
// Passthrough mode (Quest Pro, etc.)
const session = renderer.xr.getSession();
const baseLayer = session.renderState.baseLayer;
baseLayer.compositionDisabled = true; // enable passthrough
```
## Common VR Patterns
```javascript
// Detect if in VR
if (renderer.xr.isPresenting) {
// In VR mode
}
// Get VR camera (for raycasting)
const vrCamera = renderer.xr.getCamera(camera);
// Different behavior for VR vs desktop
renderer.setAnimationLoop(() => {
if (renderer.xr.isPresenting) {
// VR rendering logic
} else {
// Desktop rendering logic
}
renderer.render(scene, camera);
});
```