javascript

VR/AR в браузере. Как быстро влиться и сделать свое первое приложение, используя WebVR API

  • четверг, 23 марта 2017 г. в 03:13:12
https://habrahabr.ru/company/tuturu/blog/324570/
  • Разработка под AR и VR
  • JavaScript
  • Блог компании Туту.ру




Виртуальная и дополненные реальности активно развиваются и начинают предъявлять права на захват мира. Причем применение этих технологий разнообразно и не ограничивается только играми. А с появлением инструментов для работы с 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 — информация о кадре сцены для проекции на отдельный глаз.


Полный список об объектах можно получить в следующих источниках:


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 в той или иной мере поддерживается в следующих браузерах:
  1. Microsoft Edge в Hololens;
  2. Mozilla Servo в HTC Vive;
  3. Firefox Nightly;
  4. Chrome Canary (нужно включить флаг chrome://flags/#enable-webvr);
  5. Chrome Canary for Android;
  6. Chrome for Android (включая Daydream устройства, версия 56+);
  7. Chromium на HTC Vive, Oculus, Android;
  8. Chromium WebVR Build (https://webvr.info/get-chrome/);
  9. Samsung Internet (Samsung GR VR);
  10. Oculus Carmel (это WebVR браузер, доступен так же в Samsung GR VR);
  11. iOS Chrome (с полифилами на момент написания статьи);
  12. iOS Safari (с полифилами на момент написания...);
  13. Chrome (с полифилами на момент...);
  14. 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, то в оффлайн офисе можно было бы разместить информационные стенды и различные плюхи-приколюхи, для развлечения клиентов. Если брошюру клиент может пропустить, потому что реакция на рекламную макулатуру не всегда позитивна, то ту же рекламу, показанную через призму новых технологий, клиент может с удовольствием воспринять и остаться доволен.

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

Ссылки по теме