1. 引言
支持虚拟现实(VR)和增强现实(AR)应用的硬件现已广泛 面向消费者提供,提供了一个兼具新机遇和新挑战的沉浸式计算平台。能够直接与 沉浸式硬件交互,对于确保 Web 能够在这种环境中作为一等公民运行至关重要。
沉浸式计算引入了对高精度、低延迟通信的严格要求,以便 提供可接受的体验。它也给像 Web 这样的平台带来了独特的安全关切。 WebXR 设备 API 提供了必要的接口,使开发者能够 在 Web 上跨多种硬件形态构建引人入胜、舒适且安全的沉浸式应用。
其他 Web 接口,例如 RelativeOrientationSensor
和 AbsoluteOrientationSensor,
可以被重新用于从某些设备暴露输入,以在有限
情况下对 WebXR 设备 API 进行 polyfill。不过,这些接口无法支持高端沉浸式体验的多项功能,
例如 6DoF 跟踪、向
头显外围设备呈现,或被跟踪的输入设备。
1.1. 术语
本文档通篇使用首字母缩略词 XR 来指代用于虚拟现实、增强现实及其他相关技术的 硬件、应用和技术谱系。示例包括但不限于:
-
头戴式显示器,无论它们是不透明、透明,还是使用视频透视
-
带有位置跟踪的移动设备
-
具备空间跟踪能力的固定显示器
它们之间的重要共同点是都提供某种程度的空间跟踪,用以 模拟虚拟内容的视图。
诸如“XR device”、“XR application”等术语通常被理解为适用于上述任何内容。 本文档中仅适用于这些设备某个子集的部分,会在适当位置予以说明。
术语 3DoF 和 6DoF 在本文档通篇用于描述 XR 设备的跟踪 能力。
-
3DoF 设备是 “Three Degrees of Freedom”的缩写,指只能跟踪旋转运动的设备。这常见于 完全依赖加速度计和陀螺仪读数来提供跟踪的设备。3DoF 设备不会响应来自用户的 平移运动,尽管它们可能使用算法基于颈部或手臂建模来估计平移 变化。
-
6DoF 设备是 “Six Degrees of Freedom”的缩写,指能够同时跟踪旋转和平移,从而实现空间中精确 1:1 跟踪的设备。这通常需要某种程度上理解用户的环境。 这种环境理解可以通过由内向外跟踪来实现,即使用被跟踪设备自身上的传感器 (例如摄像头或深度传感器)来确定设备的位置;也可以通过由外向内跟踪来实现, 即放置在用户环境中的外部设备(如摄像头或发光设备)提供稳定的参考点, XR 设备可据此确定其 位置。
1.2. 应用流程
大多数使用 WebXR 设备 API 的应用都会遵循类似的使用模式:
-
查询
navigator.xr.isSessionSupported()以确定硬件和 UA 是否支持所需类型的 XR 内容。 -
如果支持,则向用户宣传该 XR 内容。
-
等待 window 具有瞬态激活。这通常表现为 用户点击页面上表示想要开始查看 XR 内容的按钮。
-
在用户激活事件中使用
navigator.xr.requestSession()请求一个XRSession。 -
如果
XRSession请求成功,则使用它运行帧循环,以响应 XR 输入,并 生成要显示在 XR 设备上的图像作为响应。
2. 模型
2.1. XR 设备
XR 设备是 能够向用户呈现沉浸式内容的物理硬件单元。如果内容产生视觉、音频、触觉或其他感官输出, 用于模拟或增强用户环境的各个方面,则该内容被认为是“沉浸式”的。 这最常涉及跟踪用户在空间中的运动,并 生成与用户运动同步的输出。在桌面客户端上,这通常是 头显外围设备。在移动客户端上,它可以表示移动设备本身与 观看器支架的结合。它也可以表示没有立体呈现能力但具有更 高级跟踪能力的设备。
一个 XR 设备具有一个受支持模式列表
(一个由字符串组成的列表),该列表包含
XRSessionMode
中该 XR 设备支持的枚举值。
每个 XR 设备对于其
受支持模式列表中的每个
XRSessionMode
都具有一个已授予特性集合,它是一个由
特性描述符组成的
集合,
并且 MUST 初始为空集合。
用户代理具有一个沉浸式 XR 设备列表(一个由 XR 设备组成的列表),该列表 MUST 初始为空 列表。
用户代理具有一个沉浸式
XR 设备(null 或 XR
设备),其初始值为 null,并
表示来自沉浸式 XR 设备列表的活动
XR 设备。此对象 MAY 位于
单独的线程上并异步更新。
用户代理 MUST 具有一个默认内联 XR 设备,它是一个
XR 设备,其
受支持模式列表
MUST 包含 "inline"。
默认内联 XR
设备 MUST NOT 报告任何姿态信息,
并且 MUST NOT 报告XR 输入
源或事件,但由指针事件创建的除外。
注:默认内联 XR 设备 完全是为开发者提供便利而存在,使他们能够对 内联和沉浸式内容使用相同的渲染和输入逻辑。默认内联 XR 设备不会暴露任何 开发者无法通过页面上的其他机制(例如用于输入的指针事件)获得的信息, 它只会以以 XR 为中心的格式暴露这些值。
用户代理 MUST 具有一个内联 XR 设备,它是一个
XR 设备,其
受支持模式列表
MUST 包含 "inline"。
如果内联 XR 设备能够将其受支持的
主视图作为 HTML 文档的一部分暴露,
则它 MAY 能够支持
inline-stereo
特性描述符。
如果沉浸式 XR 设备
提供的跟踪适合暴露给内联内容,则内联 XR 设备
MAY 是该沉浸式 XR 设备,
否则为默认内联 XR
设备。
注:在手机上,内联 XR 设备可能报告 来源于手机内部传感器(如陀螺仪和加速度计)的姿态信息。在没有类似传感器的台式机和笔记本电脑上, 内联 XR 设备将无法报告姿态,因此 应回退到默认内联 XR 设备。如果用户代理已经 运行在XR 设备上, 内联 XR 设备将 是同一设备,并且可能支持多个视图。在提供超出 默认内联 XR 设备所暴露内容之外的任何跟踪或输入特性之前, 必须获得用户同意。
沉浸式 XR 设备列表、 内联 XR 设备和 沉浸式 XR 设备的当前值 MAY 位于单独的线程上并异步更新。这些对象 SHOULD NOT 在未 并行运行的步骤中被直接访问。
3. 初始化
3.1. navigator.xr
partial interface Navigator { [SecureContext ,SameObject ]readonly attribute XRSystem xr ; };
xr 属性的 getter MUST 返回与其关联的
XRSystem 对象。
3.2. XRSystem
[SecureContext ,Exposed =Window ]interface :XRSystem EventTarget { // MethodsPromise <boolean >isSessionSupported (XRSessionMode ); [mode NewObject ]Promise <XRSession >requestSession (XRSessionMode ,mode optional XRSessionInit = {}); // Eventsoptions attribute EventHandler ondevicechange ; };
当创建 Navigator
对象时,用户代理 MUST 创建一个
XRSystem 对象
并将其与该对象关联。
XRSystem
对象是 API 的入口点,用于查询用户代理可用的 XR 特性,并通过创建
XRSession
来启动与 XR 硬件的通信。
用户代理 MUST 能够枚举沉浸式 XR 设备,这些设备连接到系统;此时每个可用设备都会被放入 沉浸式 XR 设备列表中。后续请求枚举的算法 MUST 重用缓存的 沉浸式 XR 设备列表。枚举设备 不应初始化设备跟踪。 在首次枚举后,用户代理 MUST 开始监视设备连接和断开连接,将已连接设备添加到 沉浸式 XR 设备列表并移除已断开连接的设备。
每当沉浸式 XR 设备列表发生变化时,用户代理 should 通过运行以下步骤来选择沉浸式 XR 设备:
-
令 oldDevice 为沉浸式 XR 设备。
-
如果沉浸式 XR 设备列表是空 列表, 则将沉浸式 XR 设备设为
null。 -
如果沉浸式 XR 设备列表的 大小为一,则将沉浸式 XR 设备设为 沉浸式 XR 设备列表[0]。
-
按如下方式设置沉浸式 XR 设备:
- 如果存在任何活动的
XRSession, 且沉浸式 XR 设备列表 包含 oldDevice: -
将沉浸式 XR 设备设为 oldDevice。
- 否则:
-
将沉浸式 XR 设备设为用户代理所选择的设备。
- 如果存在任何活动的
-
如果适当,用户代理 MAY 将内联 XR 设备更新为 沉浸式 XR 设备, 否则更新为默认内联 XR 设备。
-
如果这是首次枚举设备,或者 oldDevice 等于 沉浸式 XR 设备,则中止这些步骤。
-
排队一个任务,将所有
WebGLRenderingContextBase实例的 XR compatible 布尔值设为false。 -
排队一个任务,在 相关 Global 对象的
navigator的xr上 触发 一个事件,其名称为 devicechange。 -
排队一个任务,在任何因 沉浸式 XR 设备或 内联 XR 设备的变化而受到影响的
XRPermissionStatus对象上触发适当的change事件。
注:这些步骤应始终 并行运行。
注:当 沉浸式 XR 设备列表包含多个设备时,用户代理可以使用其 想要的任何标准来选择沉浸式 XR 设备。 例如,用户代理可以始终选择列表中的第一项,或提供设置 UI 让 用户管理设备优先级。理想情况下,用于选择默认设备的算法是稳定的,并且会 在多个浏览会话中产生相同的设备选择结果。
ondevicechange 属性是
事件处理器 IDL 属性,对应
devicechange 事件类型。
isSessionSupported(mode) 方法
查询给定 mode 是否可能受到用户代理和设备能力的支持。
调用此方法时,它 MUST 运行以下步骤:
-
如果 mode 是沉浸式会话模式,且请求文档的 源不被允许使用 "xr-spatial-tracking" 权限策略,则用 "
SecurityError"DOMException拒绝 promise 并返回它。 -
按如下方式检查会话 mode 是否受支持:
- 如果已知用户代理和系统 绝不会支持 mode 会话
-
用
false解决 promise。 - 如果已知用户代理和系统 通常支持 mode 会话
- 否则
-
并行运行以下步骤:
-
返回 promise。
注:isSessionSupported()
的目的并不是完全准确地报告用户代理创建
XRSession
的能力,而是
告知页面是否建议宣传创建给定模式会话的能力。
即使用户代理在解决该方法之前检查了必要硬件/软件是否存在,也预期会出现
一定程度的假阳性。(例如,即使存在适当硬件,
在请求会话时它也可能已将独占访问权授予其他应用。)
预期大多数具有 XR 内容的页面会在文档生命周期早期调用
isSessionSupported()。
因此,调用 isSessionSupported()
SHOULD 避免显示任何模态或其他侵入式 UI。调用 isSessionSupported()
MUST NOT 触发设备选择 UI,MUST NOT 干扰系统上任何正在运行的 XR 应用,并且
MUST NOT 导致 XR 相关应用启动,例如系统托盘或商店前端。
immersive-vr
会话。
const supported= await navigator. xr. isSessionSupported( 'immersive-vr' ); if ( supported) { // 'immersive-vr' sessions may be supported. // Page should advertise support to the user. } else { // 'immersive-vr' sessions are not supported. }
XRSystem
对象具有一个待处理沉浸式会话布尔值,它 MUST 初始为
false;一个活动沉浸式会话,它 MUST 初始为 null;
以及一个内联
会话列表,它 MUST 初始为空。
requestSession(mode, options)
方法尝试在可能的情况下为给定 mode 初始化一个
XRSession,
并在必要时进入沉浸式模式。
调用此方法时,用户代理 MUST 运行以下步骤:
-
令 immersive 为
true,如果 mode 是 沉浸式会话模式;否则为false。 -
令 global object 为调用此方法的
XRSystem的相关 Global 对象。 -
按如下方式检查会话请求是否被允许:
- 如果 immersive 为
true: -
-
检查是否为 global object 允许沉浸式会话请求; 如果不允许,则用 "
SecurityError"DOMException拒绝 promise 并返回 promise。 -
如果待处理沉浸式会话为
true,或活动沉浸式会话不是null,则用 "InvalidStateError"DOMException拒绝 promise 并返回 promise。 -
将待处理沉浸式会话设为
true。
-
- 否则:
-
检查是否为 global object 允许内联会话请求; 如果不允许,则用 "
SecurityError"DOMException拒绝 promise 并返回 promise。
- 如果 immersive 为
-
并行运行以下步骤:
-
令 requiredFeatures 为 options 的
requiredFeatures。 -
令 optionalFeatures 为 options 的
optionalFeatures。 -
将 device 设为针对 mode、requiredFeatures 和 optionalFeatures 获得 当前设备的结果。
-
排队一个任务以执行以下步骤:
-
如果 device 为
null,或 device 的 受支持模式列表不 包含 mode,则运行以下 步骤:-
用 "
NotSupportedError"DOMException拒绝 promise。 -
如果 immersive 为
true,则将 待处理沉浸式 会话设为false。 -
中止这些步骤。
-
-
令 descriptor 为一个以 mode、requiredFeatures 和 optionalFeatures 初始化的
XRPermissionDescriptor -
令 status 为一个
XRPermissionStatus, 初始为null -
用 descriptor 和 status 请求 xr 权限。
-
如果 status 的
state为"denied", 则运行以下步骤:-
用 "
NotSupportedError"DOMException拒绝 promise。 -
如果 immersive 为
true,则将 待处理沉浸式 会话设为false。 -
中止这些步骤。
-
-
用 session、mode、granted 和 device 初始化会话。
-
按如下方式可能设置活动沉浸式会话:
-
用 session 解决 promise。
-
排队一个任务以执行以下步骤:
注:这些步骤确保初始inputsourceschange事件发生在初始会话被解决之后。-
将 session 的 promise resolved 标志设为
true。 -
令 sources 为任何已附加到 session 的现有输入源。
-
如果 sources 非空,则执行以下步骤:
-
将 session 的 活动 XR 输入源列表设为 sources。
-
在 session 上触发一个名为
inputsourceschange的XRInputSourcesChangeEvent, 并将added设为 sources。
-
-
-
-
-
返回 promise。
为了针对 XRSessionMode
mode、requiredFeatures 和 optionalFeatures
获得
当前设备,用户代理 MUST 运行以下步骤:
-
按如下方式选择 device:
- 如果 mode 是沉浸式会话模式:
-
将 device 设为确保选中沉浸式 XR 设备的结果。
- 否则,如果 requiredFeatures 或 optionalFeatures 不为空:
-
将 device 设为内联 XR 设备。
- 否则:
-
将 device 设为默认内联 XR 设备。
-
返回 device。
注:这些步骤应始终 并行运行。
immersive-vr
XRSession。
const xrSession= await navigator. xr. requestSession( "immersive-vr" );
3.3. XRSessionMode
XRSessionMode
enum 定义了 XRSession
可以运行的模式。
enum {XRSessionMode "inline" ,"immersive-vr" ,"immersive-ar" };
-
inline会话模式表示会话的输出将 作为 HTML 文档中的元素显示。inline会话内容 MUST 使用为会话暴露的 视图列表显示。除非启用了 inline-stereo 特性,否则这是 一个单一视图,其 eye 为"none"。 用户代理 MUST 允许创建inline会话。 -
immersive-vr会话模式表示会话的 输出将被授予对沉浸式 XR 设备显示器的 独占访问,并且内容并非旨在与用户环境集成。 -
immersive-ar会话模式的行为在 WebXR AR Module 中定义,并且 MUST NOT 被添加到 沉浸式 XR 设备的受支持模式列表中,除非 UA 实现了该 模块。
在本文档中,术语 内联会话指 inline
会话,术语 沉浸式会话指 immersive-vr
或 immersive-ar
会话。
沉浸式会话 MUST
提供某种程度的观看者跟踪,并且
内容 MUST 以相对于用户和/或周围环境的适当比例显示。
此外,沉浸式
会话 MUST 被授予对沉浸式 XR 设备的
独占访问,这意味着当
沉浸式会话
为 "visible"
时,
HTML 文档不会显示在沉浸式 XR
设备的显示器上,来自任何其他来源的内容也没有
独占访问权。独占访问并不阻止用户代理叠加其自己的
UI,但该 UI SHOULD
是最小化的。
注:UA 可以选择叠加用于无障碍或安全的内容, 例如 guardian 边界、障碍物或用户的手,尤其是在没有替代输入 源时。
注:未来规范或模块可能扩展 沉浸式 会话的定义,以包含其他会话模式。
注:独占访问可能呈现方式的示例包括 显示在虚拟现实头显上的立体内容。
注:作为叠加 UI 的一个示例,用户代理或 操作系统在沉浸式会话中可能会在渲染内容上显示通知。
注:虽然在 沉浸式会话期间,HTML 文档不会显示在 沉浸式 XR 设备的显示器上,但它仍可能显示在单独的显示器上,例如 当用户从其计算机上的 2d 浏览器进入 沉浸式会话,而该计算机连接到其 沉浸式 XR 设备时。
3.4. 特性依赖项
XRSession
的某些特性可能因多种原因而无法普遍可用,其中一个原因是并非所有 XR 设备都能支持
完整的特性集合。另一个考虑是某些特性会暴露
敏感信息,这些信息可能
需要明确的用户
意图信号后才能发挥作用。
初始化底层 XR 平台并创建 XRSession 后
却立即通知用户应用无法正确运行,这是一种糟糕的用户体验;因此,开发者可以通过向
requestSession()
传递 XRSessionInit
字典来指明必需特性。
如果任何必需
特性因设备限制而不可用,或者缺少明确的
用户意图信号以暴露与该特性相关的
敏感
信息,这将阻止创建 XRSession。
此外,鼓励开发者设计在更强大设备上运行时会渐进增强其功能的体验。
体验不需要但会在可用时加以利用的
可选特性也必须在
XRSessionInit
字典中指明,以确保在必要时启用该特性之前能够确定
用户
意图。
dictionary {XRSessionInit sequence <DOMString >requiredFeatures ;sequence <DOMString >optionalFeatures ; };
requiredFeatures 数组包含体验所需的任何
必需特性。
如果列表中的任何值不是可识别的特性描述符,则不会创建
XRSession。
如果 requiredFeatures
数组中列出的任何特性不受
XR
设备支持,或在必要时尚未收到明确的
用户意图信号,则不会创建
XRSession。
optionalFeatures 数组包含体验所需的任何
可选特性。
如果列表中的任何值不是可识别的特性描述符,则会被忽略。
optionalFeatures
数组中列出的特性会在被
XR
设备支持,并且在必要时获得明确的
用户意图信号时启用;
但如果不存在,则不会阻止创建
XRSession。
特性列表中给出的值如果是以下任一项,则被视为有效的 特性描述符:
-
任何
XRReferenceSpaceTypeenum 值的字符串表示 -
字符串 "tracked-sources"
-
字符串 "inline-stereo"
-
字符串 "secondary-views"
本规范的未来迭代和附加模块可能扩展可接受 特性 描述符的列表。
注:如果某个特性需要额外初始化,
XRSessionInit
应该为该特性扩展一个新字段。
根据所请求的 XRSessionMode,
某些特性描述符会默认添加到
requiredFeatures
或 optionalFeatures
列表中。下表描述与每种会话类型和特性列表关联的
默认特性:
| 特性 | 会话 | 列表 |
|---|---|---|
"viewer"
| 所有会话 | requiredFeatures
|
"local"
| 沉浸式 会话 | requiredFeatures
|
由 requiredFeatures
和 optionalFeatures
给出的特性描述符组合列表,被统称为
XRSession 的
请求特性。
某些特性 描述符在出现在 请求特性列表中时,受到权限策略和/或以下要求的约束:通过 显式同意 或隐式同意, 充分理解用户意图以使用该特性。 下表描述在启用之前必须满足的 特性要求:
| 特性 | 所需权限策略 | 所需同意 |
|---|---|---|
"local"
| "xr-spatial-tracking" | 内联会话 需要同意 |
"local-floor"
| "xr-spatial-tracking" | 始终需要同意 |
"bounded-floor"
| "xr-spatial-tracking" | 始终需要同意 |
"unbounded"
| "xr-spatial-tracking" | 始终需要同意 |
注:"local"
始终作为默认特性包含在
沉浸式会话的
请求特性中,因此
沉浸式
会话始终需要获得显式同意或
隐式同意。
inline-stereo 特性描述符请求
内联会话暴露用于内联
呈现的立体主视图。
如果启用,视图列表
MUST 包含两个
主视图,其中一个的
eye 为 "left",
另一个的eye 为 "right"。
inline-stereo 特性描述符仅适用于
"inline"
会话。它 MUST NOT 被授予沉浸式会话。
只有在 XR 设备 能够支持该特性时, 请求特性 才能为会话启用;这意味着已知该特性在某些配置下受到 XR 设备支持,即使尚未验证当前 配置是否支持该特性。用户代理 MAY 在需要时应用更严格的约束,以产生更一致的 用户体验。
注:例如,若干 VR 设备支持
配置供用户在其中移动的安全边界,或跳过边界配置并
以用户预期站立不动的模式运行。这样的设备可被视为
能够
支持 "bounded-floor"
XRReferenceSpace,
即使它们当前未配置安全边界,因为预期用户可以在体验需要时
适当地配置设备。这是为了允许用户代理在需要时避免在
解决请求特性之前
完全初始化XR
设备或等待识别用户环境。不过,如果
用户代理在请求会话时无需额外初始化即可知道边界状态,则它可以选择在安全边界尚未配置时
拒绝 "bounded-floor"
特性。
4. 会话
4.1. XRSession
与 XR 硬件的任何交互都通过一个 XRSession
对象完成,该对象只能通过在 XRSystem 对象上调用 requestSession() 来获取。一旦成功取得会话,
它可用于 轮询观看者姿态、
查询有关用户环境的信息,并向用户呈现图像。
在可能的情况下,用户代理在取得 XRSession
之前,SHOULD NOT 初始化设备跟踪或渲染
能力。这是为了防止在 XR 系统未被主动使用时启用它们而产生不必要的副作用,
例如电池使用量增加,或在首次导航到一个只想测试 XR 硬件是否存在以便宣传 XR
特性的页面时,相关实用程序应用出现。不过,并非所有 XR 平台都提供无需初始化跟踪即可检测硬件存在的方式,
因此这只是强烈建议。
enum {XRVisibilityState "visible" ,"visible-blurred" ,"hidden" , }; [SecureContext ,Exposed =Window ]interface :XRSession EventTarget { // Attributesreadonly attribute XRVisibilityState visibilityState ;readonly attribute float ?frameRate ;readonly attribute Float32Array ?supportedFrameRates ; [SameObject ]readonly attribute XRRenderState renderState ; [SameObject ]readonly attribute XRInputSourceArray inputSources ; [SameObject ]readonly attribute XRInputSourceArray trackedSources ;readonly attribute FrozenArray <DOMString >enabledFeatures ;readonly attribute boolean isSystemKeyboardSupported ; // Methodsundefined updateRenderState (optional XRRenderStateInit = {});state Promise <undefined >updateTargetFrameRate (float ); [rate NewObject ]Promise <XRReferenceSpace >requestReferenceSpace (XRReferenceSpaceType );type unsigned long requestAnimationFrame (XRFrameRequestCallback );callback undefined cancelAnimationFrame (unsigned long );handle Promise <undefined >end (); // Eventsattribute EventHandler onend ;attribute EventHandler oninputsourceschange ;attribute EventHandler onselect ;attribute EventHandler onselectstart ;attribute EventHandler onselectend ;attribute EventHandler onsqueeze ;attribute EventHandler onsqueezestart ;attribute EventHandler onsqueezeend ;attribute EventHandler onvisibilitychange ;attribute EventHandler onframeratechange ; };
每个 XRSession
都有一个
mode,它是 XRSessionMode
的值之一。
每个 XRSession
都有一个
animation frame,它是一个 XRFrame,
其初始化时将 active 设为
false,将 animationFrame 设为 true,并将 session
设为该 XRSession。
每个 XRSession
都有一个
已授予特性集合,它是由 DOMString
组成的集合,
对应于已授予给该 XRSession
的特性描述符。
enabledFeatures 属性返回
已授予特性集合中的特性,作为一个新的
DOMString
数组。
isSystemKeyboardSupported 属性
表示当 XRSession
处于活动状态时,XRSystem 是否
具备显示系统键盘的能力。如果 isSystemKeyboardSupported
为 true,则会触发覆盖键盘的 Web API(例如 focus)将显示
系统键盘。在键盘显示期间,XRSession MUST
将 XRSession
的
visibility state 设为 "visible-blurred"。
为了初始化 会话,给定 session、mode、granted 和 device, 用户代理 MUST 运行以下步骤:
-
将 session 的 mode 设为 mode。
-
将 session 的 XR device 设为 device。
-
将 session 的 已授予特性集合设为 granted。
-
如果用户代理的其他特性尚未完成,则执行必要的平台特定步骤,以初始化设备的跟踪和渲染能力, 包括向用户显示任何必要说明。
注:某些设备需要额外的用户 激活说明。例如,在基于手机的头显设备上进入沉浸式模式 需要将手机插入头显,而在连接到外部头显的桌面浏览器上执行此操作 需要佩戴头显。确保显示任何此类说明是用户代理的责任,而不是 作者的责任。
许多不同情况都可能关闭会话,这是永久且不可逆的。一旦会话已关闭,
再次访问 XR
设备的跟踪或渲染能力的唯一方法就是请求
新会话。每个 XRSession 都有一个
ended 布尔值,初始设为 false,表示它是否
已被关闭。
当一个 XRSession
session 被关闭时,运行以下步骤:
-
将 session 的 ended 值设为
true。 -
从内联会话列表中移除 session。
-
用
InvalidStateError拒绝由 session 返回的任何未决 promise, 但由end()返回的任何 promise 除外。 -
如果用户代理的其他特性没有正在主动使用它们,则执行必要的平台特定步骤以关闭设备的跟踪和渲染能力。 这 MUST 包括:
-
排队一个任务,在 session 上触发一个名为
end的XRSessionEvent。
end() 方法提供了一种手动关闭
会话的方式。调用时,它 MUST 运行以下步骤:
每个 XRSession
都有一个
活动渲染
状态,它是一个新的 XRRenderState;
以及一个 待处理渲染
状态,它是一个初始为 null 的 XRRenderState。
renderState 属性返回 XRSession 的
活动渲染
状态。
每个 XRSession
都有一个
最小
内联视场和一个 最大内联视场,以弧度定义。这些值
MUST 由用户代理确定,并且 MUST 落在 0 到 PI 的范围内。
每个 XRSession
都有一个
最小近裁剪
平面和一个 最大远裁剪平面,以米定义。这些值 MUST
由用户代理确定,并且 MUST 为非负。最小近裁剪平面 SHOULD
小于 0.1。最大远裁剪平面 SHOULD 大于
1000.0(且 MAY 为无限大)。
当用户代理将使用 XRSession
session 和 XRRenderStateInit
newState 更新待处理层状态时,它必须运行以下步骤:
-
如果 newState 的
layers的 值不是null,则抛出NotSupportedError。
注:WebXR layers module 将为 此算法引入新的语义。
当用户代理想要在 XRSession
session 上应用标称帧率 rate 时,它 MUST 运行以下步骤:
-
如果 rate 与 session 的 内部标称帧率相同,则中止 这些步骤。
-
如果 session 的 ended 值为
true,则中止这些步骤。 -
将 session 的 内部标称帧率设为 rate。
-
在 session 上触发一个名为
frameratechange的XRSessionEvent事件。
updateTargetFrameRate(rate)
方法将目标帧率
rate 传递给 XRSession。
调用此方法时,用户代理 MUST 运行以下步骤:
-
令 session 为 this。
-
如果 session 没有内部标称帧率,则用 "
InvalidStateError"DOMException拒绝 promise 并返回 promise。 -
如果 session 的 ended 值为
true,则用 "InvalidStateError"DOMException拒绝 promise 并返回 promise。 -
如果 rate 不在
supportedFrameRates中,则用 "TypeError"DOMException拒绝 promise 并返回 promise。 -
将 session 的 内部目标帧率设为 rate。
-
排队一个任务以执行以下步骤:
-
返回 promise。
如果 XR 合成器出于任何原因
(例如在 "visible-blurred"
事件期间)更改了标称
帧率,则在导致帧率变化的事件结束后,它 SHOULD 使用
内部目标帧率。
updateRenderState(newState)
方法将对活动渲染状态的更新排队,以便在下一帧应用。传递给此方法的
XRRenderStateInit
newState 中未设置的字段不会被更改。
调用此方法时,用户代理 MUST 运行以下步骤:
-
令 session 为 this。
-
如果 session 的 ended 值为
true,则抛出InvalidStateError并中止这些步骤。 -
如果 newState 的
baseLayer是使用不同于 session 的XRSession创建的,则抛出InvalidStateError并中止这些步骤。 -
如果 newState 的
inlineVerticalFieldOfView已设置,且 session 是一个沉浸式会话,则抛出InvalidStateError并中止这些步骤。 -
如果 newState 的
depthNear、depthFar、inlineVerticalFieldOfView、baseLayer、layers都未设置,则中止这些步骤。 -
用 session 和 newState 运行 更新待处理层状态。
-
令 activeState 为 session 的活动渲染状态。
-
如果 session 的待处理渲染状态为
null,则将其设为 activeState 的副本。 -
如果 newState 的
passthroughFullyObscured值已设置,则将 session 的待处理渲染状态的passthroughFullyObscured设为 newState 的passthroughFullyObscured。 -
如果 newState 的
depthNear值已设置,则将 session 的待处理渲染状态的depthNear设为 newState 的depthNear。 -
如果 newState 的
depthFar值已设置,则将 session 的待处理渲染状态的depthFar设为 newState 的depthFar。 -
如果 newState 的
inlineVerticalFieldOfView已设置,则将 session 的待处理渲染状态的inlineVerticalFieldOfView设为 newState 的inlineVerticalFieldOfView。 -
如果 newState 的
baseLayer已设置,则将 session 的待处理渲染状态的baseLayer设为 newState 的baseLayer。
在请求时,XRSession
session MUST 通过运行以下步骤来
应用待处理渲染状态:
-
令 activeState 为 session 的活动渲染状态。
-
令 newState 为 session 的待处理渲染状态。
-
将 session 的待处理渲染状态设为
null。 -
令 oldBaseLayer 为 activeState 的
baseLayer。 -
令 oldLayers 为 activeState 的
layers。 -
排队一个任务以执行以下步骤:
-
将 activeState 设为 newState。
-
如果 oldBaseLayer 不等于 activeState 的
baseLayer, oldLayers 不等于 activeState 的layers, 或任一层的尺寸已更改,则为 session 更新 视口。 -
如果 activeState 的
inlineVerticalFieldOfView小于 session 的最小内联视场,则将 activeState 的inlineVerticalFieldOfView设为 session 的最小内联视场。 -
如果 activeState 的
inlineVerticalFieldOfView大于 session 的最大内联视场,则将 activeState 的inlineVerticalFieldOfView设为 session 的最大内联视场。 -
如果 activeState 的
depthNear小于 session 的最小近 裁剪平面,则将 activeState 的depthNear设为 session 的最小近裁剪平面。 -
如果 activeState 的
depthFar大于 session 的最大远裁剪 平面,则将 activeState 的depthFar设为 session 的最大远裁剪平面。 -
令 baseLayer 为 activeState 的
baseLayer。 -
按如下方式设置 activeState 的 composition enabled 和 output canvas:
- 如果 session 是一个内联会话,且 baseLayer 是一个
XRWebGLLayer实例,并且 composition enabled 设为false: -
将 activeState 的 composition enabled 布尔值设为
false。将 activeState 的 output canvas设为 baseLayer 的 context 的
canvas。 - 否则:
-
将 activeState 的 composition enabled 布尔值设为
true。将 activeState 的 output canvas设为
null。
- 如果 session 是一个内联会话,且 baseLayer 是一个
-
requestReferenceSpace(type)
方法在可能的情况下构造一个给定 type 的新 XRReferenceSpace。
调用此方法时,用户代理 MUST 运行以下步骤:
-
并行运行以下步骤:
-
如果对 type 和 session 运行 reference space is supported 的结果为
false,则排队一个任务以用NotSupportedError拒绝 promise 并中止这些步骤。 -
设置跟踪 type 类型参考空间所需的任何平台资源。
用户代理无需等待这类参考空间的跟踪建立完成后才解决requestReferenceSpace()。 当会话最初尝试建立跟踪时,getViewerPose()返回null是可以接受的,内容可以利用这段时间显示启动画面或其他内容。注意, 如果 type 是"bounded-floor", 且边界尚未建立,则用户代理 MAY 将边界设置为一个较小的初始区域,并在边界建立时使用reset事件。 -
排队一个任务以运行以下步骤:
-
用 type 和 session 创建参考空间 referenceSpace。
-
用 referenceSpace 解决 promise。
-
-
返回 promise。
每个 XRSession
都有一个
活动
XR 输入源列表(一个由 XRInputSource
组成的列表)
以及一个 活动
XR 被跟踪源列表(一个由 XRInputSource
组成的列表),二者 MUST
初始均为空列表。
每个 XRSession
都有一个
XR 设备,它是在初始化时设置的一个
XR 设备。
inputSources 属性返回 XRSession 的
活动 XR 输入源列表。
trackedSources 属性返回 XRSession 的
活动 XR 被跟踪源列表。活动 XR
被跟踪源列表 MUST 仅在
tracked-sources 包含于
已授予特性集合中时填充。
用户代理 MUST 监视与 XR 设备相关联的任何 XR 输入源,包括检测 XR 输入源何时被 添加、移除或更改。
每个 XRSession
都有一个
promise resolved 标志,初始为 false。
注:此标志的目的是确保 添加 输入源、移除输入源和 更改输入源 算法不会在用户代码实际有机会附加事件监听器之前运行。 如果实现只是选择在会话解决后开始监听输入源变化,则可能不需要此标志。
当新的 XR 输入源变得可用于 XRSession
session 时,用户代理 MUST 运行以下步骤:
-
如果 session 的 promise resolved 标志未设置,则中止这些 步骤。
-
令 added primary sources 为新的列表。
-
令 added tracked sources 为新的列表。
-
对于每个新的 XR 输入源:
-
令 inputSource 为此
XRSession的相关 realm中的 一个新XRInputSource, 然后执行以下步骤:- 如果 inputSource 是一个主输入 源:
-
将 inputSource 添加到 added primary sources。
- 否则,如果 tracked-sources 包含在 session 的已授予特性集合中:
-
将 inputSource 添加到 added tracked sources。
-
-
排队一个任务以执行以下步骤:
-
用 added primary sources 扩展 session 的 活动 XR 输入源列表。
-
如果 added primary sources 非空,则在 session 上触发一个名为
inputsourceschange的XRInputSourcesChangeEvent, 并将added设为 added primary sources。 -
用 added tracked sources 扩展 session 的 活动 XR 被跟踪 源列表。
-
如果 added tracked sources 非空,则在 session 上触发一个名为
trackedsourceschange的XRInputSourcesChangeEvent, 并将added设为 added tracked sources。
-
当任何先前添加的 XR 输入源不再可用
于 XRSession
session 时,用户代理 MUST 运行以下步骤:
-
如果 session 的 promise resolved 标志未设置,则中止这些 步骤。
-
令 removed primary sources 为新的列表。
-
令 removed tracked sources 为新的列表。
-
对于每个不再可用的 XR 输入 源:
-
令 inputSource 为 session 的 活动 XR 输入源列表中与该 XR 输入源关联的
XRInputSource, 然后执行以下步骤:- 如果 inputSource 是一个主输入 源:
-
将 inputSource 添加到 removed primary sources。
- 否则,如果 tracked-sources 包含在 session 的已授予特性集合中:
-
将 inputSource 添加到 removed tracked sources。
-
-
排队一个任务以执行以下步骤:
-
从 session 的活动 XR 输入源列表中 移除 removed primary sources 中的每个
XRInputSource。 -
如果 removed primary sources 非空,则在 session 上触发一个名为
inputsourceschange的XRInputSourcesChangeEvent, 并将removed设为 removed primary sources。 -
从 session 的活动 XR 被跟踪 源列表中 移除 removed tracked sources 中的每个
XRInputSource。 -
如果 removed tracked sources 非空,则在 session 上触发一个名为
trackedsourceschange的XRInputSourcesChangeEvent, 并将removed设为 removed tracked sources。
-
注:当输入源暂时同时失去位置和朝向跟踪时, 用户代理 MAY 触发此事件。建议仅对物理手持控制器输入源这样做。不建议在被跟踪的手部输入源发生这种情况时触发此事件, 因为这会经常发生;也不建议在跟踪器对象输入源发生这种情况时触发, 因为这会使应用更难维持身份概念。
当任何 XR 输入源的
handedness、
targetRayMode、
profiles、
gripSpace
是否存在,或其作为主输入源或被跟踪输入源的状态
发生变化,对于 XRSession
session,用户代理 MUST 运行以下步骤:
-
如果 session 的 promise resolved 标志未设置,则中止这些 步骤。
-
令 added primary sources 为新的列表。
-
令 removed primary sources 为新的列表。
-
令 added tracked sources 为新的列表。
-
令 removed tracked sources 为新的列表。
-
对于每个发生变化的 XR 输入源:
-
令 oldInputSource 为 session 的 活动 XR 输入源列表中先前与该 XR 输入源关联的
XRInputSource, 然后执行以下步骤:- 如果 oldInputSource 是一个主输入 源,或者其状态从主输入 源变为被跟踪输入源:
-
将 oldInputSource 添加到 removed primary sources。
- 否则,如果 tracked-sources 包含在 session 的已授予特性集合中:
-
将 oldInputSource 添加到 removed tracked sources。
-
令 newInputSource 为 session 的 相关 realm中的一个 新
XRInputSource, 然后执行以下步骤:- 如果 newInputSource 是一个主输入 源,或者其状态从被跟踪输入 源变为主输入源:
-
将 newInputSource 添加到 added primary sources。
- 否则,如果 tracked-sources 包含在 session 的已授予特性集合中:
-
将 newInputSource 添加到 added tracked sources。
-
-
排队一个任务以执行以下步骤:
-
从 session 的活动 XR 输入源列表中 移除 removed primary sources 中的每个
XRInputSource。 -
用 added primary sources 扩展 session 的 活动 XR 输入源列表。
-
如果 added primary sources 或 removed primary sources 非空, 则在 session 上触发一个名为
inputsourceschange的XRInputSourcesChangeEvent, 将added设为 added primary sources,并将removed设为 removed primary sources。 -
从 session 的活动 XR 被跟踪 源列表中 移除 removed tracked sources 中的每个
XRInputSource。 -
用 added tracked sources 扩展 session 的 活动 XR 输入源列表。
-
如果 added tracked sources 或 removed tracked sources 非空, 则在 session 上触发一个名为
trackedsourceschange的XRInputSourcesChangeEvent, 将added设为 added tracked sources,并将removed设为 removed tracked sources。
-
每个 XRSession
都有一个
visibility state 值,它是一个 enum。对于
内联会话,
visibility state MUST 镜像
Document
的 visibilityState。对于
沉浸式会话,
visibility state MUST 被设置为以下值中最能匹配会话状态的一个。
-
visible状态表示XRSession渲染的图像可被用户看到,并且requestAnimationFrame()回调按 XR 设备的原生刷新率处理。输入由XRSession正常处理。 -
visible-blurred状态表示XRSession渲染的图像可能可被用户看到,但不是主要焦点。requestAnimationFrame()回调 MAY 被节流。 输入不由XRSession处理。 -
hidden状态表示XRSession渲染的图像无法被用户看到。requestAnimationFrame()回调在 visibility state 改变之前不会被处理。输入不由XRSession处理。
visibilityState 属性返回
XRSession 的
visibility state。onvisibilitychange 属性是
visibilitychange
事件类型的事件处理器 IDL 属性。
visibility state MAY 由用户代理在处理 XR animation frame 期间之外的任何时间更改,并且用户代理 SHOULD 在可能时监视 XR 平台,以观察会话可见性何时受到用户代理外部因素影响,并相应更新 visibility state。
注:XRSession 的
visibility state 不一定意味着 HTML 文档的可见性。
根据系统配置,页面在沉浸式会话处于活动状态时
可能仍继续可见。(例如,连接到 PC 的头显在从沉浸式会话查看内容时,可能仍会在显示器上显示页面。)开发者应继续依赖
页面可见性来
确定页面可见性。
注:XRSession 的
visibility state 不会影响或限制在
沉浸式会话处于活动状态时
2D 内容仍然可见的 tethered sessions 中的鼠标行为。如果内容希望对鼠标行为有更强控制,
应考虑使用 [pointerlock] API。
在 XRSystem 中,
有若干可描述帧率的定义:
-
标称帧 率:
XRSystem要求体验渲染帧以维持标称性能的速率。错过帧的体验最终可能不会真正每秒收到这么多次requestAnimationFrame()调用,但这正是XRSystem试图实现的目标。 -
有效 帧率:对体验实际每秒能够处理多少次
requestAnimationFrame()调用的性能度量。这将根据体验命中或错过XRSystem的 帧时序而波动。 -
显示帧 率:帧被绘制到物理显示器上的实际速率,该速率 MAY 从体验的 标称帧率派生。这是不会暴露给体验的硬件实现 细节。
每个 XRSession
MAY
具有一个 内部目标 frameRate,即
目标帧率。
每个 XRSession
MAY
具有一个 内部标称 frameRate,即
标称帧率。
如果有效
帧率低于标称帧率,则
XR 合成器 MAY 使用重投影或其他
技术来改善体验。它是可选的,并且 MUST NOT 存在于
内联会话中。
frameRate 属性反映
内部
标称帧率。如果 XRSession 没有
内部标称帧率,则返回
null。
onframeratechange 属性是
frameratechange
事件类型的事件处理器 IDL 属性。如果
XRSession 的
标称帧率
因任何原因被更改,则它 MUST 使用新的标称帧率和该 XRSession
应用标称帧率。
supportedFrameRates 属性返回受支持
目标帧
率值的列表。此属性是可选的,并且 MUST NOT 存在于
内联会话中,也 MUST NOT
存在于不允许作者控制帧率的
XRSystem 中。
如果 XRSession
支持 supportedFrameRates
属性,它也 MUST 支持 frameRate。
每个 XRSession
都有一个
观看者参考空间,它是一个类型为
"viewer"
的 XRReferenceSpace,
并具有恒等
变换的 origin
offset。
每个 XRSession
都有一个
视图列表,它是一个列表,其中包含与
XR 设备所提供视图相对应的
视图。视图列表在
XRSession
期间不可变,
并且 MUST 包含会话期间可能暴露的任何视图,
包括一开始可能不活动的
辅助视图。
视图列表
中的主视图 MUST 由
XRSession 的
mode 和
已授予特性集合确定:
-
对于没有在其已授予特性集合中包含 inline-stereo 的
"inline"会话,视图 列表 MUST 包含一个单一主视图,其 eye 为"none"。 -
对于在其已授予特性集合中包含 inline-stereo 的
"inline"会话,视图 列表 MUST 包含两个主视图,其中一个的 eye 为"left", 另一个的 eye 为"right"。
onend 属性是 end
事件类型的事件处理器 IDL 属性。
oninputsourceschange 属性是
inputsourceschange
事件类型的事件处理器 IDL 属性。
onselectstart 属性是
selectstart
事件类型的事件处理器 IDL 属性。
onselectend 属性是 selectend
事件类型的事件处理器 IDL 属性。
onselect 属性是 select
事件类型的事件处理器 IDL 属性。
onsqueezestart 属性是
squeezestart
事件类型的事件处理器 IDL 属性。
onsqueezeend 属性是 squeezeend
事件类型的事件处理器 IDL 属性。
onsqueeze 属性是 squeeze
事件类型的事件处理器 IDL 属性。
4.2. XRRenderState
XRRenderState
表示一组可配置值,这些值会影响 XRSession 的
输出如何被合成。给定 XRSession 的
活动渲染状态只能在帧边界之间改变,并且可以通过 updateRenderState()
将更新排队。
dictionary {XRRenderStateInit double ;depthNear double ;depthFar boolean ;passthroughFullyObscured double ;inlineVerticalFieldOfView XRWebGLLayer ?;baseLayer sequence <XRLayer >?; }; [layers SecureContext ,Exposed =Window ]interface {XRRenderState readonly attribute double depthNear ;readonly attribute double depthFar ;readonly attribute boolean ?passthroughFullyObscured ;readonly attribute double ?inlineVerticalFieldOfView ;readonly attribute XRWebGLLayer ?baseLayer ; };
每个 XRRenderState
都有一个 输出画布,它是一个初始设为 null 的 HTMLCanvasElement。
输出画布是将显示为内联
会话渲染的任何内容的 DOM 元素。
每个 XRRenderState
还有一个 composition enabled 布尔值,其初始值为
true。如果渲染命令是针对由 API 提供并由 XR 合成器显示的表面执行,则认为该 XRRenderState
具有 composition enabled。如果为内联会话执行的渲染会以
直接显示到输出画布的方式进行,则该 XRRenderState
的
composition enabled 标志 MUST 为 false。
注:此时,XRRenderState
只有在其 composition
enabled 被设为 false 时才会拥有输出画布,但本规范未来版本可能会引入
设置输出画布的方法,以支持镜像和层合成等需要合成的更高级用途。
当为一个 XRSession
session 创建 XRRenderState
对象时,用户代理 MUST 通过运行以下步骤来初始化渲染状态:
-
令 state 为 session 的 相关 realm中的一个 新
XRRenderState对象。 -
将 state 的
depthNear初始化为0.1。 -
将 state 的
depthFar初始化为1000.0。 -
将 state 的
passthroughFullyObscured初始化为false。 -
按如下方式初始化 state 的
inlineVerticalFieldOfView:- 如果 session 是一个内联会话:
-
将 state 的
inlineVerticalFieldOfView初始化为PI * 0.5。 - 否则:
-
将 state 的
inlineVerticalFieldOfView初始化为null。
-
将 state 的
baseLayer初始化为null。
depthNear 属性定义近裁剪平面距
观看者的距离,单位为米。depthFar 属性定义远裁剪平面距
观看者的距离,单位为米。
depthNear
和 depthFar
用于计算 XRView 的
projectionMatrix。
当 projectionMatrix
用于渲染时,只有与观看者的距离落在 depthNear
和 depthFar
之间的几何体才会被绘制。它们还决定 XRWebGLLayer
深度缓冲区的值如何被解释。depthNear
MAY 大于 depthFar。
注:通常,在构造用于渲染的透视 投影矩阵时,开发者会指定视锥体以及近、远裁剪平面。在显示到 沉浸式 XR 设备时,正确的视锥体由所使用的光学器件、显示器和摄像头的某种组合来确定。 不过,近裁剪平面和远裁剪平面可由应用修改,因为合适的值取决于所渲染内容的类型。
passthroughFullyObscured 属性是
作者提供给 XRSystem
的
一个提示,用于表明他们打算用虚拟内容完全覆盖视口。一旦视口不再被不透明像素覆盖,
作者 SHOULD 将此标志重新设为 false。
注:XRSystem MAY
将其用作临时禁用透视的提示。在具有透视光学器件的设备上,用户会继续看到其环境,
此标志不会产生影响。
inlineVerticalFieldOfView 属性
定义在为 "inline"
XRSession
计算投影矩阵时使用的默认垂直视场,单位为弧度。投影矩阵计算还会考虑
输出画布的宽高比。
对于启用了 inline-stereo 的内联会话,用户代理 MUST
使用输出画布几何来计算每个视图的投影矩阵。对于
沉浸式会话,此值 MUST 为
null。
baseLayer 属性定义一个 XRWebGLLayer,
XR 合成器将从中获取图像。
4.3. 动画帧
XRSession
提供有关 XR
设备跟踪状态信息的主要方式,是通过在
XRSession
实例上调用 requestAnimationFrame()
来调度的回调。
callback =XRFrameRequestCallback undefined (DOMHighResTimeStamp ,time XRFrame );frame
每个 XRFrameRequestCallback
对象都有一个 cancelled 布尔值,初始设为
false。
每个 XRSession
都有一个
动画帧回调列表,
初始为空;一个 当前正在运行的动画帧回调列表,
也初始为空;以及一个 动画
帧回调标识符,它是一个初始为零的数字。
requestAnimationFrame(callback)
方法将 callback 排队,以便在用户代理下一次希望为设备运行动画帧时运行。
调用此方法时,用户代理 MUST 运行以下步骤:
cancelAnimationFrame(handle)
方法取消一个现有动画帧回调,该回调由其动画帧回调标识符
handle 给定。
调用此方法时,用户代理 MUST 运行以下步骤:
-
令 session 为 this。
-
在 session 的动画帧回调列表或 session 的当前正在运行的动画帧回调列表中, 查找与值 handle 相关联的条目。
-
如果存在这样的条目,则将其 cancelled 布尔值设为
true,并 将其从 session 的 动画帧回调列表中移除。
renderState
state 检查层
状态,用户代理 MUST 运行以下步骤:
-
如果 state 的
baseLayer为null,则返回false。 -
返回
true。
注:WebXR layers module 将为 此算法引入新的语义。
XRSession
session 渲染一帧,用户代理 MUST 运行以下步骤:
-
如果使用 session 的
renderState检查层状态为false,则返回false。 -
如果 session 是一个内联会话,并且 session 的
renderState的 输出画布为null,则返回false。 -
返回
true。
当一个 XRSession
session 从 XR 设备接收到时间戳
frameTime 的更新后观看者状态时,它会运行一个
XR
动画帧,无论 动画帧回调列表是否为空,该动画帧都 MUST 运行以下步骤:
-
排队一个任务以执行以下步骤:
-
令 now 为当前高分辨率时间。
-
令 frame 为 session 的动画 帧。
-
将 frame 的 time 设为 frameTime。
-
将 frame 的
predictedDisplayTime设为 frameTime。 -
如果 session 是一个沉浸式会话,则将 frame 的
predictedDisplayTime设为 XR 合成器预计显示此 XR 动画帧的平均时间戳。 -
对于 视图列表中的每个 view,将 view 的 viewport modifiable 标志设为 true。
-
如果该帧应为 session 渲染:
-
将 session 的 当前 正在运行的动画帧回调列表设为 session 的 动画帧 回调列表。
-
将 session 的 动画帧 回调列表设为空列表。
-
将 frame 的 active 布尔值设为
true。 -
为 frame 应用帧更新。
-
按顺序,对于 session 的 当前 正在运行的动画帧回调列表中的每个 entry:
-
如果 entry 的 cancelled 布尔值为
true,则继续处理下一个条目。 -
以 « now, frame » 和 "
report" 调用 entry。 -
将 session 的 当前 正在运行的动画帧回调列表设为空 列表。
-
将 frame 的 active 布尔值设为
false。
-
-
Window
接口的 requestAnimationFrame()
方法的行为不会因任何活动的 XRSession
的存在而改变,
在任何 XRSession
上调用 requestAnimationFrame()
也不会以任何方式与 Window
的
requestAnimationFrame()
交互。如果一个活动沉浸式会话导致页面被遮挡,则它 MAY 影响
浏览上下文的渲染机会。如果 2D 浏览器视图在
活动沉浸式会话期间可见(即,当会话运行在
tethered headset 上时),使用 Window
的
requestAnimationFrame()
和 requestIdleCallback()
运行的回调时序 MAY NOT 与会话的 requestAnimationFrame()
一致,并且用户不应依赖它来渲染 XR 内容。
注:如果在通过 Window
的
requestAnimationFrame()
调度的回调期间调用了 XRSession 的
requestAnimationFrame(),
用户代理可能希望向开发者控制台显示警告,因为如果活动沉浸式会话
影响了浏览上下文的
渲染机会,则这些回调并不保证会发生,而且即使运行,
也可能没有正确的时序。
Window
requestAnimationFrame()
的回调可能在会话处于活动状态时不会被处理。这取决于所使用设备的类型,并且最可能发生在移动或独立设备上,
在这些设备中沉浸式内容会完全遮挡 HTML 文档。因此,开发者不得依赖 Window
requestAnimationFrame()
回调来调度 XRSession
requestAnimationFrame()
回调,反之亦然,即使它们共享相同的渲染逻辑也是如此。不遵循此指导的应用可能无法在所有平台上正确执行。
对于希望在这两类动画循环之间转换的应用,更有效的模式如下所示:
let xrSession= null ; function onWindowAnimationFrame( time) { window. requestAnimationFrame( onWindowAnimationFrame); // This may be called while an immersive session is running on some devices, // such as a desktop with a tethered headset. To prevent two loops from // rendering in parallel, skip drawing in this one until the session ends. if ( ! xrSession) { renderFrame( time, null ); } } // The window animation loop can be started immediately upon the page loading. window. requestAnimationFrame( onWindowAnimationFrame); function onXRAnimationFrame( time, xrFrame) { xrSession. requestAnimationFrame( onXRAnimationFrame); renderFrame( xrFrame. predictedDisplayTime, xrFrame); } function renderFrame( time, xrFrame) { // Shared rendering logic. } // Assumed to be called by a user gesture event elsewhere in code. async function startXRSession() { xrSession= await navigator. xr. requestSession( 'immersive-vr' ); xrSession. addEventListener( 'end' , onXRSessionEnded); // Do necessary session setup here. // Begin the session's animation loop. xrSession. requestAnimationFrame( onXRAnimationFrame); } function onXRSessionEnded() { xrSession= null ; }
使用内联会话向 HTML 文档进行渲染的应用,不需要采取任何特殊步骤来协调动画循环, 因为在沉浸式会话处于活动状态时, 用户代理会自动暂停任何内联会话的动画循环。
4.4. XR 合成器
用户代理 MUST 维护一个 XR 合成器,它处理向 XR 设备的呈现和帧时序。 合成器 MUST 使用一个独立的渲染上下文,其状态与文档创建的任何图形上下文的状态相隔离。 合成器 MUST 防止页面破坏合成器状态,或回读来自其他页面或应用的内容。合成器还 MUST 在单独线程或 进程中运行,以将页面性能与以适当帧率向用户呈现新图像的能力解耦。合成器 MAY 在渲染内容上合成额外的设备或 用户代理 UI,例如设备菜单。
注:本规范未来的扩展也可能利用该 合成器来合成来自同一页面的多个层。
5. 帧 循环
5.1. XRFrame
XRFrame
表示一个 XRSession
的所有被跟踪对象状态的快照。应用可以通过在 XRSession 上用
XRFrameRequestCallback
调用 requestAnimationFrame()
来获取 XRFrame。
当回调被调用时,会传入一个 XRFrame。
需要传达跟踪状态的事件,例如 select
事件,也会提供一个 XRFrame。
[SecureContext ,Exposed =Window ]interface { [XRFrame SameObject ]readonly attribute XRSession session ;readonly attribute DOMHighResTimeStamp predictedDisplayTime ;XRViewerPose ?getViewerPose (XRReferenceSpace );referenceSpace XRPose ?getPose (XRSpace ,space XRSpace ); };baseSpace
每个 XRFrame 都有
一个 active 布尔值,初始设为 false,以及一个 animationFrame 布尔值,初始设为 false。
session 属性返回生成该 XRFrame 的
XRSession。
对于沉浸式
会话,predictedDisplayTime 属性 MUST
返回与此 XRFrame
预计显示在设备显示器上的平均时间点相对应的
DOMHighResTimeStamp。
对于内联会话,predictedDisplayTime
MUST 返回与传递给 XRFrameRequestCallback
的时间戳相同的值。
predictedDisplayTime
旨在允许以该帧显示时应处的状态来渲染动画 XR 场景,而不是以 requestAnimationFrame()
回调被调度或执行时的状态来渲染。
predictedDisplayTime
并非旨在用于推断应用有多少时间进行渲染,因为在帧提交后,XR
合成器通常还需要进行额外处理。
如果体验假定它可以一直处理到 predictedDisplayTime,
XR 合成器将无法使用提交的帧,
应用也将无法达到目标帧率。
每个 XRFrame
都表示给定 time 下所有被跟踪对象的状态,并且要么存储,
要么能够查询该状态在此 time 的具体信息。
getViewerPose(referenceSpace) 方法
以 XRViewerPose
的形式提供观看者相对于
referenceSpace 的姿态,时间为该 XRFrame 的
time。
调用此方法时,用户代理 MUST 运行以下步骤:
-
令 frame 为 this。
-
令 session 为 frame 的
session对象。 -
如果 frame 的 animationFrame 布尔值为
false,则抛出InvalidStateError并中止这些步骤。 -
令 pose 为 session 的 相关 realm中的一个 新
XRViewerPose对象。 -
在 referenceSpace 中,于 frame 表示的时间,将 session 的 观看者参考空间的姿态 填充到 pose pose 中,并将
force emulation设为true。 -
如果 pose 为
null,则返回null。 -
令 xrviews 为空列表。
-
令 offset 为
0。 -
对于
session上的 视图列表中的每个 活动 视图 view, 执行以下步骤:-
将 xrview 的 underlying view 初始化为 view。
-
将 xrview 的
index初始化为 offset。 -
将 xrview 的 frame 初始化为 frame。
-
将 xrview 的 session 初始化为 session。
-
将 xrview 的 reference space 初始化为 referenceSpace。
-
令 viewtransform 为 session 的 相关 realm中一个新的
XRRigidTransform对象,等于 view 在 视图偏移中的值。 -
将 xrview 的
transform属性设为在 session 的相关 realm 中,将XRViewerPose的transform与 viewtransform 变换相乘所得的结果。 -
将 xrview 追加到 xrviews。
-
将 offset 增加
1。
-
将 pose 的
views设为 xrviews -
返回 pose。
getPose(space, baseSpace) 方法
以 XRPose 的形式,
在 XRFrame
表示的时间,
提供 space 相对于 baseSpace 的姿态。
调用此方法时,用户代理 MUST 运行以下步骤:
帧更新是一种在给定
XRFrame 时可运行的算法,
旨在每个 XRFrame 运行。
每个 XRSession
都有
一个 帧更新列表,它是由帧更新组成的
列表,初始为空
列表。
为了为 XRFrame
frame 应用帧更新,用户代理 MUST 运行以下步骤:
注:本规范未定义任何 帧更新,但其他 规范可能会添加一些。
6. 空间
WebXR 设备 API 的一项核心特性是提供空间跟踪的能力。空间是一种接口, 使应用能够推理被跟踪实体与用户物理环境以及彼此之间的空间关系。
6.1. XRSpace
XRSpace
表示一个具有原点的虚拟坐标系,该原点对应于一个物理位置。从 API 请求或提供给 API 的空间数据,
始终在特定 XRSpace 中,
于特定 XRFrame
的时间表达。
姿态位置等数值是在该空间中相对于其原点的坐标。该接口有意保持不透明。
[SecureContext ,Exposed =Window ]interface :XRSpace EventTarget { };
每个 XRSpace 都有一个
session,它被设为创建该 XRSpace 的
XRSession。
每个 XRSpace 都有一个
native origin,它是空间中的一个位置和朝向。XRSpace 的
native origin
可以由 XR
设备的底层跟踪系统更新,并且不同的 XRSpace 可以定义
不同的语义,说明其 native
origins 如何被跟踪和更新。
每个 XRSpace 都有
一个 effective origin,它是该 XRSpace 的
坐标系的基础。
从有效空间到 native
origin 空间的变换,由一个
origin offset
定义,它是一个初始设为恒等变换的
XRRigidTransform。
换言之,可以通过将 origin
offset 和 native origin
相乘来获得
effective
origin。
XRSpace 的
effective
origin 只能在另一个 XRSpace 的坐标系中,
作为由 XRFrame 的
getPose()
方法返回的 XRPose
被观察到。XRSpace 之间的
空间关系 MAY 在不同 XRFrame 之间发生变化。
为了在一个 XRSpace
baseSpace 中,于一个 XRFrame
frame 表示的时间,将一个 XRSpace
space 的 pose
填充到一个 XRPose
pose 中,并带有可选的 force emulation 标志,用户代理 MUST 运行以下
步骤:
-
如果 frame 的 active 布尔值为
false,则抛出InvalidStateError并中止这些步骤。 -
令 session 为 frame 的
session对象。 -
如果 space 的 session 不等于 session,则抛出
InvalidStateError并中止这些步骤。 -
如果 baseSpace 的 session 不等于 session,则抛出
InvalidStateError并中止这些步骤。 -
检查是否可以报告姿态,如果不能,则抛出
SecurityError并中止这些步骤。 -
如果 session 的
visibilityState为"visible-blurred", 且 space 或 baseSpace 与XRInputSource相关联,则将 pose 设为null并中止这些步骤。 -
令 limit 为在 space 和 baseSpace 之间是否 必须限制姿态的结果。
-
令 transform 为 pose 的
transform。 -
查询 XR 设备的 跟踪系统,以获取 space 在 frame 的 time 相对于 baseSpace 的姿态, 然后执行以下步骤:
- 如果 limit 为
false,并且跟踪系统为 space 相对于 baseSpace 的姿态提供了一个位置被主动跟踪或静态已知的 6DoF 姿态: -
将 transform 的
orientation设为 space 的 effective origin 在 baseSpace 的 坐标 系中的朝向。将 transform 的
position设为 space 的 effective origin 在 baseSpace 的 坐标 系中的位置。如果支持,将 pose 的
linearVelocity设为 space 的 effective origin 相对于 baseSpace 的 坐标 系的线速度。如果支持,将 pose 的
angularVelocity设为 space 的 effective origin 相对于 baseSpace 的 坐标 系的角速度。将 pose 的
emulatedPosition设为false。 - 否则,如果 limit 为
false,并且跟踪系统提供了一个 3DoF 姿态,或一个其位置既未被主动跟踪也非静态已知的 6DoF 姿态,用于表示 space 相对于 baseSpace 的姿态: -
将 transform 的
orientation设为 space 的 effective origin 在 baseSpace 的 坐标 系中的朝向。将 transform 的
position设为跟踪系统对 space 的 effective origin 在 baseSpace 的 坐标系中位置的最佳估计。这 MAY 包括 诸如颈部或手臂模型之类的计算偏移。如果位置估计不可用, 则 MUST 使用最后已知位置。将 pose 的
linearVelocity设为null。将 pose 的
angularVelocity设为null。将 pose 的
emulatedPosition设为true。 - 否则,如果 space 相对于 baseSpace 的姿态过去已被确定,
且 force emulation 为
true: -
将 transform 的
position设为 space 的 effective origin 在 baseSpace 的 坐标 系中的最后已知位置。将 transform 的
orientation设为 space 的 effective origin 在 baseSpace 的 坐标 系中的最后已知朝向。将 pose 的
linearVelocity设为null。将 pose 的
angularVelocity设为null。将 pose 的
emulatedPosition布尔值设为true。 - 否则:
-
将 pose 设为
null。
- 如果 limit 为
注:XRPose 的
emulatedPosition
布尔值并不表示 baseSpace 的位置是否被模拟,而只表示评估 space 相对于
baseSpace 的位置是否依赖模拟。例如,具有 3DoF
跟踪的控制器,在其 targetRaySpace
或 gripSpace
针对 XRReferenceSpace
查询时,会报告 emulatedPosition
为 true 的姿态;但如果在 gripSpace
中查询 targetRaySpace
的姿态,则会报告 emulatedPosition
为 false,因为这两个空间之间的关系应当是精确已知的。
6.2. XRReferenceSpace
XRReferenceSpace
是若干常见 XRSpace
之一,
应用可以用它来建立与用户物理环境的空间关系。
XRReferenceSpace
通常预期在 XRSession
持续期间保持静态,
最常见的例外是用户在会话中重新配置。每个 XRReferenceSpace
的 native
origin 描述一个坐标系,
其中 +X 被视为“右”,+Y 被视为“上”,-Z 被视为“前”。
enum {XRReferenceSpaceType "viewer" ,"local" ,"local-floor" ,"bounded-floor" ,"unbounded" }; [SecureContext ,Exposed =Window ]interface :XRReferenceSpace XRSpace { [NewObject ]XRReferenceSpace getOffsetReferenceSpace (XRRigidTransform );originOffset attribute EventHandler onreset ; };
每个 XRReferenceSpace
都有一个 type,它是一个 XRReferenceSpaceType。
XRReferenceSpace
最常通过调用 requestReferenceSpace()
获得;如果传入调用的 XRReferenceSpaceType
enum 值受支持,它会创建一个 XRReferenceSpace
实例(或扩展它的接口)。该类型指明该参考空间将表现出的跟踪行为:
-
传入
viewer类型会创建一个XRReferenceSpace实例。它表示一个跟踪空间,其 native origin 跟踪 观看者的位置和朝向。每个XRSessionMUST 支持"viewer"XRReferenceSpace。 -
传入
local类型会创建一个XRReferenceSpace实例。它表示一个跟踪空间,其 native origin 在创建时位于 观看者附近。确切的位置和朝向将根据底层平台的约定进行初始化。使用此参考空间时,用户预期不会远离其初始位置太多, 如果有移动也很少,并且跟踪会针对该用途进行优化。对于具有 6DoF 跟踪的设备,local参考空间应强调保持原点相对于用户环境的稳定。 -
传入
local-floor类型会创建一个XRReferenceSpace实例。它表示一个跟踪空间,其 native origin 位于 地板上用户可以安全站立的位置。Y轴在地板高度等于0,X和Z的位置和朝向根据底层平台的约定初始化。如果地板高度未知, 则 MUST 估计,并带有某个 估计 地板高度。如果估计地板高度是用非默认值确定的,则 MUST 对其进行充分 舍入 以防止指纹识别。使用此参考空间时,用户预期不会远离其初始位置太多,如果有移动也很少,并且跟踪会针对该用途进行优化。 对于具有 6DoF 跟踪的设备,local-floor参考空间应强调保持原点相对于用户环境的稳定。注:如果
"local-floor"参考空间的地板高度被调整以防止指纹识别,建议舍入到最接近的 1cm。 -
传入
bounded-floor类型会创建一个XRBoundedReferenceSpace实例。它表示一个跟踪空间,其 native origin 位于地板上, 且用户预期在一个预先建立的边界内移动,该边界以boundsGeometry给出。bounded-floor参考空间中的跟踪会针对保持 native origin 和boundsGeometry相对于用户环境稳定而优化。 -
传入
unbounded类型会创建一个XRReferenceSpace实例。它表示一个跟踪空间,其中用户预期可以在其环境中自由移动,甚至可能移动到距离起点很远的位置。unbounded参考空间中的跟踪会针对用户当前位置周围的稳定性进行优化,因此 native origin 可能会随时间漂移。
注:假定底层平台关于参考空间 Y 轴的约定在不同类型的
XRReferenceSpace
之间保持一致。换言之,如果 XR 系统支持多个参考空间,则在创建它们的 XRSession
持续期间,它们的 Y 轴将彼此平行并指向同一方向。这不适用于 "viewer",
后者不依赖底层平台的朝向约定。"unbounded"
参考空间在其原点相近时应使其 Y 轴与其他参考空间对齐,但如果用户移动了很长距离,则可能发生偏离。
支持 "local"
参考空间的设备 MUST 支持 "local-floor"
参考空间,必要时通过模拟实现,反之亦然。
onreset 属性是 reset
事件类型的事件处理器 IDL 属性。
当为 XRSession
session 请求一个类型为 XRReferenceSpaceType
type 的 XRReferenceSpace
时,用户代理 MUST 通过运行以下步骤来创建参考空间:
-
按如下方式初始化 referenceSpace:
- 如果 type 为
bounded-floor: -
令 referenceSpace 为 session 的 相关 realm中的一个 新
XRBoundedReferenceSpace。 - 否则:
-
令 referenceSpace 为 session 的 相关 realm中的一个 新
XRReferenceSpace。
- 如果 type 为
-
将 referenceSpace 的 type 初始化为 type。
-
将 referenceSpace 的 session 初始化为 session。
-
返回 referenceSpace。
XRSession
session 是否支持参考空间,运行以下步骤:
-
如果 type 为
viewer, 则返回true。 -
如果 type 为
local或local-floor, 且 session 是一个沉浸式会话,则返回true。 -
如果 type 为
local或local-floor, 且 XR 设备支持报告朝向数据,则返回true。 -
如果 type 为
bounded-floor且 session 是一个沉浸式会话,则返回 XR 设备是否支持有界参考空间的结果。 -
如果 type 为
unbounded, session 是一个沉浸式会话,并且 XR 设备支持在用户附近进行 无限制距离的稳定跟踪,则返回true。 -
返回
false。
getOffsetReferenceSpace(originOffset)
方法在调用时 MUST 执行以下步骤:
-
令 base 为调用此方法的
XRReferenceSpace。 -
按如下方式初始化 offsetSpace:
- 如果 base 是
XRBoundedReferenceSpace的实例: -
令 offsetSpace 为 base 的 相关 realm中的一个 新
XRBoundedReferenceSpace, 并将 offsetSpace 的boundsGeometry设为 base 的boundsGeometry, 其中每个点都乘以 originOffset 的inverse。 - 否则:
-
令 offsetSpace 为 base 的 相关 realm中的一个 新
XRReferenceSpace。
- 如果 base 是
-
将 offsetSpace 的 origin offset 设为在 base 的 相关 realm中,将 base 的 origin offset 与 originOffset 相乘所得的结果。
-
返回 offsetSpace。
注:预计一些应用会使用
getOffsetReferenceSpace()
来实现基于鼠标、键盘、触摸或游戏手柄输入的场景导航控制。这会导致
getOffsetReferenceSpace()
被频繁调用,在活动输入期间至少每帧调用一次。因此,强烈鼓励 UA 使通过
getOffsetReferenceSpace()
创建新的 XRReferenceSpace
成为轻量级操作。
6.3. XRBoundedReferenceSpace
XRBoundedReferenceSpace
扩展了 XRReferenceSpace,
以包含 boundsGeometry,
用于指示用户空间的预配置边界。
[SecureContext ,Exposed =Window ]interface :XRBoundedReferenceSpace XRReferenceSpace {readonly attribute FrozenArray <DOMPointReadOnly >boundsGeometry ; };
XRBoundedReferenceSpace
的原点 MUST 位于地板处,使得 Y 轴在地板高度等于 0。X 和
Z 的位置和朝向根据底层平台的约定初始化,通常预期位于房间中心附近,并朝向合乎逻辑的前方
方向。
注:其他 XR 平台有时会将
bounded-floor
参考空间提供的跟踪类型称为“房间尺度”跟踪。XRBoundedReferenceSpace
并非旨在描述多房间空间、地板高度不平的区域,或非常大的开放区域。
需要处理这些场景的内容应使用 unbounded
参考空间。
每个 XRBoundedReferenceSpace
都有一个 native bounds geometry,用于描述
XRBoundedReferenceSpace
周围的边界,用户可以预期在该边界内安全移动。该多边形边界以 DOMPointReadOnly
数组给出,表示安全空间边缘处的一圈点。这些点描述了相对于
native
origin 的偏移,单位为米。从上方向下看、朝向 Y 轴负端时,这些点 MUST 以顺时针顺序给出。
每个点的 y
值 MUST 为 0,并且每个点的 w
值 MUST 为 1。可认为边界源自地板并无限向上延伸。它描述的形状 MAY 为凸形或凹形。
native bounds geometry 中的每个点 MUST 被限制在距参考空间的 native origin 的合理距离内。
注:建议将 native bounds geometry 的点在所有方向上 限制在距 native origin 15 米以内。
native bounds geometry 中的每个点还 MUST 被充分量化,以防止指纹识别。为保证用户安全, 量化后的点值 MUST NOT 落在平台报告的边界之外。
注:建议将 native bounds geometry 的点 量化到最接近的 5cm。
boundsGeometry 属性
是一个由 DOMPointReadOnly
组成的数组,使得每个条目等于 XRBoundedReferenceSpace
的
native bounds geometry 中对应条目,
预乘以 origin
offset 的 inverse。
换言之,它在相对于 effective origin 的 XRBoundedReferenceSpace
坐标中提供同一边界。
如果 native bounds geometry 暂时
不可用,可能原因包括 XR 设备初始化期间、长时间跟踪丢失,或在预配置空间之间移动,则
boundsGeometry
MUST 报告为空数组。
注:如果在请求参考空间时边界或地板高度尚未解析, 但已知 XR 设备支持它们,则可以返回有界参考空间。
注:内容不应要求用户移动到
boundsGeometry
之外。如果用户的物理环境允许,用户可能移动到边界之外,从而产生位于它们所描述多边形之外的位置值。
这不是错误条件,页面内容应优雅地处理。
注:内容通常不应提供
boundsGeometry
的可视化,因为确保向用户提供安全关键信息是用户代理的责任。
7. 视图
7.1. XRViewGeometry
包含 XRViewGeometry
interface mixin 的对象,表示 XR 设备用于向用户呈现图像的显示器,或用于收集真实世界视觉信息的传感器。
这些对象包含一个视图几何。
视图几何 对应于一组内参和外参,用于在 viewer reference space 的 坐标系中的点与 containing object 的 屏幕空间中的点之间进行转换。
视图几何具有一个 containing object,它是该 视图几何 包含数据所对应的物理硬件部件。
视图几何的 containing object 具有一个关联的 屏幕空间,它被描述为该 containing object 从中读取数据或向其渲染 数据的 2D 平面。
视图几何具有一个
关联的 视图偏移,它是一个 XRRigidTransform,
描述 containing
object 在 viewer reference space 的
坐标系中的位置和朝向。
注:对视图偏移可能是什么没有约束,并且允许视图具有不同的朝向。 这可能出现在眼部显示器以某个角度居中的头戴式设备中,也可能在更极端的情况中出现,例如 CAVE 渲染。因此,z 排序和剔除等技术可能需要按眼睛分别执行。
视图几何具有一个 关联的 投影矩阵,它是一个矩阵,描述向底层 XR 设备提供的 containing object 进行渲染时要使用的投影。 投影矩阵 MAY 包含诸如剪切之类的变换,使得无法用简单视锥体 准确描述该投影。
注:此矩阵的逆适合用于从 屏幕空间中“读取”像素,并将其转换回以 containing object 为原点的坐标系。
[SecureContext ,Exposed =Window ]interface mixin {XRViewGeometry readonly attribute Float32Array projectionMatrix ; [SameObject ]readonly attribute XRRigidTransform transform ; };
每个 XRViewGeometry
都有关联的 内部投影矩阵,用于存储其
containing
object 的 投影矩阵。它最初为 null。
注:transform
可用于在许多渲染库中定位相机对象。如果应用需要更传统的视图矩阵,可以通过调用
transform.inverse.matrix 来获取。
projectionMatrix 属性是底层
视图几何的
投影矩阵。强烈建议应用在不修改或分解的情况下使用此矩阵。
渲染时未使用所提供的投影矩阵,可能导致呈现的帧失真或严重错位,从而造成不同程度的用户不适。
此属性 MUST 通过为该 XRViewGeometry
获得投影矩阵来计算。
transform 属性是该对象的 XRRigidTransform。
它表示该对象在用于获取该对象的 XRReferenceSpace
中的位置和朝向。
为了为给定 XRViewGeometry
view geometry 获得投影矩阵:
7.2. XRView
视图对应于 XR 设备
用于向用户呈现图像的显示器或显示器的一部分。它们用于检索渲染内容所需的全部信息,使内容与该
视图的物理输出属性良好对齐,包括视场、眼睛偏移和其他光学属性。
视图可以覆盖用户视觉的重叠区域。
不保证任何 XR 设备使用的视图数量或顺序,也不要求视图数量在 XRSession
持续期间保持不变。
视图具有一个关联的
eye,
它是一个 XREye,
描述该视图预期显示给哪只眼睛。如果该视图没有内在关联的眼睛(例如显示器是单目的),则此值
MUST 被设为 "none"。
视图具有一个 active 标志,该标志可能在
XRSession
的生命周期中变化。
Primary views MUST 始终将
active 标志设为
true。
注:许多 HMD 会请求内容渲染两个 视图,一个用于左眼,一个用于右眼,而大多数 magic window 设备只会请求一个视图,但应用绝不应假定特定的视图配置。例如:magic window 设备如果支持立体输出, 可能请求两个视图,但如果关闭立体输出模式,出于性能原因也可能退回到请求单个视图。同样,HMD 可能会请求两个以上的视图,以支持宽视场或不同像素密度的显示器。
视图具有一个内部
viewport
modifiable 标志,用于指示此时会话中是否可以通过 requestViewportScale()
调用更改视口缩放。它在动画帧开始时被设为 true,并在调用
getViewport()
时设为 false。
视图具有一个内部
requested
viewport scale 值,表示该视图请求的视口缩放。它最初设为 1.0,并且如果系统支持动态视口缩放,
可由 requestViewportScale()
方法修改。
视图具有一个内部
current viewport
scale 值,表示系统内部用于此视图的当前视口缩放。它最初设为 1.0。当通过
getViewport()
调用成功应用视口更改时,它会更新为匹配
requested viewport
scale。
视图具有一个
reference space,
它是在 getViewerPose()
中用于获取此视图的
XRReferenceSpace
空间。
注:动态视口缩放允许应用使用每个动画帧都可以更改的缩放因子,
渲染到完整大小视口的一个子集。其意图是在不重新分配的情况下,以每帧为基础高效修改。为正确渲染,
XR 系统和应用必须对活动视口达成一致。应用可以在单个动画帧内对一个
XRView
多次调用 requestViewportScale(),
但请求的缩放直到应用为该视图调用 getViewport()
才会生效。动画帧中的第一次 getViewport 调用会应用更改(立即对当前动画帧生效),
锁定该视图在此动画帧剩余期间的当前缩放视口,并将该缩放设为未来动画帧的新默认值。
可选地,系统可以通过 recommendedViewportScale
属性,基于内部性能启发式和目标帧率提供建议值。
enum {XREye ,"none" ,"left" }; ["right" SecureContext ,Exposed =Window ]interface {XRView readonly attribute XREye eye ;readonly attribute unsigned long index ;readonly attribute double ?recommendedViewportScale ;undefined requestViewportScale (double ?); };scale XRView includes XRViewGeometry ;
transform
在其 reference
space 中给出。
eye 属性描述底层视图的 eye。此属性的主要目的,是确保预渲染的立体
内容可以将内容的正确部分呈现给正确的眼睛。
index 属性描述当此 XRView 由
getViewerPose()
在 views
数组中返回时的偏移。
可选的 recommendedViewportScale 属性包含
UA 推荐的视口缩放值,应用可将其用于 requestViewportScale()
调用,以配置动态视口缩放。如果系统未实现用于确定推荐缩放的启发式或方法,则它为
null。如果不为 null,则该值 MUST 是大于 0.0 且小于或等于 1.0 的数值,并且
MUST 被量化,以避免提供详细的性能或 GPU 利用率
数据。
注:建议通过将推荐的视口缩放舍入到一小组可能缩放值中最接近的值, 并使用滞后来避免接近边界值时立即发生变化,从而量化推荐的视口缩放。(这也有助于避免快速振荡的缩放值, 因为这可能造成视觉干扰或不适。)
每个 XRView 都有
一个关联的 session,它是生成该对象的 XRSession。
每个 XRView 都有
一个关联的 frame,它是生成该对象的 XRFrame。
每个 XRView 都有
一个关联的 underlying view,它是其所表示的底层视图。
requestViewportScale(scale)
方法请求用户代理将此视口的 requested viewport
scale 设置为所请求的值。
当此方法在一个 XRView
xrview 上调用时,用户代理 MUST 运行以下步骤:
-
如果 scale 为 null 或 undefined,则中止这些步骤。
-
如果 scale 小于或等于 0.0,则中止这些步骤。
-
如果 scale 大于 1.0,则将 scale 设为 1.0。
-
令 view 为 xrview 的 underlying view。
-
将 view 的 requested viewport scale 值设为 scale。
注:该方法会忽略 null 或 undefined 缩放
值,使应用即使在系统不提供推荐缩放时,也能安全地使用
view.requestViewportScale(view.recommendedViewportScale)。
为了为给定 XRView
view 和 XRSession
session 获得缩放视口:
-
令 glFullSizedViewport 为来自与 view 关联的 完整大小视口列表的 WebGL viewport。
-
令 scale 为 view 的 current viewport scale。
-
用户代理 MAY 选择钳制 scale,以应用最小视口缩放因子。
-
令 glViewport 为一个新的 WebGL viewport。
-
将 glViewport 的
width设为小于或等于 glFullSizedViewport 的width乘以 scale 的整数值。 -
如果 glViewport 的
width小于 1,则将其设为 1。 -
将 glViewport 的
height设为小于或等于 glFullSizedViewport 的height乘以 scale 的整数值。 -
如果 glViewport 的
height小于 1,则将其设为 1。 -
将 glViewport 的
x分量设为一个整数值,该值位于 glFullSizedViewport 的x分量(含)与 glFullSizedViewport 的x分量加上 glFullSizedViewport 的width再减去 glViewport 的width(含)之间。 -
将 glViewport 的
y分量设为一个整数值,该值位于 glFullSizedViewport 的y分量(含)与 glFullSizedViewport 的y分量加上 glFullSizedViewport 的height再减去 glViewport 的height(含)之间。 -
令 viewport 为 session 的 相关 realm中的一个 新
XRViewport。 -
将 viewport 的
x初始化为 glViewport 的x分量。 -
将 viewport 的
y初始化为 glViewport 的y分量。 -
将 viewport 的
width初始化为 glViewport 的width。 -
将 viewport 的
height初始化为 glViewport 的height。 -
返回 viewport。
注:具体整数值的计算有意留给 UA 自行决定。
直接向下舍入 width/height 并按原样使用 x 和 y 偏移是有效的,
但 UA MAY 也选择在指定约束内略作调整的值,例如为了效率将视口对齐到 2 的幂像素网格。
缩放视口 MUST 完全包含在完整大小视口内,但 MAY 按 UA 自行决定放置在完整大小视口内的任意位置。
大小和位置计算 MUST 是确定性的,并且在会话内对相同输入值返回一致结果。
7.3. 主视图和次视图
当为了 XR 体验必须向某个 视图渲染时,该视图就是
主视图。主
视图 MUST 在整个 XRSession
持续期间保持活动。
当内容可以选择不向某个 视图渲染且仍能产生可用的 沉浸式体验时,该视图就是 次视图。当内容选择不向这些视图渲染时, 用户代理 MAY 能够通过重投影重建它们。除非启用了 "secondary-views" 特性,否则次视图 MUST NOT 为 活动。
次 视图的示例包括用于视频捕获的 第一人称观察者视图,或每只眼睛有两个不同分辨率和视场的视图的 “quad views”。
views
数组做出错误假设,因此在呈现超过两个视图时会出错。
由于用户代理可能具备使用重投影等机制来替代内容渲染这些 次视图的能力, 因此最好能够区分哪些内容计划自行处理这些 次视图, 以及哪些内容要么不知道这些 次视图的存在,要么不希望处理它们。
为支持这一点,暴露 次视图的用户代理 MUST 支持 "secondary-views" 特性描述符作为提示。启用此特性的内容预期会:
当启用 "secondary-views" 时,用户代理 MAY
在必要时向 XRSession
公开设备支持的任何次视图。
在这种情况下,用户代理 MUST NOT 使用重投影来重建次视图,而应依赖内容决定渲染的任何内容。
注:我们建议内容使用 optionalFeatures
启用 "secondary-views",以确保最大兼容性。
如果次视图具有较低的
底层帧率,则 XRSession MAY
选择执行以下一项或多项:
7.4. XRViewport
XRViewport
对象描述图形表面的视口,或矩形区域。
[SecureContext ,Exposed =Window ]interface {XRViewport readonly attribute long x ;readonly attribute long y ;readonly attribute long width ;readonly attribute long height ; };
x 和 y 属性定义相对于表面原点的偏移,而
width 和 height 属性定义视口的矩形尺寸。
视口值的确切解释取决于与该视口关联的图形 API 的约定:
-
当与
XRWebGLLayer一起使用时,x和y属性以像素为单位指定视口矩形的左下角,视口矩形从x向右延伸width像素,并从y向上延伸height像素。这些值可以直接传递给 WebGL viewport 函数。
XRViewerPose
的所有 XRView,
为每一个从 XRWebGLLayer
查询一个 XRViewport,
并用它们设置用于渲染的适当 WebGL
viewport。
xrSession. requestAnimationFrame(( time, xrFrame) => { const viewer= xrFrame. getViewerPose( xrReferenceSpace); gl. bindFramebuffer( xrWebGLLayer. framebuffer); for ( xrViewof viewer. views) { let xrViewport= xrWebGLLayer. getViewport( xrView); gl. viewport( xrViewport. x, xrViewport. y, xrViewport. width, xrViewport. height); // WebGL draw calls will now be rendered into the appropriate viewport. } });
8. 几何基元
8.1. 矩阵
WebXR 以 矩阵的形式提供各种变换。WebXR 在传递矩阵时使用 WebGL
约定,其中 4x4 矩阵作为 16 元素的 Float32Array
给出,采用列优先存储,并通过从左侧预乘矩阵应用到列向量。它们可以直接传递给 WebGL 的
uniformMatrix4fv
函数,用于创建等效的 DOMMatrix,
或与各种第三方数学库一起使用。
Float32Array,
布局如下:
[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15]
将此矩阵作为变换应用于一个指定为 DOMPointReadOnly
的列向量,如下:
{x:X, y:Y, z:Z, w:1}
会产生以下结果:
a0 a4 a8 a12 * X = a0 * X + a4 * Y + a8 * Z + a12 a1 a5 a9 a13 Y a1 * X + a5 * Y + a9 * Z + a13 a2 a6 a10 a14 Z a2 * X + a6 * Y + a10 * Z + a14 a3 a7 a11 a15 1 a3 * X + a7 * Y + a11 * Z + a15
8.2. 归一化
有若干算法要求对向量或四元数进行归一化,这意味着缩放其各分量,使其总体大小为 1.0。
为了归一化一个分量列表, UA MUST 执行以下步骤:
-
令 length 为各分量平方之和的平方根。
-
如果 length 为
0,则抛出InvalidStateError并中止这些步骤。 -
将每个分量除以 length 并设置该分量。
8.3. XRRigidTransform
XRRigidTransform
是由 position
和 orientation
描述的变换。在解释 XRRigidTransform
时,orientation
始终先于 position
应用。
XRRigidTransform
包含一个 内部矩阵,它是一个矩阵。
[SecureContext ,Exposed =Window ]interface {XRRigidTransform constructor (optional DOMPointInit = {},position optional DOMPointInit = {}); [orientation SameObject ]readonly attribute DOMPointReadOnly position ; [SameObject ]readonly attribute DOMPointReadOnly orientation ;readonly attribute Float32Array matrix ; [SameObject ]readonly attribute XRRigidTransform inverse ; };
XRRigidTransform(position, orientation)
构造函数在调用时 MUST 执行以下步骤:
-
令 transform 为新的
XRRigidTransform对象,位于当前 realm 中。 -
令 transform 的
position为新的DOMPointReadOnly, 位于当前 realm 中。 -
如果 position 或 orientation 的一个或多个值为
NaN或 其他非有限数,例如infinity,则抛出TypeError并中止这些步骤。 -
将 transform 的
position的x值设为 position 的 x 字典成员,将y值设为 position 的 y 字典成员,将z值设为 position 的 z 字典成员,并将w值设为 position 的 w 字典成员。 -
令 transform 的
orientation为新的DOMPointReadOnly, 位于当前 realm 中。 -
将 transform 的
orientation的x值设为 orientation 的 x 字典成员,将y值设为 orientation 的 y 字典成员,将z值设为 orientation 的 z 字典成员,并将w值设为 orientation 的 w 字典成员。 -
令 transform 的 内部矩阵为
null。 -
返回 transform。
position 属性是一个三维点,以米为单位给出,
描述该变换的平移分量。position
的
w
属性 MUST 为 1.0。
orientation 属性是描述该变换旋转分量的四元数。
orientation
MUST 被归一化,使其长度为 1.0。
matrix 属性以矩阵形式返回由 position
和 orientation
属性描述的变换。此属性 MUST 通过为该 XRRigidTransform
获得矩阵来计算。
注:当此矩阵预乘到列向量上时,会先按照
orientation
描述的 3D 旋转来旋转该向量,然后再按 position
对其进行平移。在数学上的列向量表示法中,这是 M = T * R,其中 T 是对应于
position
的平移矩阵,而 R 是对应于 orientation
的旋转矩阵。
为了为给定 XRRigidTransform
transform 获得矩阵:
-
如果 transform 的 内部矩阵不是
null, 则执行以下步骤:-
如果 内部矩阵上的
IsDetachedBuffer操作为false,则返回 transform 的 内部矩阵。
-
-
令 translation 为新的矩阵,该矩阵是对应于
position的列向量平移矩阵。数学上,如果position为(x, y, z),则该矩阵为 -
令 rotation 为新的矩阵,该矩阵是对应于
orientation的列向量旋转矩阵。数学上,如果orientation是单位四元数 (qx, qy, qz, qw),则该矩阵为 -
将 transform 的 内部矩阵设为 transform 的 相关 realm中的一个 新
Float32Array, 其值是在 transform 的 相关 realm中,将 translation 和 rotation 相乘且 translation 在左侧(translation * rotation)的结果。 数学上,此矩阵为 -
返回 transform 的 内部矩阵。
XRRigidTransform
transform 的 inverse
属性返回一个位于
transform 的相关 realm 中的 XRRigidTransform,
如果将其应用于之前已由 transform 变换过的对象,它会撤销该变换并将对象恢复到其初始姿态。
此属性 SHOULD 惰性求值。由 inverse
返回的 XRRigidTransform
MUST 将 transform 作为其 inverse
返回。
一个 XRRigidTransform,
其 position
为 { x: 0, y: 0, z: 0 w: 1 },且其 orientation
为 { x: 0, y: 0, z: 0, w: 1 },称为 恒等变换。
为了在 Realm
realm 中将两个 XRRigidTransform
相乘,
B 和 A,UA MUST 执行以下步骤:
-
令 result 为 realm 中一个新的
XRRigidTransform对象。 -
将 result 的
matrix设为 realm 中一个新的Float32Array, 其值为将 B 的matrix从左侧预乘到 A 的matrix上的结果。 -
将 result 的
orientation设为 realm 中一个新的DOMPointReadOnly, 其值为描述 result 的matrix左上 3x3 子矩阵所表示旋转的四元数。 -
将 result 的
position设为 realm 中一个新的DOMPointReadOnly, 其值为 result 的matrix第四列给出的向量。 -
返回 result。
result 是从 A 的源空间到 B 的目标空间的变换。
注:这等价于构造一个 XRRigidTransform,
其 orientation
是 A 和 B 的朝向组合,且其 position
等于 A 的 position
经 B 的 orientation
旋转后,再加上 B 的 position。
9. 姿态
9.1. XRPose
XRPose 描述
空间中相对于一个 XRSpace
的
位置和朝向。
[SecureContext ,Exposed =Window ]interface { [XRPose SameObject ]readonly attribute XRRigidTransform transform ; [SameObject ]readonly attribute DOMPointReadOnly ?linearVelocity ; [SameObject ]readonly attribute DOMPointReadOnly ?angularVelocity ;readonly attribute boolean emulatedPosition ; };
transform 属性描述相对于基准 XRSpace 的
位置和朝向。
linearVelocity 属性描述相对于基准 XRSpace 的线速度,
单位为米每秒。如果用户代理无法填充它,则允许返回 null。
angularVelocity 属性描述相对于基准 XRSpace 的角速度,
单位为弧度每秒。如果用户代理无法填充它,则允许返回 null。
当 transform
表示基于传感器读数的主动跟踪 6DoF 姿态时,
emulatedPosition 属性为 false;
如果其 position
值包含一个计算偏移,例如由颈部或手臂模型提供的偏移,则为
true。在确定一个 XRPose 是否包含
计算
偏移时,MUST NOT 考虑估计地板
高度。
9.2. XRViewerPose
XRViewerPose
是一个 XRPose,
描述由 XR 设备跟踪的 XR
场景
观看者状态。观看者可以表示被跟踪的硬件部件、相对于硬件观察到的用户位置,
或计算进入 XR 场景的一系列视点的其他方式。XRViewerPose
只能相对于 XRReferenceSpace
查询。除了 XRPose 值之外,
它还提供一个视图数组,其中包含刚性变换,
用于指示视点和投影矩阵。应用在渲染 XR 场景的一帧时应使用这些值。
[SecureContext ,Exposed =Window ]interface :XRViewerPose XRPose { [SameObject ]readonly attribute FrozenArray <XRView >views ; };
views 数组是一个 XRView 序列,
描述 XR 场景的视点,这些视点相对于查询该 XRViewerPose
时所用的 XRReferenceSpace。
为了在 XR
设备上正确显示,数组中 XR 场景的每个
视图都必须被渲染。每个 XRView 都包含
刚性变换,用于指示视点和投影矩阵,并且在需要时可用于从层中查询 XRViewport。
注:XRViewerPose
的
transform
可用于为场景的旁观者视图或多用户交互定位观看者的图形表示。
10. 输入
10.1. XRInputSource
XRInputSource
表示一个 XR 输入
源,它是任何允许用户在与观看者相同的
虚拟空间中执行定向操作的输入机制。XR
输入源的示例
包括但不限于手持控制器、光学跟踪的手,以及基于注视、并基于观看者姿态
运行的输入方法。未明确关联到 XR
设备的输入机制,例如传统游戏手柄、鼠标或键盘,SHOULD NOT 被
视为 XR 输入
源。
enum {XRHandedness ,"none" ,"left" };"right" enum {XRTargetRayMode "gaze" ,"tracked-pointer" ,"screen" ,"transient-pointer" }; [SecureContext ,Exposed =Window ]interface {XRInputSource readonly attribute XRHandedness handedness ;readonly attribute XRTargetRayMode targetRayMode ; [SameObject ]readonly attribute XRSpace targetRaySpace ; [SameObject ]readonly attribute XRSpace ?gripSpace ; [SameObject ]readonly attribute FrozenArray <DOMString >profiles ;readonly attribute boolean skipRendering ; };
注:XRInputSource
接口也由 WebXR 游戏手柄
模块扩展
handedness 属性描述该 XR 输入源关联到哪只手,
如果有关联的话。没有自然 handedness 的输入源(例如头显安装的控制器),或当前 handedness 未知的输入源,
MUST 将此属性设为 "none"。
targetRayMode 属性描述用于生成目标射线的方法,
并指示应用在需要时应如何向用户呈现目标射线。
-
gaze表示目标射线将起源于观看者,并跟随其朝向。 (在头戴式显示器的上下文中,这通常称为“注视输入”设备。) -
tracked-pointer表示目标射线源自 手持设备或其他手部跟踪机制,并表示用户正在使用其手部或所持设备进行指向。目标射线相对于被跟踪对象的朝向 MUST 在可用时遵循平台特定的人体工学指南。在缺少平台特定指导时,如果用户的食指伸直,目标射线 SHOULD 指向与其食指相同的方向。如果XRSystem确定手持设备的一部分旨在或变为旨在接触真实世界表面(例如笔尖),则目标射线 MUST 起源于该点。 -
screen表示输入源是与内联会话输出上下文关联的 canvas 元素上的交互,例如鼠标点击或触摸事件。 -
transient-pointer表示输入源是作为 操作系统交互意图的一部分生成的,而不是由某个特定硬件生成的。一些示例包括基于过于敏感而不能直接暴露的信息 (例如注视)的用户意图、来自 WebDriver 的合成输入,或由辅助技术生成的输入。只有在它也被用作主要 输入时,才应将其用于辅助技术,以免根据 W3C 设计原则无意中表明正在使用辅助技术。
targetRaySpace 属性是一个 XRSpace,它具有一个
native
origin,
跟踪该 XRInputSource
的首选指向射线的位置和朝向(沿其 -Z 轴),如 targetRayMode
所定义。
对于 targetRayMode
为 "transient-pointer"
的输入源,targetRaySpace
表示交互开始时指向交互目标的射线。其姿态应在此 XRInput 的 gripSpace 内保持静态。
gripSpace 属性是一个 XRSpace,它具有一个
native
origin,
跟踪应当用于渲染虚拟对象的姿态,使这些对象看起来像被握在用户手中。如果用户握着一根直杆,则此
XRSpace 将
native origin
放在其弯曲手指的质心处,并使 -Z 轴沿着杆的长度指向其拇指。X 轴垂直于所描述的手背,
用户右手手背指向 +X,用户左手手背指向 -X。Y 轴由
X 与 Z 轴之间的关系隐含,其中 +Y 大致指向用户手臂的方向。
skipRendering 属性指示此输入是可见的,并且
MAY NOT 需要由当前会话渲染。如果 skipRendering
为 true 且 targetRayMode 为 "tracked-pointer",则用户代理 MUST 确保始终向用户显示该
XR 输入源的表示。
向用户显示控制器的示例包括控制器位于显示器和用户之间、显示器是透明的,或控制器由操作系统渲染。
skipRendering
是给开发者的提示,表示不要渲染控制器等输入源。拾取射线和光标仍应被渲染。
对于 targetRayMode
为 "transient-pointer"
的输入源,如果存在关联的用户手势,则 gripSpace
应为该关联用户手势,否则应为用户控制的另一个空间,例如 ViewerSpace,或另一个 XRInput 的 gripSpace 或
targetRaySpace。这样做是为了允许用户仍然操纵 targetRaySpace。
如果输入源并非固有可跟踪,例如 targetRayMode
为 "gaze"
或 "screen"
的输入源,则 gripSpace
MUST 为 null。
profiles 属性是一个列表,包含若干
输入配置文件
名称,用于指示输入源的首选视觉表示和行为。
输入配置文件名称是一个 ASCII 小写的 DOMString,
不包含空格,并用连字符(-)连接分隔的单词。应选择描述性名称,并尽可能使用设备供应商首选的措辞。
如果平台提供了适当的标识符,例如 USB 供应商和产品 ID,则 MAY 使用它。MUST NOT 使用唯一标识单个设备的值,
例如序列号。输入配置文件名称 MUST NOT 包含设备 handedness 的指示。
如果多个用户代理暴露同一设备,它们 SHOULD 努力报告相同的
输入配置文件名称。WebXR 输入
配置文件注册表是管理输入配置文件
名称的推荐位置。
Profiles 按具体性降序给出。列表中第一个条目之后给出的任何输入配置文件 名称都应提供 fallback 值,表示设备的替代表示。这可以包括设备的更通用或先前版本、足够相似且更广泛认可的设备, 或设备类型的宽泛描述(例如 "generic-trigger-touchpad")。如果给出多个 profiles,则它们描述的布局必须都表示 列表中其他每个 profile 的超集或子集。
如果 XRSession
是
一个内联会话,则 profiles
MUST 是一个空列表。
用户代理 MAY 自行决定只报告适当的通用输入配置文件 名称或空列表。适合这样做的一些场景包括:输入设备无法被可靠识别、没有已知输入 profiles 与输入设备匹配, 或用户代理希望掩盖正在使用的输入设备。
例如,Samsung HMD Odyssey 的控制器是标准 Windows Mixed Reality 控制器的设计变体。两个控制器共享相同的输入布局。
因此,Samsung HMD Odyssey 控制器的 profiles
可以是:
["samsung-odyssey", "microsoft-mixed-reality", "generic-trigger-squeeze-touchpad-thumbstick"]。
控制器外观由列表中的第一个 profile 最精确地传达,第二个 profile 描述可接受的替代项,最后一个 profile 是通用
fallback,以最粗略的意义描述该设备。(它是一个带有扳机、握压按钮、触摸板和摇杆的控制器。)
同样,Valve Index 控制器向后兼容 HTC Vive 控制器,但 Index 控制器有额外的按钮和轴。因此,Valve Index 控制器的
profiles
可以是:
["valve-index", "htc-vive", "generic-trigger-squeeze-touchpad-thumbstick"]。在这种情况下,
"valve-index" profile 描述的输入布局是 "htc-vive" profile 描述的布局的超集。
此外,"valve-index" profile 指示控制器的精确外观,而 "htc-vive" 控制器具有显著不同的外观。
在这种情况下,UA 会认为这种差异是可接受的。并且与第一个示例一样,最后一个 profile 是通用 fallback。
(确切字符串仅为示例。实际 profile 名称在 WebXR 输入
配置文件注册表中管理。)
注:XRInputSource
在 XRSession
的
inputSources
数组中是“live”的。因此,它们内部的值会被就地更新。这意味着保存某一帧上
XRInputSource
的
属性引用,并在后续帧中将其与同一属性比较以测试状态变化,是不可行的,因为它们将是同一个对象。因此,
希望逐帧比较输入状态的开发者应复制相关状态的内容。
如果一个 XR 输入源
支持 主要操作,
则它是一个 主要输入
源。主要操作是一种平台特定操作,当其被触发时,会产生 selectstart、
selectend
和 select
事件。可能的主要操作示例包括按下扳机、触摸板或按钮,说出命令,或做出手势。如果平台指南定义了推荐的
主要输入,则应将其用作主要
操作,否则用户代理可自由选择一个。设备 MUST 支持至少一个主要输入
源。
如果一个 XR 输入源不支持 主要操作,则它是一个 被跟踪输入 源。这些输入主要旨在提供姿态数据。 注:被跟踪输入源的一个示例是用于用户腿部或道具的跟踪附件。如果未执行手势识别来检测 主要 操作,则被跟踪的手也可被视为被跟踪输入源。
每个 XR 输入源 MAY
定义一个 主要
squeeze 操作。主要 squeeze 操作是一种平台特定操作,当其被触发时,会产生 squeezestart、
squeezeend
和 squeeze
事件。主要 squeeze
操作应用于大致映射为挤压或抓取的操作。可能的
主要 squeeze
操作示例包括
按下 grip trigger 或做出抓取手势。如果平台指南定义了推荐的主要 squeeze 操作,则应将其用作
主要 squeeze
操作,否则用户代理 MAY 选择一个。
当 XRSession
session 的一个 XR
输入
源 source 开始其主要 squeeze 操作时,UA MUST 运行以下步骤:
当 XRSession
session 的一个 XR
输入
源 source 结束其主要 squeeze 操作时,UA MUST 运行以下步骤:
有时,平台特定行为可能导致主要操作或 主要 squeeze 操作被 中断或取消。例如,一个 XR 输入源可能在主要操作或 主要 squeeze 操作开始之后、结束之前,从 XR 设备中被移除。
当 XRSession
session 的一个 XR
输入
源 source 的主要 squeeze 操作被取消时,UA MUST 运行以下
步骤:
-
令 frame 为 session 的 相关 realm中的一个 新
XRFrame, 其session为 session,time 为该操作发生的时间。 -
排队一个任务,以触发输入 源事件,触发一个名称为
squeezeend的XRInputSourceEvent, frame 为 frame,source 为 source。
10.2. 瞬态输入
一些 XR 设备可能支持 瞬态输入 源,其中 XR 输入源只有在执行一个 瞬态操作时才有意义, 该操作可以是 主要操作 (针对主要输入 源),也可以是设备特定的 辅助操作(针对被跟踪输入源)。
一个示例是针对内联会话的鼠标、触摸或触控笔输入,它
MUST 产生一个瞬态 XRInputSource,
其 targetRayMode
设为 screen,
并被视为主要操作
(针对 主指针),以及非主指针的非主要
辅助操作。
另一个示例是来自操作系统的意图,其输入派生自无法直接暴露的敏感信息,例如基于注视的交互。这些会产生一个瞬态
XRInputSource,
其 targetRayMode
设为 transient-pointer,
并被视为主要
操作。
瞬态输入 源只在瞬态操作持续期间存在于会话的 活动 XR 输入源列表中。
瞬态输入 源在处理瞬态操作时遵循以下序列,而不是用于非瞬态 主要操作的算法:
当 XRSession
session 的一个 瞬态输入源 source 结束其
瞬态操作时,UA MUST
运行以下步骤:
10.3. XRInputSourceArray
XRInputSourceArray
表示一个由 XRInputSource
组成的列表。
当列表内容预期会随时间变化时使用它,
而不是使用 冻结
数组类型,例如 XRSession 的
inputSources
属性。
[SecureContext ,Exposed =Window ]interface {XRInputSourceArray iterable <XRInputSource >;readonly attribute unsigned long length ;getter XRInputSource (unsigned long ); };index
XRInputSourceArray
的 length 属性指示该
XRInputSourceArray
中包含多少个 XRInputSource。
XRInputSourceArray
的 索引属性 getter 会检索所提供索引处的
XRInputSource。
11. 层
注:虽然本规范只定义了 XRWebGLLayer
层,但预计规范的未来扩展会添加更多层类型以及它们所绘制的图像源。
11.1. XRLayer
[SecureContext ,Exposed =Window ]interface :XRLayer EventTarget {};
XRLayer 是
XRWebGLLayer
和未来扩展引入的其他层类型的基类。
11.2. XRWebGLLayer
XRWebGLLayer
是一个提供 WebGL framebuffer 以供渲染的层,使硬件加速的 3D 图形渲染能够呈现在
XR 设备上。
typedef (WebGLRenderingContext or WebGL2RenderingContext );XRWebGLRenderingContext dictionary {XRWebGLLayerInit boolean =antialias true ;boolean =depth true ;boolean =stencil false ;boolean =alpha true ;boolean =ignoreDepthValues false ;double = 1.0; }; [framebufferScaleFactor SecureContext ,Exposed =Window ]interface :XRWebGLLayer XRLayer {constructor (XRSession ,session XRWebGLRenderingContext ,context optional XRWebGLLayerInit = {}); // AttributeslayerInit readonly attribute boolean antialias ;readonly attribute boolean ignoreDepthValues ;attribute float ?fixedFoveation ; [SameObject ]readonly attribute WebGLFramebuffer ?framebuffer ;readonly attribute unsigned long framebufferWidth ;readonly attribute unsigned long framebufferHeight ; // MethodsXRViewport ?getViewport (XRView ); // Static Methodsview static double getNativeFramebufferScaleFactor (XRSession ); };session
每个 XRWebGLLayer
都有一个 context 对象,初始为 null,它是
WebGLRenderingContext
或 WebGL2RenderingContext
的实例。
每个 XRWebGLLayer
都有关联的 session,它是创建该对象时所用的 XRSession。
XRWebGLLayer(session, context, layerInit)
构造函数在调用时 MUST 执行以下步骤:
-
令 layer 为 session 的 相关 realm中的一个 新
XRWebGLLayer。 -
如果 session 的 ended 值为
true,则抛出InvalidStateError并中止这些步骤。 -
如果 context is lost,则抛出
InvalidStateError并中止这些步骤。 -
如果 session 是一个沉浸式会话,并且 context 的 XR compatible 布尔值为
false,则抛出InvalidStateError并中止这些步骤。 -
将 layer 的 context 初始化为 context。
-
将 layer 的 session 初始化为 session。
-
按如下方式初始化 layer 的
ignoreDepthValues:- 如果 layerInit 的
ignoreDepthValues值为false且 XR Compositor 将使用深度值: -
将 layer 的
ignoreDepthValues初始化为false。 - 否则:
-
将 layer 的
ignoreDepthValues初始化为true。
- 如果 layerInit 的
-
按如下方式初始化 layer 的 composition enabled 布尔值:
- 如果 session 是一个内联会话:
-
将 layer 的 composition enabled 初始化为
false。 - 否则:
-
将 layer 的 composition enabled 布尔值初始化为
true。
-
- 如果 layer 的 composition enabled 布尔值为
true: -
-
令 scaleFactor 为 layerInit 的
framebufferScaleFactor。 -
用户代理 MAY 在此处选择按其认为合适的方式钳制或舍入 scaleFactor, 例如,如果它出于性能原因希望将缓冲区尺寸适配为 2 的幂。
-
令 framebufferSize 为 推荐的 WebGL framebuffer 分辨率,其宽度和高度分别乘以 scaleFactor。
-
将 layer 的
framebuffer初始化为 context 的 相关 realm中的一个 新WebGLFramebuffer, 它是一个由 context 创建、尺寸为 framebufferSize 的 不透明 framebuffer,其 session 初始化为 session,并使用 layerInit 的depth、stencil和alpha值。 -
根据支持 layer 合成的需要,分配并初始化与 session 的 XR 设备兼容的资源,包括 GPU 可访问 内存缓冲区。
-
如果 layer 的资源因任何原因无法创建,则抛出
OperationError并中止这些步骤。
- 否则:
-
-
将 layer 的
antialias初始化为 layer 的context的 实际上下文参数中的antialias值。 -
将 layer 的
framebuffer初始化为null。
-
- 如果 layer 的 composition enabled 布尔值为
-
返回 layer。
注:如果一个 XRWebGLLayer
的
composition enabled 布尔值被设为
false,则 XRWebGLLayerInit
对象上的所有值都会被忽略,因为 WebGLRenderingContext
的
默认 framebuffer 已经使用上下文的实际上下文参数分配,且不能被覆盖。
context 属性是创建该 XRWebGLLayer
时所用的 WebGLRenderingContext。
每个 XRWebGLLayer
都有一个 composition enabled 布尔值,初始设为
true。如果设为 false,则表示该 XRWebGLLayer
MUST NOT 分配自己的 WebGLFramebuffer,
并且 XRWebGLLayer
上所有反映 framebuffer
属性的属性 MUST 改为反映 context 的默认 framebuffer 的属性。
XRWebGLLayer
的 framebuffer 属性是一个 WebGLFramebuffer
实例;如果 composition
enabled 为 true,它已被标记为不透明,否则为 null。在
XRWebGLLayer
创建之后,开发者不能调整 framebuffer
大小。
不透明
framebuffer 的功能与标准 WebGLFramebuffer
相同,但有以下更改,使其行为更像默认
framebuffer:
-
不透明 framebuffer MAY 支持抗锯齿,即使在 WebGL 1.0 中也是如此。
-
不透明 framebuffer 的附件不能被检查或更改。对一个 不透明 framebuffer 调用
framebufferTexture2D、framebufferRenderbuffer、deleteFramebuffer或getFramebufferAttachmentParameterMUST 生成INVALID_OPERATION错误。 -
不透明 framebuffer 有一个相关的 session,即创建它所针对的
XRSession。 -
在
requestAnimationFrame()回调之外,不透明 framebuffer 被视为不完整。当不处于其 session 的requestAnimationFrame()回调中时,对checkFramebufferStatus的调用 MUST 生成FRAMEBUFFER_UNSUPPORTED错误,并且尝试清除、绘制到或读取 不透明 framebuffer MUST 生成INVALID_FRAMEBUFFER_OPERATION错误。 -
以
depthtrue初始化的不透明 framebuffer 将附加一个深度缓冲区。 -
以
stenciltrue初始化的不透明 framebuffer 将附加一个模板缓冲区。 -
不透明 framebuffer 的颜色缓冲区当且仅当
alpha为true时具有 alpha 通道。 -
XR Compositor 会假定 不透明 framebuffer 包含预乘 alpha 的颜色。无论
premultipliedAlpha值在context的 实际上下文参数中如何设置,都是如此。
注:用户代理需要尊重 depth
和 stencil
的 true 值,这类似于 WebGL 在创建绘图缓冲区时的行为。
附加到不透明
framebuffer的缓冲区 MUST 在首次创建时,或在处理每个
XR 动画帧之前,被清除为下表中的值。
这与 WebGL 上下文的默认
framebuffer行为相同。无论关联 WebGL 上下文的 preserveDrawingBuffer
值如何,不透明
framebuffers 都将始终被清除。
| 缓冲区 | 清除值 |
|---|---|
| 颜色 | (0, 0, 0, 0) |
| 深度 | 1.0 |
| 模板 | 0 |
注:实现可以优化掉不透明 framebuffer所需的隐式清除操作,只要能保证开发者无法访问来自另一进程的 缓冲区内容。例如,如果开发者执行了显式清除,则不需要隐式清除。
如果一个 XRWebGLLayer
创建时 alpha
被设为 true,则 framebuffer
必须由具有 RGBA
颜色格式的纹理支持。
如果一个 XRWebGLLayer
创建时 alpha
被设为 false,则 framebuffer
必须由具有 RGB
颜色格式的纹理支持。
但是,XR Compositor
MUST 将 framebuffer
的
backing 像素视为处于 SRGB8_ALPHA8
或 SRGB8
colorFormat
中。
注:这意味着 XR Compositor 在处理作为
framebuffer
backing 的纹理时,MUST 不对线性 RGBA
或 RGB
执行任何 gamma 转换。否则,最终渲染中的像素会显得过亮,这将与常规 2D
WebGLRenderingContext
上下文中的渲染不匹配。
当一个 XRWebGLLayer
被设置为一个沉浸式
会话的 baseLayer
时,不透明 framebuffer
的内容会在一个
XR 动画
帧完成后立即呈现给 沉浸式
XR 设备,但仅当自前一个
XR 动画帧以来至少发生以下任一情况:
-
在
XRWebGLLayer关联的WebGLRenderingContext当前绑定的 framebuffer 为该不透明 framebuffer期间,调用了clear、drawArrays、drawElements或任何其他类似影响 framebuffer 颜色值的渲染操作。
在不透明 framebuffer呈现给沉浸式 XR 设备之前,用户代理应确保所有渲染操作都已刷新到 不透明 framebuffer。
每个 XRWebGLLayer
都有一个 target framebuffer,如果
composition enabled 为 true,它就是
framebuffer,
否则是 context 的
默认 framebuffer。
framebufferWidth 和 framebufferHeight 属性分别返回
target framebuffer 附件的宽度和高度。
antialias 属性在
target framebuffer 使用 UA 选择的技术支持抗锯齿时为
true,如果不会执行抗锯齿则为 false。
ignoreDepthValues 属性如果为
true,则表示 XR
Compositor MUST NOT 在渲染时使用深度缓冲区附件中的值。
当该属性为 false 时,表示深度缓冲区附件的内容将由
XR Compositor
使用,并预期能代表渲染到该层中的场景。
存储在缓冲区中的深度值预期位于 0.0 和 1.0 之间,其中
0.0 表示 depthNear
的距离,1.0 表示 depthFar
的距离,中间值线性插值。这是 WebGL 的默认行为。(更多细节见 depthRange
函数文档。)
注:使场景的深度缓冲区可供 compositor 使用,可允许某些平台提供质量和舒适度改进, 例如改进的重投影。
fixedFoveation 属性控制
XR
Compositor 使用的 foveation 量。如果用户代理或设备不支持此属性,它们在获取时应返回
null,设置时应为 no-op。将 fixedFoveation
设为小于 0 的值会将其设为 0,设为大于 1 的值会将其设为
1。0 设置最小的 foveation 量,而 1 设置最大值。XR Compositor 如何解释这些值由用户代理决定。
如果 fixedFoveation
级别发生变化,它将在下一个 XRFrame 生效。
注:Fixed foveation 是一种降低用户视场边缘附近内容渲染分辨率的技术。 它可以显著改善受限于 GPU 填充性能的体验。它会降低功耗,并使应用能够提高眼部纹理的分辨率。它最适用于低对比度纹理, 例如背景图像,而较不适用于高对比度内容,例如文本或详细图像。作者可以按帧调整级别,以实现性能和视觉质量之间的最佳权衡。
每个 XRWebGLLayer
MUST 有一个 完整大小视口列表,它是一个列表,包含一个
WebGL viewport,对应 XRSession
可能暴露的每个
视图,包括当前未活动但可能在当前会话中变为
活动的次
视图。这些视口 MUST 具有大于 0 的 width
和 height,
并且 MUST 描述一个不超过 target framebuffer 边界的矩形。这些视口 MUST NOT
重叠。
如果 composition enabled 为 false,且
session 的
视图
列表包含单个 视图,其
eye 为
"none",
则 完整大小视口列表 MUST 包含单个
WebGL viewport,覆盖 context 的整个默认
framebuffer。
如果 composition enabled 为 false,且
session 的
视图
列表包含两个主
视图,其中一个的 eye 为
"left",
另一个的 eye 为
"right",
则 完整大小视口列表 MUST 包含两个
WebGL viewport:一个关联到 "left"
视图,另一个关联到
"right"
视图。用户代理在受上述约束限制的情况下确定这些视口的大小和
位置。
注:本规范不要求立体内联视口采用特定打包方式,例如并排排列。
作者应为每个 XRView
使用 getViewport(),
而不是假定某种布局。
每个 XRWebGLLayer
MUST 有一个 视口对象
列表,它是一个列表,包含一个 XRViewport,
对应 XRSession
当前暴露的每个活动
视图。
getViewport()
查询给定 XRView
在渲染到该层时应使用的 XRViewport。
getViewport(view) 方法在
XRWebGLLayer
layer 上调用时,MUST 运行以下步骤:
-
令 session 为 view 的 session。
-
令 frame 为 session 的 animation frame。
-
如果 session 不等于 layer 的 session,则抛出
InvalidStateError并中止这些步骤。 -
如果 frame 的 active 布尔值为
false,则抛出InvalidStateError并中止这些步骤。 -
如果 view 的 frame 不等于 frame,则抛出
InvalidStateError并中止这些步骤。 -
如果 viewport modifiable 标志为
true,且 view 的 requested viewport scale 不等于 current viewport scale:-
在 视口对象列表中,将与 view 关联的
XRViewport设为从与 session 的 view 关联的 完整大小视口列表中 获得缩放视口所得的XRViewport结果。
-
将 view 的 viewport modifiable 标志设为 false。
-
令 viewport 为 视口对象列表中与 view 关联的
XRViewport。 -
返回 viewport。
注:viewport modifiable
标志会被有意设为 false,
即使 current viewport
scale 没有变化也是如此。这确保 getViewport(view) 调用在一个动画帧内对于该视图总是返回一致的结果,
因此第一个检索到的值会在该帧剩余期间被锁定。如果应用在 getViewport() 之后调用
requestViewportScale(),
则请求的值只会在未来帧中再次调用 getViewport() 时才被应用。
每个 XRSession
MUST 标识一个 原生 WebGL framebuffer 分辨率,即为匹配
XR 设备的物理像素分辨率而需要的
WebGL framebuffer 像素分辨率。
一个 XRSession
session 的 原生 WebGL framebuffer 分辨率通过运行以下步骤确定:
-
如果 session 是一个内联会话,则将 原生 WebGL framebuffer 分辨率设为 session 的
renderState的 output canvas 的物理显示像素大小, 每次 canvas 大小发生变化或output canvas 发生变化时重新评估这些步骤,并中止这些步骤。 -
将 原生 WebGL framebuffer 分辨率设为如下所需的分辨率: 一个足够大、能够包含会话所有
XRView的 framebuffer 的像素,与最高放大倍率下显示区域中的物理屏幕像素之间具有 1:1 比率。如果不存在如上所述确定原生分辨率的方法, MAY 使用 推荐的 WebGL framebuffer 分辨率。
此外,XRSession
MUST 标识一个 推荐的 WebGL framebuffer 分辨率,它表示一个最佳估计的
WebGL framebuffer 分辨率:足够大以包含会话所有 XRView,
并为普通应用在性能和质量之间提供良好平衡。它 MAY 小于、大于或等于
原生 WebGL framebuffer 分辨率。新的
不透明 framebuffer
将使用此分辨率创建,其宽度和高度分别按任何所提供的 XRWebGLLayerInit
的
framebufferScaleFactor
缩放。
注:用户代理可自由使用其选择的任何方法来估计
推荐的 WebGL framebuffer
分辨率。如果存在用于查询推荐大小的平台特定方法,
建议使用它们,但并非必须。framebufferScaleFactor
和 getNativeFramebufferScaleFactor()
使用的缩放因子分别应用于宽度和高度,因此缩放因子为二会导致总体像素数变为四倍。如果平台暴露基于面积的渲染缩放,
且它基于像素数,则用户代理需要对其取平方根,以将其转换为 WebXR 缩放因子。
getNativeFramebufferScaleFactor(session)
方法在调用时 MUST 运行以下步骤:
-
令 session 为 this。
-
如果 session 的 ended 值为
true,则返回0.0并中止这些步骤。 -
返回一个值,使 session 的 推荐的 WebGL framebuffer 分辨率的宽度和高度分别乘以该值后,得到 session 的 原生 WebGL framebuffer 分辨率。
11.3. WebGL 上下文兼容性
为了让 WebGL 上下文可用作沉浸式 XR 图像的源,它必须为沉浸式 XR 设备创建在一个 兼容的
图形适配器上。什么被视为兼容的图形
适配器取决于平台,但可理解为该图形适配器能够在没有过度延迟的情况下向
沉浸式 XR 设备提供图像。如果
WebGL 上下文尚未创建在
兼容的图形适配器上,则在它能够与
XRWebGLLayer
一起使用之前,通常必须在相关适配器上重新创建它。
注:在只有单个 GPU 的 XR 平台上,可以安全地假定该 GPU 与平台公布的 沉浸式 XR 设备兼容,因此任何硬件加速的 WebGL 上下文也同样兼容。 在同时具有集成 GPU 和独立 GPU 的 PC 上,独立 GPU 通常被视为兼容的图形适配器,因为它通常是性能更高的芯片。在安装了多个图形适配器的台式 PC 上,与沉浸式 XR 设备物理连接的那个适配器可能会被视为兼容的图形 适配器。
注:内联会话使用与 canvas 相同的图形适配器进行渲染,
因此不需要 xrCompatible
上下文。
partial dictionary WebGLContextAttributes {boolean =xrCompatible false ; };partial interface mixin WebGLRenderingContextBase { [NewObject ]Promise <undefined >makeXRCompatible (); };
当用户代理实现本规范时,它 MUST 在每个 WebGLRenderingContextBase
上设置一个 XR compatible
布尔值,初始设为 false。
一旦 XR compatible 布尔值
被设为 true,该上下文就可与从当前沉浸式 XR 设备请求的任何 XRSession
的层一起使用。
注:此标志会引入缓慢的同步行为,因此不建议使用。请考虑改为调用
makeXRCompatible()
作为异步解决方案。
XR compatible 布尔值可以在
上下文创建时设置,也可以在上下文创建之后设置,后者可能导致上下文丢失。要在上下文创建时设置
XR compatible 布尔值,
在请求 WebGL 上下文时,xrCompatible
上下文创建属性必须设为 true。如果发起请求的文档的
origin 不允许使用 "xr-spatial-tracking" 权限策略,则 xrCompatible
没有效果。
xrCompatible
标志位于 WebGLContextAttributes
上;如果为 true,它会通过请求用户代理为沉浸式 XR
设备使用兼容的图形适配器来创建 WebGL
上下文,从而影响上下文创建。如果用户代理成功执行此操作,则创建的上下文的
XR compatible 布尔值将被设为
true。为了获取
沉浸式
XR 设备,SHOULD 调用确保已选择沉浸式 XR 设备。
注:确保已选择沉浸式 XR 设备需要
并行运行,这会在主线程上引入缓慢的同步行为。用户代理 SHOULD 向控制台打印警告,
要求改用 makeXRCompatible()。
XRWebGLLayer。
function onXRSessionStarted( xrSession) { const glCanvas= document. createElement( "canvas" ); const gl= glCanvas. getContext( "webgl" , { xrCompatible: true }); loadWebGLResources(); xrSession. updateRenderState({ baseLayer: new XRWebGLLayer( xrSession, gl) }); }
要在上下文创建之后设置 XR compatible
布尔值,需要使用 makeXRCompatible()
方法。
注:在某些系统上,此标志可能会开启高功率独立 GPU,或者将所有命令代理到设备上的
GPU。例如,如果你处于可能使用也可能不使用 XR 的情形,建议仅在打算启动沉浸式会话时才调用 makeXRCompatible()。
makeXRCompatible()
方法确保该 WebGLRenderingContextBase
正在为沉浸式 XR
设备运行在兼容的图形适配器上。
当调用此方法时,用户代理 MUST 运行以下步骤:
-
如果发起请求的文档的origin 不允许使用 "xr-spatial-tracking" 权限策略,则resolve promise 并返回它。当 XR 权限策略被禁用时,我们希望在这种情况下表现得仿佛没有 XR 设备, 因为
makeXRCompatible()应该是一个设置后即忘的方法。 -
令 promise 为在此
WebGLRenderingContextBase的 Realm 中创建的新 Promise。 -
令 context 为 this。
-
并行运行以下步骤:
-
令 device 为确保已选择沉浸式 XR 设备的结果。
-
按如下方式设置 context 的 XR compatible 布尔值:
- 如果 context 的 WebGL context lost flag 已设置:
-
排队一个任务,将 context 的 XR compatible 布尔值设为
false,并以InvalidStateErrorreject promise。 - 如果 device 为
null: -
排队一个任务,将 context 的 XR compatible 布尔值设为
false,并以InvalidStateErrorreject promise。 - 如果 context 的 XR compatible 布尔值为
true: - 如果 context 是在用于 device 的 兼容的图形适配器上创建的:
-
排队一个任务,将 context 的 XR compatible 布尔值设为
true,并resolve promise。 - 否则:
-
在 WebGL task source 上排队一个任务,以执行以下步骤:
-
强制 context 丢失。
-
按照 WebGL 规范所述处理上下文丢失:
-
令 canvas 为 context 的 canvas。
-
如果 context 的 webgl context lost flag 已设置,则中止这些步骤。
-
设置 context 的 webgl context lost flag。
-
设置由 context 创建的每个
WebGLObject实例的 invalidated 标志。 -
禁用除 "WEBGL_lose_context" 之外的所有扩展。
-
在 WebGL task source 上 排队一个任务,以执行以下步骤:
-
在 canvas 上触发一个 WebGL 上下文事件 e,名称为 "webglcontextlost",并将
statusMessage设为 ""。 -
如果 e 的 canceled flag 未设置, 则以
AbortErrorreject promise 并中止这些步骤。 -
并行运行以下步骤。
-
等待用于 device 的 兼容的 图形适配器上的可恢复绘图缓冲区。
-
在 WebGL task source 上 排队一个任务, 以执行以下步骤:
-
将 context 的 XR compatible 布尔值设为
true。 -
Resolve promise。
-
-
-
-
-
-
返回 promise。
此外,当任何 WebGL 上下文丢失时,在触发 "webglcontextlost" 事件之前运行以下步骤:
-
将该上下文的 XR compatible 布尔值设为
false。
XRWebGLLayer。
const glCanvas= document. createElement( "canvas" ); const gl= glCanvas. getContext( "webgl" ); loadWebGLResources(); glCanvas. addEventListener( "webglcontextlost" , ( event) => { // 表示 WebGL 上下文可以恢复。 event. canceled= true ; }); glCanvas. addEventListener( "webglcontextrestored" , ( event) => { // 上下文丢失后需要重新创建 WebGL 资源。 loadWebGLResources(); }); async function onXRSessionStarted( xrSession) { // 确保我们想要使用的 canvas 上下文与设备兼容。 // 可能触发上下文丢失。 await gl. makeXRCompatible(); xrSession. updateRenderState({ baseLayer: new XRWebGLLayer( xrSession, gl) }); }
12. 事件
除非另有规定,本规范中所有排队的 任务源都是 XR 任务源。
12.1. XRSessionEvent
XRSessionEvent
会被触发,以指示 XRSession
状态的变化。
[SecureContext ,Exposed =Window ]interface :XRSessionEvent Event {(constructor DOMString ,type XRSessionEventInit ); [eventInitDict SameObject ]readonly attribute XRSession session ; };dictionary :XRSessionEventInit EventInit {required XRSession ; };session
session 属性指示生成该事件的 XRSession。
12.2. XRInputSourceEvent
XRInputSourceEvent
会被触发,以指示 XRInputSource
状态的变化。
[SecureContext ,Exposed =Window ]interface :XRInputSourceEvent Event {(constructor DOMString ,type XRInputSourceEventInit ); [eventInitDict SameObject ]readonly attribute XRFrame frame ; [SameObject ]readonly attribute XRInputSource inputSource ; };dictionary :XRInputSourceEventInit EventInit {required XRFrame ;frame required XRInputSource ; };inputSource
inputSource 属性指示生成此事件的 XRInputSource。
frame 属性是一个 XRFrame,它
对应该事件发生的时间。它可以表示历史数据。当在 frame
上调用 getViewerPose()
时,MUST 抛出异常。
当用户代理必须以名称 name、XRFrame
frame 和 XRInputSource
source 触发输入源事件时,它 MUST 运行以下步骤:
-
创建一个
XRInputSourceEventevent,其type为 name,frame为 frame,并且inputSource为 source。 -
将 frame 的 active 布尔值设为
true。 -
对 frame 应用帧更新。
-
将 frame 的 active 布尔值设为
false。
12.3. XRInputSourcesChangeEvent
XRInputSourcesChangeEvent
会被触发,以指示可供 XRSession
使用的活动 XR 输入源列表的变化。
[SecureContext ,Exposed =Window ]interface :XRInputSourcesChangeEvent Event {(constructor DOMString ,type XRInputSourcesChangeEventInit ); [eventInitDict SameObject ]readonly attribute XRSession session ; [SameObject ]readonly attribute FrozenArray <XRInputSource >added ; [SameObject ]readonly attribute FrozenArray <XRInputSource >removed ; };dictionary :XRInputSourcesChangeEventInit EventInit {required XRSession ;session required sequence <XRInputSource >;added required sequence <XRInputSource >; };removed
session 属性指示生成该事件的
XRSession。
added 属性是一个列表,包含在事件发生时被添加到
XRSession 的
XRInputSource。
removed 属性是一个列表,包含在事件发生时从
XRSession 中
移除的 XRInputSource。
12.4. XRReferenceSpaceEvent
XRReferenceSpaceEvent
会被触发,以指示 XRReferenceSpace
状态的变化。
[SecureContext ,Exposed =Window ]interface :XRReferenceSpaceEvent Event {(constructor DOMString ,type XRReferenceSpaceEventInit ); [eventInitDict SameObject ]readonly attribute XRReferenceSpace referenceSpace ; [SameObject ]readonly attribute XRRigidTransform ?transform ; };dictionary :XRReferenceSpaceEventInit EventInit {required XRReferenceSpace ;referenceSpace XRRigidTransform ?=transform null ; };
referenceSpace 属性
指示生成此事件的 XRReferenceSpace。
可选的 transform 属性描述事件后
referenceSpace
的
native origin
在事件前坐标系中的位置和朝向。如果 XRSystem 无法
确定旧坐标系和新坐标系之间的 delta,则此属性 MAY 为 null。
注:referenceSpace
或 referenceSpace
可能发生的情况包括头显在两个不同位置之间被摘下并重新戴上。在这种情况下,如果体验依赖世界锁定内容,它应警告用户并重置场景。
12.5. XRVisibilityMaskChangeEvent
由于视锥体并不与矩形显示区域精确相交,XRLayer 的整个区域
MAY 不会被显示。此事件会将 XRView 中显示给用户的区域
告知体验。
当用户代理想要通知体验 XRLayer
的显示区域已发生变化时,会触发
XRVisibilityMaskChangeEvent
事件。体验 MAY 选择只绘制该区域,这 MAY 有助于性能。
注:体验 MUST 在 requestSession
的 promise 解析期间注册此事件。否则,该事件可能会触发,而 mask 将对体验丢失。
[SecureContext ,Exposed =Window ]interface :XRVisibilityMaskChangeEvent Event {(constructor DOMString ,type XRVisibilityMaskChangeEventInit ); [eventInitDict SameObject ]readonly attribute XRSession session ;readonly attribute XREye eye ;readonly attribute unsigned long index ; [SameObject ]readonly attribute Float32Array vertices ; [SameObject ]readonly attribute Uint32Array indices ; };dictionary :XRVisibilityMaskChangeEventInit EventInit {required XRSession ;session required XREye ;eye required unsigned long ;index required Float32Array ;vertices required Uint32Array ; };indices
session 属性指示生成该事件的
XRSession。
eye 属性指示该 mask 适用于哪个
XREye。
index 属性指示此 mask 所适用的
XRView在
视图列表中的偏移。
vertices 属性是一个列表,包含
X、Y 坐标。体验 MUST 假定 Z 坐标为
-1。每个 X、Y、Z 坐标描述一个顶点。
如果此数组为空,则整个 XRView
区域 SHOULD
被绘制。
indices 属性是一个列表,包含若干索引,
描述进入由 vertices
所描述的顶点列表中的索引。这些索引将描述眼睛的 XRView 中 SHOULD
被绘制的区域。
如果此数组为空,则整个 XRView
区域 SHOULD
被绘制。
该区域 MUST 使用 projectionMatrix
,该矩阵来自 eye
的 XRView,
并使用默认的 XRRigidTransform。
注:这意味着该区域 MUST NOT 使用 XRRigidTransform
,该 transform 来自 eye
的当前 XRView。
12.6. 事件类型
用户代理 MUST 提供以下新事件。事件的注册和触发必须遵循 DOM Events 的通常行为。
用户代理 MUST 在 XRSystem 对象上
触发事件,该事件名为 devicechange,以指示
沉浸式 XR
设备的可用性已发生变化,除非文档的
origin 不允许使用 "xr-spatial-tracking" 权限策略。
每当 XRSession
的
visibility state 发生变化时,用户代理 MUST 在一个
XRSession
上使用 XRSessionEvent
触发一个事件,其名称为 visibilitychange。
当会话由应用或用户代理结束时,用户代理 MUST 在一个 XRSession
上使用 XRSessionEvent
触发一个事件,其名称为 end。
当会话的活动 XR 输入源列表发生变化时,用户代理 MUST 在一个
XRSession
上使用 XRInputSourcesChangeEvent
触发一个事件,其名称为 inputsourceschange。
当会话的活动 XR 跟踪源列表发生变化时,用户代理 MUST 在一个
XRSession
上使用 XRInputSourcesChangeEvent
触发一个事件,其名称为 trackedsourceschange。仅当
tracked-sources 包含在会话的
已授予特性集合中时,才 MUST 触发此事件。
当一个 XRSession
的某个 XRInputSource
开始其主要操作时,用户代理
MUST 在该会话上使用 XRInputSourceEvent
触发一个事件,其名称为 selectstart。
该事件 MUST 为 类型。
当一个 XRSession
的某个 XRInputSource
结束其主要操作,或一个已开始
主要
操作的 XRInputSource
被断开连接时,用户代理 MUST 在该会话上使用 XRInputSourceEvent
触发一个事件,其名称为 selectend。
当一个 XRSession
的某个 XRInputSource
已完全完成一个主要
操作时,用户代理 MUST 在该会话上使用 XRInputSourceEvent
触发一个事件,其名称为 select。
当一个 XRSession
的某个 XRInputSource
开始其主要 squeeze
操作时,用户代理 MUST 在该会话上使用
XRInputSourceEvent
触发一个事件,其名称为 squeezestart。
当一个 XRSession
的某个 XRInputSource
结束其主要 squeeze
操作,或一个已开始
主要 squeeze
操作的 XRInputSource
被断开连接时,用户代理 MUST 在该会话上使用 XRInputSourceEvent
触发一个事件,其名称为 squeezeend。
当一个 XRSession
的某个 XRInputSource
已完全完成一个主要
squeeze 操作时,用户代理 MUST 在该会话上使用
XRInputSourceEvent
触发一个事件,其名称为 squeeze。
当 XR Compositor
改变 XRSession 的
内部标称帧率时,用户代理 MUST 在一个
XRSession
上使用 XRSessionEvent
触发一个事件,其名称为 frameratechange。
当 XRSystem 想要
发出显示给用户的 XRView
可见区域变化的信号时,
用户代理 MUST 在一个 XRSession
上使用 XRVisibilityMaskChangeEvent
触发一个事件,其名称为 visibilitymaskchange。
当 native
origin 或 effective origin 发生不连续时,
即 origin 相对于用户环境的位置或朝向发生显著变化时,用户代理 MUST 在一个 XRReferenceSpace
上使用 XRReferenceSpaceEvent
触发一个事件,其名称为 reset。(例如:在用户重新校准其 XR 设备之后,或 XR
设备在丢失并重新获得跟踪后自动移动其 origin 时。)当 XRBoundedReferenceSpace
的 boundsGeometry
发生变化时,也 MUST 派发 reset
事件。如果 viewer 的姿态发生不连续,但 XRReferenceSpace
的
origin 物理映射保持稳定,则 MUST NOT 派发 reset
事件,例如当 viewer
在同一跟踪区域内短暂丢失并重新获得跟踪时。如果未发生显著不连续,
当一个 unbounded
reference space 随时间对其 native origin 做出小调整以维持用户附近的空间稳定性时,也 MUST NOT
派发 reset
事件。该事件 MUST 在执行任何使用新 origin 的
XR 动画
帧之前派发。一个 reference space 触发 reset
事件时,MUST 在其所有 offset reference spaces 上也派发 reset
事件,并且 offset XRBoundedReferenceSpace
的
boundsGeometry
也应重新计算。
注:这确实意味着会话需要持有对任何具有
reset
监听器的 XRReferenceSpace
的强引用。
注:viewer 位置的跳变可由应用通过观察 emulatedPosition
布尔值来处理。如果 viewer 位置的跳变
与 emulatedPosition
从 true 切换到 false 同时发生,这表示
viewer 已重新获得跟踪,并且其新位置表示对先前模拟值的修正。
对于没有“传送”机制的体验,即 viewer
无需物理移动即可在虚拟世界中移动的体验,这通常是应用所期望的行为。但是,如果某个体验确实提供了“传送”机制,
在跟踪恢复后将 viewer 的位置跳回可能会造成不必要的突兀感。
相反,当此类应用恢复跟踪时,它可以通过将位置中的突然跳变吸收到其传送偏移中,简单地从
viewer 在虚拟世界中的当前位置恢复体验。
为此,开发者调用 getOffsetReferenceSpace()
来创建替代 reference space,并按自上一帧以来 viewer
位置跳变的量调整其
effective
origin。
13. 安全、隐私与舒适性考量
WebXR Device API 提供了强大的新特性,这些特性也带来了若干独特的隐私、安全和舒适性风险, 用户代理必须采取措施加以缓解。
13.1. 敏感信息
在 XR 的上下文中,敏感信息包括但不限于
用户可配置的数据,例如瞳距(IPD),以及基于传感器的数据,例如 XRPose。所有沉浸式会话
都会暴露一定量的敏感数据,因为渲染任何内容都需要用户姿态。
但是,在某些情况下,同样的敏感信息也会通过内联会话暴露。
13.2. 用户意图
给定操作的用户意图 是来自用户的信号,表明此类操作是有意的并且得到了用户同意。
在暴露敏感信息或允许对用户体验产生重大影响的操作之前,通常需要确定用户意图。这种意图可以通过多种方式传达或观察到。
注:确定用户意图的一种常见方式是 UI 控件的瞬态激活,通常是一个“进入 VR”按钮。由于激活是瞬态的, 请求 XR 会话的浏览上下文必须是包含该 UI 控件的上下文的祖先,或同同源域的后代,并且必须在近期 曾是该浏览上下文的活动文档。
13.2.1. 用户激活
瞬态激活 MAY 在某些场景中作为用户意图的指示。13.2.2. 启动 Web 应用程序
在某些环境中,页面可能会以应用程序形式呈现,并以运行沉浸式内容的明确意图安装。 在这种情况下,启动 Web 应用程序 MAY 也作为用户意图的指示。13.2.3. 隐式和显式同意
隐式同意是指 用户代理在不明确询问用户的情况下,对用户的同意作出判断,例如基于 Web 应用程序的安装状态、 访问频率和近因,或用户代理定义的某个操作,其中用户明确表示其希望进入沉浸式体验的意图。 鉴于 XR 数据的敏感性,在依赖隐式信号时强烈建议保持谨慎。
显式同意是指 用户代理基于已明确询问用户这一事实,对用户的同意作出判断。在收集显式 同意时,用户代理会说明正在请求的内容,并向用户提供拒绝选项。 用户同意请求可以根据受保护特性和用户代理选择,以多种视觉形式呈现。Web 应用程序的安装状态 MAY 被视为 显式同意的信号, 前提是在安装时请求了某种形式的显式同意。
13.2.4. 同意的持续时间
建议一旦针对特定显式同意被授予某个origin,该同意就持续到浏览上下文结束为止。用户代理可基于用户意图的隐式或显式信号选择延长或缩短此同意持续时间, 但建议实现在偏离此建议时保持谨慎,尤其是在依赖隐式信号时。例如,对于以运行沉浸式内容为明确意图而安装的 Web 应用程序,持久化用户同意可能是合适的,但对于沉浸式内容只是次要特性的已安装 Web 应用程序则不一定合适。无论用户代理选择将用户同意持久化多长时间,敏感信息 MUST 只能由尚未
结束的 XRSession
暴露。
13.3. 会话中同意
有多个非 XR API 会导致用户代理请求显式同意以使用某个特性。如果用户代理将在存在 活动沉浸式会话期间请求用户同意, 则用户代理 MUST 在向用户显示同意请求之前关闭会话。如果在创建活动沉浸式会话之前, 用户已授予该特性的同意,则不需要终止该会话。
注:此限制旨在确保在就用户代理应如何管理会话中的显式 同意达成共识之前,所有用户代理之间具有行为一致性。预计这不会是长期要求。
13.4. 数据调整
在某些情况下,可以通过数据调整来缓解安全和隐私威胁, 例如节流、量化、舍入、限制,或以其他方式操纵从 XR 设备报告的数据。有时即使已经建立 用户意图,这也可能是避免指纹识别所必需的。 但是,数据调整缓解措施 MUST 只能用于不会导致用户不适的情形。
13.4.1. 节流
节流是指以低于原本可能频率的频率报告敏感 信息。此缓解措施有可能降低站点推断用户意图、推断位置或执行用户画像的能力。但是,如果使用不当, 节流有很大风险导致用户不适。此外,在许多情况下,它可能不足以提供完整的缓解。13.4.2. 舍入、量化和模糊化
舍入、量化和模糊化是三类缓解措施,它们会修改原本将返回给开发者的原始数据。 舍入通过减少用于表达数据的位数来降低数据精度。量化 将连续数据约束为报告一组离散值。模糊化是向数据中引入轻微的随机误差。 总体而言,这些缓解措施有助于避免指纹识别,并且在不会对用户舒适性造成明显影响时尤其有用。13.4.3. 限制
限制是指只有当数据位于特定范围内时才报告数据。 例如,当用户移动到距离已批准位置超过特定距离之外时,可以在保持舒适的情况下限制位置姿态数据的报告。 采用此缓解措施时应谨慎,以确保用户体验不会受到负面影响。通常希望避免在范围末端出现“硬停止”, 因为这可能造成破坏性的用户体验。13.5. 受保护功能
该 API 暴露的敏感 信息可以被划分为若干类别,这些类别具有共同的威胁概况以及针对这些威胁所需的保护。
13.5.1. 沉浸性
用户必须能够控制何时创建沉浸式会话,因为创建它会在用户机器上造成侵入性变化。例如,启动一个 沉浸式会话将启用 XR 设备传感器,接管对设备显示器的访问, 并开始呈现沉浸式内容,这可能会终止另一个应用程序对 XR 硬件的访问。 在某些系统上,这还可能产生显著的功耗或性能开销,或触发状态托盘或商店界面的启动。要确定对于给定的 global object,沉浸式会话请求是否被允许,用户代理 MUST 运行以下步骤:
启动内联会话 并不隐式带有与启动沉浸式会话相同的要求, 但取决于会话的请求特性,可能会施加附加要求。
要确定对于给定的 global object,内联会话请求是否被允许,用户代理 MUST 运行以下步骤:
-
如果会话请求包含除 inline-stereo 之外的任何 必需 特性或可选特性,则令 requiresUserIntent 为
true,否则为false。 -
如果 requiresUserIntent 为
true,且请求不是在 global object 具有瞬态激活时发起,也不是在启动 Web 应用程序时发起,则返回false。 -
如果 global object 不是
Window, 则返回false。 -
返回
true。
13.5.2. 姿态
当基于传感器数据时,XRPose 和
XRViewerPose
将暴露敏感
信息,这些信息可能以多种方式被滥用,包括输入嗅探、注视跟踪或指纹识别。
要确定姿态是否可以被报告给一个 XRSession
session,用户代理 MUST 运行以下步骤:
-
如果 session 的
visibilityState为, 则返回false。 -
按如下方式确定是否可以返回姿态数据:
注:用户代理用于确定姿态不会暴露可指纹识别数据的方法, 由用户代理自行决定。
XRViewerPose
与 XRPose 之间的主要区别是
包含 XRView 信息。
当存在多个视图,且这些视图之间的物理关系可由用户配置时,这些视图之间的关系会被视为敏感信息,
因为它可用于对用户进行指纹识别或画像。
如果 XRView
之间的关系可能唯一标识
XR 设备,则用户代理 MUST 对
XRView 数据进行匿名化,
以防止指纹识别。匿名化方法由用户代理自行决定。
注:此外,如果 XRView 之间的关系
受用户配置的瞳距(IPD)影响,则强烈建议用户代理在会话创建期间、报告任何 XRView 数据之前,
要求显式
同意。
13.5.3. 参考空间
取决于所使用的参考空间,可能会向应用暴露几种不同类型的敏感信息。-
在支持 6DoF 跟踪的设备上,
"local"reference spaces 可用于执行步态分析,从而允许用户画像和指纹识别。 -
在支持 6DoF 跟踪的设备上,
"local-floor"reference spaces 可用于执行步态分析,从而允许用户画像和指纹识别。 此外,由于"local-floor"reference spaces 提供已建立的地面高度,站点可能能够推断用户身高,从而允许用户画像和指纹识别。 -
"bounded-floor"reference spaces 在尺寸受到充分约束时,不会让开发者能够确定地理位置。但是,由于地面高度已建立且用户能够四处走动, 站点可能能够推断用户身高或执行步态分析,从而允许用户画像和指纹识别。此外,也可能使用 bounded reference space 报告的边界执行指纹识别。 -
"unbounded"reference spaces 会揭示最大量的空间数据,并可能导致用户画像和指纹识别。例如,这些数据可能允许确定用户的具体地理位置, 或执行步态分析。
因此,各种 reference space 类型在创建时受到限制,以确保暴露的敏感 信息得到安全处理:
大多数 reference spaces 要求通过显式同意或隐式同意很好地理解使用该 reference space 的 用户意图。详见特性 要求表。
任何由 "local"、
"local-floor"
和 "bounded-floor"
reference spaces 组成、且能够彼此关联的组,MUST 共享一个共同的
native
origin;此限制仅在 "unbounded"
reference spaces 的创建受到限制时适用。
要确定在两个空间 space 和 baseSpace 之间姿态是否必须受限制,用户代理 MUST 运行以下步骤:
-
如果 space 或 baseSpace 任一是
XRBoundedReferenceSpace, 且另一个空间的 native origin 超出native bounds geometry 的距离大于用户代理确定的合理距离,则返回 true。 -
如果 space 或 baseSpace 任一是
XRReferenceSpace, 且其 type 为"local"或"local-floor", 并且这些空间的 native origins 之间的距离大于用户代理确定的合理距离, 则返回true。 -
返回
false。
注:文档可见性的要求基于 [DEVICE-ORIENTATION]。
注:建议将相对于
"local"
或 "local-floor"
reference space 报告的姿态限制在距离
XRReferenceSpace
的
native
origin 15 米范围内。
注:建议将相对于
XRBoundedReferenceSpace
报告的姿态限制在该
XRBoundedReferenceSpace
的
native bounds geometry 外 1 米范围内。
13.6. 可信环境
可信 UI是由用户代理呈现的界面, 用户能够与之交互,但页面不能。用户代理 MUST 支持显示可信 UI。
可信 UI MUST 具有以下属性:
-
它必须不可被伪造
-
它指示所显示的请求/内容来自哪里
-
如果它依赖与用户共享的秘密,则该共享秘密不能被混合现实捕获观察到 (例如,它不能是摄像头可见的手势)
-
它在同一 UA 的沉浸式体验之间保持一致
宽泛地说,希望支持可信 UI的用户代理有两个选项。其中一个选项是
可信沉浸式 UI,它是一种不会退出
沉浸模式的可信 UI。实现可信沉浸式 UI可能很有挑战性,因为
XRWebGLLayer 缓冲区会填满 XR 设备显示器,而用户代理通常不会为自身使用“保留”
像素。用户代理不需要支持可信沉浸式
UI,它们可以改为
暂时暂停/退出沉浸模式,并向用户显示非沉浸式可信 UI。
-
未处于沉浸模式时显示的默认 2D 模式浏览器
-
沉浸模式中显示的提示,只能通过保留的硬件按钮与之交互,以防止伪造
-
暂停沉浸式会话,并显示某种可在其中显示提示的原生系统环境
读取姿态和输入信息的能力会对可信
UI的完整性构成风险,因为页面可能使用这些信息窥探用户在与
可信 UI交互时作出的选择,包括猜测键盘输入。为防止此风险,
当用户正在与 URL 栏或系统对话框等可信 UI(沉浸式或非沉浸式)交互时,用户代理 MUST 将所有
XRSession 的
visibility state 设为
或 "visible-blurred"。
此外,为防止恶意页面能够监控其他页面上的输入,如果当前聚焦区域不属于创建
XRSession
的文档,
则用户代理 MUST 将该 XRSession 的
visibility state 设为 。
在为特定可信
UI实例选择使用
还是 "visible-blurred"
时,用户代理 MUST 考虑姿态信息是否构成安全风险。例如,涉及文本输入,尤其是密码输入的可信 UI,
可能通过用户输入时的移动泄漏输入文本。在这些情况下,用户代理 SHOULD 也停止暴露任何与眼动跟踪相关的信息。
用户代理 MUST 使用可信 UI 来显示权限提示。
如果虚拟环境不能以低延迟和高帧率持续更新用户视点,用户可能会迷失方向或身体不适。 由于无法强制页面生成始终高性能且正确的内容,用户代理 MUST 提供一个被跟踪的可信环境,以及一个 与页面内容异步运行的 XR Compositor。compositor 负责合成可信和不可信内容。如果内容性能不足、不提交帧或意外终止, 用户代理应能够继续呈现响应式的可信 UI。
此外,页面内容还能以与性能无关的方式让用户感到不适。 错误应用的跟踪、频闪颜色,以及意图冒犯、惊吓或恐吓的内容,都是可能导致用户想要快速退出 XR 体验的内容示例。 在这些情况下,移除 XR 设备并不总是快速或实际的选择。为适应这一点,用户代理 MUST 向用户提供一个操作, 例如按下保留的硬件按钮或执行手势,以退出 WebXR 内容并显示用户代理的可信 UI。
13.7. 上下文隔离
可信 UI 必须由独立的渲染上下文绘制,其状态与页面使用的任何渲染上下文隔离。 (例如,任何 WebGL 渲染上下文。)这是为了防止页面破坏可信 UI 上下文的状态, 从而可能阻止其正确渲染被跟踪的环境。它还防止页面可能捕获来自可信 UI 的图像, 这可能导致私密信息泄漏。
此外,为防止 CORS 相关漏洞,每个浏览上下文都会看到 API 返回对象的新实例,例如 XRSession。
在一个具有某个相关 realm的 XRWebGLLayer
上设置的 context
等属性,不应能通过具有不同
相关 realm且不具有相同
origin的 XRWebGLLayer
读取。类似地,在 API 上调用的方法 MUST NOT 对其他浏览上下文造成可观察的状态变化。例如:不会暴露可启用系统级朝向重置的方法,
因为恶意页面可能反复调用它,以阻止其他页面正确跟踪。但是,用户代理 MUST 尊重由用户手势或系统菜单触发的系统级朝向重置。
注:这不适用于由一个浏览上下文进入沉浸模式、获取设备锁,并可能在其他浏览上下文上触发 devicechange 事件所导致的状态变化。
13.8. 指纹识别
鉴于该 API 描述用户可用的硬件及其能力,它不可避免地会为指纹识别提供额外表面。 虽然不可能完全避免这一点,用户代理应采取措施缓解该问题。本规范将可用硬件的报告限制为一次只报告单个设备, 这防止将连接多个头显的罕见情况用作指纹识别信号。此外,被报告的设备没有字符串标识符,并且在创建 XRSession 之前几乎不暴露关于设备能力的信息;而当敏感信息将被暴露时,创建 XRSession 需要额外保护。
13.8.1. isSessionSupported()
的指纹识别考量
由于 isSessionSupported()
可以在没有用户激活的情况下调用,它可能被用作指纹识别向量。
"xr-session-supported" 强大特性限制对 isSessionSupported()
API 的访问。
"xr-session-supported" 的权限相关算法和类型定义如下:
- 权限描述符类型
-
dictionary :XRSessionSupportedPermissionDescriptor PermissionDescriptor {XRSessionMode ; };mode
13.8.2. 何时自动授予 "xr-session-supported" 的考量
Web 上的隐私和个性化之间经常存在张力。本节提供指导,说明可以在何处限定这种权衡,
以及用户代理何时可以通过 isSessionSupported()
向站点描述浏览器的 WebXR 能力而不造成任何隐私降低。
"xr-session-supported" 可基于以下标准在某些系统上自动授予。 这可以提供更好的用户体验,并缓解权限疲劳。
如果一组用户代理都报告相同的 userAgent
和 appVersion,
则它们是通过用户代理字符串不可区分的。
此类通常由浏览器版本以及运行平台/设备标识,但不能通过任何已连接外部设备的状态来区分。我们可以使用
通过用户代理字符串不可区分的用户代理概念,
来正确评估指纹识别风险。
某些通过用户代理字符串不可区分的用户代理将
永不支持
给定 XRSessionMode
的会话。例如:运行在已知不满足移动 AR 支持要求的某款手机型号上的用户代理。
在这些情况下,让 isSessionSupported()
始终报告该 XRSessionMode
不受支持,几乎没有指纹识别风险,因为每个此类设备都会一致报告相同值,并且假定设备类型和型号可通过其他方式推断,
例如通过 userAgent。
因此,在此类系统上,用户代理应自动拒绝相关 XRSessionMode
的
"xr-session-supported"。
其他通过用户代理字符串不可区分的用户代理将
通常支持
给定 XRSessionMode
的会话。例如:已知支持 WebXR 且专门运行在 VR 头显内的用户代理,可能支持
"immersive-vr"
会话,除非用户明确阻止。 在这些情况下,报告该 XRSessionMode
不受支持虽然准确,却会提供更多可唯一标识用户的信息。因此,报告该 XRSessionMode
始终可用,并允许 requestSession()
失败,更能保护隐私,同时也不太可能成为用户困惑的来源。在此类系统上,用户代理应自动授予相关
XRSessionMode
的
"xr-session-supported"。
对于 XR 能力可用性高度可变的通过用户代理字符串不可区分的用户代理,
例如支持 XR 外设的桌面系统,呈现最高的指纹识别风险。此类设备上的用户代理不应以允许
isSessionSupported()
API 提供额外指纹识别位的方式,自动授予 "xr-session-supported"。
-
在调用
isSessionSupported()时,始终判断用户是否对 "xr-session-supported" 给出了 显式同意(可使用可能被缓存的权限提示或类似机制)。 -
自动授予 "xr-session-supported",但让
isSessionSupported()始终报告true,即使在并非始终具备 XR 能力的平台上也是如此,无论相应硬件或软件是否存在。 这会以用户工效为代价,因为它会导致页面向无法查看 XR 内容的用户宣传 XR 内容。 -
当适当硬件存在时,让
isSessionSupported()请求用户对 "xr-session-supported" 给出 显式同意;当此类硬件 _不存在_ 时,在适当随机长度的时间之后返回false。在此类实现中,内容不得能够区分用户代理未连接 XR 硬件的情况, 与用户代理已连接 XR 硬件但用户拒绝提供显式同意的情况。
无论选择何种技术,都不应在没有显式 同意的情况下揭示有关已连接 XR 硬件的额外知识。
14. 集成
14.1. 权限策略
本规范定义了一个策略控制特性,该特性控制是否可由 requestSession()
返回任何需要使用空间跟踪的 XRSession,
以及是否可由 isSessionSupported()
或 navigator.xr
对象上的 devicechange 事件表明需要空间跟踪的会话模式支持。
此特性的特性标识符是 "xr-spatial-tracking"。
此特性的默认允许列表为
["self"]。
注:如果文档的origin 不允许使用 "xr-spatial-tracking" 权限策略,任何沉浸式会话都将被阻止,
因为所有沉浸式
会话都需要某种空间跟踪使用。"inline"
会话仍会被允许,但会被限制为仅使用 "viewer"
XRReferenceSpace。
14.2. Permissions API 集成
[permissions] API 提供了一种统一方式, 使网站能够向用户请求权限,并查询它们已被授予哪些权限。
"xr" 强大特性的权限相关算法和类型定义如下:
- 权限描述符类型
-
dictionary :XRPermissionDescriptor PermissionDescriptor {XRSessionMode ;mode sequence <DOMString >;requiredFeatures sequence <DOMString >; };optionalFeatures name对于XRPermissionDescriptor是 "xr"。 - 权限结果类型
-
[
Exposed =Window ]interface :XRPermissionStatus PermissionStatus {attribute FrozenArray <DOMString >; };granted - 权限查询算法
-
要使用
XRPermissionDescriptordescriptor 和XRPermissionStatusstatus 查询 "xr" 权限,UA MUST 运行以下步骤:-
如果 status 的
state为"denied", 则将 status 的granted设为空的FrozenArray并中止这些步骤。 -
令 result 为给定 descriptor 的
requiredFeatures、optionalFeatures和mode解析所请求特性的结果。 -
如果 result 为
null,则运行以下步骤:-
将 status 的
granted设为空的FrozenArray。 -
中止这些步骤。
-
-
令 (consentRequired, consentOptional, granted) 为 result 的字段。
-
将 status 的
granted设为 granted。 -
如果 consentRequired 为空且 consentOptional 为空, 则将 status 的
state设为"granted"并中止这些步骤
- 权限请求算法
-
要使用
XRPermissionDescriptordescriptor 和XRPermissionStatusstatus 请求 "xr" 权限,UA MUST 运行以下步骤:-
将 status 的
granted设为空的FrozenArray。 -
令 requiredFeatures 为 descriptor 的
requiredFeatures。 -
令 optionalFeatures 为 descriptor 的
optionalFeatures。 -
令 device 为针对 mode、requiredFeatures 和 optionalFeatures 获取当前设备的结果。
-
令 result 为给定 requiredFeatures、optionalFeatures 和
mode解析所请求特性的结果。 -
如果 result 为
null,则运行以下步骤: -
令 (consentRequired, consentOptional, granted) 为 result 的字段。
-
用户代理 MAY 在此时请求用户许可调用算法使用 consentRequired 和 consentOptional 中的任何特性。这些提示的结果应在确定是否存在明确的 用户 意图信号来启用这些特性时纳入考虑。
-
对 consentRequired 中的每个 feature 执行以下步骤:
-
对 consentOptional 中的每个 feature 执行以下步骤:
-
将 status 的
granted设为 granted。 -
将 granted 的所有元素添加到 device 针对 mode 的已授予特性集合中。
注:在判断是否存在明确的 用户意图信号时, 用户代理可以自由地将所有所请求特性的权限提示合并批量显示,也允许逐个显示。
注:在确定 Web 应用程序的用户意图时, 用户代理必须检查它是否作为 Web 应用程序由用户明确启动。 它们 MUST NOT 只检查 origin 是否与某个已安装 Web 应用程序的 origin 匹配。
-
要为 XRSessionMode
mode,在给定 requiredFeatures 和 optionalFeatures 的情况下
解析所请求的特性,
用户代理 MUST 运行以下步骤:
-
令 device 为针对 mode、requiredFeatures 和 optionalFeatures 获取当前设备的结果。
-
令 previouslyEnabled 为 device 针对 mode 的已授予特性集合。
-
如果 device 为
null,或 device 的支持模式列表不 包含 mode,则运行以下步骤:-
返回 元组 (consentRequired, consentOptional, granted)
-
-
对 requiredFeatures 中的每个 feature 执行以下步骤:
-
对 optionalFeatures 中的每个 feature 执行以下步骤:
-
返回 元组
(|consentRequired|, |consentOptional|, |granted|)
更改
相对于 候选推荐快照,2022 年 3 月 31 日的更改
-
添加 inline-stereo 会话特性
-
暴露 XRSession 的已授予特性(GitHub #1296)
-
添加对 isSystemKeyboardSupported 属性的支持(GitHub #1314)
-
澄清 getPose 在 visibile-blurred 下的行为(GitHub #1332)
-
添加瞬态意图(GitHub #1343)
-
首次草案:向 XRInputSource 添加属性以表明它在其他地方可见(GitHub #1353)
-
澄清 rgb 与 srgb 行为(GitHub #1359)
相对于 2020 年 7 月 24 日工作草案的更改
-
修正 predictedDisplayTime,并定义 inline 行为(GitHub #1230)
-
添加 XRFrame.predictedDisplayTime(GitHub #1217)
-
添加对 targetFrameRate 和 supportedFrameRates 的支持(GitHub #1201)
-
添加对 foveation 的支持(
fixedFoveation(GitHub #1195) -
仅允许会话使用其显式请求的特性,或基于模式隐式授予的特性 (GitHub #1189)
-
增强隐式用户意图示例(GitHub #1188)
-
添加对角速度和线速度的支持(GitHub #1182)
-
确保 XRReferenceSpaces 的平台约定保持一致(GitHub #1180)
-
将 composition disabled 标志反转为 composition enabled(GitHub #1172)
-
如果会话已结束,则拒绝 end() 返回的 promise(GitHub #1170)
-
在 requestAnimationFrame 期间检测会话是否已结束(GitHub #1169)
-
要求对 recommendedViewportScale 进行量化(GitHub #1151)
-
使 sessionsupported 偏好自动授予变为非规范性(GitHub #1146)
-
添加 XR device 的定义,包含非视觉用途(GitHub #927)
-
纳入近期隐私讨论的结论(GitHub #1124)
-
将 isSessionSupported 从使用用户意图改为使用权限(GitHub #1136)
-
澄清 minimum viewport scale 可能变化(GitHub #1134)
-
改进 fingerprinting PR(GitHub #1133)
-
添加 requestViewportScale/recommendedViewportScale(GitHub #1132)
-
澄清 framebuffer scale factor 分别应用于 width/height(GitHub #1131)
-
确保 pending render state 始终被应用(GitHub #1128)
-
通过 xr-spatial-tracking 权限策略限制 context XR 兼容性。(GitHub #1126)
-
更改 updateRenderState 更改生效的时机(GitHub #1111)
相对于 2019 年 10 月 10 日工作草案的更改
新特性:
-
添加 secondary views 特性(GitHub #1083)
-
使用 layers 序列更新 XRRenderStateInit(GitHub #999)
-
将 input sources 拆分为 primary/auxiliary(GitHub #929)
-
定义 squeeze 事件(GitHub #893)
更改:
-
Primary views MUST 始终 active(GitHub #1105)
-
正确处理 makeXRCompatible() 中的 context loss(GitHub #1097)
-
引入 active view 概念(GitHub #1096)
-
修正 frame 和 viewport 缓存,使其显式化(GitHub #1093)
-
允许缓存各种对象(GitHub #1088)
-
允许钳制 framebufferScaleFactor(GitHub #1084)
-
澄清 "ensure an immersive device is selected" 的线程性质,废弃 xrCompatible(GitHub #1081)
-
澄清 native origins 的若干问题(GitHub #1071)
-
将 document visibility 检查改为由 UA 选择(GitHub #1067)
-
添加 “check the layers state” 算法(GitHub #1064)
-
围绕 null 和 emulated poses 的各种更改(GitHub #1058)
-
提及 XRInputSource/frame 上正确的 input frame 语义(GitHub #1053)
-
添加 XRRigidTransform 的验证(GitHub #1043)
-
对空 input profile 数组何时适当作出小幅更改。(GitHub #1037)
-
允许 trusted ui 使用 visible-blurred,并警示文本输入泄漏(GitHub #1034)
-
澄清 window.rAF() 的若干问题(GitHub #1033)
-
清理任务和 promises 的处理方式(GitHub #1032)
-
如果没有传入 render state,则短路 updateRenderState()(GitHub #1031)
-
移除对 responsible、active 和 focused documents 的使用(GitHub #1030)
-
澄清 context isolation 中 browsing contexts 和 realms 相关情况(GitHub #1029)
-
显式规定 reset events 作用于 offset spaces(GitHub #1024)
-
明确每个对象在哪个 realm 中创建(GitHub #1023)
-
对 rAF() 回调参数使用当前时间戳(GitHub #1015)
-
会话特性请求不再需要 session 参数(GitHub #1012)
-
允许从 rAF 内部取消 rAF 回调(GitHub #1005)
-
提及 opaque framebuffer 持有对特定 session 的引用(GitHub #1004)
-
将初始 inputsourcechange 事件推迟到 promise 解析之后(GitHub #1002)
-
记录 framebufferScaleFactor 的影响(GitHub #993)
-
如果请求了 depth||stencil,则允许 depth&&stencil 结果(GitHub #987)
-
允许 isSessionSupported 返回内容更具灵活性(GitHub #986)
-
澄清何时通过 inline devices 暴露 tracking/input 数据(GitHub #985)
-
添加对 viewport shape 的常识性限制(GitHub #976)
-
规定 preserveDrawingBuffer 在这里不起作用(GitHub #975)
-
澄清 inline sessions 的 visiblityState 行为(GitHub #974)
-
定义 opaque framebuffer 何时被视为 dirty(GitHub #970)
-
当 device 发生变化时可能更新 inline device(GitHub #947)
-
澄清 bounded reference space 行为(GitHub #938)
-
多项 XR compatibility 算法修复(GitHub #921)
-
补充 trusted UI 小节(GitHub #875)
-
更好地定义 depthNear 和 depthFar 的使用方式(GitHub #888)
-
澄清当 local-floor space 使用估计高度时 emulatedPosition 不为 true(GitHub #871)
相对于 2019 年 2 月 5 日首次公开工作草案的更改
新特性:
-
为 input profile name 列表添加 XRInputSource->profiles(GitHub #695)
-
向 XREye 添加 none 变体(GitHub #641)
-
添加显式 inline XR device(GitHub #737)
-
添加关于姿态隐私的数据调整和受保护功能考量(GitHub #761)
-
添加 reference space 隐私考量(GitHub #762)
-
定义 sensitive information 和 user intent(GitHub #757)
-
required 和 optional features(特性依赖)(GitHub #749)
-
tracking loss 和 tracking recovery(GitHub #559)
-
将 blur/focus 改为 visibilitychange(GitHub #687)
-
定义 input sources 的事件顺序(GitHub #629)
-
描述 input source list 如何维护(GitHub #628)
-
使 origin offset 不可变(GitHub #612)
-
向 XRRenderState 添加 inlineVerticalFieldOfView(GitHub #519)
-
记录 Gamepad 集成(GitHub #553)
-
向 XRWebGLLayer 添加 ignoreDepthValues 属性(GitHub #548)
-
添加 XRSpace XREnvironmentBlendMode.viewerSpace(GitHub #522)
-
向规范添加 XRPose 及相关重构(GitHub #496)
移除的特性:
-
将 canvas inline 改为 single,删除 XRPresentationContext(GitHub #656)
-
移除 XRLayer 基类型(GitHub #688)
-
移除 XRWebGLLayer.requestViewportScaling()(GitHub #631)
-
移除 XRWebGLLayer 的 context Attribute(GitHub #707)
-
移除只反映请求值的 attribs(GitHub #574)
-
移除 XRSessionCreationOptions(GitHub #566)
更改:
-
描述 XRWebGLLayer 所需的清除行为(GitHub #866)
-
规定 XRWebGLLayer framebuffers 始终使用 premultiplied alpha(GitHub #840)
-
澄清 reset 事件的 transform 方向(GitHub #843)
-
定义如何满足 feature requirements(GitHub #839)
-
在适当情况下检查 session 是否为 inline 而非 immersive(GitHub #834)
-
暂时禁止 stereo inline sessions(GitHub #829)
-
在 projectionMatrix 中处理 detached buffers(GitHub #830)
-
在 makeXRCompatible() 中确保已选择 immersive device(GitHub #809)
-
将 features 改为 'any' 的序列(GitHub #807)
-
链接到 “fire an input source” 算法,并显式构造 frame(GitHub #797)
-
从规范和 explainer 中移除 Environment blend mode(GitHub #804)
-
为每个方法提供描述(GitHub #798)
-
要求 UA 显示手动设备激活步骤(GitHub #799)
-
稍微澄清 compositor(GitHub #805)
-
澄清为 rigid transforms 获取矩阵的矩阵数学(GitHub #806)
-
允许 UA 约束 clip planes(GitHub #802)
-
禁止在 getViewport() 中使用过期 XRViews(GitHub #796)
-
显式提及 depth/alpha/stencil 值如何被使用(GitHub #800)
-
当 profiles 变化时触发 input source events(GitHub #795)
-
澄清 reset event 何时触发(GitHub #637)
-
显式规定 requestReferenceSpace() 何时可拒绝 queries(GitHub #651)
-
使 XRRay.matrix 唯一,并添加获取它的步骤(GitHub #655)
-
使用 TAG 对返回 promises 的建议(GitHub #700)
-
将 requestSession() 的易竞争部分移动到主线程(GitHub #706)
-
澄清在独占访问中允许小型 overlay UI(GitHub #709)
-
将 “end the session” 与 “shut down the session” 合并,进行澄清,并添加 onend 事件(GitHub #710)
-
不对 inline sessions 检查 XR compat flag(GitHub #705)
-
验证 position DOMPointInit(GitHub #568)
-
显式规定 native origins(GitHub #621)
-
移除 buttonIndex(GitHub #741)
-
移除对 "immersive-ar" 和 XRRay 的引用(GitHub #784)
-
从 explainer 和 index.bs 中移除对 XRInputSource.gamepad 的引用(GitHub #782)
-
使用无效 view 调用 getViewport 会抛出错误(GitHub #771)
-
阻止会话中同意请求(GitHub #767)
-
规范化 XRPresentationContext 创建(GitHub #501)
-
将 inputSources getter 从方法改为属性(GitHub #624)
-
将 required gamepad index 改为 -1(GitHub #690)
-
处理 detached arrays(GitHub #684)
-
要求 sensitive UI 隐藏 WebXR content(GitHub #742)
-
使 xr-standard gamepad mapping 更严格(GitHub #735)
-
修复计算 XRRay.matrix 的算法(GitHub #728)
-
修复 XRRay.matrix 算法中的 detached array(GitHub #716)
-
简化 requestSession() 中对 unsupported modes 的处理(GitHub #714)
-
一些 XRRenderState 澄清(GitHub #703)
-
用 “pending render state” 替换 “list of pending render states”(GitHub #701)
-
更好地定义 gamepad placeholder buttons 和 axes(GitHub #661)
-
澄清 touchpad 未被触摸时应报告什么值(GitHub #660)
-
将 getPose 参数 referenceSpace 重命名为 baseSpace(GitHub #659)
-
修复 transforms 的乘法顺序(GitHub #649)
-
澄清 local 和 local-floor tracking 的假设(GitHub #648)
-
简化 available spaces(GitHub #626)
-
requestSession:先检查 user activation(GitHub #685)
-
使 boundsGeometry 相对于 effective origin 工作(GitHub #613)
-
显式规定 views array 如何填充(GitHub #614)
-
标识 Gamepad id 应未知的情况(GitHub #615)
-
全面重写 XRSpace、get(Viewer)Pose 定义(GitHub #609)
-
将 supportsSessionMode 重命名为 supportsSession(GitHub #595)
-
合并 reference space types 和 interfaces(GitHub #587)
-
inverse attribute 始终返回同一个对象(GitHub #586)
-
在 session shut down 时拒绝未完成的 promises(GitHub #585)
-
规定 projection matrices 可包含 shear(GitHub #575)
-
描述 updateRenderState 可能抛出的异常(GitHub #511)
-
编辑 requestSession() 和 Initialize the session(GitHub #601)
-
将 XRRigidTransform inverse 从方法改为属性(GitHub #560)
-
指明何时 compositing 使用 depth values(GitHub #563)
-
Stationary subtype 支持是全有或全无(GitHub #537)
-
将 outputContext 移至 XRRenderState(GitHub #536)
-
规定 getViewerPose 会对非 rAF XRFrames 抛出错误(GitHub #535)
-
移除 viewMatrix 并添加 XRTransform.inverse()(GitHub #531)
-
将 XRHandedness enum 改为使用 'none' 而不是 ''(GitHub #526)
-
指明 tracked-pointer ray 的首选人体工学(GitHub #524)
-
澄清 XRRay 构造函数并定义 normalization(GitHub #521)
-
identity reference space 的规范文本(GitHub #520)
-
澄清 immersive sessions 何时被拒绝(GitHub #360)
-
规定没有 base layer 时不调用 frame callbacks(GitHub #512)
15. 致谢
感谢以下个人对 WebXR Device API 规范作出的贡献:
并特别感谢 Vladimir Vukicevic(Unity)开启了这整段冒险!