Виртуальная реальность приходит в Интернет

Несколько основных моментов, которые помогут вам подготовиться к целому спектру иммерсивных впечатлений: виртуальной реальности, дополненной реальности и всему, что между ними.

Джо Медли
Joe Medley

В Chrome 79 появились возможности для полного погружения в виртуальную реальность. API WebXR Device API обеспечивает поддержку виртуальной реальности, а поддержка дополненной реальности появилась в Chrome 81. Обновление API GamePad расширяет возможности управления в виртуальной реальности. Вскоре эти характеристики будут поддерживать и другие браузеры, включая Firefox Reality, Oculus Browser, Edge и браузер Helio от Magic Leap, и многие другие.

Эта статья открывает серию публикаций об иммерсивном интернете. В этой части рассматривается настройка базового приложения WebXR, а также вход и выход из XR-сессии. В последующих статьях будут рассмотрены цикл кадров (основной элемент работы WebXR), особенности дополненной реальности и API WebXR Hit Test, средство обнаружения поверхностей в AR-сессии. Если не указано иное, все, что я рассматриваю в этой и последующих статьях, в равной степени применимо как к AR, так и к VR.

Что такое иммерсивный интернет?

Хотя для описания иммерсивных впечатлений мы используем два термина — дополненная реальность и виртуальная реальность — многие представляют их как спектр от полной реальности до полной виртуальности, с промежуточными степенями погружения. Буква «X» в аббревиатуре XR призвана отразить это мышление, представляя собой своего рода алгебраическую переменную, обозначающую всё, что находится в спектре иммерсивных впечатлений.

График, иллюстрирующий спектр визуальных впечатлений от полной реальности до полного погружения.
Спектр иммерсивных впечатлений

Примерами иммерсивных впечатлений являются:

  • Игры
  • 360° видео
  • Традиционные 2D (или 3D) видеоролики, представленные в иммерсивной обстановке.
  • покупка дома
  • Осмотрите товары у себя дома перед покупкой.
  • Иммерсивное искусство
  • Что-то крутое, о чём ещё никто не додумался.

Понятия и применение

Я объясню несколько основных моментов использования WebXR Device API. Если вам требуется более подробная информация, чем та, что я предоставил, ознакомьтесь с примерами WebXR от Immersive Web Working Group или постоянно пополняющимися справочными материалами MDN . Если вы знакомы с ранними версиями WebXR Device API, вам следует бегло просмотреть весь этот материал. Были внесены изменения.

Код в этой статье основан на базовом примере рабочей группы по иммерсивным веб-технологиям ( демо , исходный код ), но отредактирован для большей ясности и простоты.

Частью работы над спецификацией WebXR стала проработка мер безопасности и конфиденциальности для защиты пользователей. Следовательно, реализации должны соответствовать определенным требованиям. Веб-страница или приложение должны быть активны и находиться в фокусе, прежде чем они смогут запрашивать у пользователя какую-либо конфиденциальную информацию. Веб-страницы или приложения должны предоставляться по протоколу HTTPS. Сам API предназначен для защиты информации, получаемой от датчиков и камер, которая необходима ему для функционирования.

Запросить сессию

Для входа в XR-сессию требуется жест пользователя. Чтобы его получить, используйте обнаружение функций для проверки XRSystem (через navigator.xr ) и вызовите метод XRSystem.isSessionSupported() . Обратите внимание, что в версиях Chrome 79 и 80 объект XRSystem назывался XR .

В приведенном ниже примере я указал, что хочу создать сеанс виртуальной реальности с типом сессии 'immersive-vr' . Другие типы сессий'immersive-ar' и 'inline' . Сессия inline предназначена для отображения контента внутри HTML и в основном используется для тизерного контента. Пример сессии Immersive AR демонстрирует это. Я объясню это в последующей статье.

Как только я узнаю, что сеансы виртуальной реальности поддерживаются, я активирую кнопку, которая позволяет мне получать жест пользователя.

if (navigator.xr) {
  const supported = await navigator.xr.isSessionSupported('immersive-vr');
  if (supported) {
    xrButton.addEventListener('click', onButtonClicked);
    xrButton.textContent = 'Enter VR';
    xrButton.enabled = supported; // supported is Boolean
  }
}

После активации кнопки я жду события клика, а затем запрашиваю сессию.

let xrSession = null;
function onButtonClicked() {
  if (!xrSession) {
    navigator.xr.requestSession('immersive-vr')
    .then((session) => {
      xrSession = session;
      xrButton.textContent = 'Exit XR';
      onSessionStarted(xrSession);
    });
  } else {
    xrSession.end();
  }
}

Обратите внимание на иерархию объектов в этом коде. Она переходит от navigator к xr , а затем к экземпляру XRSession . В ранних версиях API скрипту приходилось запрашивать устройство перед запросом сессии. Теперь устройство получается неявно.

Войдите в сессию

После получения сессии мне нужно её запустить и войти в неё. Но сначала мне нужно настроить несколько вещей. Сессии нужен обработчик событий onend , чтобы приложение или веб-страница могли быть сброшены при выходе пользователя.

Мне также понадобится элемент <canvas> для отрисовки сцены. Он должен быть XR-совместимым WebGLRenderingContext или WebGL2RenderingContext . Вся отрисовка будет выполняться с использованием этих элементов или фреймворка на основе WebGL, такого как Three.js .

Теперь, когда у меня есть место для рисования, мне нужен источник контента для отрисовки на нём. Для этого я создаю экземпляр XRWebGLLayer . Я связываю его с холстом, вызывая XRSession.updateRenderState() .

Когда я начинаю сеанс, мне нужен способ определения местоположения объектов в виртуальной реальности. Мне потребуется опорное пространство. Опорное пространство 'local-floor' — это пространство, в котором начало координат находится рядом со зрителем, а ось Y равна 0 на уровне пола и не предполагается её перемещения. Существуют и другие типы опорных пространств , но это более сложная тема, чем я могу здесь описать. Я сохраняю опорное пространство в переменную, потому что оно понадобится мне при рисовании на экране.

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  let canvas = document.createElement('canvas');
  webGLRenContext = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(xrSession, webGLRenContext)
  });

  xrSession.requestReferenceSpace('local-floor')
  .then((refSpace) => {
    xrRefSpace = refSpace;
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

Получив опорное пространство, я вызываю XRSession.requestAnimationFrame() . Это начало отображения виртуального контента, которое происходит в цикле кадров.

Запустить цикл кадров

Цикл отрисовки кадров — это бесконечный цикл, управляемый пользовательским агентом, в котором контент многократно отрисовывается на экране. Контент отрисовывается дискретными блоками, называемыми кадрами. Последовательность кадров создает иллюзию движения. Для VR-приложений частота кадров может варьироваться от 60 до 144 в секунду. AR для Android работает со скоростью 30 кадров в секунду. Ваш код не должен предполагать какую-либо конкретную частоту кадров.

Основной процесс для циклической обработки кадров выглядит следующим образом:

  1. Вызовите XRSession.requestAnimationFrame() . В ответ пользовательский агент вызовет XRFrameRequestCallback , который определен вами.
  2. Внутри вашей функции обратного вызова:
    1. Вызовите XRSession.requestAnimationFrame() еще раз.
    2. Определите позу зрителя.
    3. Передайте ('bind') WebGLFramebuffer из XRWebGLLayer в WebGLRenderingContext .
    4. Пройдитесь по каждому объекту XRView , извлеките его XRViewport из XRWebGLLayer и передайте его в WebGLRenderingContext .
    5. Нарисуйте что-нибудь в буфере кадра.

В оставшейся части статьи описывается шаг 1 и часть шага 2, а именно настройка и вызов функции XRFrameRequestCallback . Остальные пункты шага 2 рассматриваются во второй части.

XRFramRequestCallback

Функция XRFrameRequestCallback определяется вами. Она принимает два параметра: объект DOMHighResTimeStamp и экземпляр XRFrame . Объект XRFrame предоставляет информацию, необходимую для отрисовки одного кадра на экране. Аргумент DOMHighResTimeStamp предназначен для использования в будущем.

Прежде чем что-либо делать, я собираюсь запросить следующий кадр анимации. Как уже говорилось, время показа кадров определяется пользовательским агентом на основе используемого оборудования. Запрос следующего кадра в первую очередь гарантирует, что цикл показа кадров продолжится, если во время обратного вызова возникнет ошибка.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  // Render a frame.
}

На этом этапе пора нарисовать что-нибудь для зрителя. Об этом мы поговорим во второй части. Прежде чем перейти к ней, позвольте мне показать вам, как завершить сеанс.

Завершить сессию

Иммерсивная сессия может завершиться по нескольким причинам, включая завершение вашим собственным кодом через вызов XRSession.end() . Другие причины включают отключение гарнитуры или перехват управления другим приложением. Именно поэтому корректно работающее приложение должно отслеживать событие end . При его возникновении сессия и связанные с ней объекты рендеринга должны быть удалены. Завершившуюся иммерсивную сессию нельзя возобновить. Чтобы снова погрузиться в иммерсивный мир, моему приложению необходимо начать новую сессию.

Напомним, что при входе в сессию во время настройки я добавил обработчик событий onend .

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);
  // More setup…
}

Внутри обработчика событий восстановите состояние приложения до того, как пользователь вошел в сессию.

function onSessionEnded(event) {
  xrSession = null;
  xrButton.textContent = 'Enter VR';
}

Заключение

Я не объяснил всё, что нужно для написания веб-приложения XR или AR. Надеюсь, я дал вам достаточно информации, чтобы вы могли самостоятельно разобраться в коде и начать экспериментировать. В следующей статье я объясню цикл отрисовки кадров, то есть процесс отрисовки контента на экране.