מדריך למתחילים בנושא Google Cardboard ל-Android NDK

במדריך הזה מוסבר איך להשתמש ב-Cardboard SDK ל-Android ליצור חוויות מציאות מדומה (VR) משלכם.

אפשר להשתמש ב-Cardboard SDK כדי להפוך סמארטפון לפלטפורמת VR. סמארטפון יכול להציג סצנות תלת ממדיות עם עיבוד סטריאוסקופי, לעקוב אחר תנועות הראש ולהגיב לתנועות כאלה, וליצור אינטראקציה עם אפליקציות באמצעות זיהוי מתי המשתמש לוחץ על לחצן הצופה.

כדי להתחיל, משתמשים ב-HelloCardboard, משחק הדגמה שמדגים את הליבה של Cardboard SDK. במשחק, המשתמשים מביטים סביב עולם וירטואלי כדי למצוא איסוף אובייקטים. המדריך מסביר איך:

  • הגדרת סביבת הפיתוח
  • הורדה ויצירה של אפליקציית ההדגמה
  • צריך לסרוק את קוד ה-QR של מכשיר Cardboard כדי לשמור את הפרמטרים שלו
  • מעקב אחר תנועות הראש של המשתמש
  • עיבוד תמונות סטריאוסקופיות על ידי הגדרה של מטריצת ההיטל המתאימה לכל עין

HelloCardboard משתמש ב-Android NDK. כל שיטה מקורית היא:

  • קשור באופן ייחודי לשיטה של כיתה HelloCardboardApp, או
  • יצירה או מחיקה של מופע של אותה מחלקה

הגדרת סביבת הפיתוח

דרישות חומרה:

  • מכשיר Android עם מערכת ההפעלה Android 8.0 "Oreo" (רמת API 26) ומעלה
  • מציג קרטון

דרישות התוכנה:

  • Android Studio גרסה 2022.1.1 'Electric Eel' ומעלה
  • Android SDK 13.0 "Tiramisu" (רמת API 33) ומעלה
  • הגרסה העדכנית של framework של Android NDK

    כדי לבדוק או לעדכן ערכות SDK מותקנות, עוברים אל העדפות > מראה והתנהגות

    הגדרות מערכת > Android SDK ב-Android Studio.

הורדה ופיתוח של אפליקציית ההדגמה

ה-SDK של Cardboard נוצר באמצעות Vulkan שעבר הידור מראש עבור כל תוכנת הצללה (shader). אפשר למצוא שלבים ליצירת קובצי כותרות מאפס כאן.

  1. מריצים את הפקודה הבאה כדי לשכפל את Cardboard SDK ואת ההדגמה של HelloCardboard אפליקציה מ-GitHub:

    git clone https://github.com/googlevr/cardboard.git
  2. ב-Android Studio, בוחרים באפשרות פתיחת פרויקט קיים של Android Studio ואז בוחרים הספרייה שבה שוכפלו ה-SDK של Cardboard ואפליקציית ההדגמה של HelloCardboard.

    הקוד יופיע בחלון 'פרויקט' ב-Android Studio.

  3. כדי להרכיב את Cardboard SDK, לוחצים לחיצה כפולה על אפשרות ההרכבה בתוך התיקייה cardboard/:sdk/Tasks/build בכרטיסייה Gradle (תצוגה > Windows בכלי > Gradle).

  4. כדי להפעיל את אפליקציית ההדגמה של HelloCardboard בטלפון, בוחרים באפשרות הפעלה > הפעלה... צריך לבחור את היעד hellocardboard-android.

סריקת קוד ה-QR

כדי לשמור את הפרמטרים של המכשיר, סורקים את קוד ה-QR במציג Cardboard:

אם המשתמש לוחץ על 'SKIP' ואין פרמטרים שנשמרו בעבר, Cardboard שומר פרמטרים של Google Cardboard v1 (הושק ב-Google I/O 2014).

נסה את ההדגמה

ב-HelloCardboard מחפשים ואוספים כדורים גיאודזיים במרחב תלת-ממדי.

כדי למצוא ולאסוף ספירה:

  1. יש להזיז את הראש בכל כיוון שהוא עד שרואים צורה צפה.

  2. הביטו ישירות בכדור. כתוצאה מכך, הצבעים משתנים.

  3. כדי לבצע "איסוף", יש ללחוץ על הלחצן של מכשיר Cardboard את כדור הארץ.

הגדרת המכשיר

כשהמשתמש מקיש על סמל גלגל השיניים כדי להחליף את מכשיר הצפייה של 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 ודוגמה:

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

כך תוכלו להפעיל את כל ממשקי ה-ABI ותגדיל משמעותית את הגודל של ממשקי ה-ABI. קובץ .aar. מידע נוסף זמין במאמר ממשקי ABI של Android. אפשר לקבל מידע נוסף.

מעקב אחר תנועות הראש

יצירת מעקב אחר תנועות הראש

מכשיר המעקב אחר הראש נוצר פעם אחת ב-constructor של 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_);