Guía de inicio rápido de Google Cardboard para el NDK de Android

En esta guía, se muestra cómo usar el SDK de Cardboard para Android a fin de crear tus propias experiencias de realidad virtual (RV).

Puedes usar el SDK de Cardboard para convertir un smartphone en una plataforma de RV. Un smartphone puede mostrar escenas en 3D con renderización estereoscópica, hacer un seguimiento de los movimientos de la cabeza y reaccionar ante ellos, e interactuar con las apps detectando cuándo el usuario presiona el botón del visor.

Para comenzar, usa HelloCardboard, un juego de demostración que muestra las funciones principales del SDK de Cardboard. En el juego, los usuarios miran alrededor de un mundo virtual para encontrar y recolectar objetos. Se muestra cómo realizar las siguientes acciones:

  • Cómo configurar tu entorno de desarrollo
  • Cómo descargar y compilar la app de demostración
  • Escanea el código QR de un visor Cardboard para guardar sus parámetros
  • Seguimiento de los movimientos de la cabeza del usuario
  • Renderiza imágenes estereoscópicas configurando la matriz de proyección de vista correcta para cada ojo.

HelloCardboard usa el NDK de Android. Todos los métodos nativos tienen las siguientes características:

  • Se vincula de forma única a un método de clase HelloCardboardApp.
  • Crea o borra una instancia de esa clase

Cómo configurar tu entorno de desarrollo

Requisitos de hardware:

  • Dispositivo Android con Android 8.0 "Oreo" (nivel de API 26) o una versión posterior
  • Visor Cardboard

Requisitos de software:

  • Android Studio versión 2022.1.1 "Electric Eel" o posterior
  • SDK de Android 13.0 "Tiramisu" (nivel de API 33) o una versión posterior
  • La versión más reciente del framework del NDK de Android

    Para revisar o actualizar los SDK instalados, ve a Preferencias > Apariencia y comportamiento.

    System Settings > Android SDK en Android Studio

Cómo descargar y compilar la app de demostración

El SDK de Cardboard se compila con un archivo de encabezado Vulkan ya compilado para cada sombreador. Aquí encontrarás los pasos para compilar los archivos de encabezado desde cero.

  1. Ejecuta el siguiente comando para clonar el SDK de Cardboard y la app de demostración de HelloCardboard desde GitHub:

    git clone https://github.com/googlevr/cardboard.git
  2. En Android Studio, selecciona Open an existing Android Studio Project y, luego, selecciona el directorio en el que se clonaron el SDK de Cardboard y la app de demostración HelloCardboard.

    Tu código aparecerá en la ventana Project de Android Studio.

  3. Para ensamblar el SDK de Cardboard, haz doble clic en la opción assemble dentro de la carpeta cardboard/:sdk/Tasks/build en la pestaña Gradle (View > Tool Windows > Gradle).

  4. Para ejecutar la app de demo de HelloCardboard en tu teléfono, selecciona Run > Run... y elige el objetivo hellocardboard-android.

Escanea el código QR

Para guardar los parámetros del dispositivo, escanea el código QR en el visor Cardboard:

Si el usuario presiona "OMITIR" y no hay parámetros guardados anteriormente, Cardboard guarda los parámetros de Google Cardboard v1 (que se lanzó en Google I/O 2014).

Probar demostración

En HelloCardboard, buscarás y recolectarás esferas geodésicas en un espacio 3D.

Para buscar y recopilar una esfera:

  1. Mueve la cabeza en cualquier dirección hasta que veas una forma flotante.

  2. Mira directamente a la esfera. Esto hace que cambie de color.

  3. Presiona el botón del visor Cardboard para "recopilar" la esfera.

Configura el dispositivo

Cuando el usuario presiona el ícono de ajustes para cambiar de visor Cardboard, se llama al método nativeSwitchViewer. nativeSwitchViewer llama a CardboardQrCode_scanQrCodeAndSaveDeviceParams, que abre la ventana para escanear el código QR del usuario. La distorsión del lente del usuario y otros parámetros se actualizan una vez que se escanea el código QR.

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

Cómo habilitar el emulador x86 de Android Studio

Si deseas compilar para el emulador de Android Studio x86, quita la siguiente línea de los archivos build.gradle en el SDK y la muestra:

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

De esta manera, se habilitan todas las ABI y se aumentará considerablemente el tamaño del archivo .aar generado. Consulta ABI de Android para obtener más información.

Seguimiento de cabeza

Crear monitor de cabeza

El seguimiento de cabeza se crea una vez en el constructor de HelloCardboardApp:

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

Cuando se crea VrActivity, se genera una instancia de la clase HelloCardboardApp con una llamada al método nativeOnCreate:

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

Pausar y reanudar el seguimiento de cabeza

Para pausar, reanudar y destruir el seguimiento de cabeza, se debe llamar a CardboardHeadTracker_pause(head_tracker_), CardboardHeadTracker_resume(head_tracker_) y CardboardHeadTracker_destroy(head_tracker_), respectivamente. En la app "HelloCardboard", los llamamos en nativeOnPause, nativeOnResume y 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;
}

Distorsión del lente

Cada vez que Cardboard escanea un código QR nuevo, el siguiente código lee los parámetros guardados y los usa para crear el objeto de distorsión de la lente, que aplica la distorsión adecuada del lente al contenido renderizado:

CardboardQrCode_getSavedDeviceParams(&buffer, &size);

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

CardboardQrCode_destroy(buffer);

Renderización

La renderización de contenido en Cardboard implica lo siguiente:

  • Crear texturas
  • Cómo obtener matrices de vista y proyección para los ojos izquierdo y derecho
  • Cómo crear el procesador y configurar la malla de distorsión
  • Cómo renderizar cada fotograma

Crea texturas

Todo el contenido se dibuja en una textura, que se divide en secciones para el ojo izquierdo y el derecho. Estas secciones se inicializan en _leftEyeTexture y _rightEyeTexture, respectivamente.

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");
}

Estas texturas se pasan como parámetros a CardboardDistortionRenderer_renderEyeToDisplay.

Obtener matrices de vistas y de proyección para el ojo izquierdo y el derecho

Primero, recupera las matrices de los ojos para el ojo izquierdo y el derecho:

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]);

A continuación, obtén las mallas de distorsión para cada uno de los ojos y pásalas al procesador de distorsión:

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

Cómo crear el renderizador y establecer la malla de distorsión correcta

El procesador debe inicializarse solo una vez. Una vez que se haya creado el procesador, configura la nueva malla de distorsión para los ojos izquierdo y derecho según los valores de la malla que muestra la función CardboardLensDistortion_getDistortionMesh.

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

Cómo procesar el contenido

Para cada fotograma, recupera la orientación actual de la cabeza desde CardboardHeadTracker_getPose:

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

Usa la orientación actual de la cabeza con las matrices de vista y proyección para componer una matriz de proyección de vista para cada uno de los ojos y renderizar el contenido en la pantalla:

// 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();
}

Usa CardboardDistortionRenderer_renderEyeToDisplay para aplicar la corrección de distorsión al contenido y renderizarlo en la pantalla.

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