1. 简介
2. 初始化
2.1. 功能描述符
为了使应用能够表明其有意在会话期间使用平面检测,必须使用适当的功能 描述符请求该会话。本模块引入字符串 plane-detection,将其作为平面检测功能的一个新的有效功能 描述符。
如果设备的跟踪系统公开了原生平面检测能力,则该设备能够支持平面检测功能。内联 XR 设备不得被视为能够支持平面检测功能。
创建启用了平面检测功能的会话时,必须将更新平面算法添加到该会话的帧更新列表中。
const session= await navigator. xr. requestSession( "immersive-ar" , { requiredFeatures: [ "plane-detection" ] });
3. 平面
3.1. XRPlaneOrientation
enum {XRPlaneOrientation "horizontal" ,"vertical" };
-
平面方向
"horizontal"表示该平面 主要沿水平方向定向(依据底层平台的约定)。 -
平面方向
"vertical"表示该平面 主要沿垂直方向定向(依据底层平台的约定)。
3.2. XRPlane
[Exposed =Window ]interface { [XRPlane SameObject ]readonly attribute XRSpace ;planeSpace readonly attribute FrozenArray <DOMPointReadOnly >;polygon readonly attribute XRPlaneOrientation ?;orientation readonly attribute DOMHighResTimeStamp ;lastChangedTime readonly attribute DOMString ?; };semanticLabel
XRPlane
表示底层 XR 系统检测到的单个平坦表面。
planeSpace
是一个用于建立平面坐标系的 XRSpace。
planeSpace
的原生原点跟踪平面的中心。底层 XR 系统定义平面中心的确切含义。由 planeSpace
定义的坐标系的 Y 轴必须表示平面的法向量。
每个 XRPlane 都有
一个关联的原生实体。
每个 XRPlane 都有
一个关联的帧。
polygon
是描述平面形状的顶点数组。这些顶点以多边形边缘上的闭合点序列形式返回,并使用由 planeSpace
定义的坐标系表示。每个顶点的 Y 坐标必须为 0.0。
semanticLabel
属性是一个描述多边形语义标签的字符串。如果不存在语义信息,该字符串可以为 null 或空字符串。XRSystem
应当使用其已知的语义标签填充该属性。
语义标签是一个
ASCII 小写 DOMString,用于描述 XRPlane
在现实世界中的名称,该名称由
XRSystem
所知。语义标签列表在语义标签注册表中定义。
orientation
描述由底层 XR 系统分类的平面方向。如果底层 XR 系统无法将方向分类为 "horizontal"
或 "vertical",
则该属性将设置为 null。
lastChangedTime
是平面某些属性最后一次发生更改的时间。
注:平面的姿态不被视为平面
属性,因此平面姿态的更新不会导致 lastChangedTime
发生变化。这是因为平面姿态是从两个不同实体派生的属性,即 planeSpace
和作为相对参照的 XRSpace,
姿态将通过 getPose()
函数相对于后者进行计算。
4. 获取检测到的平面
4.1. XRPlaneSet
[Exposed =Window ]interface {XRPlaneSet readonly setlike <XRPlane >; };
XRPlaneSet
是 XRPlane 的集合。它是
获取 XRFrame
中检测到的平面集合的主要机制。
partial interface XRFrame {readonly attribute XRPlaneSet ; };detectedPlanes
XRFrame
被扩展为包含 detectedPlanes
属性,其中包含帧中仍在跟踪的所有平面。该集合最初为空,并将由更新
平面算法填充。如果在帧未处于活动状态时访问该属性,
用户代理必须抛出 InvalidStateError。
partial interface XRSession {Promise <undefined >(); };initiateRoomCapture
XRSession
被扩展为包含一个关联的已跟踪平面集合,该集合最初
为空。集合中的元素将为 XRPlane 类型。
XRSession
被扩展为包含一个布尔值房间捕获已完成,其初始值为
false。
如果 XR 设备支持手动捕获,则它具有一个异步房间捕获方法,该方法返回一个布尔值。
XRSession
还被扩展为包含 initiateRoomCapture
方法。如果支持,该方法将要求 XR
设备捕获当前房间布局。是否使用捕获结果替换或扩充已跟踪
平面集合,由 XR
设备决定。
-
令 session 为此对象
-
如果 session 的
ended值为 `true`,则使用一个“InvalidStateError”DOMException拒绝 promise,并返回 promise。 -
如果房间捕获已完成为 `true`,则使用一个“
InvalidStateError”DOMException拒绝 promise, 并返回 promise。 -
如果 plane-detection 功能描述符未被包含在 session 的XR 设备针对 session 的模式的已启用功能列表中,则使用一个“
NotSupportedError”DOMException拒绝 promise,并返回 promise。 -
排入一个任务以执行以下步骤:
-
运行以下步骤:
- 如果 result 为 `true`:
-
兑现 promise
- 否则:
-
使用一个“
OperationError”DOMException拒绝 promise
-
将 session 的房间捕获已完成设置为 `true`。
-
返回 promise。
-
令 session 为 frame 的会话。
-
令 device 为 session 的XR 设备。
-
如果 plane-detection 功能描述符未被包含在 device 针对 session 的模式的已启用功能列表中,则中止这些步骤。
-
令 trackedPlanes 为调用 device 的原生平面检测能力,以获取 frame 的时间所对应时刻的已跟踪 平面后得到的结果。
-
对 trackedPlanes 中的每个 native plane,运行:
-
从 session 的已跟踪 平面集合中移除在本算法调用期间既未创建也未更新的每个对象。
-
将 frame 的
detectedPlanes设置为已跟踪平面集合。
XRFrame
frame 创建平面
对象,用户代理必须运行以下步骤:
-
令 result 为
XRPlane的新实例。 -
将 result 的原生实体设置为 native plane。
-
将 result 的
planeSpace设置为一个新的XRSpace对象;创建该对象时,将会话设置为 frame 的session, 并将原生原点设置为跟踪 native plane 的 原生原点。 -
以 result、native plane 和 frame 调用更新平面对象算法。
-
返回 result。
以这种方式创建的平面对象 result 被称为与传入的原生平面 对象 native plane 对应。
XRFrame
frame 更新平面
对象 plane,用户代理必须运行以下步骤:
-
将 plane 的帧设置为 frame。
-
如果底层系统将 native plane 分类为垂直方向,则将 plane 的
orientation设置为"vertical"。 否则,如果底层系统将 native plane 分类为水平方向,则将 plane 的orientation设置为"horizontal"。 否则,将 plane 的orientation设置为null。 -
将 plane 的
polygon设置为表示 native plane 多边形的新顶点数组,并执行所有必要的转换, 以处理原生平面多边形表示方式的差异。 -
将 plane 的
semanticLabel设置为包含语义标签的新字符串。 -
如有需要,按照§ 6 隐私与安全考量中的描述,降低 plane 的
polygon的细节级别。 -
将 plane 的
lastChangedTime设置为时间。
以下示例演示了应用如何获取检测到的平面相关信息并据此采取操作。 可用于渲染平面图形表示的代码未在此展示。
// `planes` 将跟踪应用已知的所有检测到的平面, // 以及它们更新时的时间戳。最初,这是一个空映射。 const planes= Map(); function onXRFrame( timestamp, frame) { const detectedPlanes= frame. detectedPlanes; // 首先,检查我们之前知道的平面中是否有任何平面已不再被跟踪: for ( const [ plane, timestamp] of planes) { if ( ! detectedPlanes. has( plane)) { // 处理已移除的平面——`plane` 存在于上一帧中, // 但现在已不再被跟踪。 // 我们知道该平面已不存在,因此将其从映射中移除: planes. delete ( plane); } } // 接下来,处理所有仍在跟踪的平面。 // 其中既包括我们之前见过的已跟踪平面(可能已更新), // 也包括新平面。 detectedPlanes. forEach( plane=> { if ( planes. has( plane)) { // 处理之前见过的平面: if ( plane. lastChangedTime> planes. get( plane)) { // 处理之前见过且已更新的平面。 // 这意味着该平面的某个属性与之前不同—— // 最有可能是多边形发生了变化。 ... // 渲染平面或为渲染准备平面等。 // 更新我们更新该平面的时间: planes. set( plane, plane. lastChangedTime); } else { // 处理之前见过但未在当前帧中更新的平面。 // 请注意,平面相对于其他某个空间的姿态可能已发生变化。 } } else { // 处理新平面。 // 设置我们更新该平面的时间: planes. set( plane, plane. lastChangedTime); } // 无论之前是否见过该平面, // 也无论其是否已更新,其姿态都可能已发生变化: const planePose= frame. getPose( plane. planeSpace, xrReferenceSpace); }); frame. session. requestAnimationFrame( onXRFrame); }
5. 原生设备概念
5.1. 原生平面检测
平面检测 API 提供有关在用户环境中检测到的平坦表面的信息。本规范假定,用户代理在实现
plane-detection 功能时可以依赖底层平台提供的原生平面检测能力。具体而言,底层
XR 设备应提供一种方式,用于查询在与特定 XRFrame
的时间相对应的时刻跟踪的所有平面。
此外,本规范假定,被称为原生平面对象的已跟踪平面会跨帧保持其标识——
也就是说,给定底层系统在时间 t0 返回的平面对象 P,
以及底层系统在时间 t1 返回的平面对象 Q,
用户代理可以查询底层系统,以判断
P 和 Q 是否对应同一个逻辑平面对象。底层系统还应
提供一个原生原点,可用于查询时间 t 时
姿态的位置,但不能保证平面姿态始终已知(例如,对于仍被跟踪但在给定时刻
无法定位的平面)。此外,原生平面对象应公开一个描述检测到的平面近似形状的多边形。
此外,为了创建
XRAnchor,
底层系统应将原生平面识别为原生实体。有关更多信息,请参阅
WebXR 锚点模块
§ native-anchor一节。
6. 隐私与安全考量
平面检测 API 会公开有关用户物理环境的信息。如果用户代理选择如此做,则可以限制公开的平面
信息(例如平面的多边形)。用户代理可以通过以下方式减少公开的信息:在更新平面对象算法中降低
平面多边形的细节级别(例如减少顶点数量,或者对顶点坐标进行舍入或量化),
或者在更新平面算法中表现得如同该平面对象
不存在于 trackedPlanes 集合中一样,从而完全移除该平面(例如,如果检测到的平面被认为
太小或过于细致而不应公开,并且用户代理未实现减少平面所公开细节的机制,则可以这样做)。平面的姿态
(可通过 planeSpace
获取)也可以进行量化。
由于平面检测 API 中的概念可用于[webxr-anchors-module] 规范公开的方法,因此与 WebXR 锚点模块相关的一些隐私与安全考量也适用于此处。有关详细信息,请参阅 WebXR 锚点模块 § privacy-security一节。
由于平面检测 API 扩展了 WebXR Device API,因此WebXR Device API § 13. 安全、隐私和舒适性考量一节也适用于 WebXR 平面检测模块公开的功能。
7. 致谢
以下人员为 WebXR 平面检测规范的设计作出了贡献: