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

Стандарт заменил ранний интерфейс и покрывает оба вида реальности

Первая попытка дать браузеру доступ к устройствам погружения появилась весной 2014 года. Её предложил разработчик одной из компаний, и работа шла силами нескольких команд. Через два года увидела свет первая версия предложения.

Ограничение раннего интерфейса было заложено в названии: он умел работать только с виртуальной реальностью. Дополненная реальность, где изображение накладывается на реальный мир, оставалась за рамками.

В 2018 году появился новый стандарт, который заменил прежний и охватил оба направления сразу. Рабочая группа по иммерсивным технологиям стала официальной двадцать четвёртого сентября 2018 года. Спецификация основного интерфейса получила статус кандидата в рекомендации, а отдельные модули продолжают разрабатываться.

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

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

Запуск сцены требует защищённого соединения и действия пользователя

Первое ограничение знакомо по другим чувствительным возможностям браузера: сцена работает только в защищённом контексте. Второе ограничение специфично для иммерсивных сценариев и связано с защитой от внезапного перехода.

Браузер требует, чтобы запрос на полноценную сессию исходил от действия человека. Проще говоря, нажать нужно самому. Автоматический переход в полноэкранный трёхмерный режим при загрузке страницы запрещён, и это правильно: иначе случайно открытая ссылка уводила бы пользователя в шлем без предупреждения.

Работа начинается с проверки поддержки и запроса сессии:

async function startImmersive(mode) {
  if (!navigator.xr) {
    return null;
  }

  const supported = await navigator.xr.isSessionSupported(mode);
  if (!supported) {
    return null;
  }

  const session = await navigator.xr.requestSession(mode, {
    requiredFeatures: ["local-floor"],
    optionalFeatures: ["hit-test", "anchors"],
  });

  return session;
}

document.querySelector("#enter").addEventListener("click", async () => {
  const session =
    (await startImmersive("immersive-ar")) ||
    (await startImmersive("immersive-vr"));
  if (!session) {
    console.log("Погружение недоступно на этом устройстве");
    return;
  }
  await session.end();
});

Обратите внимание на разделение требований на обязательные и необязательные. Если запросить всё сразу как необходимое, сцена не запустится на устройстве, где чего-то не хватает. Обязательным стоит оставлять минимум.

Второй важный момент в примере: попытка сначала дополненной реальности, потом виртуальной. Это частый практический приём, поскольку очки и телефоны поддерживают разные режимы.

Кадр рисуется в цикле и должен укладываться в жёсткий бюджет

В отличие от обычной страницы, где перерисовка происходит по необходимости, иммерсивная сцена рисуется непрерывно. Каждый кадр нужно успеть подготовить за доли секунды, иначе изображение дёргается, а у пользователя возникает неприятное ощущение.

Целевая частота определяется устройством. Для комфортного восприятия она обычно составляет от семидесяти двух до ста двадцати кадров в секунду. Несложно посчитать бюджет одного кадра: при девяноста герцах на всё отводится примерно одиннадцать миллисекунд. Внутри этого времени нужно успеть обновить логику, собрать данные о положении головы и рук, отрисовать сцену дважды, по разу для каждого глаза.

Отсюда следуют практические ограничения. Тяжёлые вычисления в цикле недопустимы. Загрузка данных идёт заранее или в отдельном потоке. Количество полигонов и текстур подбирается с запасом.

let session = null;
let referenceSpace = null;
let renderer = null;

async function startRenderLoop(newSession, gl, xrRenderer) {
  session = newSession;
  renderer = xrRenderer;
  referenceSpace = await session.requestReferenceSpace("local-floor");

  renderer.setSize(gl.canvas.width, gl.canvas.height, false);
  renderer.xr.enabled = true;
  await renderer.xr.setSession(session);

  session.addEventListener("end", () => {
    session = null;
    referenceSpace = null;
  });
}

В основе лежит опорная система координат, которая описывает, где находится пользователь и куда он смотрит. Есть базовый вариант, привязанный к положению в момент запуска, и вариант с привязкой к полу, когда нужно знать высоту. Для сцен, где объект стоит на поверхности, второй вариант обязателен.

Дополненная реальность требует понимания окружающего пространства

Виртуальность строится в пустоте, и вопросов о взаимодействии с миром не возникает. Дополненная реальность сложнее: объект должен вести себя правдоподобно по отношению к реальным поверхностям.

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

Второе это якоря. Когда место найдено, создаётся привязка, и объект держится за неё даже при движении пользователя. Без якоря модель уплывает при малейшем смещении.

Третье это освещение. Найденные поверхности и оценки освещённости позволяют подобрать яркость и тени так, чтобы объект не выглядел наклейкой. Освещение оценивается приблизительно, но даже грубая оценка заметно улучшает правдоподобие.

async function placeObject(session, frame, referenceSpace, scene) {
  const hits = frame.getHitTestResults(hitTestSource);
  if (hits.length === 0) {
    return null;
  }

  const pose = hits[0].getPose(referenceSpace);
  if (!pose) {
    return null;
  }

  const anchor = await hits[0].createAnchor();
  const model = scene.cloneModel();
  model.position.set(
    pose.transform.position.x,
    pose.transform.position.y,
    pose.transform.position.z,
  );
  anchors.set(anchor, model);
  return model;
}

Работа с якорями требует освобождения ресурсов. Если пользователь разместил десяток объектов и ушёл на другой экран, якоря стоит удалить, иначе они продолжат занимать память и вычислительное время.

Ввод строится на положении контроллеров и положении рук

Управление в иммерсивных сценах отличается от мыши и клавиатуры. Основных источников ввода три.

Первый это контроллеры. У каждого есть положение в пространстве, кнопки и, в части моделей, аналоговые стики. Состояние читается каждый кадр и передаётся приложению.

Второй это отслеживание рук. Камера распознаёт положение пальцев и жесты, и приложение может реагировать на касание виртуальных кнопок без физического устройства.

Третий это взгляд. Направление взгляда используется как указатель, а длительная задержка на объекте воспринимается как выбор. Для людей с ограниченной подвижностью это единственный доступный способ.

function readInputs(frame, inputSource, referenceSpace) {
  const state = { select: false, position: null };

  for (const source of frame.session.inputSources) {
    if (source.handedness !== inputSource) {
      continue;
    }

    const pose = frame.getPose(source.targetRaySpace, referenceSpace);
    if (pose) {
      state.position = pose.transform.position;
    }

    const gamepad = source.gamepad;
    if (gamepad && gamepad.buttons[0]) {
      state.select = gamepad.buttons[0].pressed;
    }
  }

  return state;
}

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

Ограничения устройств диктуют скромность сцен

Очки носят на голове, и это определяет жёсткие рамки.

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

Второе ограничение это поле зрения. У части устройств оно уже, чем у человеческого глаза. Объекты, расположенные у краёв обзора, плохо видны, поэтому важную информацию стоит держать в центральной зоне.

Третье ограничение это удобство длительного ношения. Полчаса в шлеме утомляют, а час способен вызвать дискомфорт. Сценарии стоит рассчитывать на короткие сеансы с перерывами.

Четвёртое ограничение это гигиена и совместное использование. Одно устройство на бригаду требует протирки и продуманного порядка передачи.

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

Область применения определяется выгодой, а не новизной

Технология выглядит эффектно, и это главная ловушка. Эффектность не заменяет пользу.

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

Инструкции на месте работы это второй сценарий. Вместо бумажной папки перед сотрудником появляется подсказка, привязанная к конкретному узлу. Для сложного оборудования это сокращает время поиска.

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

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

Есть и сценарии, где технология проигрывает. Показ сложных данных на трёхмерной модели часто хуже читается, чем обычный график на экране. Совместная работа в шлемах утомляет быстрее, чем видеозвонок. Для этих задач стоит честно признать, что привычные инструменты справляются лучше.

Практический порядок внедрения экономит бюджет

Начинать стоит с простого, и это не компромисс, а способ снизить риск.

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

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

Третий шаг это измерение пользы. Сценарий сравнивается с обычным способом выполнения той же задачи. Если время сократилось или число ошибок уменьшилось, внедрение оправдано. Если разницы нет, эффектность не поможет.

Четвёртый шаг это расширение парка. Когда сценарий отлажен и польза доказана, добавляются другие устройства и режимы.

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