Запуск и завершение сеанса WebXR
Защищённый контекст: Эта функция доступна только в защищённых контекстах (HTTPS) в некоторых или во всех поддерживающих браузерах.
Предполагая, что вы уже знакомы с 3D-графикой в целом и WebGL в частности, переход к смешанной реальности — представлению искусственных декораций или объектов в дополнение к реальному миру или вместо него — не слишком сложен. Прежде чем вы сможете начать визуализировать свой сценарий дополненной или виртуальной реальности, вам необходимо создать и настроить сеанс WebXR, а также знать, как его правильно завершить. Вы узнаете, как это сделать в этой статье.
Доступ к API WebXR
Доступ вашего приложения к API WebXR начинается с объекта XRSystem. Этот объект представляет собой весь набор устройств WebXR, доступный вам через оборудование и драйверы, доступные на устройстве пользователя. Глобальный объект XRSystem доступен для использования вашим документом через свойство Navigator xr, которое возвращает объект XRSystem, если подходящее оборудование XR доступно для использования вашим документом в зависимости от имеющегося оборудования и среды документа.
Таким образом, самый простой код для получения объекта XRSystem:
const xr = navigator.xr;
Значение xr будет null или undefined, если WebXR недоступен.
Доступность WebXR
Как новый и всё ещё разрабатываемый API, поддержка WebXR ограничена определёнными устройствами и браузерами; и даже на них она может быть не включена по умолчанию. Однако могут быть доступны опции, позволяющие экспериментировать с WebXR, даже если у вас нет совместимой системы.
Полифил WebXR
Команда, разрабатывающая спецификацию WebXR, опубликовала полифил WebXR, который можно использовать для имитации WebXR в браузерах, не поддерживающих API WebXR. Если браузер поддерживает более старый API WebVR, он используется. В противном случае полифил использует реализацию, основанную на API Google Cardboard VR.
Полифил поддерживается вместе со спецификацией и обновляется в соответствии с ней. Кроме того, он обновляется для поддержания совместимости с браузерами по мере изменения их поддержки WebXR и других связанных с ним технологий и реализации полифила со временем.
Внимательно прочтите файл readme; полифил выпускается в нескольких версиях в зависимости от степени совместимости с новыми функциями JavaScript в ваших целевых браузерах.
Использование эмулятора
Хотя это несколько неудобно по сравнению с использованием реального шлема, это позволяет экспериментировать с кодом WebXR и разрабатывать его на настольном компьютере, где WebXR обычно недоступен. Это также позволяет выполнить некоторые базовые тесты перед переносом кода на реальное устройство. Однако имейте в виду, что эмулятор ещё не полностью эмулирует весь API WebXR, поэтому вы можете столкнуться с непредсказуемыми проблемами. Снова внимательно прочтите файл readme и убедитесь, что вы знакомы с ограничениями перед началом.
Важно: Вы всегда должны тестировать свой код на реальном оборудовании AR и/или VR перед выпуском или отправкой продукта! Эмулированные, имитированные или полифил-среды не являются надлежащей заменой реальному тестированию на физических устройствах.
Получение расширения
Загрузите эмулятор API WebXR для вашего браузера ниже:
Исходный код расширения также доступен на GitHub.
Проблемы и заметки по эмулятору
Хотя это не место для полной статьи об этом расширении, есть несколько конкретных моментов, которые стоит упомянуть.
В версии 0.4.0 расширения, объявленной 26 марта 2020 года, была добавлена поддержка дополненной реальности (AR) через модуль WebXR AR, который приближается к стабильному состоянию. Документация по AR появится в ближайшее время на MDN.
Другие улучшения включают обновление эмулятора для переименования интерфейса XR в XRSystem, добавление поддержки источников ввода сжатия (захвата), и добавление поддержки свойства XRInputSource profiles.
Требования к контексту
Совместимая с WebXR среда начинается с надёжно загруженного документа. Ваш документ должен быть загружен либо с локального диска (например, с помощью URL-адреса, такого как http://localhost/…), или с использованием HTTPS при загрузке страницы. Код JavaScript также должен быть загружен надёжно.
Если документ не загружен надёжно, вы не добьётесь успеха. Свойство navigator.xr даже не существует, если документ не загружен надёжно. Это также может быть связано с отсутствием совместимого оборудования XR. В любом случае, вам нужно быть готовым к отсутствию свойства xr и либо обработать ошибку должным образом, либо предоставить какой-либо вариант отката.
Откат к полифилу WebXR
Один из вариантов отката — полифил WebXR, предоставленный Рабочей группой Immersive Web, которая отвечает за процесс стандартизации WebXR. Полифил обеспечивает поддержку WebXR в браузерах, которые не имеют собственной поддержки WebXR, и сглаживает несоответствия между реализациями в браузерах, которые её имеют, поэтому он иногда полезен даже если WebXR доступен в родном виде.
Здесь мы определяем функцию getXR(), которая возвращает объект XRSystem после необязательной установки полифила, предполагая, что полифил включён или загружен с помощью предыдущего тега <script>.
let webxrPolyfill = null;
function getXR(usePolyfill) {
let tempXR;
switch (usePolyfill) {
case "if-needed":
tempXR = navigator.xr;
if (!tempXR) {
webxrPolyfill = new WebXRPolyfill();
tempXR = webxrPolyfill;
}
break;
case "yes":
webxrPolyfill = new WebXRPolyfill();
tempXR = webxrPolyfill;
break;
case "no":
default:
tempXR = navigator.xr;
break;
}
return tempXR;
}
const nativeXr = getXR("no"); // Get the native XRSystem object
const polyfilledXr = getXR("yes"); // Always returns an XRSystem from the polyfill
const xr = getXR("if-needed"); // Use the polyfill only if navigator.xr missing
Возвращённый объект XRSystem может использоваться в соответствии с документацией, предоставленной здесь на MDN. Глобальная переменная webxrPolyfill используется только для хранения ссылки на полифил, чтобы гарантировать, что он останется доступным до тех пор, пока вам больше не понадобится. Установка её в null сигнализирует о том, что полифил можно удалить из памяти, когда им больше не пользуются.
Конечно, вы можете упростить это в зависимости от своих потребностей; так как ваше приложение, вероятно, не будет часто менять использование полифила, вы можете упростить это до конкретного необходимого вам случая.
Разрешения и безопасность
Существует ряд мер безопасности, связанных с WebXR. Прежде всего, использование режима immersive-vr — который полностью заменяет представление пользователем мира — требует наличия политики разрешений xr-spatial-tracking permission policy. Кроме того, документ должен быть защищённым и в данный момент фокусированным. Наконец, вы должны вызвать requestSession() из обработчика события пользователя, например, обработчика события click.
Для получения более подробной информации о защите действий и использования WebXR см. статью Разрешения и безопасность для WebXR.
Подтверждение доступности необходимого типа сеанса
Перед попыткой создания нового сеанса WebXR часто полезно сначала проверить, поддерживает ли оборудование и программное обеспечение пользователя желаемый режим презентации. Это также можно использовать для определения того, использовать ли интерактивную или встроенную презентацию, например.
Чтобы узнать, поддерживается ли данный режим, вызовите метод XRSystem isSessionSupported(). Это возвращает промис, который разрешается в true, если данный тип сеанса доступен для использования, или в false в противном случае.
const immersiveOK = await navigator.xr.isSessionSupported("immersive-vr");
if (immersiveOK) {
// Create and use an immersive VR session
} else {
// Create an inline session instead, or tell the user about the
// incompatibility if inline is required
}
Создание и запуск сеанса
Сеанс WebXR представлен объектом XRSession. Чтобы получить сеанс, вы вызываете метод XRSystem вашего объекта requestSession(), который возвращает промис, который разрешается с объектом XRSession, если сеанс успешно создан. В общем случае, это выглядит так:
xr.requestSession("immersive-vr").then((session) => {
xrSession = session;
/* continue to set up the session */
});
Обратите внимание на параметр, переданный в requestSession() в этом фрагменте кода: immersive-vr. Эта строка определяет тип сеанса WebXR, который вы хотите установить — в данном случае, полностью погружающий виртуальную реальность. Существуют три варианта:
immersive-vr-
Полностью погружающий сеанс виртуальной реальности с использованием гарнитуры или аналогичного устройства, которое полностью заменяет окружающий мир пользователя отображаемыми вами изображениями.
immersive-ar-
Сеанс дополненной реальности, в котором изображения добавляются к реальному миру с помощью гарнитуры или аналогичного устройства. Этот вариант пока не широко поддерживается, так как спецификация AR находится в процессе разработки.
inline-
Отображение XR-изображений на экране в контексте окна документа.
Если сеанс не может быть создан по какой-либо причине, например, из-за политики ограничений или отказа пользователя предоставить разрешение на использование гарнитуры, промис отклоняется. Таким образом, более полная функция, которая запускает и возвращает сеанс WebXR, может выглядеть так:
async function createImmersiveSession(xr) {
session = await xr.requestSession("immersive-vr");
return session;
}
Эта функция возвращает новый объект XRSession или вызывает исключение, если при создании сеанса произошла ошибка.
Настройка сеанса
В дополнение к режиму отображения метод requestSession() может принимать необязательный объект с параметрами инициализации для настройки сеанса. В настоящее время единственный настраиваемый аспект сеанса — это выбор пространств отсчета, используемых для представления координатной системы мира. Вы можете указать необходимые или необязательные пространства отсчета, чтобы получить сеанс, совместимый с требуемыми или предпочтительными пространствами отсчета.
Например, если вам нужно пространство отсчета unbounded, вы можете указать его как обязательное свойство, чтобы гарантировать, что полученный сеанс может использовать неограниченные пространства:
async function createImmersiveSession(xr) {
session = await xr.requestSession("immersive-vr", {
requiredFeatures: ["unbounded"],
});
return session;
}
С другой стороны, если вам нужен сеанс встроенного типа и вы предпочитаете пространство отсчета local, вы можете сделать это так:
async function createInlineSession(xr) {
session = await xr.requestSession("inline", {
optionalFeatures: ["local"],
});
return session;
}
Эта функция createInlineSession() будет пытаться создать встроенный сеанс, совместимый с пространством отсчета local. Когда вы готовы создать пространство отсчета, вы можете попытаться создать локальное пространство, и если это не удастся, перейти к пространству отсчета viewer, которое должно поддерживаться всеми устройствами.
Подготовка нового сеанса к использованию
После успешного разрешения промиса, возвращаемого методом requestSession(), вы имеете доступный сеанс WebXR. Затем вы можете подготовиться к использованию сеанса и начать анимацию.
Основные действия (или те, что могут понадобиться) для завершения настройки сеанса включают:
- Добавление обработчиков для событий, которые вам нужно отслеживать. Скорее всего, это включает в себя событие
endкак минимум, чтобы вы могли обнаружить завершение сеанса. - Если вы используете XR-контроллеры ввода, отслеживайте событие
inputsourceschange, чтобы обнаружить добавление или удаление XR-контроллеров ввода, а также различные события действий select и squeeze action. - Возможно, вам нужно отслеживать событие
XRSystemdevicechange, чтобы получить уведомление о изменении набора доступных устройств погружения. - Получите контекст WebGL для холста, в который вы планируете рендерить кадры, вызвав метод
HTMLCanvasElementgetContext()на целевом контексте. - Настройте свои данные и модели WebGL и подготовьтесь к рендерингу сцены.
- Установите контекст WebGL в качестве источника для XR-системы, создав слой
XRWebGLLayerи установив значение свойства сеансаrenderStatebaseLayer. - Выполните вычисления для начального положения и масштаба ваших объектов при необходимости.
- Запустите цикл рендеринга кадров.
В общем виде код для выполнения этой окончательной настройки может выглядеть примерно так:
async function runSession(session) {
session.addEventListener("end", onSessionEnd);
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl", { xrCompatible: true });
// Set up WebGL data and such
const worldData = loadGLPrograms(session, "world-data.xml");
if (!worldData) {
return null;
}
// Finish configuring WebGL
worldData.session.updateRenderState({
baseLayer: new XRWebGLLayer(worldData.session, gl),
});
// Start rendering the scene
referenceSpace = await worldData.session.requestReferenceSpace("unbounded");
worldData.referenceSpace = referenceSpace.getOffsetReferenceSpace(
new XRRigidTransform(
worldData.playerSpawnPosition,
worldData.playerSpawnOrientation,
),
);
worldData.animationFrameRequestID =
worldData.session.requestAnimationFrame(onDrawFrame);
return worldData;
}
Для целей этого примера создается объект с именем worldData, который объединяет данные о мире и среде рендеринга. Он включает в себя сам объект XRSession, все данные, используемые для рендеринга сцены в WebGL, пространство отсчета мира и ID, возвращаемый методом requestAnimationFrame().
Сначала настраивается обработчик события end. Затем получен холст рендеринга, и получен контекст WebGL, указав параметр xrCompatible при вызове метода getContext().
Далее выполняются все необходимые действия для данных и настройки WebGL-рендера, прежде чем настроить WebGL на использование буфера кадра контекста WebGL в качестве собственного буфера кадра. Это делается с помощью метода XRSession updateRenderState() для установки свойства baseLayer состояния рендеринга на только что созданный объект XRWebGLLayer, содержащий WebGL-контекст.
Подготовка к рендерингу сцены
На данном этапе сам сеанс XRSession полностью настроен, поэтому мы можем начать рендеринг. Сначала нам нужно пространство отсчета, в котором будут задаваться координаты мира. Мы можем получить начальное пространство отсчета для сеанса, вызвав метод requestReferenceSpace() объекта XRSession. При вызове requestReferenceSpace() мы указываем тип пространства отсчета, которое нам нужно; в данном случае, unbounded. Вы также можете указать local или viewer, в зависимости от ваших потребностей.
Примечание: Чтобы понять, как выбрать подходящее пространство отсчета для ваших нужд, см. Выбор типа пространства отсчета.
Пространство отсчета, возвращаемое методом requestReferenceSpace(), помещает начало координат (0, 0, 0) в центр пространства. Это отлично подходит, если точка обзора игрока находится в точном центре мира. Но скорее всего, это не так. Если это так, вызывайте метод getOffsetReferenceSpace() на начальном пространстве отсчета для создания нового пространства отсчета с смещением координатной системы таким образом, чтобы (0, 0, 0) находился в позиции зрителя, а ориентация также была смещена для отображения в нужном направлении. В качестве входного значения в метод getOffsetReferenceSpace() передается объект XRRigidTransform, содержащий положение и ориентацию игрока, как указано в стандартных координатах мира.
С новым пространством отсчета, сохранённым в объекте worldData, мы вызываем метод requestAnimationFrame() сеанса, чтобы запланировать обратный вызов, который будет выполнен, когда придёт время рендерить следующий кадр анимации для сеанса WebXR. Возвращаемое значение — ID, который мы можем использовать позже для отмены запроса при необходимости, поэтому мы также сохраняем его в объекте worldData.
В конечном итоге, объект worldData возвращается вызывающей стороне, чтобы позволить главному коду получить доступ к необходимым данным позже. На этом этапе процесс настройки завершен, и мы переходим к стадии рендеринга нашего приложения. Чтобы узнать больше о рендеринге, см. статью Рендеринг и обратный вызов анимации кадров WebXR.
О практических деталях
Конечно, это был всего лишь пример. Вам не нужен объект worldData, чтобы хранить всё; вы можете хранить необходимую информацию любым удобным вам способом. У вас могут быть разные потребности или требования, которые заставят вас делать вещи по-разному или в другом порядке.
Точно так же конкретный метод загрузки моделей и другой информации, а также настройка ваших WebGL-данных (текстур, буферов вершин, шейдеров и т. д.) будут сильно отличаться в зависимости от ваших потребностей, используемых вами фреймворков и т. д.
Важные события для обслуживания сеанса
В ходе вашего сеанса WebXR вы можете получить любое из ряда событий, которые указывают на изменения состояния сеанса или сообщают вам о действиях, необходимых для правильной работы сеанса.
Обнаружение изменений состояния видимости сеанса
Когда состояние видимости сеанса XRSession изменяется (например, когда сеанс скрыт или показан, или когда пользователь сфокусировался на другом контексте), сеанс получает событие visibilitychange.
session.onvisibilitychange = (event) => {
switch (event.session.visibilityState) {
case "hidden":
myFrameRate = 10;
break;
case "blurred-visible":
myFrameRate = 30;
break;
case "visible":
default:
myFrameRate = 60;
break;
}
};
В этом примере переменная myFrameRate изменяется в зависимости от состояния видимости при его изменении. Предположительно, рендерер использует это значение для вычисления частоты рендеринга новых кадров по мере продвижения цикла анимации, тем самым рендеря реже по мере того, как сцена становится более «размытой».
Обнаружение сбросов пространства отсчета
Иногда при отслеживании позиции пользователя в мире могут возникнуть разрывы или скачки в родном начале отсчета. Наиболее распространенные сценарии, в которых это происходит, — это когда пользователь запрашивает перекалибровку своего XR-устройства или когда в потоке данных отслеживания, получаемых от XR-оборудования, происходит сбой или ошибка. Эти ситуации приводят к резкому скачку родного начала отсчета на расстояние и в направлении, необходимом для совмещения родного начала отсчета с позицией и направлением взгляда пользователя.
Когда это происходит, событие reset отправляется в XRReferenceSpace сессии. Свойство transform события представляет собой XRRigidTransform, описывающий преобразование, необходимое для повторного совмещения родного начала отсчета.
Примечание: Событие reset срабатывает в XRReferenceSpace, а не в XRSession!
Еще одной распространенной причиной событий reset является изменение геометрии ограниченного пространства отсчета (bounded-floor) по спецификации свойства XRBoundedReferenceSpace boundsGeometry.
Дополнительные распространенные причины сбросов пространства отсчета, а также более подробная информация и примеры кода приведены в документации по событию reset.
Обнаружение изменений доступного набора элементов управления вводом WebXR
WebXR поддерживает список элементов управления вводом, специфичный для системы WebXR. Эти устройства включают в себя такие компоненты, как ручные контроллеры, датчики движения, датчики движения перчаток и другие устройства обратной связи. При подключении или отключении устройства управления вводом WebXR к системе пользовательского интерфейса срабатывает событие inputsourceschange в XRSession. Это возможность уведомить пользователя об доступности устройства, начать отслеживать ввод с него, предложить параметры настройки или выполнить любые другие необходимые действия с ним.
Завершение сессии WebXR
Когда сессия виртуальной или дополненной реальности пользователя подходит к концу, сессия завершается. Завершение XRSession может произойти по нескольким причинам: сама сессия может принять решение о завершении (например, если пользователь выключит свое XR-устройство), пользователь может нажать кнопку для завершения сессии или по какой-либо другой причине, соответствующей вашей программе.
Здесь мы рассмотрим как запросить завершение сессии WebXR, так и как определить, когда сессия завершилась, независимо от того, было это сделано вами или по другим причинам.
Завершение сессии
Для корректного завершения сессии WebXR, когда вы закончили с ней, следует вызвать метод end() сессии. Этот метод возвращает promise, который вы можете использовать для отслеживания завершения операции.
async function shutdownXR(session) {
if (session) {
await session.end();
/* At this point, WebXR is fully shut down */
}
}
Когда shutdownXR() возвращается в вызывающий метод, сессия WebXR полностью и безопасно завершается.
Если вам нужно выполнить какие-то действия при завершении сессии, например, освободить ресурсы, сделайте это в обработчике события end, а не в основном коде. Таким образом, вы обрабатываете завершение независимо от того, было ли оно инициировано автоматически или вручную.
Обнаружение завершения сессии
Как уже отмечалось, вы можете определить, когда сессия WebXR завершилась — независимо от того, вызвали ли вы её метод end(), пользователь выключил гарнитуру или в системе XR произошла неразрешимая ошибка — отслеживая событие end, которое отправляется в XRSession.
session.onend = (event) => {
/* the session has shut down */
freeResources();
};
Здесь, когда сессия завершена и получено событие end, вызывается функция freeResources(), чтобы освободить ранее выделенные и/или загруженные ресурсы для обработки XR-представления. Вызывая freeResources() в обработчике события end, мы вызываем её как при нажатии пользователем кнопки, запускающей завершение, например, вызывая функцию shutdownXR() показанную выше, так и при автоматическом завершении сессии по какой-либо причине, в том числе из-за ошибки.
См. также
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/WebXR_Device_API/Startup_and_shutdown