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

זו הפעם הראשונה שאתם יוצרים אפליקציית 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).

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