Краткое руководство по Google Cardboard для Android NDK

В этом руководстве показано, как использовать Cardboard SDK для Android для создания собственных возможностей виртуальной реальности (VR).

Вы можете использовать Cardboard SDK, чтобы превратить смартфон в платформу VR. Смартфон может отображать 3D-сцены со стереоскопическим рендерингом, отслеживать движения головы и реагировать на них, а также взаимодействовать с приложениями, определяя, когда пользователь нажимает кнопку просмотра.

Для начала вы воспользуетесь HelloCardboard — демонстрационной игрой, демонстрирующей основные функции Cardboard SDK. В игре пользователи осматривают виртуальный мир, находя и собирая объекты. Он покажет вам, как:

  • Настройте среду разработки
  • Загрузите и создайте демо-приложение
  • Отсканируйте QR-код просмотрщика Cardboard, чтобы сохранить его параметры.
  • Отслеживайте движения головы пользователя
  • Рендеринг стереоскопических изображений, установив правильную матрицу проекции вида для каждого глаза.

HelloCardboard использует Android NDK. Каждый нативный метод:

  • Уникально привязан к методу класса HelloCardboardApp или
  • Создает или удаляет экземпляр этого класса

Настройте среду разработки

Требования к оборудованию:

Требования к программному обеспечению:

  • Android Studio версии 2022.1.1 «Электрический угорь» или выше
  • Android SDK 13.0 «Тирамису» (уровень API 33) или выше
  • Последняя версия платформы Android NDK.

    Чтобы просмотреть или обновить установленные SDK, перейдите в «Настройки» > «Внешний вид и поведение».

    Системные настройки > Android SDK в Android Studio.

Загрузите и создайте демо-приложение

Cardboard SDK создан с использованием предварительно скомпилированного заголовочного файла Vulkan для каждого шейдера. Инструкции по созданию заголовочных файлов с нуля можно найти здесь .

  1. Выполните следующую команду, чтобы клонировать Cardboard SDK и демонстрационное приложение HelloCardboard с GitHub:

    git clone https://github.com/googlevr/cardboard.git
  2. В Android Studio выберите « Открыть существующий проект Android Studio» , затем выберите каталог, в который были клонированы Cardboard SDK и демонстрационное приложение HelloCardboard.

    Ваш код появится в окне проекта в Android Studio.

  3. Чтобы собрать Cardboard SDK, дважды щелкните параметр сборки в папке cardboard/:sdk/Tasks/build на вкладке Gradle («Просмотр» > «Инструменты Windows» > Gradle).

  4. Запустите демонстрационное приложение HelloCardboard на своем телефоне, выбрав «Выполнить» > «Выполнить...» и выберите цель hellocardboard-android .

Сканируйте QR-код

Чтобы сохранить параметры устройства, отсканируйте QR-код во просмотрщике Cardboard:

Если пользователь нажимает «Пропустить» и ранее сохраненных параметров нет, Cardboard сохраняет параметры Google Cardboard v1 (запущен на Google I/O 2014).

Попробуйте демо

В HelloCardboard вы будете искать и собирать геодезические сферы в трехмерном пространстве.

Чтобы найти и собрать сферу:

  1. Поверните голову в любом направлении, пока не увидите плавающую фигуру.

  2. Посмотрите прямо на сферу. Это приводит к изменению цвета.

  3. Нажмите кнопку просмотра картона, чтобы «собрать» сферу.

Настройка устройства

Когда пользователь нажимает значок шестеренки для переключения средств просмотра Cardboard, вызывается метод nativeSwitchViewer . nativeSwitchViewer вызывает CardboardQrCode_scanQrCodeAndSaveDeviceParams , который открывает окно для сканирования QR-кода средства просмотра. Искажение объектива зрителя и другие параметры обновляются после сканирования QR-кода.

// Called by JNI method
void HelloCardboardApp::SwitchViewer() {
  CardboardQrCode_scanQrCodeAndSaveDeviceParams();
}

Включить эмулятор Android Studio x86

Чтобы выполнить сборку для эмулятора Android Studio x86, удалите следующую строку из файлов build.gradle в SDK и Sample :

abiFilters 'armeabi-v7a', 'arm64-v8a'

Это активирует все ABI и значительно увеличит размер создаваемого файла .aar . Дополнительную информацию см. в разделе Android ABI .

Отслеживание головы

Создать трекер головы

Трекер головы создается один раз в конструкторе HelloCardboardApp :

HelloCardboardApp::HelloCardboardApp(JavaVM* vm, jobject obj, jobject asset_mgr_obj) {
  Cardboard_initializeAndroid(vm, obj); // Must be called in constructor
  head_tracker_ = CardboardHeadTracker_create();
}

При создании VrActivity экземпляр класса HelloCardboardApp создается путем вызова метода nativeOnCreate :

public void onCreate(Bundle savedInstance) {
  super.onCreate(savedInstance);
  nativeApp = nativeOnCreate(getAssets());
  //...
}

Пауза и возобновление отслеживания головы

Чтобы приостановить, возобновить и уничтожить трекер головы, необходимо вызвать CardboardHeadTracker_pause(head_tracker_) , CardboardHeadTracker_resume(head_tracker_) и CardboardHeadTracker_destroy(head_tracker_) соответственно. В приложении «HelloCardboard» мы вызываем их в nativeOnPause , nativeOnResume и nativeOnDestroy :

// Code to pause head tracker in hello_cardboard_app.cc

void HelloCardboardApp::OnPause() { CardboardHeadTracker_pause(head_tracker_); }

// Call nativeOnPause in VrActivity
@Override
protected void onPause() {
  super.onPause();
  nativeOnPause(nativeApp);
  //...
}

// Code to resume head tracker in hello_cardboard_app.cc
void HelloCardboardApp::onResume() {
  CardboardHeadTracker_resume(head_tracker_);
  //...
}

// Call nativeOnResume in VrActivity
@Override
protected void onResume() {
  super.onResume();
  //...
  nativeOnResume(nativeApp);
}

// Code to destroy head tracker in hello_cardboard_app.cc
HelloCardboardApp::~HelloCardboardApp() {
  CardboardHeadTracker_destroy(head_tracker_);
  //...
}

// Call nativeOnDestroy in VrActivity
@Override
protected void onDestroy() {
  super.onDestroy();
  nativeOnDestroy(nativeApp);
  nativeApp = 0;
}

Искажение объектива

Каждый раз, когда Cardboard сканирует новый QR-код, следующий код считывает сохраненные параметры и использует их для создания объекта искажения линзы, который применяет правильное искажение линзы к отображаемому контенту:

CardboardQrCode_getSavedDeviceParams(&buffer, &size);

CardboardLensDistortion_destroy(lens_distortion_);
lens_distortion_ = CardboardLensDistortion_create(
    buffer, size, screen_width_, screen_height_);

CardboardQrCode_destroy(buffer);

Рендеринг

Рендеринг контента в Cardboard включает в себя следующее:

  • Создание текстур
  • Получение матриц вида и проекции для левого и правого глаза
  • Создание рендерера и настройка сетки искажений
  • Рендеринг каждого кадра

Создание текстур

Весь контент рисуется на текстуре, которая разделена на секции для левого и правого глаза. Эти разделы инициализируются в _leftEyeTexture и _rightEyeTexture соответственно.

void HelloCardboardApp::GlSetup() {
  LOGD("GL SETUP");

  if (framebuffer_ != 0) {
    GlTeardown();
  }

  // Create render texture.
  glGenTextures(1, &texture_);
  glBindTexture(GL_TEXTURE_2D, texture_);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screen_width_, screen_height_, 0,
               GL_RGB, GL_UNSIGNED_BYTE, 0);

  left_eye_texture_description_.texture = texture_;
  left_eye_texture_description_.left_u = 0;
  left_eye_texture_description_.right_u = 0.5;
  left_eye_texture_description_.top_v = 1;
  left_eye_texture_description_.bottom_v = 0;

  right_eye_texture_description_.texture = texture_;
  right_eye_texture_description_.left_u = 0.5;
  right_eye_texture_description_.right_u = 1;
  right_eye_texture_description_.top_v = 1;
  right_eye_texture_description_.bottom_v = 0;

  //...
  CHECKGLERROR("GlSetup");
}

Эти текстуры передаются в качестве параметров CardboardDistortionRenderer_renderEyeToDisplay .

Получите матрицы просмотра и проекции для левого и правого глаза.

Сначала извлеките матрицы глаз для левого и правого глаза:

CardboardLensDistortion_getEyeFromHeadMatrix(
    lens_distortion_, kLeft, eye_matrices_[0]);
CardboardLensDistortion_getEyeFromHeadMatrix(
    lens_distortion_, kRight, eye_matrices_[1]);
CardboardLensDistortion_getProjectionMatrix(
    lens_distortion_, kLeft, kZNear, kZFar, projection_matrices_[0]);
CardboardLensDistortion_getProjectionMatrix(
    lens_distortion_, kRight, kZNear, kZFar, projection_matrices_[1]);

Затем получите сетки искажений для каждого глаза и передайте их рендереру искажений:

CardboardLensDistortion_getDistortionMesh(lens_distortion_, kLeft, &left_mesh);
CardboardLensDistortion_getDistortionMesh(lens_distortion_, kRight, &right_mesh);

Создайте рендерер и установите правильную сетку искажений.

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

distortion_renderer_ = CardboardOpenGlEs2DistortionRenderer_create();
CardboardDistortionRenderer_setMesh(distortion_renderer_, &left_mesh, kLeft);
CardboardDistortionRenderer_setMesh(distortion_renderer_, &right_mesh, kRight);

Рендеринг контента

Для каждого кадра получите текущую ориентацию головы из CardboardHeadTracker_getPose :

CardboardHeadTracker_getPose(head_tracker_, monotonic_time_nano, &out_position[0], &out_orientation[0]);

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

// Draw eyes views
for (int eye = 0; eye < 2; ++eye) {
  glViewport(eye == kLeft ? 0 : screen_width_ / 2, 0, screen_width_ / 2,
             screen_height_);

  Matrix4x4 eye_matrix = GetMatrixFromGlArray(eye_matrices_[eye]);
  Matrix4x4 eye_view = eye_matrix * head_view_;

  Matrix4x4 projection_matrix =
      GetMatrixFromGlArray(projection_matrices_[eye]);
  Matrix4x4 modelview_target = eye_view * model_target_;
  modelview_projection_target_ = projection_matrix * modelview_target;
  modelview_projection_room_ = projection_matrix * eye_view;

  // Draw room and target. Replace this to render your own content.
  DrawWorld();
}

Используйте CardboardDistortionRenderer_renderEyeToDisplay , чтобы применить коррекцию искажений к содержимому и отобразить содержимое на экране.

// Render
CardboardDistortionRenderer_renderEyeToDisplay(
    distortion_renderer_, /* target_display = */ 0, /* x = */ 0, /* y = */ 0,
    screen_width_, screen_height_, &left_eye_texture_description_,
    &right_eye_texture_description_);