Урок · Середній · ~50 хв
WebXR Device API — перші кроки
WebXR — це браузерний стандарт для іммерсивних VR та AR. Він керує трекінгом голови, стереоскопічним рендерингом, позами контролерів і тактильною віддачею — і все це з JavaScript. Цей урок проведе вас через вхід у вбудовану (inline) сесію попереднього перегляду, перехід до immersive-vr, обробку введення з контролера та відмалювання простої інтерактивної VR-сцени за допомогою Three.js.
1Перевірка підтримки XR і показ кнопки
const btn = document.getElementById('vr-btn'); if (!navigator.xr) {
btn.textContent = 'WebXR not supported'; btn.disabled = true; } else {
// Перевіряємо, чи доступний immersive-vr (потребує HTTPS + гарнітуру)
navigator.xr.isSessionSupported('immersive-vr').then(supported => { if
(supported) { btn.textContent = 'Enter VR';
btn.addEventListener('click', startXR); } else { btn.textContent = 'VR
not available on this device'; btn.disabled = true; } }); } // Завжди
показуємо вбудований 3D-перегляд (працює на будь-якому пристрої)
navigator.xr?.isSessionSupported('inline').then(ok => { if (ok)
startInlineSession(); });
WebXR потребує HTTPS (або localhost). Локально використовуйте
serve / vite — звичайні
file:// URL не працюватимуть.
2Запуск сесії immersive-vr
import * as THREE from
'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.xr.enabled = true;
document.body.appendChild(renderer.domElement); let xrSession = null;
async function startXR() { xrSession = await
navigator.xr.requestSession('immersive-vr', { requiredFeatures:
['local-floor'], // опорний простір відносно підлоги optionalFeatures:
['hand-tracking'], // суглоби рук, якщо доступні }); // Повідомляємо Three.js
про сесію — стереоскопічний рендеринг він обробляє внутрішньо await
renderer.xr.setSession(xrSession); // Сесія завершується, коли користувач знімає
гарнітуру xrSession.addEventListener('end', () => { xrSession = null;
btn.textContent = 'Enter VR'; }); btn.textContent = 'Exit VR'; }
3Цикл рендерингу XR
У режимі XR Three.js перебирає на себе requestAnimationFrame.
Використовуйте renderer.setAnimationLoop — він працює як на сторінці, так і
у VR:
const scene = new THREE.Scene(); const camera = new
THREE.PerspectiveCamera(70, innerWidth/innerHeight, 0.01, 100);
scene.add(camera); // Додаємо кілька об'єктів на сцену const geo = new
THREE.BoxGeometry(0.2, 0.2, 0.2); const mat = new
THREE.MeshStandardMaterial({ color: 0x22c55e }); for (let i = 0; i
< 10; i++) { const box = new THREE.Mesh(geo, mat);
box.position.set( (Math.random() - 0.5) * 4, Math.random() * 1.5, -1 -
Math.random() * 3 ); scene.add(box); } // Додаємо світло scene.add(new
THREE.AmbientLight(0xffffff, 0.5)); const dirLight = new
THREE.DirectionalLight(0xffffff, 1); dirLight.position.set(2, 4, 2);
scene.add(dirLight); // Цикл — викликається браузером із частотою 90 Гц у
VR renderer.setAnimationLoop((time, frame) => { renderer.render(scene,
camera); });
Аргумент
frame — це XRFrame; використовуйте його, щоб
у тому ж циклі запитувати пози контролерів, суглоби рук та AR-hit-тести.
4Зчитування поз контролерів
// Three.js XRControllerModelFactory автоматично керує мешами
контролерів import { XRControllerModelFactory } from
'https://cdn.jsdelivr.net/npm/three@0.160/examples/jsm/webxr/XRControllerModelFactory.js';
const cmf = new XRControllerModelFactory(); const controllers = [0,
1].map(i => { // Простір хвата: фізична модель контролера const grip =
renderer.xr.getControllerGrip(i);
grip.add(cmf.createControllerModel(grip)); scene.add(grip); // Простір
променя: промінь вказування const ctrl = renderer.xr.getController(i);
ctrl.addEventListener('selectstart', onSelectStart);
ctrl.addEventListener('selectend', onSelectEnd); scene.add(ctrl); //
Візуалізуємо промінь const ray = new THREE.Line( new
THREE.BufferGeometry().setFromPoints([ new THREE.Vector3(0, 0, 0), new
THREE.Vector3(0, 0, -1), ]), new THREE.LineBasicMaterial({ color:
0x22c55e }) ); ctrl.add(ray); return ctrl; }); function
onSelectStart(e) { // Контролер натиснув курок const ctrl =
e.target; console.log('Select start from', ctrl === controllers[0] ?
'left' : 'right'); }
5Кнопки геймпада та тактильна віддача
renderer.setAnimationLoop((time, frame) => { if (frame) { const
session = renderer.xr.getSession(); for (const source of
session.inputSources) { const gp = source.gamepad; if (!gp) continue;
// Стандартне розкладання кнопок (профіль геймпада OpenXR): // buttons[0] =
курок, [1] = хват, [3] = натискання стіка, [4/5] = A/B const trigger
= gp.buttons[0]?.value ?? 0; // аналогове 0–1 const grip =
gp.buttons[1]?.pressed ?? false; // Аналоговий стік const stickX =
gp.axes[2] ?? 0; // -1 вліво, +1 вправо const stickY = gp.axes[3] ?? 0;
// -1 вгору, +1 вниз // Тактильний імпульс (якщо доступний) if (trigger > 0.9
&& source.gamepad.hapticActuators?.length) {
source.gamepad.hapticActuators[0].pulse(trigger * 0.5, 50); } } }
renderer.render(scene, camera); });
Тактильна віддача потребує короткого імпульсу < 250 мс — довші імпульси більшість
середовищ виконання мовчки ігнорує. Діапазон інтенсивності — 0–1.