WebXR та 3D симуляції у AR/VR

🎨 Комп'ютерна графіка ⏱ ~10 хв читання Середній рівень
WebXR VR AR Стерео-рендеринг Immersive Web

WebXR Device API перетворює вкладку браузера на портал, крізь який можна пройти шоломом чи камерою телефону: та сама WebGL/WebGPU-сцена, що рендериться на плаский canvas, може передаватись VR-шолому для стерео-рендерингу або накладатись на потік з камери телефону для AR — без жодного магазину додатків, без нативного SDK. Для освітніх фізичних симуляцій це означає прогулянку навколо сонячної системи у натуральну величину чи занурення руки у симуляцію рідини замість перетягування мишею. Ця стаття охоплює модель сесій, вхідні дані та дисципліну продуктивності, яку вимагає immersive-рендеринг.

Коротко: WebXR Device API дозволяє браузеру виводити фізичну симуляцію прямо у VR-шолом або на камеру AR-телефону без встановлення застосунку. Стаття пояснює типи сесій, системи координат (reference spaces), цикл рендерингу для двох очей, вхід з контролерів і рук, розміщення об'єктів в AR та жорсткий бюджет кадрової частоти (до ~8мс), потрібний, щоб уникнути заколисування.

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 потім тримають розміщений об'єкт просторово фіксованим навіть коли пристрій уточнює трекінг з часом.

На телефонах (handheld AR) потік passthrough-камери компонується самим браузером — сторінка бачить лише відрендерений 3D-шар, ніколи сирі кадри камери, з міркувань приватності. Саме тому WebXR AR не може робити власну обробку зображення з камери так, як це міг би нативний ARKit/ARCore додаток.

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.

🪐 Відкрити Сонячну систему →

🔗 Пов'язані симуляції

🪐Сонячна система 🌌N-тіл