Всем привет! VR нынче в тренде, тема злободневная, различных проектов и способов реализовывать отображение трёхмерной картинки, кажется, скоро будет миллион. Сегодня у нас в гостях Михаил Вайсман, основатель компании по разработке мобильных приложений Trinity Digital. Он вызвался рассказать про работу с библиотеками для отображения 360-градусных панорам, за что мы выражаем ему признательность. Ладно, вы все пришли сюда за интересным опытом, а не за контентом до ката. ;)
Что здесь происходит?
Привет! Представить меня уже успели, но правила есть правила. Я — Михаил Вайсман. Опыт работы с VR я приобрел, работая в Trinity Digital над проектом Airpano Virtual Travel вместе с командой Doubble. Airpano — приложение с возможностью просмотра VR фото- и видеопанорам. В процессе работы я столкнулся с проблемой: тема VR уже не первый год в тренде, но готовых библиотек, позволяющих качественно отобразить VR контент, нет. Пришлось выбирать между написанием с нуля собственного решения и применением того, что уже есть на рынке. Выбрали компромиссный вариант — взять имеющееся на рынке open source решение и дорабатывать под наши цели. У последного варианта есть свои плюсы, но и о минусах забывать не стоит. Мы попробовали три библиотеки, и вот что из этого вышло:
Krpano — библиотека на основе WebView
Мы начали работу с варианта, реализованного на основе WebView — с библиотеки krpano. Причиной выбора стали:
- Быстрая возможность интеграции в проект;
- Декларация поддержки всех Android устройства с версией не ниже 4.2;
- Универсальность решения, возможность переиспользования для разных платформ.
В реальности с библиотекой krpano все оказалось не столь радужно, потому что у krpano браузерный движок. Начать можно хотя бы с того, что не на всех устройствах есть современные движки хрома, которые могут максимально эффективно реализовывать возможности данной библиотеки. Большинство не самых топовых устройств работали медленно с панорамами исключительно из-за использования webview, а на части из них библиотека и вовсе не работала. В качестве потенциального решения мы попробовали интегрировать в приложение движки от Chrome и Firefox (сначала один, потом другой — для тестов). В какой-то степени это помогло. Работала данная штука везде, вот только…
В итоге приложение занимало более 100 мегабайт — и это не наши файлы или код, а библиотека, плюс браузерный движок в проекте. Самое обидное, что даже эти извращения не помогли значительно оптимизировать качество и скорость работы — они оставались на непримлемо низком уровне. Решающим минусом krpano стала необходимость программировать на уровне xml-конфигураций (подобное конфигурирование проходит долго и увеличивает цену разработки для каждой панорамы).
PanoramaGL — библиотека на основе OpenGL
После месяца попыток (или пыток) с krpano, перепробовав несколько видов библиотек, мы остановились на panoramagl — это библиотека работала быстрее других, и не обладала теми критичными недостатками, какие мы обнаружили в krpano. Плюс Panoramagl оказался проще во внедрении и поддерживал работу на слабых устройствах.
Но и здесь не обошлось без недоработок: например, в panorama gl была реализация только на основе activity. Нам пришлось её переписывать, добавлять поддержку фрагментов.
Кроме отсутсвия поддержки фрагментов, в panoramagl нашлось множество функций, требующих адаптации и переписывания — чтобы обеспечить поддержку наших задач: например, стерео режим для поддержки cardboard в panoramagl не реализован. При этом интегрировать Cardboard SDK в PanoramaGL невозможно — из-за того что последний использует OpenGL.
Зато с помощью данной либы достаточно просто сделать базовую, не совсем корректную версию стерео режима — картинка просто дублируется на две части экрана, без сдвига левой части относительно правой.
Так выглядит корректно реализованный стерео-режим — правая картинка сдивнута относительно левой:
А вот так выглядит стерео-режим, реализованный нами на основе в библиотеке PanoramaGL — правая картинка идентичная левой, сдвига между картинками нет:
Библиотека Panframe от mindlight
Еще в процессе доработки напильником panoramagl у нас появилась необходимость добавить видео-контент в приложение. В panoramagl такой возможности нет, и мы вновь встали перед дилеммой: написать самим библиотеку для отображения видео контента, либо использовать готовое решение. Остановились на условно-платной библиотеке Panframe.
Выбрали её за поддержку видео- и фотопанорам «из коробки», а также за хорошую скорость работы видео- и фотопанорам. Всё нормально работало даже на типичных «среднячках», не говоря уж о флагманах. Работать с ней — одно удовольствие.
Используем библиотеку для отображения видео
1. Указываем permission:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2. Инициализируем view:
PFView vrView = PFObjectFactory.view(this); //для отображения одной камеры. в качестве параметра передается activity
PFView vrView = PFObjectFactory.vr(this); //для отображения двух камер с эффектом линз для очков. в качестве параметра передается activity. работает в ландшафтной ориентации
PFAsset asset = PFObjectFactory.assetFromUri(this, Uri.parse(filename), this); //в качестве параметров передается: контекст, путь до видео, интерфейс PFAssetObserver для получения событий загрузки контента
vrView.displayAsset(asset); //указыаем контент для отображения
viewContainer.addView(vrView.getView(), 0); //добавляем view На layout. viewContainer — ViewGroup с layout
asset.play();//запускаем отображение контента
Список аргументов простой:
PFView — view объект с 360 контентом;
PFAssetObserver содержит сигнатуру метода onStatusMessage c двумя параметрами:
PFAsset — интерфейс контента с сигнатурами управляющих методов:
- void play();
- void stop();
- void pause();
- float getPlaybackTime();
- void setPLaybackTime(float var1);
- float getDuration();
- String getUrl();
- PFAssetStatus getStatus();
- int getDownloadProgress();
- void release();
- void setVolume(float var1);
Ещё есть PFAssetStatus — enum со набором статусов:
- LOADED;
- PLAYING;
- PAUSED;
- STOPPED;
- COMPLETE;
- DOWNLOADING;
- DOWNLOADED;
- DOWNLOADCANCELLED;
- ERROR;
- BUFFERING;
- BUFFERFULL.
Пример отображения изображения
С панорамными фото всё также неплохо, просто вместо PFAsset достаточно использовать метод injectImage(Bitmap image) или injectImageFromResource(int id)
При этом если задать setMode() с типом 1, данные не будут отображены:
vrView = PFObjectFactory.view(this);
vrView.injectImageFromResource(R.raw.geysers_1);
container.addView(vrView.getView(), 0);
Также библиотека поддерживает анимированные hotspot'ы:
PFHotspot hp = vrView.createHotspot(BitmapFactory.decodeResource(getResources(), R.raw.hotspot)); //R.raw.hotspot — id изображения hotspot'а
hp.setCoordinates(60, 40, 0);
hp.setClickListener(this); //в качестве параметра передается объект, реализующий интерфейс PFHotspotClickListenerPFHotspot — интерфейс с сигнатурами методов для управления
void setEnabled(boolean var1);
void setSize(float var1);
void setCoordinates(float var1, float var2, float var3); полярные координаты в градусах
void setTag(int var1);
int getTag();
void setClickListener(PFHotspotClickListener var1);
void animate(); //анимирует hotspot анимация пульсированияPFHotspotClickListener — простой интерфейс с методом onclick с параметром PFHotspot
Кое-что ещё
У PFView есть метод setMode со следующими параметрами:
mode:
0 — сферическая панорама;
1 — плоское изображение;
2 — для двух камер (для vr не надо указывать отдельно).aspect: The aspect ratio (соотношение сторон) контента для плоского изображения.
setFormat:
0 — сферическая панорама;
1 — top-down formatted сферическая панорамаДля указания метода навигации (гироскоп или тач) используется метод vrView.setNavigationMode с параметром PFNavigationMode:
MOTION — навигация с гироскопом
TOUCH — навигация тачем
Из явных минусов: исходники библиотеки обфусцированы — прочитать и расширить функциональность будет загруднительно; понять текст ошибок невозможно. В сочетании
с медленными ответами разработчика библиотеки (Мы больше трёх месяцев ждали ответ на вопрос: «Как добавить cardboard-режим») — решение panframe в качестве основной фото и видео библиотеки в приложении оказалось сложным в плане расширения для дальнейшей эксплуатации и совершенствования продукта.
VR View от Google
Библиотека для отображения VR контента от Google была представлена в марте 2016 года. Я с интересом следил за развитием библиотеки, и на конференции Google I/O 2016 обратил особое внимание на лекции по теме VR, а также поговорил с разработчиками Google насчет планов развития библиотеки. Интегрировать библиотеку в ваше продукт можно следующим образом.
Скачиваем Google VR по этой ссылке ссылке:
developers.google.com/vr/android/get-started#using_android_studio
А также забираем Release notes последних версий библиотеки:
developers.google.com/vr/android/release-notes#cardboard_sdk_for_android_v070
Google VR SDK содержит несколько aar-библиотек:
- audio
- base
- common
- commonwidget
- controller
- panowidget
- videowidget
и несколько sample проектов:
- controllerclient — пример для daydream контроллера
- simplepanowidget — пример использования 360 изображения VrPanoramaView
- simplevideowidget — пример использования 360 видео VrVideoView
- treasurehunt — пример использования GL сцены через GvrView
Для применения VrPanoramaView в нашем проекте выполняем следующие шаги:
- Импортируем библиотеки: common, commonwidget, panowidget;
- Добавляем view (com.google.vr.sdk.widgets.pano.VrPanoramaView) в layout;
- Вызываем метод VrPanoramaView.loadImageFromBitmap, принимающий в качестве аргументов:
Public класс VrPanoramaView наследуется от VrWidgetView и содержит VrPanoramaRenderer и VrPanoramaEventListener, public класс VrWidgetView наследуется от FrameLayout и содержит GL-сцену и кнопки управления.
Ещё нам потребуются вот эти ребята:
- VrPanoramaEventListener, унаследованный от VrEventListener, простой класс, содержащий несколько методов;
- onLoadSuccessJni;
- onLoadErrorJni;
- onLoadSuccess — если Bitmap был успешно загружен, иначе — onLoadError;
- onClick;
- onDisplayModeChanged;
Класс VrPanoramaRenderer с пакетным доступом наследуется от VrWidgetView и реализует загрузку Bitmap, после чего вызывает native arm библиотеку panorenderer из состава panowidget.aar.
Public abstract VrWidgetView реализует интерфейс Renderer и вызывает native методы по событиям Renderer — интерфейса из состава openGl c сигнатурами методов onSurfaceCreated, onSurfaceChanged, onDrawFrame.
Таким образом можно быстро (в три шага) развернуть приложение, поддерживающее отображение простого растрового (Bitmap) изображения с возможностью просмотра в MONO и STEREO режиме с интерфейсом Google. Однако, если необходимо реализовать динамический контент, например, отображение маркеров поверх Bitmap, чтение камеры, подписаться на renderer события, то VrPanoramaView не предоставляет данные возможности, так что для расширения функциональности вам придётся править native библиотеку panorenderer.
Для нашего проекта в GVR не хватало поддержки перехода между панорамами через метки, установленные на той или иной точки панорам. Разработчики VR View предлагают отправлять feature requests на нужные вам фичи в репозиторий библиотеки на github. Главная интрига, будет ли google развивать библиотеку на уровень выше, чем MVP и поддерживать функциональность для сложных проектов. Ответа мы пока не знаем, но уже сейчас можно за 10 минут добавить хорошо работающие VR-возможности в ваш проект, год назад такое было сложно представить.
Выводы
Рынок VR библиотек пока не поспевает за спросом, но лёд уже тронулся, состав отправился и всё такое. Процесс не остановить: разработчикам стали доступные первые неплохие библиотеки для технических задач в VR проектах, готовые к использованию «из коробки». Ниже — краткое резюме плюсов и минусов рассмотренных решений:
Библиотека | Плюсы | Минусы |
KRpano | Быстрая интеграция в проект;
Кроссплатформенность. |
Медленная работа на старых устройствах;
Полный отказ работы на ряде устройств. |
PanoramaGL | Относительно быстрая интеграция в проект;
Поддержка не только самой базовой функциональности, но и, например, перехода между панорамами; Не плохая скорость работы панорам на медленных устройствах. |
Давно не поддерживается (последний коммит был два года назад);
Некорректная реализация стерео-режима; Отсутствие поддержки Cardboard SDK. |
Panframe | Поддержка 360-градусных видеопанорам;
Хорошая скорость работы панорам на медленных устройствах. |
Закрытость исходных кодов;
Условно-платность; Медленные/отсутсвующие ответы саппорта. |
VR View | Быстрая возможность интеграции;
Базовые возможности MVP; Хорошая скорость работы панорам на медленных устройствах Поддержка Google; Возможность оставить feature request на добавление необходимых фич. |
Библиотека пока что не поддерживает функции, выходящие за рамки MVP
(например, наложение картинки поверх панорамы, переход между панорамами). |
Выводы — если вам нужно быстро интегрировать VR в ваше приложение, будь то специфически заточенный под VR проект, или любое другое приложение, где было бы интересно добавить VR — вы можете использовать VR View от Google.
Если в вашем проекте нужна поддержка сложных эффектов и продвинутых возможностей (наложение картинки, переходы и т.п.) — используйте конфигурацию Panframe + Panoramagl, но будьте готовы к тому, что придется изрядно попотеть и дописать эти библиотеки под нужды вашего проекта. Альтернатива — дожидаться развития GVR. Сделайте feature request нужных вам функций — высока вероятность, что их добавит либо сам Google в следующих релизах, либо сторонние разработчики, Так как библиотека выложена, как open source проект.
На этом всё, спасибо за внимание, если есть вопросы — задавайте, буду рад ответить.
Автор: Google