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

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

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

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

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

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

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

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

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

Cardboard SDK מבוסס על קובצי מקור של Protocol Buffers ו-C++ שעברו הידור מראש. הוראות ליצירת קובצי מקור מאפס מפורטים כאן.

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

    git clone https://github.com/googlevr/cardboard.git
  2. מתקינים את התלות של Protocol Buffers בפרויקט Xcode על ידי הרצת הפקודה הזו ברמה הבסיסית של המאגר:

    pod install
  3. פותחים את סביבת העבודה של Cardboard (Cardboard.xcworkspace) ב-Xcode.

  4. כדי שתהיה לך אפשרות לחתום על האפליקציה עם הצוות שלך, צריך לשנות את מזהה החבילה של האפליקציה.

  5. עוברים אל SDK > שלבי Build > קישור בינארי עם ספריות

    1. הסרה של libPods-sdk.a מהרשימה בלחיצה על הלחצן '-'.
    2. כדי להוסיף את libProtobuf-C++.a לרשימה, לוחצים על הלחצן '+' ובוחרים אותו. אם קופצת הודעה שמציעה להשתמש ב-XCFramework, לוחצים על "Add בכל זאת".
  6. לוחצים על Run.

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

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

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

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

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

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

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

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

הגדרת המכשיר

כשהמשתמש מקיש על סמל גלגל השיניים כדי להחליף מכשירי Cardboard, מתבצעת קריאה ל-method didTapSwitchButton ב-HelloCardboardOverlayView.

- (void)didTapSwitchButton:(id)sender {
  if ([self.delegate respondsToSelector:@selector(didTapBackButton)]) {
    [self.delegate didChangeViewerProfile];
  }
  self.settingsBackgroundView.hidden = YES;
}

הפעולה הזו מפעילה את CardboardQrCode_scanQrCodeAndSaveDeviceParams, ואז נפתח החלון לסריקת קוד ה-QR של הצופה. כשהמשתמש סורק את קוד ה-QR, פרמטרי העיוות של המכשיר מתעדכנים.

- (void)switchViewer {
  CardboardQrCode_scanQrCodeAndSaveDeviceParams();
}

- (void)didChangeViewerProfile {
  [self pauseCardboard];
  [self switchViewer];
  [self resumeCardboard];
}

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

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

הכלי למעקב אחר תנועות הראש נוצר פעם אחת בשיטה viewDidLoad של HelloCardboardViewController:

_cardboardHeadTracker = CardboardHeadTracker_create();

השהיה והמשך של מעקב הראש

השיטות pauseCardboard ו-resumeCardboard בשיעור HelloCardboardViewController משהות ומפעילות את הכלי למעקב אחרי תנועות הראש, בהתאמה. resumeCardboard מגדיר גם את הדגל _updateParams, שגורם לעדכון הפרמטרים של המכשיר בקריאה הבאה לשרטוט.

- (void)pauseCardboard {
  self.paused = true;
  CardboardHeadTracker_pause(_cardboardHeadTracker);
}

- (void)resumeCardboard {
  // Parameters may have changed.
  _updateParams = YES;

  // Check for device parameters existence in app storage. If they're missing,
  // we must scan a Cardboard QR code and save the obtained parameters.
  uint8_t *buffer;
  int size;
  CardboardQrCode_getSavedDeviceParams(&buffer, &size);
  if (size == 0) {
    [self switchViewer];
  }
  CardboardQrCode_destroy(buffer);

  CardboardHeadTracker_resume(_cardboardHeadTracker);
  self.paused = false;
}

עיוות עדשה

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

CardboardQrCode_getSavedDeviceParams(&encodedDeviceParams, &size);

// Create CardboardLensDistortion.
CardboardLensDistortion_destroy(_cardboardLensDistortion);
_cardboardLensDistortion =
    CardboardLensDistortion_create(encodedDeviceParams, size, width, height);

// Initialize HelloCardboardRenderer.
_renderer.reset(new cardboard::hello_cardboard::HelloCardboardRenderer(
      _cardboardLensDistortion, _cardboardHeadTracker, width, height));

רינדור

עיבוד תוכן ב-Cardboard כולל את הפעולות הבאות:

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

יצירת מרקמים

התוכן משורטט על מרקם המפוצל לקטעים עבור עין שמאל ועין ימין. הקטעים האלה מאותחלים ב-_leftEyeTexture וב-_rightEyeTexture, בהתאמה. באפליקציה לדוגמה נעשה שימוש במרקם יחיד עבור שתי העיניים, אבל ניתן גם ליצור מרקם נפרד לכל עין.

// Generate texture to render left and right eyes.
glGenTextures(1, &_eyeTexture);
glBindTexture(GL_TEXTURE_2D, _eyeTexture);
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, _width, _height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);

_leftEyeTexture.texture = _eyeTexture;
_leftEyeTexture.left_u = 0;
_leftEyeTexture.right_u = 0.5;
_leftEyeTexture.top_v = 1;
_leftEyeTexture.bottom_v = 0;

_rightEyeTexture.texture = _eyeTexture;
_rightEyeTexture.left_u = 0.5;
_rightEyeTexture.right_u = 1;
_rightEyeTexture.top_v = 1;
_rightEyeTexture.bottom_v = 0;
CheckGLError("Create Eye textures");

המרקמים האלה מועברים כפרמטרים אל CardboardDistortionRenderer_renderEyeToDisplay.

קבלת מטריצות של תצוגה והיטל עבור עין שמאל ועין ימין

קודם כול, צריך למצוא את מטריצות העין של עין שמאל ועיניים ימין:

CardboardLensDistortion_getEyeFromHeadMatrix(_lensDistortion, kLeft, _eyeMatrices[kLeft]);
CardboardLensDistortion_getEyeFromHeadMatrix(_lensDistortion, kRight, _eyeMatrices[kRight]);
CardboardLensDistortion_getProjectionMatrix(_lensDistortion, kLeft, kZNear, kZFar,
                                            _projMatrices[kLeft]);
CardboardLensDistortion_getProjectionMatrix(_lensDistortion, kRight, kZNear, kZFar,
                                            _projMatrices[kRight]);

לאחר מכן, משיגים את רשתות העיוות של כל אחת מהעיניים ומעבירים אותן לכלי העיוות:

CardboardLensDistortion_getDistortionMesh(_lensDistortion, kLeft, &leftMesh);
CardboardLensDistortion_getDistortionMesh(_lensDistortion, kRight, &rightMesh);

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

צריך לאתחל את ה-Renderer פעם אחת בלבד. אחרי שיוצרים את כלי הרינדור, מגדירים את רשת העיוות החדשה לעיניים השמאליות והימניות בהתאם לערכי הרשת שהוחזרו מהפונקציה CardboardLensDistortion_getDistortionMesh.

_distortionRenderer = CardboardOpenGlEs2DistortionRenderer_create();
CardboardDistortionRenderer_setMesh(_distortionRenderer, &leftMesh, kLeft);
CardboardDistortionRenderer_setMesh(_distortionRenderer, &rightMesh, kRight);

רינדור התוכן

מאחזרים את כיוון הראש הנוכחי מ-CardboardHeadTracker_getPose:

CardboardHeadTracker_getPose(_headTracker, targetTime, position, orientation);
_headView =
    GLKMatrix4Multiply(GLKMatrix4MakeTranslation(position[0], position[1], position[2]),
                       GLKMatrix4MakeWithQuaternion(GLKQuaternionMakeWithArray(orientation)));

השתמשו בכיוון הראש הנוכחי במטריצות התצוגה וההיטל כדי ליצור מטריצה של היטל תצוגה, והשתמשו בהן כדי לעבד את התוכן בעולם עבור כל אחת מהעיניים:

// Draw left eye.
glViewport(0, 0, _width / 2.0, _height);
glScissor(0, 0, _width / 2.0, _height);
DrawWorld(_leftEyeViewPose, GLKMatrix4MakeWithArray(_projMatrices[kLeft]));

// Draw right eye.
glViewport(_width / 2.0, 0, _width / 2.0, _height);
glScissor(_width / 2.0, 0, _width / 2.0, _height);
DrawWorld(_rightEyeViewPose, GLKMatrix4MakeWithArray(_projMatrices[kRight]));

בעזרת CardboardDistortionRenderer_renderEyeToDisplay אפשר להחיל את תיקון העיוות על התוכן ולעבד את התוכן במסך.

CardboardDistortionRenderer_renderEyeToDisplay(_distortionRenderer, renderTarget, /*x=*/0,
                                               /*y=*/0, _width, _height, &_leftEyeTexture,
                                               &_rightEyeTexture);