WebXR 命中测试模块

W3C 工作草案,

关于本文档的更多详细信息
本版本:
https://www.w3.org/TR/2025/WD-webxr-hit-test-1-20251211/
最新发布版本:
https://www.w3.org/TR/webxr-hit-test-1/
编辑草案:
https://immersive-web.github.io/hit-test/
先前版本:
历史记录:
https://www.w3.org/standards/history/webxr-hit-test-1/
反馈:
GitHub
规范内联
编辑:
(Google)
前任编辑:
(Google)
参与:
提交议题 (未解决的议题)
邮件列表存档
W3C 的 #immersive-web IRC

摘要

描述一种针对现实世界几何体执行命中测试的方法,以便与 WebXR Device API 一起使用。

本文档的状态

本节描述本文档在发布时的状态。当前 W3C 发布物和本 技术报告的最新修订版本列表,可在 W3C 标准与草案索引中找到。

沉浸式 Web 工作组维护着该组尚未处理的所有错误 报告列表。本草案突出显示了一些仍有待工作组讨论的待决议题。尚未就这些议题的结果作出任何决定, 包括它们是否有效。 强烈鼓励为未决议题提交包含拟议规范文本的拉取请求。

本文档由沉浸式 Web 工作组作为工作草案,使用推荐标准 轨道发布。本文档旨在成为一份 W3C 推荐标准。

作为工作草案发布并不意味着 W3C 及其成员的认可。这是一份草案文档,可能随时被其他文档 更新、替换或废弃。除作为进行中的工作外,不适合以其他方式引用本文档。

本文档由一个遵循 W3C 专利政策运作的小组产出。 W3C 维护着与该组交付物相关的任何 专利 披露的公开列表;该页面还包括 披露专利的说明。实际知晓其认为包含 必要 权利要求的专利的个人,必须依照 W3C 专利政策 第 6 节披露相关信息。

本文档受 2025年8月18日 W3C 流程文档管辖。

1. 引言

本模块描述一种机制,允许 WebXR 应用将射线投射到用户的现实世界 环境中,并根据 XR 设备所能掌握的最佳知识,报告该射线与物理对象相交的点, 以及被相交表面的朝向。这允许将虚拟对象与这些表面对齐放置,例如将对象真实地 放在地板上或附着到墙上。命中测试 API 是 WebXR Device API 的扩展,并构建在 WebXR 增强现实模块之上。

1.1. 术语

本文档所理解的命中测试,是指检查一条理想化的数学射线 (半直线)是否与底层增强现实硬件和 软件所理解的现实世界相交的行为。针对使用该 API 的应用所创建的虚拟对象进行的射线相交, 明确不属于命中测试 API 的范围。

2. 初始化

2.1. 特性描述符

为了让应用表明其有意在会话期间执行命中测试, 必须使用适当的 特性 描述符来请求会话。本模块引入字符串 hit-test,作为命中测试 特性的新有效特性描述符。

如果设备暴露了原生命中测试 能力,则该设备能够支持命中测试特性。内联 XR 设备不得被视为能够 支持命中测试特性。

命中测试特性受特性策略约束,并要求在请求文档的源上允许 "xr-spatial-tracking" 策略。

3. 命中测试选项

3.1. XRHitTestTrackableType

XRHitTestTrackableType 枚举指定可用于创建命中测试源的实体类型。

enum XRHitTestTrackableType {
  "point",
  "plane",
  "mesh"
};

3.2. XRHitTestOptionsInit

XRHitTestOptionsInit 字典表示一组可配置值,这些值会影响正在执行的命中测试的行为。

dictionary XRHitTestOptionsInit {
  required XRSpace space;
  sequence<XRHitTestTrackableType> entityTypes;
  XRRay offsetRay;
};

space 字典成员指定 XRSpaceoffsetRay 是相对于该空间来指定的。

entityTypes 字典成员 指定将用于计算命中测试结果的 XRHitTestTrackableTypes 数组。

offsetRay 字典成员 指定将用于执行命中测试的 XRRayoffsetRay 将被解释为以 space 所定义的坐标系表示。

XRHitTestOptionsInit 字典具有关联的有效 entityTypes,如果在字典构造时提供了 entityTypes, 则它被设置为该值。如果在构造时未提供 entityTypes, 则有效 entityTypes 被设置为包含单个元素 "plane" 的数组。

XRHitTestOptionsInit 字典具有关联的有效 offsetRay,如果在字典构造时提供了 offsetRay, 则它被设置为该值。如果在构造时未提供 offsetRay, 则有效 offsetRay 被设置为一个通过不带任何参数调用 XRRay() 而构造的 XRRay

3.3. XRTransientInputHitTestOptionsInit

XRTransientInputHitTestOptionsInit 字典表示一组可配置值,这些值会影响正在为瞬态 输入执行的命中测试的行为。

dictionary XRTransientInputHitTestOptionsInit {
  required DOMString profile;
  sequence<XRHitTestTrackableType> entityTypes;
  XRRay offsetRay;
};

profile 字典成员 指定将用于计算命中测试结果的瞬态输入源的输入配置文件名称

entityTypes 字典成员 指定将用于计算命中测试结果的 XRHitTestTrackableTypes 数组。

offsetRay 字典成员 指定将用于执行命中测试的 XRRayoffsetRay 在为瞬态输入源计算命中测试结果时,将被解释为以其 profile 匹配传入的 profileXRInputSource 所定义的坐标系表示。

XRTransientInputHitTestOptionsInit 字典具有关联的有效 entityTypes,如果在字典构造时提供了 entityTypes, 则它被设置为该值。如果在构造时未提供 entityTypes, 则有效 entityTypes 被设置 为包含单个元素 "plane" 的数组。

XRTransientInputHitTestOptionsInit 字典具有关联的有效 offsetRay,如果在字典构造时提供了 offsetRay, 则它被设置为该值。如果在构造时未提供 offsetRay, 则有效 offsetRay 被设置为一个 通过不带任何参数调用 XRRay() 而构造的 XRRay

4. 命中测试源

4.1. XRHitTestSource

[SecureContext, Exposed=Window]
interface XRHitTestSource {
  undefined cancel();
};

XRHitTestSource 对象充当对命中测试的活动订阅的句柄。

每个 XRHitTestSource 都具有关联的session,它存储用于创建该命中测试源的 XRSession

每个 XRHitTestSource 都具有关联的native origin,它存储足以标识用于请求命中 测试XRSpacenative origin 的信息。随后在计算命中测试结果时将使用这些信息。

每个 XRHitTestSource 都具有关联的entity types,它是一个 XRHitTestTrackableTypes 数组,描述在计算命中测试结果时 将考虑哪些实体类型。

每个 XRHitTestSource 都具有关联的offset ray,它是一个在计算命中测试结果时将使用的 XRRay

只要 XRHitTestSource 存在于 session活动命中测试源集合中,它就被视为 活动

为了从 sessionspaceentityTypesoffsetRay 创建命中测试源,用户代理必须运行以下 步骤:

  1. hitTestSource 为一个新的 XRHitTestSource

  2. hitTestSourcesession 初始化为 session

  3. hitTestSourcenative origin 初始化为 spacenative origin

  4. hitTestSourceentity types 初始化为 entityTypes

  5. 根据 offsetRayspace 计算 transformedOffsetRay,使得 transformedOffsetRay 在被解释为 spacenative origin 坐标系中的射线时,表示的射线与 offsetRay 在被解释为 spaceeffective origin 坐标系中的射线相同。

  6. hitTestSourceoffset ray 初始化为 transformedOffsetRay

  7. 返回 hitTestSource

当在 XRHitTestSource hitTestSource 上调用 cancel() 方法时,它表示应用不再有兴趣为指定的 hitTestSource 获取命中测试结果。

调用 cancel() 方法时,用户代理必须通过运行以下步骤来取消命中测试源

  1. 如果 hitTestSource 不是活动的,则抛出 InvalidStateError 并中止这些步骤。

  2. session活动命中测试源集合中移除 hitTestSource

当应用不再保留对特定 XRHitTestSource hitTestSource 的任何引用时,如果 hitTestSource 仍然 活动, 用户代理可以取消命中测试源。该取消可以在未指定的时间发生 (也可以完全不发生),应用不应依赖此行为来进行清理。

4.2. XRTransientInputHitTestSource

[SecureContext, Exposed=Window]
interface XRTransientInputHitTestSource {
  undefined cancel();
};

XRTransientInputHitTestSource 对象充当针对瞬态输入源的命中测试活动订阅的句柄。

每个 XRTransientInputHitTestSource 都具有关联的 session,它存储用于创建该命中测试源的 XRSession

每个 XRTransientInputHitTestSource 都具有关联的 profile,它存储输入源的输入配置文件名称。随后在计算瞬态输入源的命中测试结果时将使用这些信息。

每个 XRTransientInputHitTestSource 都具有关联的 entity types,它是一个 XRHitTestTrackableTypes 数组,描述在计算命中测试结果时 将考虑哪些实体类型。

每个 XRTransientInputHitTestSource 都具有关联的 offset ray,它是一个在计算命中测试结果时将使用的 XRRay

只要 XRTransientInputHitTestSource 存在于 session瞬态输入的活动命中测试源 集合中,它就被视为 活动

为了从 sessionprofileentityTypesoffsetRay 为瞬态输入创建命中测试源,用户 代理必须运行以下步骤:

  1. hitTestSource 为一个新的 XRTransientInputHitTestSource

  2. hitTestSourcesession 初始化为 session

  3. hitTestSourceprofile 初始化为 profile

  4. hitTestSourceentity types 初始化为 entityTypes

  5. hitTestSourceoffset ray 初始化为 offsetRay

  6. 返回 hitTestSource

当在 XRTransientInputHitTestSource hitTestSource 上调用 cancel() 方法时, 它表示应用不再有兴趣为指定的 hitTestSource 获取命中测试结果。

调用 cancel() 方法时,用户代理必须通过运行以下步骤来取消瞬态输入的命中测试源

  1. 如果 hitTestSource 不是活动的,则抛出 InvalidStateError 并中止这些步骤。

  2. session瞬态输入的活动命中 测试源集合中移除 hitTestSource

当应用不再保留对特定 XRTransientInputHitTestSource hitTestSource 的任何引用时,如果 hitTestSource 仍然活动,用户代理可以取消瞬态 输入的命中测试源。该取消可以在未指定的时间发生(也可以完全不发生),应用不应依赖此行为来进行清理。

5. 命中测试结果

5.1. XRHitTestResult

[SecureContext, Exposed=Window]
interface XRHitTestResult {
  XRPose? getPose(XRSpace baseSpace);
};

XRHitTestResult 包含命中测试的单个结果。它封装了用于执行命中测试的射线与底层 XR 设备所理解的用户环境 的相交点相关的信息。

每个 XRHitTestResult 都具有关联的 frame,它是为其计算该结果的 XRFrame

每个 XRHitTestResult 都具有关联的 native origin。此 native origin 定义一个新的坐标系, 其 Y 轴表示相交点处表面的法向量。

为了在给定 XRFrame frameXRHitTestTrackableType 数组 entityTypes,以及原生命中测试结果 nativeResult 的情况下创建命中测试结果,用户 代理必须运行以下步骤:

  1. hitTestResult 为一个新的 XRHitTestResult

  2. sessionframesession

  3. devicesessionXR 设备

  4. device 查询 nativeResult原生实体类型 nativeEntityType

  5. nativeEntityType 从原生实体类型转换entityType

  6. 如果 entityTypenull,或者不存在于 entityTypes 数组中, 则返回 null 并中止这些步骤。

  7. hitTestResultframe 设置为 frame

  8. hitTestResultnative origin 设置为从 nativeResult 获得的 native origin。

  9. 返回 hitTestResult

当在 XRHitTestResult hitTestResult 上以 baseSpace 参数调用 getPose(baseSpace) 方法时,它以 XRPose 的形式,提供在 frame 所表示的时间,hitTestResult 相对于 baseSpace 的姿态。

当在 hitTestResult 上调用 getPose(baseSpace) 方法时, 用户代理必须运行以下步骤:

  1. framehitTestResultframe

  2. 如果 frameactive 布尔值为 false,则抛出 InvalidStateError 并中止这些步骤。

  3. pose 为一个新的 XRPose

  4. space 为一个新的 XRSpace, 其 native origin 设置为native originorigin offset 设置为恒等变换,并且 session 设置为 frame 的 session。

  5. frame 所表示的时间,填充姿态:将 spacebaseSpace 中的姿态填入 pose

  6. 返回 pose

5.2. XRTransientInputHitTestResult

[SecureContext, Exposed=Window]
interface XRTransientInputHitTestResult {
  [SameObject] readonly attribute XRInputSource inputSource;
  readonly attribute FrozenArray<XRHitTestResult> results;
};

XRTransientInputHitTestResult 包含按 XRInputSource inputSource 分组的瞬态输入命中测试结果数组。

inputSource 属性包含用于计算 results 数组的 XRInputSource

results 属性包含计算得到的 XRHitTestResult 数组。

每个 XRTransientInputHitTestResult 都具有关联的 frame,它是为其计算这些结果的 XRFrame

为了在给定 XRInputSource inputSourceXRFrame frameXRHitTestTrackableType 数组 entityTypes,以及原生命中测试结果数组 nativeResults 的情况下 为瞬态输入创建命中测试结果, 用户代理必须运行以下步骤:

  1. hitTestResult 为一个新的 XRTransientInputHitTestResult

  2. hitTestResultframe 设置为 frame

  3. hitTestResultinputSource 设置为 inputSource

  4. results 为一个空的 XRHitTestResult 数组。

  5. nativeResults 中的每个 nativeResult

    1. frameentityTypesnativeResult 创建命中测试结果 result

    2. 如果 resultnull,则继续处理 nativeResults 中的下一项。

    3. result 添加到 results 数组。

  6. hitTestResultresults 设置为 results

  7. 返回 hitTestResult

6. 请求命中测试

partial interface XRSession {
  Promise<XRHitTestSource> requestHitTestSource(XRHitTestOptionsInit options);
  Promise<XRTransientInputHitTestSource> requestHitTestSourceForTransientInput(XRTransientInputHitTestOptionsInit options);
};

XRSession 被扩展为包含一个关联的活动命中测试源集合, 该集合将在计算命中测试结果时使用。

XRSession 被扩展为包含一个关联的瞬态输入的活动命中测试 源集合,该集合将在计算瞬态输入的命中测试结果时使用。

当活动命中测试源的总数或最近发出的请求数被认为对于 API 的合法使用而言过高时, 应用被视为发出了不合理数量的请求。 这是用户代理可以采取的可选隐私措施,用以避免滥用。

应用可以使用 XRSessionrequestHitTestSource() 方法来请求 命中测试

当在 XRSession session 上调用 requestHitTestSource(options) 方法时,必须运行以下步骤:

  1. promise一个新的 Promise

  2. 如果 hit-test 特性描述符 未包含session已启用特性列表中,则以 NotSupportedError 拒绝 promise 并中止这些步骤。

  3. 如果 sessionended 值为 true,则抛出 InvalidStateError 并中止这些步骤。

  4. 如果存在不合理数量的请求,则用户代理可以用 NotAllowedError 拒绝 promise 并中止这些步骤。

  5. 如果 session帧更新列表中尚未存在计算所有命中测试结果算法,则将其添加到该列表中。

  6. 使用 sessionoptionsspaceoptions有效 entityTypesoptions有效 offsetRay 创建命中测试源 hitTestSource

  7. 如果 hitTestSourcenull,则以 OperationError 拒绝 promise 并中止这些步骤。

  8. 将创建的 hitTestSource 存储在 session活动命中测试源集合中。

  9. 用创建的 hitTestSource 兑现 promise

当在 XRSession session 上调用 requestHitTestSourceForTransientInput(options) 方法时,必须运行以下步骤:

  1. promise一个新的 Promise

  2. 如果 hit-test 特性 描述符未包含session已启用特性列表中,则以 NotSupportedError 拒绝 promise 并中止这些步骤。

  3. 如果 sessionended 值为 true,则抛出 InvalidStateError 并中止这些步骤。

  4. 如果存在不合理数量的请求,则用户代理可以用 NotAllowedError 拒绝 promise 并中止这些步骤。

  5. 如果 session帧更新列表中尚未存在计算所有命中测试结果算法,则将其添加到该列表中。

  6. 使用 sessionoptionsprofileoptions有效 entityTypesoptions有效 offsetRay 为瞬态输入创建命中测试源 hitTestSource

  7. 如果 hitTestSourcenull,则以 OperationError 拒绝 promise 并中止这些步骤。

  8. 将创建的 hitTestSource 存储在 session瞬态输入的活动命中 测试源集合中。

  9. 用创建的 hitTestSource 兑现 promise

7. 计算命中测试结果

为了为给定的 XRFrame frame 计算所有命中测试结果,用户代理必须执行以下步骤:

  1. frame 调用计算命中测试结果算法。

  2. frame 调用计算瞬态输入的命中测试结果算法。

为了为给定的 XRFrame frame 计算命中测试结果,对于存在于 session活动命中测试源集合中的每个命中测试源 hitTestSource,用户代理必须执行以下步骤:

  1. entityTypeshitTestSourceentity types

  2. sessionframesession

  3. devicesessionXR 设备

  4. device 的跟踪系统查询 hitTestSourcenative origin 的最新 coordinates

  5. hitTestSourceoffset ray offsetRay 解释为 相对于 coordinates 表示,并使用该解释执行原生命中 测试,获得原生命中测试结果 nativeResults

  6. hitTestResults 为一个空列表

  7. 对于 nativeResults 中的每个原生命中测试结果 nativeResult,执行以下 步骤:

    1. frameentityTypesnativeResult 创建命中测试结果 hitTestResult

    2. 如果 hitTestResultnull,则继续处理 nativeResults 中的下一项。

    3. hitTestResult 添加到 hitTestResults 中,使该列表仍按 从 offsetRaynativeResult沿射线距离排序。

  8. hitTestResults 存储在 frame命中测试源到命中 测试结果的映射中,以 hitTestSource 为键。

为了为给定的 XRFrame frame 计算瞬态输入的命中测试结果,对于存在于 session瞬态输入的活动命中测试 源集合中的每个命中测试源 hitTestSource,用户代理必须执行以下步骤:

  1. entityTypeshitTestSourceentity types

  2. sessionframesession

  3. devicesessionXR 设备

  4. candidateInputSources 为包含在活动 XR 输入源列表中、且被认为是瞬态的 所有 session 输入源的集合。

  5. matchingInputSourcescandidateInputSources 中所有其 profiles 数组包含一个等于 hitTestSourceprofile 的项的输入源集合。

  6. hitTestResults 为一个空的 XRTransientInputHitTestResults 数组。

  7. 对于 matchingInputSources 中的每个瞬态输入源 inputSource

    1. device 的跟踪系统查询 inputSourcetargetRaySpacenative origin 的最新 coordinates

    2. hitTestSourceoffset ray 解释为 相对于 coordinates 表示,并使用该解释执行原生 命中测试,获得原生命中测试结果 nativeResults

    3. frameinputSourceentityTypesnativeResults 为瞬态输入创建命中测试结果 hitTestResult

    4. hitTestResult 添加到 hitTestResults 数组。

  8. hitTestResults 存储在 frame瞬态输入的 命中测试源到命中测试结果的映射中,以 hitTestSource 为键。

8. 获取命中测试结果

partial interface XRFrame {
  sequence<XRHitTestResult> getHitTestResults(XRHitTestSource hitTestSource);
  sequence<XRTransientInputHitTestResult> getHitTestResultsForTransientInput(XRTransientInputHitTestSource hitTestSource);
};

XRFrame 被扩展为包含一个关联的命中测试源到命中 测试结果的映射,它存储从 XRHitTestSourceXRHitTestResults 数组的映射。

XRFrame 被扩展为包含一个关联的瞬态输入的命中 测试源到命中测试结果的映射,它存储从 XRTransientInputHitTestSourceXRTransientInputHitTestResults 数组的映射。

应用可以通过使用 XRFramegetHitTestResults() 方法,从 XRHitTestSource 获取命中测试结果。

当在 XRFrame frame 上调用 getHitTestResults(hitTestSource) 方法时,用户代理必须运行以下步骤:

  1. 如果 frameactive 布尔值为 false,则抛出 InvalidStateError 并中止这些步骤。

  2. 检查 frame命中测试源到 命中测试结果的映射中是否存在 hitTestSource 的项。如果该项不存在,则抛出 InvalidStateError 并中止这些步骤。

  3. frame命中测试源到 命中测试结果的映射中查找 hitTestSource 的项,并将其赋给 results

  4. 返回 results

应用可以通过使用 XRFramegetHitTestResultsForTransientInput() 方法,从 XRTransientInputHitTestSource 获取瞬态输入的命中测试结果。

当在 XRFrame frame 上调用 getHitTestResultsForTransientInput(hitTestSource) 方法时,用户代理必须运行以下步骤:

  1. 如果 frameactive 布尔值为 false,则抛出 InvalidStateError 并中止这些步骤。

  2. 检查 frame瞬态输入的 命中测试源到命中测试结果的映射中是否存在 hitTestSource 的项。如果该项不存在, 则抛出 InvalidStateError 并中止这些步骤。

  3. frame瞬态输入的 命中测试源到命中测试结果的映射中查找 hitTestSource 的项,并将其赋给 results

  4. 返回 results

9. 几何基元

9.1. XRRayDirectionInit

XRRayDirectionInit 字典表示要传递给 XRRay(origin, direction) 构造器的方向向量。

dictionary XRRayDirectionInit {
  double x = 0;
  double y = 0;
  double z = -1;
  double w = 0;
};

9.2. XRRay

XRRay 是一条 由 origin 点和 direction 向量描述的几何射线。

XRRay 包含一个 matrix,它是一个矩阵

[SecureContext, Exposed=Window]
interface XRRay {
  constructor(optional DOMPointInit origin = {}, optional XRRayDirectionInit direction = {});
  constructor(XRRigidTransform transform);
  [SameObject] readonly attribute DOMPointReadOnly origin;
  [SameObject] readonly attribute DOMPointReadOnly direction;
  [SameObject] readonly attribute Float32Array matrix;
};

XRRay(origin, direction) 构造器 在被调用时必须执行以下步骤:

  1. ray 为一个新的 XRRay

  2. rayorigin 初始化为 { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }

  3. raydirection 初始化为 { x: 0.0, y: 0.0, z: -1.0, w: 0.0 }

  4. 如果 directionxyz 全部为零,则抛出 TypeError 并中止这些步骤。

  5. 如果 directionw 不是 0.0,则抛出 TypeError 并中止这些步骤。

  6. 如果 originw 不是 1.0,则抛出 TypeError 并中止这些步骤。

  7. rayoriginx 值初始化为 originxy 值初始化为 originy, 并将 z 值初始化为 originz

  8. raydirectionx 值初始化为 directionxy 值初始化为 directiony, 并将 z 值初始化为 directionz

  9. 归一化 raydirectionxyz 分量。

  10. raymatrix 初始化为 null

  11. 返回 ray

XRRay(transform) 构造器在被调用时必须 执行以下步骤:

  1. ray 为一个新的 XRRay

  2. rayorigin 初始化为 { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }

  3. raydirection 初始化为 { x: 0.0, y: 0.0, z: -1.0, w: 0.0 }

  4. 通过预乘 transformmatrix 来变换 rayorigin, 并将 ray 设置为结果。

  5. 通过预乘 transformmatrix 来变换 raydirection, 并将 ray 设置为结果。

  6. 归一化 raydirectionxyz 分量。

  7. raymatrix 初始化为 null

  8. 返回 ray

origin 属性定义射线起源处的三维空间点, 以米为单位给出。originw 属性必须为 1.0

direction 属性定义射线的三维 方向向量。directionw 属性必须为 0.0,并且该向量必须归一化为长度 1.0

matrix 属性是一个矩阵,它表示一个可用于沿 XRRay 放置对象的变换。它是从一条起源于 [0, 0, 0] 且沿负 Z 轴延伸的射线,到由 XRRayorigindirection 所描述射线的变换。这样的矩阵必须具有一个旋转分量,该旋转分量使任何垂直于 directionZ 轴的向量保持不变。此属性必须通过为 XRRay 获取矩阵来计算。此 属性应该惰性求值。

注:XRRaymatrix 可用于在渲染时轻松定位射线的图形表示。

要为给定的 XRRay ray 获取矩阵

  1. 如果 raymatrix 不是 null,则执行以下步骤:

    1. 如果对 matrix 执行的 IsDetachedBuffer 操作为 false,则返回 raymatrix

  2. z 为向量 [0, 0, -1]

  3. axiszraydirection 的向量叉积, z × direction

  4. cos_anglezraydirection 的标量点积, z · direction

  5. 根据以下内容设置 rotation

    如果 cos_angle 大于 -1 且小于 1
    rotation 设置为表示绕 axisarccos(cos_angle) 进行右手平面旋转的旋转矩阵。
    否则,如果 cos_angle 为 -1
    rotation 设置为表示绕向量 [1, 0, 0]arccos(cos_angle) 进行右手平面旋转的旋转矩阵。
    否则
    rotation 设置为恒等矩阵。
  6. translation 为其分量对应于 rayorigin 的平移矩阵。

  7. matrix 为在列向量表示法中,将 rotation 从左侧预乘到 translation 上的结果(即 translation * rotation)。

  8. raymatrix 设置为 matrix

  9. 返回 matrix

沿 射线距离 distance,从 XRRay ray 到任何实体 entity,定义为使 ray.origin + ray.direction * distance 得到属于实体 entity 的点,distance 为非负数,并且不存在更小的 distance 值 仍能使上述谓词成立。由 XR 设备来定义“属于某个实体的点”的含义。

10. 原生设备概念

实现命中测试 API 的用户代理必须有一种方式从底层 XR 设备获取有关用户环境的信息。 本节试图描述与设备原生能力相关的要求和概念,并且必然保持足够的不完全规定,以便为 不同的底层框架/设备留出充分空间。

10.1. 原生命中测试

在本规范中,假定 XR 设备暴露了一种供 用户代理执行满足以下要求的原生命中测试的方式:

注:对于不以原生方式暴露命中测试 功能的设备,用户代理仍有可能通过利用 XR 设备可能暴露的其他获取用户环境信息的方式来实现本规范。

10.2. 原生实体类型

原生命中测试 结果XR 设备返回,并应包含用于计算结果的实体的 类型信息。这类原生类型可以包括但不限于:

从原生实体类型转换XRHitTestTrackableType, 用户代理必须运行以下步骤:

  1. nativeEntityType 为要转换的原生实体类型。

  2. entityType 为一个新的 XRHitTestTrackableType

  3. 按如下方式初始化 entityType

    如果 nativeEntityType 包含与 "point" 对应的类型
    entityType 设置为 "point"
    否则,如果 nativeEntityType 包含与 "plane" 对应的类型
    entityType 设置为 "plane"
    否则,如果 nativeEntityType 包含与 "mesh" 对应的类型
    entityType 设置为 "mesh"
    否则
    entityType 设置为 null
  4. 返回 entityType

10.3. 原生命中测试结果

从 XR 设备返回的原生命中测试结果应包含与用户环境的相交点位置。 根据原生实体类型以及 XR 设备可用的信息,结果还应包含以某种方式定义的朝向, 以允许用户代理计算用户环境在相交点处的表面法线。

关于相交点的位置和朝向信息应包含在原生命中测试 结果的 native origin 中。Native origin 以某种方式定义一个新的坐标系,使其 Y 轴 表示相交点处表面的法向量。如果 XR 设备未返回朝向,则用户代理应将 native origin 设置为使其定义的坐标 系的 Y 轴指向上方(朝向负重力向量)。

确定是否需要 指定由命中测试结果的 native origin 所定义坐标系的其他轴,以维持不同实现和不同 AR 框架之间的兼容性。

11. 隐私与安全考量

命中测试 API 可通过向多个方向发送命中测试射线,以或高或低的 精度来映射用户环境。在创建 XR 会话时,命中测试必须作为特性 描述符声明,这将 允许用户代理通知用户,允许网站使用命中测试 API 可能带来的隐私影响。此外,当用户代理 认为针对真正的非隐私侵入性使用发出了不合理 数量的请求时,可以拒绝命中测试请求。

变更

2021年8月31日首份公开工作 草案以来的变更

12. 致谢

以下个人为 WebXR 命中测试规范的设计作出了贡献:

符合性

文档 约定

符合性要求通过 描述性断言 和 RFC 2119 术语的组合来表达。 本文档规范性部分中的关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、 “MAY” 和 “OPTIONAL” 应按 RFC 2119 中所述解释。 但是,出于可读性考虑, 这些词在本规范中并不全部以大写字母出现。

除明确标记为非规范性的章节、示例和注释之外, 本规范的所有文本都是规范性的。 [RFC2119]

本规范中的示例以 “for example” 一词引入, 或通过 class="example" 与规范性文本分隔开来, 如下所示:

这是一个信息性示例。

信息性注释以 “Note” 一词开头, 并通过 class="note" 与规范性文本分隔开来, 如下所示:

注意,这是一个信息性注释。

符合性 算法

作为算法一部分以祈使句表述的要求 (例如 "strip any leading space characters" 或 "return false and abort these steps") 应按引入该算法时使用的关键词 ("must"、"should"、"may" 等) 的含义解释。

表述为算法或具体步骤的符合性要求 可以以任何方式实现, 只要最终结果等价即可。 特别是,本规范中定义的算法 旨在易于理解, 而并非旨在具备高性能。 鼓励实现者进行优化。

索引

本 规范定义的术语

由引用 定义的术语

参考文献

规范性参考文献

[ECMASCRIPT]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[GEOMETRY-1]
Simon Pieters; Chris Harrelson. 几何接口模块 第 1 级. 2018年12月4日. CR. URL: https://www.w3.org/TR/geometry-1/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. RFC 中用于 表示要求级别的关键词. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. Living Standard. URL: https://webidl.spec.whatwg.org/
[WEBXR]
Brandon Jones; Manish Goregaokar; Rik Cabanier. WebXR Device API. 2025年10月1日. CRD. URL: https://www.w3.org/TR/webxr/

IDL 索引

enum XRHitTestTrackableType {
  "point",
  "plane",
  "mesh"
};

dictionary XRHitTestOptionsInit {
  required XRSpace space;
  sequence<XRHitTestTrackableType> entityTypes;
  XRRay offsetRay;
};

dictionary XRTransientInputHitTestOptionsInit {
  required DOMString profile;
  sequence<XRHitTestTrackableType> entityTypes;
  XRRay offsetRay;
};

[SecureContext, Exposed=Window]
interface XRHitTestSource {
  undefined cancel();
};

[SecureContext, Exposed=Window]
interface XRTransientInputHitTestSource {
  undefined cancel();
};

[SecureContext, Exposed=Window]
interface XRHitTestResult {
  XRPose? getPose(XRSpace baseSpace);
};

[SecureContext, Exposed=Window]
interface XRTransientInputHitTestResult {
  [SameObject] readonly attribute XRInputSource inputSource;
  readonly attribute FrozenArray<XRHitTestResult> results;
};

partial interface XRSession {
  Promise<XRHitTestSource> requestHitTestSource(XRHitTestOptionsInit options);
  Promise<XRTransientInputHitTestSource> requestHitTestSourceForTransientInput(XRTransientInputHitTestOptionsInit options);
};

partial interface XRFrame {
  sequence<XRHitTestResult> getHitTestResults(XRHitTestSource hitTestSource);
  sequence<XRTransientInputHitTestResult> getHitTestResultsForTransientInput(XRTransientInputHitTestSource hitTestSource);
};

dictionary XRRayDirectionInit {
  double x = 0;
  double y = 0;
  double z = -1;
  double w = 0;
};

[SecureContext, Exposed=Window]
interface XRRay {
  constructor(optional DOMPointInit origin = {}, optional XRRayDirectionInit direction = {});
  constructor(XRRigidTransform transform);
  [SameObject] readonly attribute DOMPointReadOnly origin;
  [SameObject] readonly attribute DOMPointReadOnly direction;
  [SameObject] readonly attribute Float32Array matrix;
};

议题索引

确定是否需要指定由命中测试结果的 native origin 定义的坐标系的其他轴, 以维持不同实现与不同 AR 框架之间的兼容性。
MDN

XRFrame/getHitTestResults

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRFrame/getHitTestResultsForTransientInput

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRHitTestResult/getPose

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRHitTestResult

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRHitTestSource/cancel

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRHitTestSource

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRay/XRRay

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRay/direction

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRay/matrix

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRay/origin

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRay

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/requestHitTestSource

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet12.1+Opera Mobile?
MDN

XRSession/requestHitTestSourceForTransientInput

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRTransientInputHitTestResult/inputSource

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRTransientInputHitTestResult/results

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRTransientInputHitTestResult

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRTransientInputHitTestSource/cancel

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRTransientInputHitTestSource

In only one current engine.

FirefoxNoneSafariNoneChrome81+
Opera?Edge81+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?