1. סקירה כללית
ARCore היא פלטפורמה ליצירת אפליקציות של מציאות רבודה ב-Android. תמונות מורחבות מאפשרות לכם ליצור אפליקציות AR שיכולות לזהות תמונות דו-ממדיות שרשומות מראש בעולם האמיתי, ולמקם תוכן וירטואלי מעליהן.
בשיעור Codelab הזה נסביר איך לשנות אפליקציית דוגמה קיימת של ARCore כדי לשלב בה תמונות מוגדלות שנעות או קבועות במקום.
מה תפַתחו
בשיעור Codelab הזה תלמדו איך להשתמש באפליקציית דוגמה קיימת של ARCore. בסוף השיעור, האפליקציה שלכם תוכל:
- זיהוי של יעד תמונה וצירוף של מבוך וירטואלי ליעד
- מעקב אחרי המטרה הנעה כל עוד היא בטווח זווית הצילום של המצלמה

זו הפעם הראשונה שאתם יוצרים אפליקציית ARCore?
האם בכוונתך לכתוב קוד לדוגמה ב-codelab הזה או שאתה רק רוצה לקרוא את הדפים האלה?
מה תלמדו
- איך משתמשים בתמונות במציאות רבודה ב-ARCore ב-Java
- איך מעריכים את היכולת של תמונה להיות מזוהה על ידי ARCore
- איך מצרפים תוכן וירטואלי לתמונה ועוקבים אחרי התנועה שלו
דרישות מוקדמות
כדי להשלים את ה-Codelab הזה, תצטרכו חומרה ותוכנה ספציפיות.
דרישות חומרה
- מכשיר שתומך ב-ARCore שמחובר למחשב הפיתוח באמצעות כבל USB
דרישות תוכנה
- ARCore APK 1.9.0 ואילך. קובץ ה-APK הזה מותקן בדרך כלל במכשיר באופן אוטומטי דרך חנות Play
- מחשב פיתוח עם Android Studio (גרסה 3.1 ואילך)
- גישה לאינטרנט, כי תצטרכו להוריד ספריות במהלך הפיתוח
עכשיו כשהכול מוכן, אפשר להתחיל!
2. הגדרת סביבת הפיתוח
הורדת ה-SDK
נתחיל בהורדה של ARCore Android SDK העדכני מ-GitHub. פורסים אותו למיקום הרצוי. ב-codelab הזה, גרסת ה-SDK המוקדמת ביותר היא 1.18.1. התיקייה תסומן כ-arcore-android-sdk-x.xx.x, והערך המדויק יהיה גרסת ה-SDK שבה אתם משתמשים.
מפעילים את Android Studio ולוחצים על Open an existing Android Studio project (פתיחת פרויקט קיים של Android Studio).

עוברים לתיקייה הזו שחולצה מקובץ ה-ZIP:
arcore-android-sdk-x.xx.x/samples/augmented_image_java
לוחצים על פתיחה.
ממתינים עד ש-Android Studio יסיים לסנכרן את הפרויקט. אם ב-Android Studio חסרים רכיבים נדרשים, יכול להיות שהפעולה תיכשל ותוצג ההודעה Install missing platform and sync project. פועלים לפי ההוראות כדי לפתור את הבעיה.
הרצת האפליקציה לדוגמה
עכשיו, אחרי שיש לכם פרויקט אפליקציית ARCore שעובד, נריץ אותו כדי לבדוק אותו.
מחברים את מכשיר ARCore למחשב הפיתוח ומשתמשים בתפריט הפעלה > הפעלת האפליקציה כדי להפעיל את גרסת ניפוי הבאגים במכשיר. בתיבת הדו-שיח שמופיעה ומבקשת לבחור את המכשיר שממנו רוצים להפעיל את ההדפסה, בוחרים את המכשיר המחובר ולוחצים על אישור.


בפרויקט לדוגמה הזה נעשה שימוש ב-targetSdkVersion 28. אם מופיעה שגיאת build כמו Failed to find Build Tools revision 28.0.3, צריך לפעול לפי ההוראות שמופיעות ב-Android Studio כדי להוריד ולהתקין את הגרסה הנדרשת של Android Build Tools.
אם הכול יפעל כמו שצריך, אפליקציית הדוגמה תופעל במכשיר ותבקש הרשאה לאפשר ל-Augmented Image לצלם תמונות וסרטונים. כדי לתת הרשאה, מקישים על אישור.
בדיקה באמצעות תמונה לדוגמה
אחרי שמגדירים את סביבת הפיתוח, אפשר לבדוק את האפליקציה על ידי מתן תמונה לבדיקה.
ב-Android Studio, בחלון Project (פרויקט), עוברים אל app > assets (אפליקציה > נכסים) ולוחצים לחיצה כפולה על הקובץ default.jpg כדי לפתוח אותו.

מכוונים את המצלמה של המכשיר לתמונה של כדור הארץ במסך ופועלים לפי ההוראות כדי להתאים את התמונה שסורקים לכוונת.
מסגרת תמונה תוצג כשכבת-על מעל התמונה, כך:

בשלב הבא, נבצע שיפורים קטנים באפליקציית הדוגמה.
3. הצגת מודל של מבוך בתמונה דו-ממדית
כדי להתחיל לשחק עם תמונות משופרות, אפשר להציג מודל תלת-ממדי מעל התמונה.
הורדת מודל תלת-ממדי
בשיעור Codelab הזה נשתמש ב-Circle Maze - Green של Evol, שזמין ברישיון CC-BY 3.0. שמרתי עותק של המודל התלת-ממדי הזה במאגר GitHub של ה-Codelab הזה, שאפשר למצוא כאן.
כדי להוריד את המודל ולכלול אותו ב-Android Studio:
- עוברים אל מאגר GitHub של ה-Codelab הזה, הספרייה third_party.
- לוחצים על GreenMaze_obj.zip ואז על לחצן ההורדה.
קובץ בשם GreenMaze_obj.zip יורד.
- ב-Android Studio, יוצרים את הספרייה
green-mazeבקטע app > assets > models - פותחים את ה-ZIP
GreenMaze_obj.zipומעתיקים את התוכן למיקום הזה:arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze - ב-Android Studio, עוברים אל app > assets > models > green-maze.
צריכים להיות שני קבצים בתיקייה הזו: GreenMaze.obj ו-GreenMaze.mtl.

עיבוד של מודל המבוך
כדי להציג את GreenMaze.obj המודל התלת-ממדי מעל התמונה הדו-ממדית הקיימת, פועלים לפי השלבים הבאים.
ב-AugmentedImageRenderer.java, מוסיפים משתנה חבר שנקרא mazeRenderer כדי להציג את מודל המבוך. המבוך צריך להיות מצורף לתמונה, ולכן הגיוני להוסיף את mazeRenderer בתוך המחלקה AugmentedImageRenderer.
AugmentedImageRenderer.java
// Add a member variable to hold the maze model.
private final ObjectRenderer mazeRenderer = new ObjectRenderer();
בפונקציה createOnGlThread(), טוענים את GreenMaze.obj. כדי לפשט את התהליך, אפשר להשתמש באותו מרקם של המסגרת כמו המרקם של התמונה.
AugmentedImageRenderer.java
// Replace the definition of the createOnGlThread() function with the
// following code, which loads GreenMaze.obj.
public void createOnGlThread(Context context) throws IOException {
mazeRenderer.createOnGlThread(
context, "models/green-maze/GreenMaze.obj", "models/frame_base.png");
mazeRenderer.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);
}
מחליפים את ההגדרה של הפונקציה draw() בהגדרה הבאה. ההגדרה הזו משנה את הגודל של המבוך לגודל של התמונה שזוהתה, ומציגה אותו על המסך.
AugmentedImageRenderer.java
// Adjust size of detected image and render it on-screen
public void draw(
float[] viewMatrix,
float[] projectionMatrix,
AugmentedImage augmentedImage,
Anchor centerAnchor,
float[] colorCorrectionRgba) {
float[] tintColor =
convertHexToColor(TINT_COLORS_HEX[augmentedImage.getIndex() % TINT_COLORS_HEX.length]);
final float mazeEdgeSize = 492.65f; // Magic number of maze size
final float maxImageEdgeSize = Math.max(augmentedImage.getExtentX(), augmentedImage.getExtentZ()); // Get largest detected image edge size
Pose anchorPose = centerAnchor.getPose();
float mazeScaleFactor = maxImageEdgeSize / mazeEdgeSize; // scale to set Maze to image size
float[] modelMatrix = new float[16];
// OpenGL Matrix operation is in the order: Scale, rotation and Translation
// So the manual adjustment is after scale
// The 251.3f and 129.0f is magic number from the maze obj file
// You mustWe need to do this adjustment because the maze obj file
// is not centered around origin. Normally when you
// work with your own model, you don't have this problem.
Pose mazeModelLocalOffset = Pose.makeTranslation(
-251.3f * mazeScaleFactor,
0.0f,
129.0f * mazeScaleFactor);
anchorPose.compose(mazeModelLocalOffset).toMatrix(modelMatrix, 0);
mazeRenderer.updateModelMatrix(modelMatrix, mazeScaleFactor, mazeScaleFactor/10.0f, mazeScaleFactor); // This line relies on a change in ObjectRenderer.updateModelMatrix later in this codelab.
mazeRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
}
עכשיו המבוך אמור להופיע מעל התמונה של כדור הארץ default.jpg.
הערה: מכיוון שאין לכם שליטה מלאה במודל התלת-ממדי לדוגמה הזה, הקוד שלמעלה משתמש בכמה מספרים 'קסומים'. המידות של מודל המבוך הן 492.65 x 120 x 492.65, והמרכז שלו נמצא בנקודה (251.3, 60, -129.0). טווח הערכים של קואורדינטות X, Y ו-Z של הקודקודים הוא [5.02, 497.67], [0, 120] ו-[-375.17, 117.25] בהתאמה. לכן, קנה המידה של מודל המבוך צריך להיות image_size / 492.65. הערך mazeModelLocalOffset מופיע כי המודל התלת-ממדי של המבוך לא ממורכז סביב המקור (0, 0, 0).
הקיר של המבוך עדיין גבוה מדי מכדי להתאים לחלק העליון של התמונה. תצור פונקציית עזר updateModelMatrix() שיכולה לשנות את הגודל של X, Y ו-Z באופן לא אחיד כדי לשנות את הגובה של המבוך ב-0.1. הערה: צריך לשמור את הפונקציה הקיימת updateModelMatrix(float[] modelMatrix, float scaleFactor) ולהוסיף את הפונקציה העמוסה מדי updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) כפונקציה חדשה.
common/rendering/ObjectRenderer.java
// Scale X, Y, Z coordinates unevenly
public void updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) {
float[] scaleMatrix = new float[16];
Matrix.setIdentityM(scaleMatrix, 0);
scaleMatrix[0] = scaleFactorX;
scaleMatrix[5] = scaleFactorY;
scaleMatrix[10] = scaleFactorZ;
Matrix.multiplyMM(this.modelMatrix, 0, modelMatrix, 0, scaleMatrix, 0);
}
מריצים את הקוד. המבוך אמור להתאים עכשיו באופן מושלם מעל התמונה.

4. תעזור לי להוסיף את אנדי למבוך
עכשיו, אחרי שיצרתם מבוך, מוסיפים דמות שתנוע בתוכו. משתמשים בקובץ andy.obj שכלול ב-ARCore Android SDK. שומרים על המרקם של מסגרת התמונה כפי שהוא, כי הוא נראה שונה מהמבוך הירוק שמוצג מעל התמונה.
ב-AugmentedImageRenderer.java, מוסיפים ObjectRenderer פרטי כדי להציג את אנדי.
AugmentedImageRenderer.java
// Render for Andy
private final ObjectRenderer andyRenderer = new ObjectRenderer();
אחר כך, מאתחלים את andyRenderer בסוף createOnGlThread().
AugmentedImageRenderer.java
public void createOnGlThread(Context context) throws IOException {
// Initialize andyRenderer
andyRenderer.createOnGlThread(
context, "models/andy.obj", "models/andy.png");
andyRenderer.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);
}
לבסוף, מציגים את אנדי עומד על גבי המבוך בסוף הפונקציה draw().
AugmentedImageRenderer.java
public void draw(
float[] viewMatrix,
float[] projectionMatrix,
AugmentedImage augmentedImage,
Anchor centerAnchor,
float[] colorCorrectionRgba) {
// Render Andy, standing on top of the maze
Pose andyModelLocalOffset = Pose.makeTranslation(
0.0f,
0.1f,
0.0f);
anchorPose.compose(andyModelLocalOffset).toMatrix(modelMatrix, 0);
andyRenderer.updateModelMatrix(modelMatrix, 0.05f); // 0.05f is a Magic number to scale
andyRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
}
מריצים את הקוד. אנדי אמור להופיע בחלק העליון של המבוך.

קביעת איכות התמונה הרצויה
ARCore מסתמך על תכונות חזותיות כדי לזהות תמונות. בגלל הבדלים באיכות, לא תמיד קל לזהות את כל התמונות.
arcoreimg הוא כלי שורת פקודה שמאפשר לכם לקבוע עד כמה התמונה תהיה מזוהה על ידי ARCore. הפונקציה מחזירה מספר בין 0 ל-100, כאשר 100 הוא הערך הכי קל לזיהוי.
. הנה דוגמה:
arcore-android-sdk-x.xx.x/tools/arcoreimg/macos$
$ ./arcoreimg eval-img --input_image_path=/Users/username/maze.jpg
100
הערך של maze.jpg הוא 100, ולכן קל לזהות אותו באמצעות ARCore.
5. אופציונלי: מזיזים את אנדי במבוך
לבסוף, מוסיפים קוד כדי לגרום לאנדי לנוע במבוך. לדוגמה, אפשר להשתמש במנוע הפיזיקה jBullet בקוד פתוח כדי לטפל בהדמיה הפיזיקלית. אפשר לדלג על החלק הזה.
מורידים את PhysicsController.java ומוסיפים אותו לפרויקט בספרייה
arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/java/com/google/ar/core/examples/java/augmentedimage/
ב-Android Studio, מוסיפים את GreenMaze.obj לספרייה project assets כדי שיהיה אפשר לטעון אותו בזמן הריצה. מעתיקים את GreenMaze.obj מ-app > assets > models > green-maze אל app > assets.
מוסיפים את יחסי התלות הבאים לקובץ build.gradle של האפליקציה.
app/build.gradle
// jbullet library
implementation 'cz.advel.jbullet:jbullet:20101010-1'
מגדירים משתנה andyPose כדי לאחסן את המיקום של התנוחה הנוכחית של אנדי.
AugmentedImageRenderer.java
// Create a new pose for the Andy
private Pose andyPose = Pose.IDENTITY;
משנים את AugmentedImageRenderer.java כך שהדמות של אנדי תוצג באמצעות המשתנה החדש andyPose.
AugmentedImageRenderer.java
public void draw(
float[] viewMatrix,
float[] projectionMatrix,
AugmentedImage augmentedImage,
Anchor centerAnchor,
float[] colorCorrectionRgba) {
// Use these code to replace previous code for rendering the Andy object
//
// Adjust the Andy's rendering position
// The Andy's pose is at the maze's vertex's coordinate
Pose andyPoseInImageSpace = Pose.makeTranslation(
andyPose.tx() * mazeScaleFactor,
andyPose.ty() * mazeScaleFactor,
andyPose.tz() * mazeScaleFactor);
anchorPose.compose(andyPoseInImageSpace).toMatrix(modelMatrix, 0);
andyRenderer.updateModelMatrix(modelMatrix, 0.05f);
andyRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
}
הוספנו פונקציית עזר חדשה, updateAndyPose(), לקבלת עדכונים לגבי התנוחה של אנדי.
AugmentedImageRenderer.java
// Receive Andy pose updates
public void updateAndyPose(Pose pose) {
andyPose = pose;
}
ב-AugmentedImageActivity.java, יוצרים אובייקט PhysicsController שמשתמש במנוע הפיזיקה JBullet כדי לנהל את כל הפונקציות שקשורות לפיזיקה.
AugmentedImageActivity.java
import com.google.ar.core.Pose;
// Declare the PhysicsController object
private PhysicsController physicsController;
במנוע הפיזיקה, אנחנו משתמשים בכדור קשיח כדי לייצג את אנדי ומעדכנים את התנוחה שלו באמצעות התנוחה של הכדור. מתקשרים אל PhysicsController כדי לעדכן את הפיזיקה בכל פעם שהאפליקציה מזהה תמונה. כדי להזיז את הכדור כמו בעולם האמיתי, צריך להחיל כוח משיכה כמו בעולם האמיתי כדי להזיז את הכדור במבוך.
AugmentedImageActivity.java
// Update the case clause for TRACKING to call PhysicsController
// whenever the app recognizes an image
private void drawAugmentedImages(
...
case TRACKING:
// Switch to UI Thread to update View
this.runOnUiThread(
new Runnable() {
@Override
public void run() {
fitToScanView.setVisibility(View.GONE);
}
});
// Create a new anchor for newly found images
if (!augmentedImageMap.containsKey(augmentedImage.getIndex())) {
Anchor centerPoseAnchor = augmentedImage.createAnchor(augmentedImage.getCenterPose());
augmentedImageMap.put(
augmentedImage.getIndex(), Pair.create(augmentedImage, centerPoseAnchor));
physicsController = new PhysicsController(this);
} else {
Pose ballPose = physicsController.getBallPose();
augmentedImageRenderer.updateAndyPose(ballPose);
// Use real world gravity, (0, -10, 0), as gravity
// Convert to Physics world coordinate(maze mesh has to be static)
// Use the converted coordinate as a force to move the ball
Pose worldGravityPose = Pose.makeTranslation(0, -10f, 0);
Pose mazeGravityPose = augmentedImage.getCenterPose().inverse().compose(worldGravityPose);
float mazeGravity[] = mazeGravityPose.getTranslation();
physicsController.applyGravityToBall(mazeGravity);
physicsController.updatePhysics();
}
break;
מפעילים את האפליקציה. עכשיו, כשתטו את התמונה, אנדי אמור לנוע בצורה ריאליסטית.
בדוגמה שלמטה השתמשנו בטלפון אחר כדי להציג את התמונה, אבל אתם יכולים להשתמש בכל דבר שנוח לכם, כמו טאבלט, הכריכה של ספר מודפס או פשוט דף מודפס שמחובר לאובייקט שטוח.

זהו! תיהנו לנסות להוציא את אנדי מהמבוך. טיפ: קל יותר למצוא את היציאה כשמחזיקים את תמונת המטרה הפוכה.
6. מזל טוב
כל הכבוד, הגעתם לסוף ה-Codelab הזה, ולכן:
- פיתוח והרצה של דוגמה ל-AugmentedImage Java ב-ARCore.
- הדוגמה עודכנה כך שיוצג מודל של מבוך בתמונה, בקנה מידה מתאים.
- השתמשתי בתנוחה שבתמונה כדי לעשות משהו כיפי.
אם רוצים לעיין בקוד המלא, אפשר להוריד אותו כאן.