XR_EXT_spatial_anchor
名稱字串
XR_EXT_spatial_anchor
擴充功能類型
執行個體擴充功能
擴充功能註冊編號
763
修訂版本
1
批准狀態
批准
擴充功能和版本依附元件
XR_EXT_spatial_entity
著作人
Google 的 Nihav Jain
Meta 的 Natalie Fleury
Meta 的 Yuichi Taguchi
Meta 的 Ron Bessems
Microsoft 的 Yin Li
ByteDance 的 Jimmy Alamparambil
ByteDance 的 Zhipeng Liu
ByteDance 的 Jun Yan
總覽
這項擴充功能以 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 陣列的指標。
應用程式可以在 XrSpatialContextCreateInfoEXT :: capabilityConfigs 中加入 XrSpatialCapabilityConfigurationAnchorEXT 結構的指標,啟用 XR_SPATIAL_CAPABILITY_ANCHOR_EXT 空間功能。
如果 capability 不是 XR_SPATIAL_CAPABILITY_ANCHOR_EXT,執行階段「必須」傳回 XR_ERROR_VALIDATION_FAILURE。
有效使用 (隱含)
-
XR_EXT_spatial_anchor擴充功能必須先啟用,才能使用 XrSpatialCapabilityConfigurationAnchorEXT -
type必須為XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT -
next必須是NULL,或是結構體鏈結中下一個結構體的有效指標 -
capability必須是有效的 XrSpatialCapabilityEXT 值 -
enabledComponents必須是指向enabledComponentCount個有效 XrSpatialComponentTypeEXT 值的陣列的指標 -
enabledComponentCount參數必須大於0
保證元件
支援 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 的陣列。
如果 XrSpatialComponentAnchorListEXT 位於 XrSpatialComponentDataQueryResultEXT :: next 鏈結中,但 XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT 未包含在 XrSpatialComponentDataQueryConditionEXT :: componentTypes 中,則執行階段「必須」從 xrQuerySpatialComponentDataEXT 傳回 XR_ERROR_VALIDATION_FAILURE。
如果 locationCount 小於 XrSpatialComponentDataQueryResultEXT :: entityIdCountOutput,執行階段必須從 xrQuerySpatialComponentDataEXT 傳回 XR_ERROR_SIZE_INSUFFICIENT。
有效使用 (隱含)
-
XR_EXT_spatial_anchor擴充功能必須先啟用,才能使用 XrSpatialComponentAnchorListEXT -
type必須為XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT -
next必須是NULL,或是結構體鏈結中下一個結構體的有效指標 -
locations必須是指向locationCountXrPosef 結構體陣列的指標 -
locationCount參數必須大於0
設定
如果 XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT 列舉於 XrSpatialCapabilityComponentTypesEXT :: componentTypes 的某項功能中,應用程式可以在支援此元件的功能的 XrSpatialCapabilityConfigurationBaseHeaderEXT 衍生結構體中,將列舉值納入 XrSpatialCapabilityConfigurationBaseHeaderEXT :: enabledComponents 清單,藉此啟用該功能。
如要將這個元件納入 XrSpatialCapabilityConfigurationBaseHeaderEXT :: next 鏈結,不需要進行任何特殊設定。
建立空間錨點
xrCreateSpatialAnchorEXT 函式定義如下:
XrResult xrCreateSpatialAnchorEXT(
XrSpatialContextEXT spatialContext,
const XrSpatialAnchorCreateInfoEXT* createInfo,
XrSpatialEntityIdEXT* anchorEntityId,
XrSpatialEntityEXT* anchorEntity);
參數說明
spatialContext是先前使用 xrCreateSpatialContextAsyncEXT 建立的 XrSpatialContextEXT。createInfo是 XrSpatialAnchorCreateInfoEXT 的指標。anchorEntityId是XrSpatialEntityIdEXT的指標,錨點實體的 ID 會傳回至該指標。anchorEntity是 XrSpatialEntityEXT 的指標,錨點實體的控制代碼會傳回至該指標。
應用程式可以使用 xrCreateSpatialAnchorEXT 建立空間錨點。
如要取得錨點的更新元件資料,請在建立快照時,將 anchorEntity 中填入的值傳遞至 XrSpatialUpdateSnapshotCreateInfoEXT :: entities。應用程式可以使用 anchorEntityId,在使用 xrQuerySpatialComponentDataEXT 時,在 XrSpatialComponentDataQueryResultEXT :: entityIds 陣列中,準確識別這個錨點。
如果未針對 spatialContext 設定 XR_SPATIAL_CAPABILITY_ANCHOR_EXT,執行階段必須從 xrCreateSpatialAnchorEXT 傳回 XR_ERROR_VALIDATION_FAILURE。如要為 XR_SPATIAL_CAPABILITY_ANCHOR_EXT 功能設定 XrSpatialContextEXT,請參閱「設定」。
anchorEntity 代表的錨點僅在 spatialContext 的生命週期內有效,或直到應用程式對其呼叫 xrDestroySpatialEntityEXT 為止 (以較早者為準)。其他擴充功能可能提供功能,可在多個 XrSession 中保留這個新建立的錨點,或跨程序界線與其他應用程式共用。
新建立的錨點必須可在父項空間環境中探索,直到遭到毀損為止。也就是說,如果錨點符合 XrSpatialDiscoverySnapshotCreateInfoEXT 中設定的探索條件,執行階段必須在透過 xrCreateSpatialDiscoverySnapshotAsyncEXT 為 spatialContext 建立的快照中加入 anchorEntityId。新建立的錨點可能也會在以 XR_SPATIAL_CAPABILITY_ANCHOR_EXT 設定的其他空間環境中探索到,但由於特定 XrSpatialEntityIdEXT 是 XrSpatialContextEXT 專屬的,因此會使用不同的 XrSpatialEntityIdEXT。
有效使用 (隱含)
- 呼叫 xrCreateSpatialAnchorEXT 前,
XR_EXT_spatial_anchor擴充功能必須啟用 -
spatialContext必須是有效的 XrSpatialContextEXT 控制代碼 -
createInfo必須是指向有效 XrSpatialAnchorCreateInfoEXT 結構體的指標 -
anchorEntityId必須是指向XrSpatialEntityIdEXT值的指標 -
anchorEntity必須是指向 XrSpatialEntityEXT 控制代碼的指標
傳回代碼
XR_SUCCESSXR_SESSION_LOSS_PENDING
XR_ERROR_FUNCTION_UNSUPPORTEDXR_ERROR_HANDLE_INVALIDXR_ERROR_INSTANCE_LOSTXR_ERROR_LIMIT_REACHEDXR_ERROR_OUT_OF_MEMORYXR_ERROR_POSE_INVALIDXR_ERROR_RUNTIME_FAILUREXR_ERROR_SESSION_LOSTXR_ERROR_TIME_INVALIDXR_ERROR_VALIDATION_FAILUREXR_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是套用pose的 XrSpace。time是baseSpace所在的XrTime(以及pose適用的)pose。pose是錨點實體的位置。
有效使用 (隱含)
- 必須先啟用
XR_EXT_spatial_anchor擴充功能,才能使用 XrSpatialAnchorCreateInfoEXT -
type必須為XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT -
nextmust 為NULL,或指向結構體鏈結中下一個結構體的有效指標。另請參閱:XrSpatialAnchorParentANDROID -
baseSpace必須是有效的 XrSpace 控制代碼
查詢錨點姿勢
建立錨點後,執行階段應會隨著時間調整錨點相對於其他空間的位置和方向,盡可能與原始現實世界位置對齊,即使這會改變錨點與用於初始化錨點的原始 XrSpatialAnchorCreateInfoEXT :: baseSpace 之間的關係。
應用程式可以搭配錨點的 XrSpatialEntityEXT 使用 xrCreateSpatialUpdateSnapshotEXT,建立新的 XrSpatialSnapshotEXT,然後使用 xrQuerySpatialComponentDataEXT 查詢該快照中的 XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT 元件。應用程式可以將 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));
新指令
新結構
新增列舉常數
XR_EXT_SPATIAL_ANCHOR_EXTENSION_NAMEXR_EXT_spatial_anchor_SPEC_VERSION-
XR_SPATIAL_CAPABILITY_ANCHOR_EXT
正在擴充 XrSpatialComponentTypeEXT:
XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT
擴充 XrStructureType:
XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXTXR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXTXR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT
問題
為什麼 xrCreateSpatialAnchorEXT 會輸出實體 ID 和實體控制代碼?
- 已解決
- 答:xrCreateSpatialAnchorEXT 函式很可能只提供實體 ID 做為輸出內容,而應用程式可以使用 xrCreateSpatialEntityFromIdEXT 為該 ID 建立實體控制代碼。不過,由於應用程式通常會在每個影格查詢錨點姿勢,因此錨點很適合用於「更新快照」,這需要實體控制代碼做為輸入內容。為因應這個常見用途,xrCreateSpatialAnchorEXT 會代表應用程式執行 xrCreateSpatialEntityFromIdEXT,並提供實體控制代碼,供應用程式搭配 xrCreateSpatialUpdateSnapshotEXT 使用。
版本記錄
修訂版本 1,2024-07-10 (Google 的 Nihav Jain)
- 擴充功能初始說明