XR_EXT_spatial_anchor

מחרוזת שם

XR_EXT_spatial_anchor

סוג התוסף

תוסף למכונה

מספר השלוחה הרשום

763

גרסה

1

סטטוס האישור

אושר

תלויות של תוספים וגרסאות

XR_EXT_spatial_entity

שותפים ביצירת התוכן

Nihav Jain, Google
Natalie Fleury, Meta
Yuichi Taguchi, Meta
Ron Bessems, Meta
Yin Li, Microsoft
Jimmy Alamparambil, ByteDance
Zhipeng Liu, ByteDance
Jun Yan, ByteDance

סקירה כללית

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

עוגן שעוקב אחרי מיקום וכיוון נתונים בתוך XrSpatialContextEXT מיוצג כישות מרחבית עם הרכיב XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT (או "שיש לה" את הרכיב הזה).

יתרונות השימוש בעוגנים

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

תמיכה בסביבת זמן ריצה

אם זמן הריצה תומך בעוגנים מרחביים, הוא חייב לציין זאת על ידי ספירת XR_SPATIAL_CAPABILITY_ANCHOR_EXT ב-xrEnumerateSpatialCapabilitiesEXT .

הגדרות אישיות

המבנה של XrSpatialCapabilityConfigurationAnchorEXT מוגדר כך:

typedef struct XrSpatialCapabilityConfigurationAnchorEXT {
    XrStructureType                     type;
    const void*                         next;
    XrSpatialCapabilityEXT              capability;
    uint32_t                            enabledComponentCount;
    const XrSpatialComponentTypeEXT*    enabledComponents;
} XrSpatialCapabilityConfigurationAnchorEXT;

תיאורי חברים

  • ‫type הוא ה-XrStructureType של המבנה הזה.
  • ‫next הוא NULL או מצביע למבנה הבא בשרשרת מבנים.
  • capability הוא XrSpatialCapabilityEXT .
  • ‫enabledComponentCount הוא uint32_t שמתאר את מספר הרכיבים במערך enabledComponents.
  • ‫enabledComponents הוא מצביע למערך של XrSpatialComponentTypeEXT .

אפליקציות יכולות להפעיל את היכולת המרחבית XR_SPATIAL_CAPABILITY_ANCHOR_EXT על ידי הכללת מצביע למבנה XrSpatialCapabilityConfigurationAnchorEXT ב-XrSpatialContextCreateInfoEXT :: capabilityConfigs .

זמן הריצה חייב להחזיר את הערך XR_ERROR_VALIDATION_FAILURE אם capability הוא לא XR_SPATIAL_CAPABILITY_ANCHOR_EXT .

שימוש תקין (משתמע)

רכיבים מובטחים

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

  • XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT

רכיב העוגן

נתוני רכיבים

ה-XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT משתמש ב-XrPosef לנתונים שלו, שמספקים את המיקום והכיוון של נקודת העיגון.

מבנה רשימת הרכיבים לשליפת נתונים

המבנה של XrSpatialComponentAnchorListEXT מוגדר כך:

typedef struct XrSpatialComponentAnchorListEXT {
    XrStructureType    type;
    void*              next;
    uint32_t           locationCount;
    XrPosef*           locations;
} XrSpatialComponentAnchorListEXT;

תיאורי חברים

  • ‫type הוא ה-XrStructureType של המבנה הזה.
  • ‫next הוא NULL או מצביע למבנה הבא בשרשרת מבנים.
  • ‫locationCount הוא uint32_t שמתאר את מספר הרכיבים במערך locations.
  • ‫locations הוא מערך של XrPosef .

זמן הריצה חייב להחזיר את הערך XR_ERROR_VALIDATION_FAILURE מ-xrQuerySpatialComponentDataEXT אם XrSpatialComponentAnchorListEXT נמצא בשרשרת XrSpatialComponentDataQueryResultEXT :: next אבל XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT לא נכלל ב-XrSpatialComponentDataQueryConditionEXT :: componentTypes .

זמן הריצה חייב להחזיר את הערך XR_ERROR_SIZE_INSUFFICIENT מ-xrQuerySpatialComponentDataEXT אם locationCount קטן מ-XrSpatialComponentDataQueryResultEXT :: entityIdCountOutput .

שימוש תקין (משתמע)

  • ‫ חובה להפעיל את התוסף XR_EXT_spatial_anchor לפני שמשתמשים ב-XrSpatialComponentAnchorListEXT
  • type חייב להיות XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT
  • next חייב להיות NULL או מצביע תקין למבנה הבא בשרשרת מבנים
  • locations חייב להיות מצביע למערך של מבני locationCount XrPosef
  • ‫ הפרמטר locationCount חייב להיות גדול מ-0
הגדרות אישיות

אם XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT מופיע ב-XrSpatialCapabilityComponentTypesEXT :: componentTypes עבור יכולת מסוימת, אפליקציה יכולה להפעיל אותה על ידי הכללת הערך ברשימה XrSpatialCapabilityConfigurationBaseHeaderEXT :: enabledComponents של המבנה הנגזר XrSpatialCapabilityConfigurationBaseHeaderEXT של היכולת שתומכת ברכיב הזה.

הרכיב הזה לא דורש הגדרה מיוחדת כדי להיכלל בשרשרת XrSpatialCapabilityConfigurationBaseHeaderEXT :: next.

יצירת עוגן מרחבי

הפונקציה xrCreateSpatialAnchorEXT מוגדרת כך:

XrResult xrCreateSpatialAnchorEXT(
    XrSpatialContextEXT                         spatialContext,
    const XrSpatialAnchorCreateInfoEXT*         createInfo,
    XrSpatialEntityIdEXT*                       anchorEntityId,
    XrSpatialEntityEXT*                         anchorEntity);

תיאורי פרמטרים

האפליקציה יכולה ליצור עוגן מרחבי באמצעות xrCreateSpatialAnchorEXT .

כדי לקבל נתוני רכיבים מעודכנים של עוגן, מעבירים את הערך שמאוכלס ב-anchorEntity אל XrSpatialUpdateSnapshotCreateInfoEXT :: entities כשיוצרים תמונת מצב. האפליקציה יכולה להשתמש ב-anchorEntityId כדי לזהות באופן ייחודי את נקודת העיגון הזו במערך XrSpatialComponentDataQueryResultEXT :: entityIds כשמשתמשים ב-xrQuerySpatialComponentDataEXT .

זמן הריצה חייב להחזיר את הערך XR_ERROR_VALIDATION_FAILURE מ-xrCreateSpatialAnchorEXT אם XR_SPATIAL_CAPABILITY_ANCHOR_EXT לא הוגדר עבור spatialContext . בקטע הגדרה מוסבר איך להגדיר XrSpatialContextEXT ליכולת XR_SPATIAL_CAPABILITY_ANCHOR_EXT.

העוגן שמיוצג על ידי anchorEntity תקף רק למשך החיים של spatialContext , או עד שהאפליקציה מפעילה את xrDestroySpatialEntityEXT, המוקדם מביניהם. תוספים אחרים עשויים להציע פונקציות לשמירת העוגן החדש שנוצר בכמה XrSession או לשיתוף שלו עם אפליקציות אחרות מעבר לגבולות התהליך.

עד שנקודת עיגון חדשה שנוצרה נמחקת, חייבת להיות אפשרות לגלות אותה בהקשר המרחבי הראשי שלה. המשמעות היא שסביבת זמן הריצה חייבת לכלול את anchorEntityId בתמונת המצב שנוצרה באמצעות xrCreateSpatialDiscoverySnapshotAsyncEXT עבור spatialContext אם נקודת העיגון תואמת לקריטריונים של הגילוי שהוגדרו ב-XrSpatialDiscoverySnapshotCreateInfoEXT . יכול להיות שגם בהקשרים מרחביים אחרים שמוגדרים עם XR_SPATIAL_CAPABILITY_ANCHOR_EXT אפשר יהיה לגלות את העוגן החדש שנוצר, אבל עם XrSpatialEntityIdEXT אחר, כי XrSpatialEntityIdEXT מסוים הוא ייחודי ל-XrSpatialContextEXT שלו.

שימוש תקין (משתמע)

  • חובה להפעיל את התוסף XR_EXT_spatial_anchor לפני שקוראים לפונקציה xrCreateSpatialAnchorEXT
  • spatialContext חייב להיות כינוי תקין של XrSpatialContextEXT
  • ‫ createInfo חייב להיות מצביע למבנה XrSpatialAnchorCreateInfoEXT תקין
  • ‫ anchorEntityId חייב להיות מצביע לערך XrSpatialEntityIdEXT
  • ‫ anchorEntity חייב להיות מצביע לכינוי XrSpatialEntityEXT

קודי החזרה

הצלחה

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

נכשל

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_LIMIT_REACHED
  • XR_ERROR_OUT_OF_MEMORY
  • XR_ERROR_POSE_INVALID
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SESSION_LOST
  • XR_ERROR_TIME_INVALID
  • XR_ERROR_VALIDATION_FAILURE
  • ‫XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID (אם XR_ANDROID_spatial_entity_bound_anchor מופעל)
  • ‫XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT (אם XR_ANDROID_spatial_entity_bound_anchor מופעל)

המבנה של XrSpatialAnchorCreateInfoEXT מוגדר כך:

typedef struct XrSpatialAnchorCreateInfoEXT {
    XrStructureType    type;
    const void*        next;
    XrSpace            baseSpace;
    XrTime             time;
    XrPosef            pose;
} XrSpatialAnchorCreateInfoEXT;

תיאורי חברים

  • ‫type הוא ה-XrStructureType של המבנה הזה.
  • ‫next הוא NULL או מצביע למבנה הבא בשרשרת מבנים.
  • ‫baseSpace הוא ה-XrSpace שבו חל pose.
  • ‫time הוא XrTime שבו נמצא baseSpace (ומוחל pose).
  • ‫pose הוא המיקום של ישות העוגן.

שימוש תקין (משתמע)

תנוחת העוגן של השאילתה

אחרי שנוצר העוגן, זמן הריצה אמור להתאים את המיקום והכיוון שלו לאורך זמן ביחס למרחבים אחרים כדי לשמור על ההתאמה הטובה ביותר למיקום המקורי שלו בעולם האמיתי, גם אם זה משנה את הקשר של העוגן ל-XrSpatialAnchorCreateInfoEXT :: baseSpace המקורי ששימש לאתחול שלו.

האפליקציה יכולה להשתמש ב-xrCreateSpatialUpdateSnapshotEXT עם XrSpatialEntityEXT של העוגן כדי ליצור XrSpatialSnapshotEXT חדש, ואז לשלוח שאילתה לרכיב XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT מאותו קובץ snapshot באמצעות xrQuerySpatialComponentDataEXT . האפליקציה יכולה להוסיף את XrSpatialComponentAnchorListEXT אל XrSpatialComponentDataQueryResultEXT :: next כדי לאחזר את נתוני המיקום העדכניים ביותר של נקודות העיגון.

יכול להיות שסביבת זמן הריצה תגדיר את מצב המעקב של עוגן חדש שנוצר ל-XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT . האפליקציה חייבת לקרוא רק את המצב של ישות העוגן שסופק ב-XrSpatialComponentDataQueryResultEXT :: entityStates ואת נתוני רכיב העוגן של הישות אם מצב המעקב הוא XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT .

הנחיות לשימוש בעוגנים

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

קוד לדוגמה

הגדרת יכולת העוגן

בדוגמה הבאה מוסבר איך להגדיר את יכולת העיגון כשיוצרים הקשר מרחבי.

// Create a spatial spatial context
XrSpatialContextEXT spatialContext{};
{

  std::vector<XrSpatialComponentTypeEXT> enabledComponents = {
    XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT,
  };

  XrSpatialCapabilityConfigurationAnchorEXT anchorConfig{XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT};
  anchorConfig.capability = XR_SPATIAL_CAPABILITY_ANCHOR_EXT;
  anchorConfig.enabledComponentCount = enabledComponents.size();
  anchorConfig.enabledComponents = enabledComponents.data();

  std::array<XrSpatialCapabilityConfigurationBaseHeaderEXT*, 1> capabilityConfigs = {
    reinterpret_cast<XrSpatialCapabilityConfigurationBaseHeaderEXT*>(&anchorConfig),
  };

  XrSpatialContextCreateInfoEXT spatialContextCreateInfo{XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT};
  spatialContextCreateInfo.capabilityConfigCount = capabilityConfigs.size();
  spatialContextCreateInfo.capabilityConfigs = capabilityConfigs.data();
  XrFutureEXT createContextFuture;
  CHK_XR(xrCreateSpatialContextAsyncEXT(session, &spatialContextCreateInfo, &createContextFuture));

  waitUntilReady(createContextFuture);

  XrCreateSpatialContextCompletionEXT completion{XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT};
  CHK_XR(xrCreateSpatialContextCompleteEXT(session, createContextFuture, &completion));
  if (completion.futureResult != XR_SUCCESS) {
    return;
  }

  spatialContext = completion.spatialContext;
}

// ...
// Create spatial anchors and get their latest pose in the frame loop.
// ...

CHK_XR(xrDestroySpatialContextEXT(spatialContext));

יצירת עוגן מרחבי וקבלת המיקום שלו

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

XrSpatialAnchorCreateInfoEXT createInfo{XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT};
createInfo.baseSpace = localSpace;
createInfo.time = predictedDisplayTime;
createInfo.pose = {{0, 0, 0, 1}, {1, 1, 1}};

XrSpatialEntityIdEXT spatialAnchorEntityId;
XrSpatialEntityEXT spatialAnchorEntity;
CHK_XR(xrCreateSpatialAnchorEXT(spatialContext, &createInfo, &spatialAnchorEntityId, &spatialAnchorEntity));

auto updateAnchorLocation = [&](XrTime time) {
  // We want to get updated data for all components of the entities, so skip specifying componentTypes.
  XrSpatialUpdateSnapshotCreateInfoEXT snapshotCreateInfo{XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT};
  snapshotCreateInfo.entityCount = 1;
  snapshotCreateInfo.entities = &spatialAnchorEntity;
  snapshotCreateInfo.baseSpace = localSpace;
  snapshotCreateInfo.time = time;

  XrSpatialSnapshotEXT snapshot;
  CHK_XR(xrCreateSpatialUpdateSnapshotEXT(spatialContext, &snapshotCreateInfo, &snapshot));

  // Query for the entities that have the anchor component on them.
  std::array<XrSpatialComponentTypeEXT, 1> componentsToQuery {XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT};
  XrSpatialComponentDataQueryConditionEXT queryCond{XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT};
  queryCond.componentTypeCount = componentsToQuery.size();
  queryCond.componentTypes = componentsToQuery.data();

  XrSpatialComponentDataQueryResultEXT queryResult{XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT};
  CHK_XR(xrQuerySpatialComponentDataEXT(snapshot, &queryCond, &queryResult));

  std::vector<XrSpatialEntityIdEXT> entityIds(queryResult.entityIdCountOutput);
  std::vector<XrSpatialEntityTrackingStateEXT> entityStates(queryResult.entityIdCountOutput);
  queryResult.entityIdCapacityInput = entityIds.size();
  queryResult.entityIds = entityIds.data();
  queryResult.entityStateCapacityInput = entityStates.size();
  queryResult.entityStates = entityStates.data();

  // query for the pose data
  std::vector<XrPosef> locations(queryResult.entityIdCountOutput);
  XrSpatialComponentAnchorListEXT locationList{XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT};
  locationList.locationCount = locations.size();
  locationList.locations = locations.data();
  queryResult.next = &locationList;

  CHK_XR(xrQuerySpatialComponentDataEXT(snapshot, &queryCond, &queryResult));

  for (int32_t i = 0; i < queryResult.entityIdCountOutput; ++i) {
    if (entityStates[i] == XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT) {
      // Pose for entity entityIds[i] is locations[i].
    }
  }

  CHK_XR(xrDestroySpatialSnapshotEXT(snapshot));
};


while (1) {
  // ...
  // For every frame in frame loop
  // ...

  XrFrameState frameState;  // previously returned from xrWaitFrame
  const XrTime time = frameState.predictedDisplayTime;

  updateAnchorLocation(time);

  // ...
  // Finish frame loop
  // ...
}

CHK_XR(xrDestroySpatialEntityEXT(spatialAnchorEntity));

פקודות חדשות

מבנים חדשים

קבועי Enum חדשים

  • XR_EXT_SPATIAL_ANCHOR_EXTENSION_NAME
  • XR_EXT_spatial_anchor_SPEC_VERSION
  • הרחבה של XrSpatialCapabilityEXT :

    • XR_SPATIAL_CAPABILITY_ANCHOR_EXT
  • הרחבה של XrSpatialComponentTypeEXT :

    • XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT
  • הרחבה של XrStructureType :

    • XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT
    • XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT
    • XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT

בעיות

  • למה xrCreateSpatialAnchorEXT מוציא מזהה ישות וגם נקודת אחיזה של ישות?

    • טופל
    • תשובה: הפונקציה xrCreateSpatialAnchorEXT הייתה יכולה לספק רק את מזהה הישות כפלט, ואפליקציות היו יכולות ליצור נקודת אחיזה של ישות למזהה הזה באמצעות xrCreateSpatialEntityFromIdEXT . עם זאת, בהתחשב בשימוש הרגיל בנקודת עיגון, שבו אפליקציות שולחות שאילתות לגבי תנוחת נקודת העיגון בכל פריים, היא הופכת למועמדת טובה לשימוש ב "עדכון תמונת מצב", שדורש נקודות אחיזה של ישויות כקלט. כדי לתת מענה לתרחיש השימוש הנפוץ הזה, xrCreateSpatialAnchorEXT מבצע את xrCreateSpatialEntityFromIdEXT בשם האפליקציה ומספק לה את נקודת האחיזה של הישות לשימוש עם xrCreateSpatialUpdateSnapshotEXT .

היסטוריית הגרסאות

  • גרסה 1, 10.07.2024 (Nihav Jain, Google)

    • תיאור התוסף הראשוני