מדריך למתחילים בנושא 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) ואילך
  • מציג Cardboard

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

  • Android Studio בגרסה 2022.1.1 "Eel חשמלי" ואילך
  • Android SDK 13.0 "Tiramisu" (רמת API 33) ואילך
  • הגרסה האחרונה של המסגרת של Android NDK

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

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

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

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

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

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

    הקוד יופיע בחלון Project ב-Android Studio.

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

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

יש לסרוק את קוד ה-QR

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

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

הפעל את ההדגמה

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

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

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

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

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

הגדרת המכשיר

כשהמשתמש מקיש על סמל גלגל השיניים כדי להחליף מכשירי Cardboard, מתבצעת קריאה ל-method 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, וקובץ .aar שנוצר גדל באופן משמעותי. למידע נוסף, אפשר לקרוא את המאמר ממשקי ABI של Android.

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

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

ה-head tracker נוצר פעם אחת במבנה של 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 על ידי קריאה ל-method 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);

יוצרים את כלי הרינדור ומגדירים את רשת העיוות הנכונה

צריך לאתחל את ה-Renderer פעם אחת בלבד. אחרי שיוצרים את כלי הרינדור, מגדירים את רשת העיוות החדשה לעיניים השמאליות והימניות בהתאם לערכי הרשת שהוחזרו מהפונקציה 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_);