מדריך למפתחים למיקומים מיידיים ב-Android NDK

תוכלו ללמוד איך להשתמש ב-Instant Placement API באפליקציות שלכם.

דרישות מוקדמות

לפני שממשיכים, חשוב לוודא שאתם מבינים את המושגים הבסיסיים של AR ואת האופן שבו מגדירים סשן של ARCore.

הגדרת סשן חדש באמצעות 'מיקום מיידי'

בסשן חדש של ARCore, צריך להפעיל את המצב 'מיקום מיידי'.

// Create a session config.
ArConfig* ar_config = NULL;
ArConfig_create(ar_session, &ar_config);

// Enable Instant Placement mode.
ArConfig_setInstantPlacementMode(ar_session, ar_config,
                                 AR_INSTANT_PLACEMENT_MODE_LOCAL_Y_UP);
CHECK(ArSession_configure(ar_session, ar_config) == AR_SUCCESS);

// Release config resources.
ArConfig_destroy(ar_config);

מציבים חפץ

בסשן חדש ב-ARCore, מבצעים היט-test של מיקום מיידי באמצעות ArFrame_hitTestInstantPlacement. לאחר מכן, יוצרים ArAnchor חדש תוך שימוש בתנוחה ב-ArInstantPlacementPoint מתוך ARTrackable של תוצאת ההיט.

ArFrame* ar_frame = NULL;
if (ArSession_update(ar_session, ar_frame) != AR_SUCCESS) {
  // Get the latest frame.
  LOGE("ArSession_update error");
  return;
}

// Place an object on tap.
// Use the estimated distance from the user's device to the closest
// available surface, based on expected user interaction and behavior.
float approximate_distance_meters = 2.0f;

ArHitResultList* hit_result_list = NULL;
ArHitResultList_create(ar_session, &hit_result_list);
CHECK(hit_result_list);

// Returns a single result if the hit test was successful.
ArFrame_hitTestInstantPlacement(ar_session, ar_frame, x, y,
                                approximate_distance_meters, hit_result_list);

int32_t hit_result_list_size = 0;
ArHitResultList_getSize(ar_session, hit_result_list, &hit_result_list_size);
if (hit_result_list_size > 0) {
  ArHitResult* ar_hit_result = NULL;
  ArHitResult_create(ar_session, &ar_hit_result);
  CHECK(ar_hit_result);
  ArHitResultList_getItem(ar_session, hit_result_list, 0, ar_hit_result);
  if (ar_hit_result == NULL) {
    LOGE("ArHitResultList_getItem error");
    return;
  }

  ArTrackable* ar_trackable = NULL;
  ArHitResult_acquireTrackable(ar_session, ar_hit_result, &ar_trackable);
  if (ar_trackable == NULL) {
    LOGE("ArHitResultList_acquireTrackable error");
    return;
  }
  ArTrackableType ar_trackable_type = AR_TRACKABLE_NOT_VALID;
  ArTrackable_getType(ar_session, ar_trackable, &ar_trackable_type);

  if (ar_trackable_type == AR_TRACKABLE_INSTANT_PLACEMENT_POINT) {
    ArInstantPlacementPoint* point = (ArInstantPlacementPoint*)ar_trackable;

    // Gets the pose of the Instant Placement point.
    ArPose* ar_pose = NULL;
    ArPose_create(ar_session, NULL, &ar_pose);
    CHECK(ar_pose);
    ArInstantPlacementPoint_getPose(ar_session, point, ar_pose);

    // Attaches an anchor to the Instant Placement point.
    ArAnchor* anchor = NULL;
    ArStatus status = ArTrackable_acquireNewAnchor(ar_session, ar_trackable,
                                                   ar_pose, &anchor);
    ArPose_destroy(ar_pose);
    // Render content at the anchor.
    // ...
  }

  ArTrackable_release(ar_trackable);
}

התכונה 'מיקום מיידי' תומכת במעקב אחרי השטח במסך עם מרחק משוער, ומעבר אוטומטי למעקב מלא ברגע שנקודת המיקום המיידי מעוגנת בעולם האמיתי. מאחזרים את התנוחה הנוכחית בעזרת ArInstantPlacementPoint_getPose(). מקבלים את שיטת המעקב הנוכחית באמצעות ArInstantPlacementPoint_getTrackingMethod().

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

מעקב אחר שיטת המעקב אחר נקודות של מיקום מיידי

אם ב-ARCore יש תנוחת תלת-ממד מדויקת לArInstantPlacementPoint שהוחזר על ידי ArFrame_hitTestInstantPlacement, היא מתחילה בשיטת המעקב AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING. אחרת, הוא יתחיל עם שיטת המעקב AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE ויעבור ל-AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING אחרי שתהיה ב-ARCore מיקום מדויק בתלת-ממד. כששיטת המעקב תהיה AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING, היא לא תחזור ל-AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE.

התאמת המעבר של שיטת המעקב

כששיטת המעקב משתנה מ-AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE בפריים אחד ל-AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING בפריים הבא, התנוחה קופצת מהמיקום הראשוני בהתאם למרחק המשוער שסופק למיקום חדש, במרחק מדויק. השינוי המידי הזה בתנוחה משנה את קנה המידה הנראי של האובייקטים שמוצמדים ל-arInstantPositionPoint. כלומר, אובייקט מופיע פתאום גדול או קטן יותר ממה שהוא היה בפריים הקודם.

כדי למנוע את הקפיצה החזותית בגלל השינוי הפתאומי בסולם האובייקט לכאורה:

  1. מומלץ לעקוב אחרי התנוחה ושיטת המעקב של ArInstantPlacementPoint בכל פריים.
  2. ממתינים עד ששיטת המעקב תשתנה ל-AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING.
  3. כדי לקבוע את המרחק של העצם למכשיר בשתי התמונות, משתמשים בנקודת התנוחה מהפריים הקודם ובתנוחה הנוכחית בפריים הנוכחי.
  4. חשבו את השינוי הגלוי בקנה המידה כתוצאה משינוי המרחק מהמצלמה.
  5. התאימו את קנה המידה של האובייקט כדי להתנגד לשינוי הנתפס בקנה המידה, כך שהגודל של האובייקט לא ישתנה.
  6. אפשר גם לשנות את קנה המידה של האובייקט באופן חלק בחזרה לערך המקורי שלו במספר פריימים.
class WrappedInstantPlacement {
  ArInstantPlacementPoint* point;
  ArInstantPlacementPointTrackingMethod previous_tracking_method;
  float previous_distance_to_camera;
  float scale_factor = 1.0f;

 public:
  WrappedInstantPlacement(ArInstantPlacementPoint* point,
                          TrackingMethod previous_tracking_method,
                          float previous_distance_to_camera) {
    this.point = point;
    this.previous_tracking_method = previous_tracking_method;
    this.previous_distance_to_camera = previous_distance_to_camera;
  }
};

std::vector<WrappedInstantPlacement> wrapped_points_;

אחרי שיוצרים את נקודת המיקום המיידי, משנים את OnTouched() כדי להקיף אותה.

if (ar_trackable_type == AR_TRACKABLE_INSTANT_PLACEMENT_POINT) {
  ArInstantPlacementPoint* point = (ArInstantPlacementPoint*)ar_trackable;
  ArInstantPlacementPointTrackingMethod tracking_method;
  ArInstantPlacementPoint_getTrackingMethod(ar_session, point,
                                            &tracking_method);
  ArCamera* ar_camera = nullptr;
  ArFrame_acquireCamera(ar_session, ar_frame, &ar_camera);
  CHECK(ar_camera);
  wrapped_points_.push_back(WrappedInstantPlacement(
      point, tracking_method, Distance(point, ar_camera)));
}

כששיטת המעקב של נקודת המיקום המיידי עוברת מ-AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE ל-AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING, משתמשים במרחק שנשמר כדי להתמודד עם השינוי לכאורה בקנה המידה.

void OnUpdate() {
  for (auto& wrapped_point : wrapped_points_) {
    ArInstantPlacementPoint* point = wrapped_point.point;

    ArTrackingState tracking_state = AR_TRACKING_STATE_STOPPED;
    ArTrackable_getTrackingState(ar_session, (ArTrackable)point,
                                 &tracking_state);

    if (tracking_state == AR_TRACKING_STATE_STOPPED) {
      wrapped_points_.remove(wrapped_point);
      continue;
    }
    if (tracking_state == AR_TRACKING_STATE_PAUSED) {
      continue;
    }

    ArInstantPlacementPointTrackingMethod tracking_method;
    ArInstantPlacementPoint_getTrackingMethod(ar_session, point,
                                              &tracking_method);
    ArCamera* ar_camera = nullptr;
    ArFrame_acquireCamera(ar_session, ar_frame, &ar_camera);
    CHECK(ar_camera);
    const float distance_to_camera = Distance(point, ar_camera);
    if (tracking_method ==
        AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
      // Continue to use the estimated depth and pose. Record the distance to
      // the camera for use in the next frame if the transition to full
      // tracking happens.
      wrapped_point.previous_distance_to_camera = distance_to_camera;
    } else if (
        tracking_method ==
            AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING &&
        wrapped_point.previous_tracking_method ==
            AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
      // Change from the estimated pose to the accurate pose. Adjust the
      // object scale to counteract the apparent change due to pose jump.
      wrapped_point.scale_factor =
          distance_to_camera / wrapped_point.previous_distance_to_camera;
      // Apply the scale factor to the model.
      // ...
      previous_tracking_method =
          AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING;
    }
  }
}

שיקולי ביצועים

כשמיקום מיידי מופעל, ARCore צורך מספר מחזורי CPU נוספים. אם יש לכם חששות בנוגע לביצועים, מומלץ להשבית את התכונה 'מיקום מיידי' אחרי שהמשתמש הציג את האובייקט שלו בהצלחה ושיטת המעקב של כל נקודות המיקום המיידי השתנתה ל-AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING.

כשהתכונה 'מיקום מיידי' מושבתת, יש להשתמש ב-ArFrame_hitTest במקום ב-ArFrame_hitTestInstantPlacement.

ArConfig* ar_config = NULL;
ArConfig_create(ar_session, &ar_config);
// Disable Instant Placement.
ArConfig_setInstantPlacementMode(ar_session, ar_config,
                                 AR_INSTANT_PLACEMENT_MODE_DISABLED);
CHECK(ArSession_configure(ar_session, ar_config) == AR_SUCCESS);
ArConfig_destroy(ar_config);