WebXR та 3D симуляції у AR/VR
WebXR Device API перетворює вкладку браузера на портал, крізь який можна пройти шоломом чи камерою телефону: та сама WebGL/WebGPU-сцена, що рендериться на плаский canvas, може передаватись VR-шолому для стерео-рендерингу або накладатись на потік з камери телефону для AR — без жодного магазину додатків, без нативного SDK. Для освітніх фізичних симуляцій це означає прогулянку навколо сонячної системи у натуральну величину чи занурення руки у симуляцію рідини замість перетягування мишею. Ця стаття охоплює модель сесій, вхідні дані та дисципліну продуктивності, яку вимагає immersive-рендеринг.
1. Модель сесій WebXR
Все починається із запиту сесії заданого режиму:
immersive-vr (повністю замінює вигляд, лише шолом),
immersive-ar (накладає 3D-контент на реальний світ
через passthrough-камеру чи оптичне see-through), або
inline (рендерить стерео-здатний контент прямо на
сторінці без захоплення дисплея — корисно для попереднього
"огляду" перед входом у VR).
async function enterVR() {
if (!navigator.xr) return; // немає підтримки WebXR
const supported = await navigator.xr.isSessionSupported('immersive-vr');
if (!supported) return;
const session = await navigator.xr.requestSession('immersive-vr', {
requiredFeatures: ['local-floor'],
optionalFeatures: ['hand-tracking', 'bounded-floor'],
});
const gl = canvas.getContext('webgl2', { xrCompatible: true });
const xrLayer = new XRWebGLLayer(session, gl);
session.updateRenderState({ baseLayer: xrLayer });
const refSpace = await session.requestReferenceSpace('local-floor');
session.requestAnimationFrame(function onXRFrame(time, frame) {
session.requestAnimationFrame(onXRFrame);
const pose = frame.getViewerPose(refSpace);
if (pose) renderStereo(gl, xrLayer, pose);
});
}
Сесії ефемерні і навмисно ініціюються користувачем — WebXR
вимагає жест користувача (клік по кнопці), щоб увійти, а браузер
володіє буфером дисплея шолома через XRWebGLLayer /
XRGPUBinding для WebGPU.
2. Reference spaces: local, bounded, unbounded
Reference space визначає систему координат, у якій повідомляються пози. Помилка тут — найпоширеніша причина, чому симуляція "телепортується" чи плаває на неправильній висоті у шоломі.
| Reference space | Початок координат | Застосування |
|---|---|---|
| viewer | Напряму відстежує шолом | HUD-елементи, що завжди повернуті до користувача |
| local | Фіксований на початку сесії, довільна висота | Сидячі досвіди |
| local-floor | Фіксований на початку сесії, рівень підлоги y=0 | Симуляції стоячи/у масштабі кімнати (найпоширеніше) |
| bounded-floor | Рівень підлоги, з визначеним полігоном ігрової зони | Room-scale з обізнаністю про межі guardian |
| unbounded | Прив'язаний до світу, може дрейфувати/скидатись для великих просторів | Симуляції ходьби по великих реальних територіях |
local-floor — правильне значення за замовчуванням
майже для будь-якої освітньої симуляції: воно ставить y=0 на
справжню підлогу, тож модель N-тіл у натуральну величину чи
сонячна система, масштабована до відстані ходьби, відчувається
фізично вкоріненою, а не такою, що плаває на висоті голови.
3. XR render loop і стерео-view
На відміну від звичайного циклу requestAnimationFrame,
що рендерить одну камеру, XR-кадр надає pose з одним
view на кожне око (два для стерео-шоломів, іноді
більше для екзотичного multi-view заліза), кожен зі своєю
матрицею проєкції та view-матрицею, що відображає фізичну
міжзіничну відстань.
function renderStereo(gl, xrLayer, pose) {
gl.bindFramebuffer(gl.FRAMEBUFFER, xrLayer.framebuffer);
for (const view of pose.views) {
const vp = xrLayer.getViewport(view);
gl.viewport(vp.x, vp.y, vp.width, vp.height);
drawScene(view.projectionMatrix, view.transform.inverse.matrix);
}
}
Viewport кожного ока займає половину спільного фреймбуфера пліч-о-пліч; браузерний композитор сам обробляє корекцію дисторсії лінз і хроматичну аберацію, тож симуляції потрібно лише відрендерити сцену двічі з правильними матрицями — стан фізики обчислюється один раз за кадр і використовується для обох очей.
4. Вхід: контролери, hand tracking, погляд
session.inputSources надає доступ до кожного
відстежуваного контролера чи руки, з targetRaySpace
(куди він вказує), а для hand tracking — повний скелет із 25
суглобів на руку через frame.getJointPose() —
достатньо, щоб виявити жест защипу для взаємодій "схопи цю
частинку" взагалі без апаратних контролерів.
Відстежувані контролери
6-DOF поза + кнопки/тригери/thumbstick через властивість gamepad Gamepad API на джерелі вводу.
Hand tracking
25 суглобів на руку (зап'ясток, кожна фаланга, кінчик пальця); відстань защипу між великим і вказівним пальцем — стандартний жест "вибрати".
Погляд / екранний вхід
Fallback для шоломів чи телефонів без контролерів — приціл у центрі екрана плюс тап/затримка для вибору.
5. Особливості AR: hit-testing та anchors
Сесії immersive-ar додають розуміння реального світу
поверх тієї ж моделі рендерингу. Функція hit-test
пускає промінь від контролера чи тапу по екрану у розуміння
пристроєм реальних поверхонь (через його сенсори глибини/SLAM) і
повертає кандидатські пози, де можна розмістити віртуальний
об'єкт — саме так ви торкаєтесь столу і "кладете" на нього
симульовану сонячну систему. Anchors потім тримають
розміщений об'єкт просторово фіксованим навіть коли пристрій
уточнює трекінг з часом.
6. Бюджет продуктивності для комфорту
VR значно менш толерантний до пропущених кадрів, ніж демонстрація на плоскому екрані: більшість шоломів вимагають 72–120 fps залежно від заліза, і промах цього бюджету спричиняє помітне тремтіння (judder), яке може викликати заколисування протягом секунд, а не просто виглядати млявим. Бюджет рендерингу фактично вдвічі менший порівняно зі сценою без XR тієї ж роздільності, оскільки обидва ока мають малюватись кожен кадр.
| Клас шолома | Цільова частота оновлення | Бюджет кадру (обидва ока) |
|---|---|---|
| Автономний мобільний чіп (клас Quest) | 72–90Гц | ~11–14мс |
| Прив'язаний до ПК | 90–120Гц | ~8–11мс |
| AR на телефоні (handheld) | 30–60Гц | ~16–33мс |
Практичні важелі: зменшуйте кількість частинок симуляції саме для XR-шляху, використовуйте fixed foveated rendering там, де підтримується (рендерте периферію у нижчій роздільності), і пропускайте дорогі пости-обробки (важкий AO, bloom-проходи), які могла б дозволити собі демонстрація на плоскому екрані.
7. Прогресивне покращення та fallback
Підтримка WebXR широка в браузерах Android/Quest, але фактично
відсутня в iOS Safari станом на момент написання, тож кожній
симуляції потрібен повністю функціональний non-XR fallback: та
сама сцена, та сама фізика, орбітальна камера мишею/тачем замість
пози шолома. Перевіряйте наявність через
navigator.xr?.isSessionSupported(...) і показуйте
кнопку "Увійти у VR" лише коли перевірка повертає true — ніколи
не припускайте, що API існує лише тому, що браузер на Chromium.