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 的整个生命周期内不断更新,虚拟对象 可能 会偏离应用放置的位置,这会影响应用的真实感和用户体验的质量。通过在虚拟对象放置位置附近创建一个锚点,然后始终相对于其锚点渲染该虚拟对象,应用可以 确保每个虚拟对象看起来都保持在物理环境中的同一姿态和方向。此外,与某些参考空间不同,锚点不受系统级重新居中的影响。
运行时支持
如果运行时支持空间锚点,则必须 通过在 xrEnumerateSpatialCapabilitiesEXT 中枚举 XR_SPATIAL_CAPABILITY_ANCHOR_EXT 来指明此项支持。
配置
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 在 XrSpatialComponentDataQueryResultEXT :: entityIds 数组中唯一标识此锚点,在使用 xrQuerySpatialComponentDataEXT 时。
如果未为 spatialContext 配置 XR_SPATIAL_CAPABILITY_ANCHOR_EXT,运行时 必须 从 xrCreateSpatialAnchorEXT 返回 XR_ERROR_VALIDATION_FAILURE。如需了解如何为 XR_SPATIAL_CAPABILITY_ANCHOR_EXT 功能配置 XrSpatialContextEXT,请参阅 配置。
anchorEntity 表示的锚点仅在 spatialContext 的生命周期内有效,或者直到应用对其调用 xrDestroySpatialEntityEXT 为止(以先发生者为准)。其他扩展程序 可能 会提供一些函数,用于在多个 XrSession 中保留此新创建的锚点,或在进程边界之间与其他应用共享该锚点。
新创建的锚点在被销毁之前,必须 可在其父空间上下文中被发现。这意味着,如果锚点与 XrSpatialDiscoverySnapshotCreateInfoEXT 中设置的发现条件匹配,运行时 必须 将 anchorEntityId 包含在使用 xrCreateSpatialDiscoverySnapshotAsyncEXT 为 spatialContext 创建的快照中。新创建的锚点可能 也会在配置了 XR_SPATIAL_CAPABILITY_ANCHOR_EXT 的其他空间上下文中被发现,但具有不同的 XrSpatialEntityIdEXT,因为特定的 XrSpatialEntityIdEXT 对于其 XrSpatialContextEXT 是唯一的。
有效用法(隐式)
- 必须先启用
XR_EXT_spatial_anchor扩展程序,然后才能调用 xrCreateSpatialAnchorEXT -
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_FAILURE- 如果启用了
XR_ANDROID_spatial_entity_bound_anchor,则为XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID - 如果启用了
XR_ANDROID_spatial_entity_bound_anchor,则为XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT
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所在(以及应用pose)的XrTime。pose是锚点实体的位置。
有效用法(隐式)
- 必须先启用
XR_EXT_spatial_anchor扩展程序,然后才能使用 XrSpatialAnchorCreateInfoEXT -
type必须 为XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT -
next必须 为NULL或指向结构链中下一个结构的有效指针。另请参阅:XrSpatialAnchorParentANDROID -
baseSpace必须 是有效的 XrSpace 句柄
查询锚点姿态
创建锚点后,运行时随后应 随着时间的推移调整其相对于其他空间的姿态和方向,以便尽可能与锚点的原始现实世界位置保持一致,即使这会改变锚点与用于初始化它的原始 XrSpatialAnchorCreateInfoEXT :: baseSpace 的关系也是如此。
应用可以 将 xrCreateSpatialUpdateSnapshotEXT 与锚点的 XrSpatialEntityEXT 搭配使用来创建新的 XrSpatialSnapshotEXT,然后使用 xrQuerySpatialComponentDataEXT 从该快照中查询 XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT 组件。应用可以 将 XrSpatialComponentAnchorListEXT 添加到 XrSpatialComponentDataQueryResultEXT :: next,以检索锚点的最新位置数据。
运行时可能 会将新创建的锚点的跟踪状态设置为 XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT。只有当跟踪状态为 XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT 时,应用必须 仅读取 XrSpatialComponentDataQueryResultEXT :: entityStates 中提供的锚点实体状态和实体的锚点组件数据。
使用锚点的准则
- 每个锚点的姿态都会独立于任何其他锚点或空间进行调整。单独锚定的虚拟对象可能 会相对于彼此移动或旋转,从而在这些虚拟对象应相对于彼此保持静止的情况下破坏空间层次结构。对于这种情况,应用应 为所有不相对于彼此移动的虚拟对象重复使用同一锚点。
- 应用 应 销毁不再使用的锚点的所有 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
-
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 年 7 月 10 日(Nihav Jain,Google)
- 初始扩展程序说明