Виртуальная и дополненные реальности активно развиваются и начинают предъявлять права на захват мира. Причем применение этих технологий разнообразно и не ограничивается только играми. А с появлением инструментов для работы с VR/AR технологиями в браузере интерес возрос еще больше. Уже сейчас можно экспериментировать и создавать MVP(Minimum Viable Product) веб-проекты с помощью JavaScript API, которое так и называется — WebVR. Давайте разбираться, что это и как с этим работать. И можно ли обойтись без этого самого WebVR API?
Эта статья носит ознакомительный характер, и она является первой в серии статей про WebVR API и Web AR реализации, которые я планирую. Если тема зайдет, то продолжу развивать ее, показывая уже более конкретные примеры с использованием различных техник и фреймворков. Я хочу поделиться своим личным опытом и обсудить в статье базовые принципы работы с WebVR, как сделать Web AR, что делать если у вас iOS, и рассмотрим устройства, под которые можно все это разрабатывать уже сегодня.
В Туту.ру я работаю в должности системный архитектор подразделения Frontend. Занимаюсь различными RnD (Research and Development ) задачами. Тема VR/AR — не пересекается напрямую с моей работой, но мы уже думаем в компании над применением данной технологии в контексте туристического бизнеса. Меня всегда манят технологии будущего и мне очень нравится Frontend стек. Скорее мне нравится сама идея того, что все можно делать в браузере. Пусть пока это и накладывает ряд ограничений и доставляет некоторые трудности. Но они все решаемы, а через какое-то время и вовсе нивелируются.
Откуда ноги растут
Саму спеку начали писать еще в 2014 году, но первая версия была представлена в начале 2016 года компанией Mozilla. Тогда была представлена первая полноценная черновая спецификация WebVR API. Эта спецификация описывает программный интерфейс для работы с различными VR устройствами типа Oculus Rift и им подобным (собственно данная спека и писалась изначально под окулус). Данная спецификация разработана при участии Брэндона Джонса (Brandon Jones) из компании Google и Джастина Роджерса (Justin Rogers) из Microsoft.
Поддержка новой спецификации WebVR уже реализована в Firefox и мобильном Chrome (точнее про степень реализации и нюансы будет сказано далее). А это значит, что уже сегодня можно свободно экспериментировать и пробовать технологию завтрашнего будущего. Если ваш браузер стар, странен или не обновляется — для всего этого есть полифилы.
Зачем нужен WebVR API?
WebVR API — это программный интерфейс для работы с устройствами. Он ничего не знает про 3D графику. Работа с графикой, отрисовка сцены, установка источников света и все прочее лежит на суровых плечах программистов. WebVR API всего лишь позволяет абстрагировать доступ к устройствам. Данное API предоставляет инструменты для рендеринга картинки, для получения информации об устройстве, его возможностях и технических характеристиках, но саму картинку и 3D мир нужно рисовать, используя уже ставшие привычными веб-технологии, такие как: HTML, CSS, WebGL, Canvas, etc…
WebVR API предоставляет нам несколько основных объектов для работы:
- Navigator — позволяет получить список девайсов, определить активный;
- VRDisplay — сообщает одет ли хедсет на голову, информация о кадрах, глазах;
- VRPose — информация о позиции и ориентации девайса, скорости передвижения и направления;
- VREyeParameters — информация о том, как рендерить видео в каждый отдельный глаз;
- VRFrameData — информация о кадре сцены для проекции на отдельный глаз.
Полный список об объектах можно получить в следующих источниках:
- developer.mozilla.org/en-US/docs/Web/API/WebVR_API
- msdn.microsoft.com/en-us/library/mt806281(v=vs.85).aspx
Hello VR world
Простой код бойлерплейта для работы с VR устройством на JavaScript выглядит так:
navigator.getVRDisplays().then(displays => {
if (displays.length < 1) {
return console.warn('No displays found!');
}
displays.forEach(vrDisplay => { buildVRDisplay(vrDisplay) })
});
function buildVRDisplay(vrDisplay) {
// получаем доступ к информации о дисплее
const frameData = new VRFRameData;
// содержит информацию о проеуции левого и правого глаза
vrDisplay.getFrameData(frameData);
// получить параметры конкретного глаза
const rightEye = vrDisplay.getEyeParameters('left');
const onAnimationFrame = () => {
// здесь ваша логика анимации
vrDisplay.requestAnimationFrame(onAnimationFrame);
};
vrDisplay.requestAnimationFrame(onAnimationFrame);
}
Данный код должен дать представление, как выглядит работа с использованием WebVR API. Более подробные примеры рассмотрим далее, а сейчас давайте поговорим про поддержку браузерами и про устройства.
С какими устройствами работать?
Когда говорят про VR, то вспоминают Oculus Rift и им подобные. Если мы говорим про WebVR, логично предположить, что мы рассчитываем на разработку под браузеры.
Самые доступные и популярные на сегодня устройства — это так называемые Cardboard девайсы (или еще их называют VRBox’ы). Мобильный VR список устройств можно перечислить по пальцам:
- Google Cardboard;
- Google Daydream (новое исполнение Cardboard устройств);
- различные китайские VRBox’ы под все модели смартфонов;
- Samsung GR VR (на мой взгляд, удачное сочетание возможностей VR шлема и Cardboard устройства, да еще и с доступом в Oculus Store).
Noname VRBox
Просто бокс с линзами. Бывают в очень разных исполнениях, вплоть до копий Samsung GR VR, но с подключением через Bluetooth любой модели телефона. Делаются в разных форм факторах и разных исполнениях. Можно докупить Bluetooth джойстик, который будет работать с любым смартфоном на любой ОС. Качество очень даже хорошее, к линзам нет претензий. Вся соль кроется в смартфоне, размере дисплея и разрешении экрана.
Xiaomi VRBox
Особенности: наличие механической кнопки-манипулятора, работающей по принципу стилуса, который тапает в “слепую зону” экрана в области переносицы (инженеры жгут :)). Застежка на молнии.
Samsung GR VR
Особенности: подключается к боксу через USB, наличие сенсорной панели и кнопок на шлеме. В момент подключения активируется функционал Oculus, и телефон прикидывается Oculus-совместимым устройством.
Google Cardboard
Особенности: наличие кнопки, выполненной в виде небольшого круглого магнита. В приложении считывается изменение магнитного поля встроенным магнитометром, и данное изменение засчитывается как действие по кнопке в Cardboard-совместимых устройствах.
Google Daydream
Особенности: основное отличие от Cardboard — более основательный и удобный хеадсет и наличие геймпада. Это уже полноценный VRBox, не из картона :)
Вот про них мы и будем говорить. Если у вас есть смартфон с браузером Chrome и какая-то вариация VRBox’a, то вы уже сможете проверить в действии WebVR. Лучший вариант — Chrome Canary или Samsung Internet. Конечно, надо сказать про исключение — это iOS. Но там VR можно использовать через полифилы, поэтому демки также будут доступны и с iOS устройств (зависит от реализации). Опять же, надо понимать, что WebVR API — это не про 3D графику, и сделать WebVR мир можно и под iOS без использования этого самого VR API (ну или с полифилами).
Взглянуть на VR мир можно и через окно десктопного браузера, с которым обычно происходит разработка. Сначала мир строится без шлемов, а затем уже добавляются возможности разбиения картинки под оптическую пару. Для этого используем либо Firefox Nightly, либо Chrome вот с таким вот плагином: WebVR API Emulation. Либо Chrome Canary с включенной поддержкой, либо специальную сборку Chromium… Ну вы поняли :)
Что с поддержкой в браузерах?
Итак, на сегодня WebVR API в той или иной мере поддерживается в следующих браузерах:
- Microsoft Edge в Hololens;
- Mozilla Servo в HTC Vive;
- Firefox Nightly;
- Chrome Canary (нужно включить флаг chrome://flags/#enable-webvr);
- Chrome Canary for Android;
- Chrome for Android (включая Daydream устройства, версия 56+);
- Chromium на HTC Vive, Oculus, Android;
- Chromium WebVR Build (https://webvr.info/get-chrome/);
- Samsung Internet (Samsung GR VR);
- Oculus Carmel (это WebVR браузер, доступен так же в Samsung GR VR);
- iOS Chrome (с полифилами на момент написания статьи);
- iOS Safari (с полифилами на момент написания...);
- Chrome (с полифилами на момент...);
- Firefox (с полифилами на...).
Полный список с таблицами совместимости можно посмотреть по ссылке webvr.rocks
Не забываем включить поддержку WebVR API.
Полифилы и вспомогательные библиотеки
Если ваше устройство не поддерживает WebVR API, то можно воспользоваться полифилом, который можно подключить на страницу, либо использовать специальное расширение для браузера. Ссылка на полифил: github.com/googlevr/webvr-polyfill
WebVR API Emulation for Chrome
Поставить можно по сслылке: chrome.google.com/webstore/detail/webvr-api-emulation/gbdnpaebafagioggnhkacnaaahpiefil
Про этот плагин стоит сказать пару слов отдельно. Он не просто добавляет эмуляцию WebVR API, но также позволяет делать различные манипуляции и интегрируется в DevTools.
WebVR-UI
Если ваше устройство не является VR девайсом (а браузер в смартфоне — это всего лишь браузер), то вы можете воссоздать VR интерфейс для переключения в режим хедсета, используя библиотеку webvr-ui. С помощью этой библиотеки вы сможете сделать красивый UI интерфейс с кнопками переключения в VR режим. Ссылка на проект: github.com/googlevr/webvr-ui
Добавив несколько строк кода:
var renderer = new THREE.WebGLRenderer();
var options = {};
var enterVR = new webvrui.EnterVRButton(renderer.domElement, options);
document.body.appendChild(enterVR.domElement);
Эта библиотека совместима с фреймворком A-Frame, что очень классно. Вам достаточно добавить всего лишь атрибут в ваш код:
<a-scene webvr-ui>
...
</a-scene>
И у вас будет доступен удобный VR интерфейс. Если будет доступен WebVR, то будет предложено перейти в VR режим, иначе будет предложено попробовать запустить ваше веб-приложение в режиме демонстрации без VR режима.
Данная библиотека гибко настраиваемая, так что вы можете делать свои кастомные кнопки.
Чем нам мир построить?
Чтобы влиться в мир VR, нужно научиться работать с 3D-графикой. Это кажется сложной задачей, если вы будете осваивать WebGL с нуля. Но проще всего взять готовые библиотеки, такие как Threejs, D3.js или специализированные фреймворки для работы с VR (такие, как A-Frame от команды Mozilla). Уже даже есть превью релиз React VR для поклонников React и всего, что выходит из недр FB. Но это тема отдельной статьи.
Верстаем VR мир
Используя фреймворк A-Frame, можно сверстать VR мир с минимальными усилиями. A-Frame — это HTML-фреймворк для создания веб-приложений и сайтов под виртуальную реальность. Если смотреть на результат в браузере, то это выглядит как веб-страница, которая представляет 3D- изображение с возможностью навигации и взаимодействия с объектами. Вся картинка отрисовывается при помощи WebGL, а основная задача фреймворка — это предоставить простой инструмент, который бы позволил создавать трехмерный мир привычным для фронтендеров, похожим на HTML-разметку, способом. Сам фреймворк базируется на Threejs и является более высокоуровневой надстройкой.
Фреймворк A-Frame создает 3D-сцену через набор геометрических примитивов. Вы можете также добавлять более сложные компоненты и даже дописывать свои, если базовых вам уже не хватает. Для объектов сцены доступны типичные геометрические свойства, такие как: местоположение, вращение, масштабирование, кроме того, можно описывать расположение камер и источников света.
У A-Frame есть хороший инспектор, позволяющий делать отладку 3D мира.
Много хороших примеров можно посмотреть по ссылке: aframe.io/examples
Они просты для понимания и, изучив их, можно очень быстро сделать свой первый «Hello VR world». A-Frame можно также использовать и для AR разработки. Если углубляться в A-Frame, то тут опять же можно наговорить на целую отдельную статью (которая появится, если будет интерес к данной тематике).
React VR
ReactVR — это фреймворк, базирующийся на Reactjs. Если будет интерес и пост наберет хороший рейтинг, сделаю отдельно статью про это… Пока просто оставлю ссылку: developer.oculus.com/blog/introducing-the-react-vr-pre-release
Если статья наберет хороший рейтинг, тем самым показав что читателям интересна эта тема, то я сделаю отдельно статью про это.
Можем ли обойтись без WebVR?
Как уже было сказано выше, сам WebVR API нужен только для работы с VR устройствами, а точнее, для получения характеристик о дисплеях и для рендеринга картинки. Но если мы говорим про Mobile VR, то физически мы работаем с 1м экраном. Но мы можем эмулировать стереопару, и WebVR API нам может помочь абстрагироваться и инкапсулировать работу с экраном таким образом, что мы будем как бы работать с двумя физическими дисплеями. При этом наш код будет кроссплатформенным, и мы сможем наше WebVR приложение запускать на Oculus в браузере Carmel, к примеру.
Пример 3D мира на Threejs с использованием WebVR API
Здесь покажу пример работы с бойлерплейтом, благодаря которому можно делать WebVR проекты, которые заведутся на всех браузерах. Даже в iOS.
Готовое демо: webvr.majorov.su/sample1
<!DOCTYPE html>
<html lang="en">
<head>
<title>Web VR boilerplate demo</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
<style>
body {
width: 100%;
height: 100%;
background-color: #000;
color: #fff;
margin: 0px;
padding: 0;
overflow: hidden;
}
/* Position the button on the bottom of the page. */
#ui {
position: absolute;
bottom: 10px;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
font-family: 'Karla', sans-serif;
z-index: 1;
}
a {
color: white;
}
</style>
</head>
<body>
<div id="ui">
<div id="vr-button"></div>
<a id="magic-window" href="#">Try it without a headset</a>
</div>
</body>
<script>WebVRConfig = { BUFFER_SCALE: 0.5 }</script>
<script src="node_modules/es6-promise/dist/es6-promise.min.js"></script>
<script src="node_modules/three/build/three.min.js"></script>
<script src="node_modules/three/examples/js/controls/VRControls.js"></script>
<script src="node_modules/three/examples/js/effects/VREffect.js"></script>
<script src="node_modules/webvr-polyfill/build/webvr-polyfill.min.js"></script>
<script src="node_modules/webvr-ui/build/webvr-ui.min.js"></script>
<script>
var lastRenderTime = 0;
// Текущий активный VRDisplay.
var vrDisplay;
var boxSize = 5;
// Настройки для THREE.Objects.
var scene;
var cube;
var controls;
var effect;
var camera;
// VR UI Кнопка
var vrButton;
function onLoad() {
// Настройка three.js WebGL рендера.
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
// Вставляем наш слой для рендера в DOM
document.body.appendChild(renderer.domElement);
// Создаем сцену
scene = new THREE.Scene();
// Создаем камеру
var aspect = window.innerWidth / window.innerHeight;
camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 10000);
controls = new THREE.VRControls(camera);
controls.standing = true;
camera.position.y = controls.userHeight;
// Добавляем VR stereo рендер
effect = new THREE.VREffect(renderer);
effect.setSize(window.innerWidth, window.innerHeight);
var loader = new THREE.TextureLoader();
loader.load('img/box.png', onTextureLoaded);
// Создаем 3D объекты
var geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
var textureLoader = new THREE.TextureLoader();
var texture0 = textureLoader.load('img/1.jpg');
var texture1 = textureLoader.load('img/1.jpg');
var texture2 = textureLoader.load('img/0.jpg');
var texture3 = textureLoader.load('img/0.jpg');
var texture4 = textureLoader.load('img/2.jpg');
var texture5 = textureLoader.load('img/0.jpg');
var materials = [
new THREE.MeshBasicMaterial({map: texture0}),
new THREE.MeshBasicMaterial({map: texture1}),
new THREE.MeshBasicMaterial({map: texture2}),
new THREE.MeshBasicMaterial({map: texture3}),
new THREE.MeshBasicMaterial({map: texture4}),
new THREE.MeshBasicMaterial({map: texture5})
];
var material = new THREE.MultiMaterial(materials);
cube = new THREE.Mesh(geometry, material);
cube.position.set(0, controls.userHeight, -1);
scene.add(cube);
window.addEventListener('resize', onResize, true);
window.addEventListener('vrdisplaypresentchange', onResize, true);
// Initialize the WebVR UI.
var uiOptions = {
color: 'black',
background: 'white',
corners: 'square'
};
vrButton = new webvrui.EnterVRButton(renderer.domElement, uiOptions);
vrButton.on('exit', function () {
camera.quaternion.set(0, 0, 0, 1);
camera.position.set(0, controls.userHeight, 0);
});
vrButton.on('hide', function () {
document.getElementById('ui').style.display = 'none';
});
vrButton.on('show', function () {
document.getElementById('ui').style.display = 'inherit';
});
document.getElementById('vr-button').appendChild(vrButton.domElement);
document.getElementById('magic-window').addEventListener('click', function () {
vrButton.requestEnterFullscreen();
});
}
function onTextureLoaded(texture) {
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(boxSize, boxSize);
var geometry = new THREE.BoxGeometry(boxSize, boxSize, boxSize);
var material = new THREE.MeshBasicMaterial({
map: texture,
color: 0x01BE00,
side: THREE.BackSide
});
skybox = new THREE.Mesh(geometry, material);
skybox.position.y = boxSize / 2;
scene.add(skybox);
setupStage();
}
// Request animation frame loop function
function animate(timestamp) {
var delta = Math.min(timestamp - lastRenderTime, 500);
lastRenderTime = timestamp;
cube.rotation.y += delta * 0.0006;
if (vrButton.isPresenting()) controls.update();
effect.render(scene, camera);
vrDisplay.requestAnimationFrame(animate);
}
function onResize(e) {
effect.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
function setupStage() {
navigator.getVRDisplays().then(function (displays) {
if (displays.length > 0) {
vrDisplay = displays[0];
if (vrDisplay.stageParameters) {
setStageDimensions(vrDisplay.stageParameters);
}
vrDisplay.requestAnimationFrame(animate);
}
});
}
function setStageDimensions(stage) {
var material = skybox.material;
scene.remove(skybox);
var geometry = new THREE.BoxGeometry(stage.sizeX, boxSize, stage.sizeZ);
skybox = new THREE.Mesh(geometry, material);
skybox.position.y = boxSize / 2;
scene.add(skybox);
cube.position.set(0, controls.userHeight, 0);
}
window.addEventListener('load', onLoad);
</script>
</html>
В следующих статьях можем более подробно разобраться в деталях разработки под WebVR. Ссылка на гитхаб с бойлерплейтом github.com/borismus/webvr-boilerplate
А что там с Web AR ?
Web AR(Augmented Reality) — также возможно создавать в браузере. Но «WebAR API» не существует, это просто обозначение дополненной реальности, реализованной на веб-технологиях.
Технически вся работа точно такая же, как и работа с WebVR, но дополнительно вы получаете видеопоток с веб-камеры, используя WebRTC. Дополнительно пишите логику покадровой обработки, для поиска нужных объектов. А далее, как и в случае с WebVR — уже рисуете 3D сцену на фоне видеопотока. И, надо сказать, что AR не обязательно подразумевает наличие хеадсета. Вспомним “Pockemon GO!” — это AR проект, но без VR шлема. Отсюда следует, что, чтобы создать AR проект, не обязательно иметь VR шлем. Но при этом VR и AR понятия могут пересекаются по некоторым параметрам и технологиям.
Что с поддержкой?
Здесь все упирается, в первую очередь, в поддержку WebRTC. Поэтому можно сказать, что Web AR можно реализовать на всех Android устройствах. В iOS — нет, но если очень хочется, то…
Как быть с iOS устройствами?
Если речь идет о WebVR — то в iOS устройствах все можно реализовать через полифилы (или вовсе обойтись без WebVR API, описывая все самостоятельно и реализуя отслеживание действий через акселерометр и прочие датчики, самому бить картинку и эмулировать два дисплея). Если говорить про AR, тут все плохо, так как нет поддержки WebRTC. Но есть такой проект как Argonjs. Это проект, который состоит из фреймворка, базирующегося на A-Frame, и, внимание, специального браузера.
Argon 4 by Georgia Tech
Ссылка на проект argonjs.io
Демо argonjs.io/samples
Суть работы браузера проста: есть два слоя. Один слой — это Webkit движок, второй слой (подложка) — это вывод видеопотока с камеры. В браузере есть свой API, чем-то похоже на работу самых первых версий PhoneGap (если кто-то пробовал на заре развития этого проекта, году эдак в 2008, тогда это был специальный браузер под iPhone с расширенным JS API).
При этом Argonjs можно использовать и для VR разработки под iOS. Точнее для обкатки MVP. Если все же нужно разрабатывать кроссплатформенное приложение под VR/AR на JS, то можно смотреть в сторону React Native вкупе с React VR либо попробовать упаковать все в PhoneGap.
Важно! В AppStore есть несколько версий браузера. На данный момент надо качать версию 4, не ниже, иначе фреймворк не заведется: itunes.apple.com/us/app/argon4/id1089308600?mt=8
А что там с геймпадами?
Это дополнительная тема для изучения. Разбираемся с Bluetooth API и Gamepad API. Они есть в браузерах и поддерживаются как на десктопе так и на мобильных устройствах (опять же есть нюансы и список фаворитов среди браузеров). Изучаем документацию и учимся с ними работать. Про геймпады и взаимодействие с VR миром — это тема отдельной статьи (или даже нескольких), в двух словах не рассказать.
Юзкейсы и применение
Если говорить про Туту.ру, то у нас уже есть идеи юзкейсов для применения VR/AR на практике. К примеру, это 3D галереи для проекта Туры. Галереи 360 с небольшой доработкой можно адаптировать под VR шлемы, и все, у кого дома есть VR Box, могли бы “осмотреть” будущий отель и пляж с эффектом присутствия. Такую же технику можно применить и к 3D галереям для проекта Поезда, где можно посмотреть вагон изнутри и выбрать место прямо из VR шлема в Сапсане. Таким же образом, в будущем, можно было бы сделать и для междугородних автобусов. А для проекта экскурсии, который у нас также имеется, можно было бы делать демо-превью будущей экскурсии.
Еще один кейс — это развлечение туристов в оффлайн офисе. Пока турист ждет своей очереди к турменеджеру, вместо журналов и брошюр можно было бы положить Samsung GR VR шлемы и сделать опять же каталог путешествий, что могло бы способствовать выбору нужной турпутевки. Если развивать тему AR, то в оффлайн офисе можно было бы разместить информационные стенды и различные плюхи-приколюхи, для развлечения клиентов. Если брошюру клиент может пропустить, потому что реакция на рекламную макулатуру не всегда позитивна, то ту же рекламу, показанную через призму новых технологий, клиент может с удовольствием воспринять и остаться доволен.
В общем, есть полет для фантазии. Это я рассказал юзкейсы именно в контексте нашей компании, и они не все здесь перечислены. Если же говорить глобально, то юзкейсов можно придумать с вагон и маленькую тележку :)
Ссылки по теме
- msdn.microsoft.com/en-us/library/mt806281(v=vs.85).aspx
- developer.mozilla.org/en-US/docs/Web/API/WebVR_API
- mozvr.com
- aframe.io
- webvr.info
- webvr.rocks
Автор: Туту.ру