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
  • nextNULL 或指向结构链中下一个结构的指针。
  • capabilityXrSpatialCapabilityEXT
  • 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_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
  • nextNULL 或指向结构链中下一个结构的指针。
  • locationCount 是一个 uint32_t,用于描述 locations 数组中的元素数量。
  • locationsXrPosef 数组。

如果 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_SPATIAL_COMPONENT_TYPE_ANCHOR_EXTXrSpatialCapabilityComponentTypesEXT :: componentTypes 中针对某些功能进行了枚举,应用可以 通过将枚举数包含在支持此组件的功能的 XrSpatialCapabilityConfigurationBaseHeaderEXT 派生结构的 XrSpatialCapabilityConfigurationBaseHeaderEXT :: enabledComponents 列表中来启用它。

此组件不需要任何特殊配置即可包含在 XrSpatialCapabilityConfigurationBaseHeaderEXT :: next 链中。

创建空间锚点

xrCreateSpatialAnchorEXT 函数的定义如下:

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

参数说明

应用可以 使用 xrCreateSpatialAnchorEXT 创建空间锚点。

如需获取锚点的更新组件数据,请在创建快照时将 anchorEntity 中填充的值传递到 XrSpatialUpdateSnapshotCreateInfoEXT :: entities 中。应用 可以 使用 anchorEntityIdXrSpatialComponentDataQueryResultEXT :: 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 包含在使用 xrCreateSpatialDiscoverySnapshotAsyncEXTspatialContext 创建的快照中。新创建的锚点可能 也会在配置了 XR_SPATIAL_CAPABILITY_ANCHOR_EXT 的其他空间上下文中被发现,但具有不同的 XrSpatialEntityIdEXT,因为特定的 XrSpatialEntityIdEXT 对于其 XrSpatialContextEXT 是唯一的。

有效用法(隐式)

返回代码

成功

  • 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_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
  • nextNULL 或指向结构链中下一个结构的指针。
  • baseSpace 是应用 poseXrSpace
  • timebaseSpace 所在(以及应用 pose)的 XrTime
  • pose 是锚点实体的位置。

有效用法(隐式)

查询锚点姿态

创建锚点后,运行时随后 随着时间的推移调整其相对于其他空间的姿态和方向,以便尽可能与锚点的原始现实世界位置保持一致,即使这会改变锚点与用于初始化它的原始 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_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

问题

版本历史记录

  • 修订版本 1,2024 年 7 月 10 日(Nihav Jain,Google)

    • 初始扩展程序说明