Guida rapida di Google Cardboard per Android NDK

Questa guida mostra come utilizzare l'SDK Cardboard per Android per creare le tue esperienze di realtà virtuale (VR).

Puoi utilizzare l'SDK Cardboard per trasformare uno smartphone in una piattaforma VR. Uno smartphone può visualizzare scene 3D con rendering stereoscopico, tracciare e reagire ai movimenti della testa e interagire con le app rilevando quando l'utente preme il pulsante del visore.

Per iniziare, utilizzerai HelloCardboard, un gioco demo che mostra le funzionalità principali dell'SDK Cardboard. Nel gioco, gli utenti esplorano un mondo virtuale per trovare e raccogliere oggetti. Mostra come:

  • Configurazione dell'ambiente di sviluppo
  • Scaricare e creare l'app demo
  • Scansionare il codice QR di un visore Cardboard per salvare i relativi parametri
  • Tracciare i movimenti della testa dell'utente
  • Esegui il rendering di immagini stereoscopiche impostando la matrice di proiezione della visualizzazione corretta per ogni occhio

HelloCardboard utilizza l'NDK Android. Ogni metodo nativo:

  • Vincolato in modo univoco a un metodo di classe HelloCardboardApp o
  • Crea o elimina un'istanza di quella classe

Configurazione dell'ambiente di sviluppo

Requisiti hardware:

  • Dispositivo Android con Android 8.0 "Oreo" (livello API 26) o versioni successive
  • Visore Cardboard

Requisiti software:

  • Android Studio versione 2024.1.2 "Koala Feature Drop" o successive
  • SDK Android 15.0 "Vanilla Ice Cream" (livello API 35) o versioni successive
  • Ultima versione del framework Android NDK

    Per esaminare o aggiornare gli SDK installati, vai a Preferenze > Aspetto e comportamento.

    Impostazioni di sistema > Android SDK in Android Studio.

Scaricare e creare l'app demo

L'SDK Cardboard è creato utilizzando un file di intestazione Vulkan precompilato per ogni shader. I passaggi per creare i file di intestazione da zero sono disponibili qui.

  1. Esegui questo comando per clonare l'SDK Cardboard e l'app demo HelloCardboard da GitHub:

    git clone https://github.com/googlevr/cardboard.git
  2. In Android Studio, seleziona Apri un progetto Android Studio esistente, quindi seleziona la directory in cui sono stati clonati l'SDK Cardboard e l'app demo HelloCardboard.

    Il codice verrà visualizzato nella finestra Progetto in Android Studio.

  3. Per assemblare l'SDK Cardboard, fai doppio clic sull'opzione assemble all'interno della cartella cardboard/:sdk/Tasks/build nella scheda Gradle (Visualizza > Finestre degli strumenti > Gradle).

  4. Esegui l'app demo HelloCardboard sullo smartphone selezionando Esegui > Esegui… e seleziona la destinazione hellocardboard-android.

Scansiona il codice QR

Per salvare i parametri del dispositivo, scansiona il codice QR sul visore Cardboard:

Se l'utente preme "SALTA" e non sono presenti parametri salvati in precedenza, Cardboard salva i parametri di Google Cardboard v1 (lanciato al Google I/O 2014).

Prova la demo

In HelloCardboard, devi cercare e raccogliere sfere geodetiche nello spazio 3D.

Per trovare e raccogliere una sfera:

  1. Muovi la testa in qualsiasi direzione finché non vedi una forma fluttuante.

  2. Guarda direttamente la sfera. In questo modo, i colori cambiano.

  3. Premi il pulsante del visore Cardboard per "raccogliere" la sfera.

Configurare il dispositivo

Quando l'utente tocca l'icona a forma di ingranaggio per cambiare visori Cardboard, viene chiamato il metodo nativeSwitchViewer. nativeSwitchViewer chiamate CardboardQrCode_scanQrCodeAndSaveDeviceParams, che apre la finestra per scansionare il codice QR dello spettatore. La distorsione dell'obiettivo e altri parametri dello spettatore vengono aggiornati una volta scansionato il codice QR.

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

Attivare l'emulatore x86 di Android Studio

Per la compilazione per l'emulatore x86 di Android Studio, rimuovi la seguente riga dai file build.gradle in SDK e Sample:

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

In questo modo vengono abilitate tutte le ABI e le dimensioni del file .aar generato aumenteranno notevolmente. Per ulteriori informazioni, consulta ABI Android.

Tracciamento testa

Crea head tracker

Il rilevamento della testa viene creato una volta nel costruttore di 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 viene creato VrActivity, viene generata un'istanza della classe HelloCardboardApp chiamando il metodo nativeOnCreate:

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

Mettere in pausa e riprendere il rilevamento della testa

Per mettere in pausa, riprendere e distruggere il head tracker, CardboardHeadTracker_pause(head_tracker_), CardboardHeadTracker_resume(head_tracker_), e CardboardHeadTracker_destroy(head_tracker_) devono essere chiamati, rispettivamente. Nell'app "HelloCardboard", li chiamiamo in 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;
}

Distorsione dell'obiettivo

Ogni volta che Cardboard scansiona un nuovo codice QR, il seguente codice legge i parametri salvati e li utilizza per creare l'oggetto di distorsione della lente, che applica la distorsione della lente corretta ai contenuti sottoposti a rendering:

CardboardQrCode_getSavedDeviceParams(&buffer, &size);

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

CardboardQrCode_destroy(buffer);

Rendering

Il rendering dei contenuti in Cardboard prevede quanto segue:

  • Creazione di texture
  • Ottenere le matrici di visualizzazione e proiezione per l'occhio sinistro e destro
  • Creazione del renderer e impostazione della mesh di distorsione
  • Rendering di ogni frame

Creare texture

Tutti i contenuti vengono disegnati su una texture, che viene suddivisa in sezioni per l'occhio sinistro e quello destro. Queste sezioni vengono inizializzate rispettivamente in _leftEyeTexture e _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");
}

Queste texture vengono trasmesse come parametri a CardboardDistortionRenderer_renderEyeToDisplay.

Ottieni le matrici di visualizzazione e proiezione per l'occhio sinistro e destro

Innanzitutto, recupera le matrici degli occhi per l'occhio sinistro e destro:

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

Successivamente, ottieni le mesh di distorsione per ciascun occhio e passale al renderer di distorsione:

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

Crea il renderer e imposta la mesh di distorsione corretta

Il renderer deve essere inizializzato una sola volta. Una volta creato il renderer, imposta la nuova mesh di distorsione per l'occhio sinistro e destro in base ai valori della mesh restituiti dalla funzione CardboardLensDistortion_getDistortionMesh.

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

Rendering dei contenuti

Per ogni frame, recupera l'orientamento della testa attuale da CardboardHeadTracker_getPose:

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

Utilizza l'orientamento della testa attuale con le matrici di visualizzazione e proiezione per comporre una matrice di proiezione della visualizzazione per ciascun occhio e visualizzare i contenuti sullo schermo:

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

Utilizza CardboardDistortionRenderer_renderEyeToDisplay per applicare la correzione della distorsione ai contenuti e visualizzarli sullo schermo.

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