Guia de início rápido do Google Cardboard para Android NDK

Este guia mostra como usar o SDK do Cardboard para Android para criar suas próprias experiências de realidade virtual (RV).

Você pode usar o SDK do Cardboard para transformar um smartphone em uma plataforma de RV. Um smartphone pode exibir cenas 3D com renderização estereoscópica, rastrear e reagir aos movimentos da cabeça e interagir com apps detectando quando o usuário pressiona o botão do visualizador.

Para começar, você vai usar o HelloCardboard, um jogo de demonstração que demonstra os principais recursos do SDK do Cardboard. No jogo, os usuários olham ao redor de um mundo virtual para encontrar e coletar objetos. Ele mostra como:

  • Configurar seu ambiente de desenvolvimento
  • Fazer o download e criar o app de demonstração
  • Leia o código QR de um visualizador do Google Cardboard para salvar os parâmetros dele
  • Rastrear os movimentos da cabeça do usuário
  • Renderize imagens estereoscópicas definindo a matriz de projeção de visualização correta para cada olho

O HelloCardboard usa o Android NDK. Todo método nativo é:

  • limitadas exclusivamente a um método de classe HelloCardboardApp;
  • Cria ou exclui uma instância dessa classe

Configurar seu ambiente de desenvolvimento

Requisitos de hardware:

Requisitos de software:

  • Android Studio versão 2022.1.1 "Electric Eel" ou mais recente.
  • SDK do Android 13.0 "Tiramisu" (nível 33 da API) ou mais recente
  • A versão mais recente do framework do Android NDK.

    Para revisar ou atualizar os SDKs instalados, acesse Preferências > Aparência e comportamento.

    Configurações do sistema > SDK do Android no Android Studio.

Fazer o download e criar o app de demonstração

O SDK do Cardboard é criado usando um arquivo de cabeçalho Vulkan pré-compilado para cada sombreador. As etapas para criar os arquivos principais do zero podem ser encontradas neste link.

  1. Execute o comando a seguir para clonar o SDK do Cardboard e o app de demonstração HelloCardboard do GitHub:

    git clone https://github.com/googlevr/cardboard.git
  2. No Android Studio, selecione Open an existing Android Studio Project e escolha o diretório em que o SDK do Cardboard e o app de demonstração HelloCardboard foram clonados.

    Seu código vai aparecer na janela "Project" no Android Studio.

  3. Para montar o SDK do Cardboard, clique duas vezes na opção assemble dentro da pasta cardboard/:sdk/Tasks/build na guia Gradle (View > Tool Windows > Gradle).

  4. Execute o app de demonstração HelloCardboard no seu smartphone selecionando Run > Run... e selecione o destino hellocardboard-android.

Leia o QR code

Para salvar os parâmetros do dispositivo, leia o código QR no visor do Google Cardboard:

Se o usuário pressionar "SKIP" e não houver parâmetros salvos anteriormente, o Cardboard salvará os parâmetros do Google Cardboard v1 (lançado no Google I/O 2014).

Confira a demonstração

No HelloCardboard, você vai procurar e coletar esferas geodésicas no espaço 3D.

Para encontrar e coletar uma esfera:

  1. Mova a cabeça em qualquer direção até ver uma forma flutuante.

  2. Olhe diretamente para a esfera. Isso faz com que ele mude de cor.

  3. Pressione o botão do visualizador do Google Cardboard para "coletar" a esfera.

Configurar o dispositivo

Quando o usuário toca no ícone de engrenagem para alternar os visualizadores do Google Cardboard, o método nativeSwitchViewer é chamado. nativeSwitchViewer chama CardboardQrCode_scanQrCodeAndSaveDeviceParams, que abre a janela para ler o código QR do visualizador. A distorção da lente do visualizador e outros parâmetros são atualizados assim que o código QR é lido.

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

Ativar o emulador x86 do Android Studio

Para criar para o emulador x86 do Android Studio, remova a seguinte linha dos arquivos build.gradle no SDK e na Sample (link em inglês):

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

Isso ativa todas as ABIs e aumenta significativamente o tamanho do arquivo .aar gerado. Consulte ABIs do Android para ver mais informações.

Acompanhamento da cabeça

Criar tracker da cabeça

O rastreador de posições da cabeça é criado uma vez no construtor 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();
}

Quando VrActivity é criado, uma instância da classe HelloCardboardApp é gerada chamando o método nativeOnCreate:

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

Pausar e retomar tracker da cabeça

Para pausar, retomar e destruir o rastreador de posições da cabeça, CardboardHeadTracker_pause(head_tracker_), CardboardHeadTracker_resume(head_tracker_) e CardboardHeadTracker_destroy(head_tracker_) precisam ser chamados, respectivamente. No app HelloCardboard, elas são chamadas em nativeOnPause, nativeOnResume e 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;
}

Distorção da lente

Toda vez que o Cardboard lê um novo código QR, o código a seguir lê os parâmetros salvos e os usa para criar o objeto de distorção de lente, que aplica a distorção adequada ao conteúdo renderizado:

CardboardQrCode_getSavedDeviceParams(&buffer, &size);

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

CardboardQrCode_destroy(buffer);

Renderização

A renderização de conteúdo no Cardboard envolve o seguinte:

  • Como criar texturas
  • Como acessar matrizes de visualização e projeção para os olhos esquerdo e direito
  • Como criar o renderizador e definir a malha de distorção
  • Renderização de cada frame

Criar texturas

Todo o conteúdo é desenhado em uma textura, que é dividida em seções para os olhos esquerdo e direito. Essas seções são inicializadas em _leftEyeTexture e _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");
}

Essas texturas são transmitidas como parâmetros para CardboardDistortionRenderer_renderEyeToDisplay.

Acessar matrizes de visualização e projeção para o olho esquerdo e direito

Primeiro, recupere as matrizes oculares para os olhos esquerdo e direito:

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

Em seguida, extraia as malhas de distorção de cada um dos olhos e as transmita ao renderizador de distorção:

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

Criar o renderizador e definir a malha de distorção correta

O renderizador precisa ser inicializado apenas uma vez. Depois de criar o renderizador, defina a nova malha de distorção para os olhos esquerdo e direito de acordo com os valores da malha retornados pela função CardboardLensDistortion_getDistortionMesh.

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

Renderizar o conteúdo

Para cada frame, extraia a orientação atual da cabeça de CardboardHeadTracker_getPose:

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

Use a orientação atual da cabeça com as matrizes de visualização e projeção para compor uma matriz de projeção de visualização para cada um dos olhos e renderizar conteúdo na tela:

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

Use CardboardDistortionRenderer_renderEyeToDisplay para aplicar a correção de distorção e renderizar o conteúdo na tela.

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