Guida rapida di Google Cardboard per Android NDK

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

Puoi usare l'SDK di Cardboard per trasformare uno smartphone in una piattaforma VR. Uno smartphone può mostrare scene 3D con rendering stereoscopico, seguire 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 dimostra le caratteristiche principali dell'SDK di Cardboard. Nel gioco gli utenti esplorano il mondo virtuale per trovare e raccogliere oggetti. Ti mostra come:

  • Configurazione dell'ambiente di sviluppo
  • Scarica e crea l'app demo
  • Scansiona il codice QR di un visore Cardboard per salvarne i parametri
  • Monitorare i movimenti della testa dell'utente
  • Esegui il rendering delle immagini stereoscopiche impostando la matrice di proiezione corretta per ogni occhio

HelloCardboard utilizza l'NDK di Android. Ogni metodo nativo è:

  • Associato in modo univoco a un metodo di classe HelloCardboardApp oppure
  • 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 2022.1.1 "Electric Anguilla" o successive
  • SDK per Android 13.0 "Tiramisù" (livello API 33) o versioni successive
  • La versione più recente del framework Android NDK

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

    Impostazioni di sistema > SDK Android in Android Studio.

Scarica e crea l'app demo

L'SDK Cardboard è realizzato utilizzando un file di intestazione Vulkan precompilato per ogni Shader. La procedura per creare i file di intestazione da zero è disponibile 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 del progetto in Android Studio.

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

  4. Avvia l'app demo HelloCardboard sul tuo smartphone selezionando Esegui > Esegui... e seleziona il target hellocardboard-android.

Scansiona il codice QR

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

Se l'utente preme "IGNORA" e non esistono parametri salvati in precedenza, Cardboard salva i parametri di Google Cardboard v1 (lanciata alla conferenza Google I/O 2014).

Prova la demo

In HelloCardboard 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. Guardare direttamente la sfera. Questo causa la modifica dei colori.

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

Configura il dispositivo

Quando l'utente tocca l'icona a forma di ingranaggio per cambiare i visori Cardboard, viene richiamato il metodo nativeSwitchViewer. nativeSwitchViewer chiama CardboardQrCode_scanQrCodeAndSaveDeviceParams, che apre la finestra per scansionare il codice QR dello spettatore. La distorsione dell'obiettivo e altri parametri del visualizzatore vengono aggiornati dopo la scansione del codice QR.

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

Attiva l'emulatore x86 di Android Studio

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

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

Ciò abilita tutte le ABI e aumenterà notevolmente le dimensioni del file .aar generato. Per ulteriori informazioni, consulta le ABI Android.

Rilevamento dei movimenti della testa

Crea tracker della testa

Il tracker 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 creata VrActivity, viene generata un'istanza della classe HelloCardboardApp chiamando il metodo nativeOnCreate:

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

Metti in pausa e riprendi il tracker della testa

Per mettere in pausa, riprendere e distruggere il tracker della testa, è necessario chiamare rispettivamente CardboardHeadTracker_pause(head_tracker_), CardboardHeadTracker_resume(head_tracker_) e CardboardHeadTracker_destroy(head_tracker_). 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 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 dell'obiettivo, che applica la corretta distorsione dell'obiettivo al contenuto visualizzato:

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
  • Come ottenere le matrici di visualizzazione e di proiezione per l'occhio sinistro e destro
  • Creazione del renderer e impostazione del mesh di distorsione
  • Rendering di ogni frame

Crea texture

Tutti i contenuti vengono disegnati su una texture, suddivisa in sezioni per gli occhi destro e sinistro. Queste sezioni sono 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 passate come parametri a CardboardDistortionRenderer_renderEyeToDisplay.

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

Per prima cosa, recupera le matrici dell'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]);

Quindi ottieni i mesh di distorsione per ciascuno degli occhi e passali al renderer di distorsione:

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

Crea il renderer e imposta il mesh di distorsione corretto

Il renderer deve essere inizializzato una sola volta. Dopo aver creato il renderer, imposta il nuovo mesh di distorsione per gli occhi sinistro e destro in base ai valori 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 fotogramma, recupera l'orientamento corrente della testa da CardboardHeadTracker_getPose:

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

Utilizza l'orientamento corrente della testa con le matrici di vista e proiezione per comporre una matrice di proiezione della vista per ciascuno degli occhi e per eseguire il rendering dei 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();
}

Usa 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_);