1. 简介
支持虚拟现实(VR)和增强现实(AR)应用的硬件现在已广泛面向消费者,带来了沉浸式的计算平台,既有新的机遇,也带来了挑战。能够直接与沉浸式硬件交互对于确保 Web 能够作为该环境中的一等公民运行至关重要。
沉浸式计算对高精度、低延迟通信提出了严格要求,以提供可接受的体验。同时,这也为 Web 这样的平台带来了独特的安全性问题。WebXR 设备 API 提供了必要的接口,使开发者能够在各种硬件形态下,在 Web 上创建引人入胜、舒适且安全的沉浸式应用。
其他 Web 接口,如 RelativeOrientationSensor
和 AbsoluteOrientationSensor
,可被用于从部分设备获取输入,在有限的场景下对 WebXR 设备 API 做兼容填补。然而,这些接口无法支持高端沉浸式体验的多种特性,例如6DoF跟踪、对头显外设的内容呈现或受跟踪的输入设备。
1.1. 术语
本文档中使用缩写 XR 以指代用于虚拟现实、增强现实及其他相关技术的硬件、应用和技术手段。示例包括但不限于:
-
头戴式显示器,无论为不透明、透明还是采用视频透视方式
-
具备位置跟踪的移动设备
-
具备头部跟踪能力的固定显示设备
它们的共同点在于都提供一定程度的空间跟踪,用于模拟虚拟内容的视图。
如“XR 设备”、“XR 应用”等术语一般指上述任意设备。仅适用于设备子集的内容将会有明确说明。
术语 3DoF 和 6DoF 在本文档中用于描述XR 设备的跟踪能力。
-
3DoF 设备(即“三自由度”)只能跟踪旋转运动。这通常见于仅依赖加速度计和陀螺仪数据进行跟踪的设备。3DoF 设备无法响应用户的平移动作,尽管它们可能会通过对脖子或手臂的建模算法估算平移变化。
-
6DoF 设备(即“六自由度”)既可跟踪旋转也可跟踪平移,实现空间中的精确 1:1 跟踪。这通常需要一定程度对用户环境的理解。这种环境理解可通过内向外跟踪(如设备自带的相机或深度传感器)获得,也可通过外向内跟踪(如用户环境中的外部设备如相机或光源)来为XR 设备提供稳定的参考点以确定其位置。
1.2. 应用流程
大多数使用 WebXR 设备 API 的应用会遵循类似的使用模式:
-
查询
navigator.xr.isSessionSupported()
,以判断硬件和 UA 是否支持所需类型的 XR 内容。 -
若支持,则向用户展示 XR 内容。
-
等待窗口获得瞬时激活。最常见的形式是用户点击页面上的按钮表示希望开始查看 XR 内容。
-
在用户激活事件中,通过
XRSession
和navigator.xr.requestSession()
请求 XR 会话。
2. 模型
2.1. XR 设备
XR 设备 是能够向用户呈现沉浸式内容的物理硬件单元。只要内容能够产生视觉、音频、触觉或其他感官输出,以模拟或增强用户环境的各个方面,即可认为是“沉浸式”的。最常见的情况是跟踪用户在空间中的移动,并生成与用户动作同步的输出。在桌面客户端上,这通常是头戴式显示器外设;在移动端,则可能是配合查看器支架使用的移动设备本身。它也可以是没有立体呈现能力但拥有更高级跟踪功能的设备。
XR 设备有一个支持模式列表(list of strings),该列表包含XRSessionMode
的枚举值(即该XR 设备支持的模式)。
每个XR 设备对于其支持模式列表中的每个XRSessionMode
都有一个已授予功能集,即有序集合,包含功能描述符,且初始为一个空集合。
用户代理有一个沉浸式 XR 设备列表(list of XR 设备),初始为一个空列表。
用户代理有一个沉浸式
XR 设备(null
或 XR
设备),初始为null
,代表沉浸式 XR 设备列表中的活动设备。此对象可以存在于独立线程并异步更新。
用户代理必须有一个默认内嵌 XR 设备,即一个XR 设备,其支持模式列表中必须包含"inline"
。默认内嵌 XR
设备不能报告任何位姿信息,也不能报告XR
输入源或除指针事件外的其他事件。
注意:默认内嵌 XR 设备仅用于为开发者提供便利,使其能够对内嵌和沉浸内容使用相同的渲染和输入逻辑。默认内嵌 XR 设备不会暴露任何页面其他机制(如用于输入的指针事件)未能获取的信息,只是以 XR 为中心的格式呈现这些值。
用户代理必须有一个内嵌 XR
设备,即一个XR 设备,其支持模式列表中必须包含"inline"
。内嵌 XR 设备可以是沉浸式 XR
设备(如果其跟踪能力适合暴露给内嵌内容),否则为默认内嵌 XR 设备。
注意:在手机上,内嵌 XR 设备可以报告来自手机内部传感器(如陀螺仪和加速度计)的位姿信息。在没有类似传感器的台式机和笔记本电脑上,内嵌 XR 设备无法报告位姿,因此应回退到默认内嵌 XR 设备。如果用户代理本身运行在XR 设备上,内嵌 XR 设备即为同一设备,并可能支持多个视图。用户必须同意后,才可启用除默认内嵌 XR 设备外的任何跟踪或输入功能。
当前沉浸式 XR 设备列表、内嵌 XR 设备和沉浸式 XR 设备的值可以存在于独立线程并异步更新。这些对象不应在未并行运行的步骤中直接访问。
3. 初始化
3.1. navigator.xr
partial interface Navigator { [SecureContext ,SameObject ]readonly attribute XRSystem xr ; };
xr
属性的 getter 必须返回与其关联的 XRSystem
对象。
3.2. XRSystem
[SecureContext ,Exposed =Window ]interface :
XRSystem EventTarget { // MethodsPromise <boolean >isSessionSupported (XRSessionMode ); [
mode NewObject ]Promise <XRSession >requestSession (XRSessionMode ,
mode optional XRSessionInit = {}); // Events
options attribute EventHandler ondevicechange ; };
当创建 XRSystem
对象时,用户代理必须在创建 Navigator
对象时一同创建,并将其关联。
XRSystem
对象是该
API 的入口点,用于查询用户代理可用的 XR 功能,并通过创建 XRSession
与 XR
硬件建立通信。
用户代理必须能够枚举已连接系统的沉浸式 XR 设备,每次枚举时将每个可用设备加入沉浸式 XR 设备列表。后续请求枚举的算法必须重用缓存的沉浸式 XR 设备列表。枚举设备时不应初始化设备跟踪。首次枚举后,用户代理必须持续监测设备的连接与断开,将已连接设备添加到沉浸式 XR 设备列表,断开设备则移除。
每当沉浸式 XR 设备列表发生变化时,用户代理应通过执行以下步骤选择沉浸式 XR 设备:
-
令 oldDevice 为沉浸式 XR 设备。
-
如果沉浸式 XR 设备列表为空列表,将沉浸式 XR 设备设为
null
。 -
如果沉浸式 XR 设备列表的长度为 1,则将沉浸式 XR 设备设为沉浸式 XR 设备列表[0]。
-
按如下方式设置沉浸式 XR 设备:
- 若存在任何活动的
XRSession
,且沉浸式 XR 设备列表包含 oldDevice: -
将沉浸式 XR 设备设为 oldDevice。
- 否则:
-
将沉浸式 XR 设备设置为用户代理自行选择的设备。
- 若存在任何活动的
-
如适用,用户代理可以将内嵌 XR 设备更新为沉浸式 XR 设备,否则为默认内嵌 XR 设备。
-
如果这是首次枚举设备,或 oldDevice 等于沉浸式 XR 设备,则中止这些步骤。
-
排队一个任务,将所有
WebGLRenderingContextBase
实例的XR compatible布尔值设为false
。 -
排队一个任务,在相关全局对象的
navigator
的xr
上,派发一个名为devicechange
的事件。 -
排队一个任务,在受沉浸式 XR 设备或内嵌 XR 设备变更影响的
XRPermissionStatus
对象上派发合适的change
事件。
注意:这些步骤应始终并行执行。
注意:当沉浸式 XR 设备列表包含多个设备时,用户代理可以按照任何标准选择沉浸式 XR 设备。例如,用户代理可以始终选择列表中的第一个设备,或提供设置界面让用户管理设备优先级。理想情况下,选择默认设备的算法应当稳定,并能在多次浏览会话中选中同一设备。
ondevicechange
属性是 事件处理 IDL 属性,用于 devicechange
事件类型。
isSessionSupported(mode)
方法用于查询用户代理和设备能力是否可能支持给定的 mode。
调用该方法时,必须执行以下步骤:
-
如果请求文档的origin未被允许使用 "xr-spatial-tracking" 权限策略,reject promise,异常为 "
SecurityError
"DOMException
,并返回。 -
按如下方式检查会话 mode 是否受支持:
- 如果用户代理和系统已知永不支持 mode 会话
-
resolve promise 为
false
。 - 如果用户代理和系统已知通常支持 mode 会话
-
只要所有 UA 字符串不可区分的实例都产生相同结果,promise可以resolve为
true
。 - 否则
-
按如下步骤并行执行:
-
令 device 为确保已选择沉浸式 XR 设备 的结果。
-
如果 device 为 null,resolve promise 为
false
,并中止这些步骤。 -
如果 device 的支持模式列表不包含 mode,排队一个任务,resolve promise 为
false
,并中止这些步骤。 -
请求权限使用强大特性 "xr-session-supported" ,权限描述符为
XRSessionSupportedPermissionDescriptor
,mode
值为 mode。如返回"denied"
,排队一个任务,resolve promise 为false
,并中止这些步骤。详见指纹识别考量。
-
-
返回 promise。
注意:isSessionSupported()
的目的并不是精确报告用户代理是否可以创建 XRSession
,而是告知页面是否建议展示可创建给定模式会话的能力。即便用户代理在解析方法前检查了所需硬件/软件的存在,仍可能有一定程度的误报(比如硬件虽然存在,但在会话请求时已被其他应用独占)。
预期大多数含 XR 内容的页面会在文档生命周期早期调用 isSessionSupported()
。因此调用
isSessionSupported()
应避免显示模态或其他打扰性 UI。调用 isSessionSupported()
不得触发设备选择 UI,不得干扰系统上正在运行的 XR 应用,也不得引发 XR 相关应用(如系统托盘或商店)的启动。
immersive-vr
会话。
const supported= await navigator. xr. isSessionSupported( 'immersive-vr' ); if ( supported) { // 可能支持 'immersive-vr' 会话。 // 页面应向用户展示支持信息。 } else { // 不支持 'immersive-vr' 会话。 }
XRSystem
对象有一个待处理沉浸式会话布尔值,初始为false
,一个活动沉浸式会话,初始为
null
,以及一个内嵌会话列表,初始为空。
requestSession(mode, options)
方法在可能时尝试为给定 mode 初始化一个 XRSession
,如有需要进入沉浸式模式。
调用该方法时,用户代理必须执行以下步骤:
-
若 mode 是沉浸式会话模式,则 immersive 为
true
,否则为false
。 -
按如下方式检查会话请求是否被允许:
- 若 immersive 为
true
: -
-
检查 global object 是否被允许请求沉浸式会话,如不允许则reject promise,异常为 "
SecurityError
"DOMException
,并返回 promise。 -
若待处理沉浸式会话为
true
或活动沉浸式会话不为null
,则reject promise,异常为 "InvalidStateError
"DOMException
,并返回 promise。 -
将待处理沉浸式会话设为
true
。
-
- 否则:
-
检查 global object 是否被允许请求内嵌会话,如不允许则reject promise,异常为 "
SecurityError
"DOMException
,并返回 promise。
- 若 immersive 为
-
按如下步骤并行执行:
-
令 requiredFeatures 为 options 的
requiredFeatures
。 -
令 optionalFeatures 为 options 的
optionalFeatures
。 -
将 device 设为获取当前设备的结果,参数为 mode、requiredFeatures 和 optionalFeatures。
-
排队一个任务执行如下步骤:
-
如果 device 为
null
或 device 的支持模式列表不包含 mode,则:-
reject promise,异常为 "
NotSupportedError
"DOMException
。 -
若 immersive 为
true
,将待处理沉浸式会话设为false
。 -
中止这些步骤。
-
-
令 descriptor 为用 mode、requiredFeatures 和 optionalFeatures 初始化的
XRPermissionDescriptor
。 -
令 status 为
XRPermissionStatus
,初始为null
。 -
请求 xr 权限,参数为 descriptor 和 status。
-
如果 status 的
state
为"denied"
,则:-
reject promise,异常为 "
NotSupportedError
"DOMException
。 -
若 immersive 为
true
,将待处理沉浸式会话设为false
。 -
中止这些步骤。
-
-
用 session、mode、granted 和 device 初始化会话。
-
按如下方式可选地设置活动沉浸式会话:
-
resolve promise,值为 session。
-
排队一个任务执行如下步骤:
注意:这些步骤确保首次inputsourceschange
事件在初始会话 resolve 后发生。-
设置 session 的promise 已 resolve 标志为
true
。 -
令 sources 为 session 已附加的所有输入源。
-
若 sources 非空,执行如下步骤:
-
设置 session 的活动 XR 输入源列表为 sources。
-
在 session 上派发名为
inputsourceschange
的XRInputSourcesChangeEvent
,其added
设为 sources。
-
-
-
-
-
返回 promise。
要获取当前设备,针对 XRSessionMode
的 mode、requiredFeatures 和 optionalFeatures,用户代理必须执行:
-
按如下方式选择 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
枚举定义了 XRSession
可运行的模式。
enum {
XRSessionMode "inline" ,"immersive-vr" ,"immersive-ar" };
-
inline
会话模式表示会话输出将作为 HTML 文档中的一个元素显示。inline
会话内容必须以单眼(即单个视图)方式显示。它可以允许观察者跟踪。用户代理必须允许创建inline
会话。 -
immersive-ar
会话模式的行为由 WebXR AR 模块 定义,并且除非 UA 实现了该模块,否则不得将其添加到 沉浸式 XR 设备的支持模式列表中。
在本文档中,inline session
一词等同于 inline
会话,immersive session
指的是 immersive-vr
或 immersive-ar
会话。
沉浸式会话必须提供某种程度的观察者跟踪,并且内容必须以相对于用户和/或周围环境的正确比例进行显示。此外,沉浸式会话必须获得对沉浸式 XR 设备的独占访问,即在沉浸式会话处于"visible"
状态时,HTML
文档不会显示在沉浸式 XR
设备的显示上,也不会有其他来源的内容获得独占访问权限。独占访问不会阻止用户代理叠加自己的 UI,但此类 UI 应尽量简洁。
注意:UA 可以为辅助功能或安全性目的叠加内容,例如守护边界、障碍物或在没有其他输入源时显示用户的手部。
注意:未来的规范或模块可能会扩展沉浸式会话,包括更多会话模式。
注意: 独占访问的展现方式示例包括在虚拟现实头显上显示立体内容等。
注意:作为叠加 UI 的示例,用户代理或操作系统在沉浸式会话中可能会在渲染内容上方显示通知。
注意:在沉浸式会话期间,HTML 文档不会显示在沉浸式 XR 设备的显示器上,但仍可能显示在其他屏幕上,例如用户从连接到沉浸式 XR 设备的电脑的 2D 浏览器进入沉浸式会话时。
3.4. 特性依赖
XRSession
的某些特性可能因多种原因无法通用提供,其中包括并非所有 XR 设备都能支持全部特性。另一个考量是部分特性会暴露敏感信息,这可能需要明确的用户意图信号后才能启用。
由于初始化底层 XR 平台并创建 XRSession
后又立刻告知用户应用无法正常运行是糟糕的用户体验,开发者可通过向 XRSessionInit
字典传递必需特性,用于 requestSession()
。如果由于设备限制或未获得用户意图信号,导致任一必需特性不可用,则会阻止创建 XRSession
。
此外,鼓励开发者设计在功能更强大设备上能渐进增强体验的应用。对于不是必须但可用时会加以利用的可选特性,也必须在 XRSessionInit
字典中指明,以确保必要时能在启用前获取用户意图。
dictionary {
XRSessionInit sequence <DOMString >requiredFeatures ;sequence <DOMString >optionalFeatures ; };
requiredFeatures
数组包含了该体验的所有必需特性。如果列表中有任何值不是合法的特性描述符,则不会创建 XRSession
。如果
requiredFeatures
列表中的任何特性不被XR
设备支持,或如有必要,未获得用户意图信号,也不会创建
XRSession
。
optionalFeatures
数组包含该体验的所有可选特性。如果列表中有任何值不是合法的特性描述符,则会被忽略。只要XR
设备支持,且如有必要已获得用户意图信号,optionalFeatures
列表中的特性将被启用,但如缺失不会阻止 XRSession
创建。
如特性列表中给定的值属于以下情况,则认为是合法的特性描述符:
-
任意
XRReferenceSpaceType
枚举值的字符串表示
该规范的后续版本及其他模块可能会扩展可接受的特性描述符列表。
注意:如某特性需要额外初始化,应扩展 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"
总是作为沉浸式会话的请求特性之一被包含,作为默认特性,因此沉浸式会话总是需要获得显式同意或隐式同意。
请求特性只有在XR 设备具备支持能力时才能为会话启用,也就是该特性已知在该XR 设备的某些配置中受支持,即使当前配置尚未确认支持该特性。用户代理可自行决定施加更严格约束,以提供更一致的用户体验。
注意:例如,部分 VR
设备既可配置用户活动边界,也可跳过边界配置以原地操作。这类设备即便当前未配置安全边界,也可视为"bounded-floor"
XRReferenceSpace
的具备支持能力,因为如有需要用户可配置设备以支持体验。这允许用户代理在需要时避免完全初始化XR
设备,或在解析请求特性前等待用户环境被识别。但如果用户代理在会话请求时已知边界状态,则可在安全边界未配置时拒绝 "bounded-floor"
特性。
4. 会话
4.1. XRSession
所有与 XR 硬件的交互都通过 XRSession
对象完成,只能通过在 XRSystem
对象上调用 requestSession()
获取。一旦成功获取会话,可用于 轮询观察者姿态
,
查询用户环境信息,以及向用户呈现图像。
用户代理在可能的情况下,不应初始化设备跟踪或渲染能力,直到获取了 XRSession
。这样可避免在用户未实际使用
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
有一个 动画帧,是一个以 active 设为 false
、animationFrame 设为
true
,session
设为该 XRSession
的
XRFrame
。
每个 XRSession
有一个 已授予功能集,是一个 有序集合,包含已授予该 XRSession
的 特性描述符对应的 DOMString
。
enabledFeatures
属性返回 已授予功能集中的功能,作为新的 DOMString
数组。
isSystemKeyboardSupported
属性表示 XRSystem
在 XRSession
活动时能否显示系统键盘。如果 isSystemKeyboardSupported
为 true
,则触发覆盖键盘的 Web API(如 focus)时会显示系统键盘。当键盘显示时,XRSession
必须将会话的 可见性状态 设为 "visible-blurred"
。
要 初始化会话,给定 session、mode、granted 和 device,用户代理必须执行以下步骤:
-
将 session 的 mode 设为 mode。
-
将 session 的 XR 设备 设为 device。
-
将 session 的 已授予功能集 设为 granted。
-
如用户代理的其他功能尚未执行,按平台相关步骤初始化设备的跟踪和渲染能力,包括向用户显示必要的指引。
注意:部分设备激活前需要用户额外操作。例如,手机头显设备需将手机插入头显,在连接外部头显的桌面浏览器则需佩戴头显。确保相关指引展示是用户代理的责任,而非内容作者。
多种情况可能会关闭会话,该操作是永久且不可逆的。会话关闭后,想再次访问 XR 设备 的跟踪或渲染能力,只能重新请求会话。每个 XRSession
有一个
ended 布尔值,初始为 false
,指示是否已关闭。
当 XRSession
session 被关闭时,执行以下步骤:
-
将 session 的 ended 设为
true
。 -
从 内嵌会话列表中移除 session。
-
拒绝 session 返回的所有未完成的 promise,抛出
InvalidStateError
,但end()
返回的 promise 除外。 -
如用户代理没有其他功能仍在用,按平台相关步骤关闭设备的跟踪和渲染能力,必须包括:
-
排队一个任务,在 session 上派发名为
XRSessionEvent
的end
事件。
end()
方法用于手动关闭会话。调用时,必须执行以下步骤:
每个 XRSession
有一个 活动渲染状态,即一个新的
XRRenderState
,以及一个
待处理渲染状态,最初为
null
的 XRRenderState
。
renderState
属性返回 XRSession
的 活动渲染状态。
每个 XRSession
有 最小内嵌视场
和 最大内嵌视场,以弧度为单位。其值必须由用户代理确定,范围在 0
到 PI
之间。
每个 XRSession
有 最小近裁剪面 和
最大远裁剪面,以米为单位。其值必须由用户代理确定且为非负数。最小近裁剪面应小于 0.1
,最大远裁剪面应大于
1000.0
(也可为无穷大)。
当用户代理要用 XRSession
session 和 XRRenderStateInit
newState 更新待处理图层状态时,必须执行以下步骤:
-
若 newState 的
layers
不为null
,则抛出NotSupportedError
。
注: WebXR 图层模块将为该算法引入新语义。
当用户代理要在 XRSession
session 上 应用名义帧率 rate 时,必须执行以下步骤:
-
如果 rate 与 session 的 内部名义帧率相同,则中止这些步骤。
-
如果 session 的 ended 为
true
,中止这些步骤。 -
将 session 的 内部名义帧率设为 rate。
-
在 session 上派发名为
XRSessionEvent
的frameratechange
事件。
updateTargetFrameRate(rate)
方法将 目标帧率
rate 传递给 XRSession
。
调用该方法时,用户代理必须执行以下步骤:
-
令 session 为 this。
-
如果 session 没有 内部名义帧率,则 reject promise,抛出 "
InvalidStateError
"DOMException
并返回 promise。 -
如果 session 的 ended 为
true
,则 reject promise,抛出 "InvalidStateError
"DOMException
并返回 promise。 -
如果 rate 不在
supportedFrameRates
中,reject promise,抛出 "TypeError
"DOMException
并返回 promise。 -
将 session 的 内部目标帧率设为 rate。
-
排队一个任务,执行下列步骤:
-
返回 promise。
如果 XR 合成器因任何原因更改了名义帧率(例如在 "visible-blurred"
事件期间),则该事件结束后应继续使用 内部目标帧率。
updateRenderState(newState)
方法会排队更新活动渲染状态,将在下一帧应用。传入方法的 XRRenderStateInit
newState 未设置的字段不会被更改。
调用时,用户代理必须执行以下步骤:
-
令 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 的相应值。 -
如果 newState 的
depthNear
已设置,则将 session 的 待处理渲染状态 的depthNear
设为 newState 的相应值。 -
如果 newState 的
depthFar
已设置,则将 session 的 待处理渲染状态 的depthFar
设为 newState 的相应值。 -
如果 newState 的
inlineVerticalFieldOfView
已设置,则将 session 的 待处理渲染状态 的inlineVerticalFieldOfView
设为 newState 的相应值。 -
如果 newState 的
baseLayer
已设置,则将 session 的 待处理渲染状态 的baseLayer
设为 newState 的相应值。
当被请求时,XRSession
session 必须通过执行以下步骤来应用待处理渲染状态:
-
令 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 的 mode 为
"inline"
且 baseLayer 是XRWebGLLayer
的实例,且其 composition enabled 设为false
: -
将 activeState 的 composition enabled 设为
false
。将 activeState 的 output canvas 设为 baseLayer 的 context 的
canvas
。 - 否则:
-
将 activeState 的 composition enabled 设为
true
。将 activeState 的 output canvas 设为
null
。
- 如果 session 的 mode 为
-
requestReferenceSpace(type)
方法用于根据给定的 type 构造一个新的 XRReferenceSpace
,如果可能的话。
调用该方法时,用户代理必须执行以下步骤:
-
按如下步骤 并行执行:
-
如果对 type 和 session 执行 reference space is supported 结果为
false
,排队一个任务,reject promise,异常为NotSupportedError
,并中止这些步骤。 -
设置跟踪 type 类型参考空间所需的任何平台资源。
用户代理不需要等待此类参考空间的跟踪建立后再 resolverequestReferenceSpace()
。在会话最初尝试建立跟踪时,getViewerPose()
返回null
是可以的,内容可在此期间显示启动画面等。注意若 type 为"bounded-floor"
且边界尚未建立,用户代理可以将边界设为一个较小的初始区域,并在边界建立时通过reset
事件通知。 -
排队一个任务,执行以下步骤:
-
创建参考空间 referenceSpace,参数为 type 和 session。
-
resolve promise,值为 referenceSpace。
-
-
返回 promise。
每个 XRSession
有一个 活动 XR
输入源列表(list of XRInputSource
)和一个
活动 XR
跟踪源列表(list of XRInputSource
),两者初始都为空列表。
每个 XRSession
有一个 XR 设备,该设备在初始化时设置。
inputSources
属性返回 XRSession
的 活动 XR 输入源列表。
trackedSources
属性返回 XRSession
的 活动 XR 跟踪源列表。
用户代理必须监控与 XR 设备 相关的所有 XR 输入源,包括检测 XR 输入源 的新增、移除或更改。
每个 XRSession
有一个 promise 已 resolve 标志,初始为 false
。
注: 该标志用于确保 添加输入源、移除输入源和更改输入源算法不会运行,直到用户代码有机会绑定事件监听器。如果实现选择在会话 resolve 后才开始监听输入源变化,则可能不需要该标志。
当 有新的 XR 输入源 可用 于 XRSession
session 时,用户代理必须执行以下步骤:
-
如果 session 的 promise 已 resolve 标志 未设置,则中止这些步骤。
-
令 added primary sources 为一个新的 列表。
-
令 added tracked sources 为一个新的 列表。
-
对每个新的 XR 输入源:
-
令 inputSource 为该
XRSession
的 相关 realm 中的 新XRInputSource
,然后:- 如果 inputSource 是 主输入源:
-
将 inputSource 添加到 added primary sources。
- 否则:
-
将 inputSource 添加到 added tracked sources。
-
-
排队一个任务,执行以下步骤:
-
扩展 session 的 活动 XR 输入源列表,添加 added primary sources。
-
如果 added primary sources 非空,在 session 上派发名为
XRInputSourcesChangeEvent
的inputsourceschange
事件,added
设为 added primary sources。 -
扩展session 的 活动 XR 跟踪源列表,将 added tracked sources 添加进去。
-
如果 added tracked sources 非空,在 session 上派发名为
XRInputSourcesChangeEvent
的trackedsourceschange
事件,added
设为 added tracked sources。
-
当之前添加的XR 输入源不再可用于
XRSession
session 时,用户代理必须执行以下步骤:
-
如果 session 的 promise 已 resolve 标志 未设置,则中止这些步骤。
-
令 removed primary sources 为一个新的 列表。
-
令 removed tracked sources 为一个新的 列表。
-
对每个不再可用的 XR 输入源:
-
令 inputSource 为 session 的 活动 XR 输入源列表中与该 XR 输入源关联的
XRInputSource
,然后:- 如果 inputSource 是主输入源:
-
将 inputSource 添加到 removed primary sources。
- 否则:
-
将 inputSource 添加到 removed tracked sources。
-
-
排队一个任务,执行以下步骤:
-
移除 removed primary sources 中的每个
XRInputSource
,从 session 的 活动 XR 输入源列表中。 -
如果 removed primary sources 非空,在 session 上派发名为
XRInputSourcesChangeEvent
的inputsourceschange
事件,removed
设为 removed primary sources。 -
移除 removed tracked sources 中的每个
XRInputSource
,从 session 的 活动 XR 跟踪源列表中。 -
如果 removed tracked sources 非空,在 session 上派发名为
XRInputSourcesChangeEvent
的trackedsourceschange
事件,removed
设为 removed tracked sources。
-
注意:当输入源暂时失去位置和朝向跟踪时,用户代理可以触发此事件。建议仅对物理手持控制器输入源这样做。对于手部跟踪输入源,由于这种情况频繁发生,不建议这样做;对于跟踪器对象输入源也不推荐这样做,因为这会让应用难以维护身份概念。
当 handedness
、
targetRayMode
、
profiles
、
是否存在 gripSpace
,
或作为主输入源或
跟踪输入源的状态
发生变化时,对于
XRSession
session,用户代理必须执行以下步骤:
-
如果 session 的 promise 已 resolve 标志 未设置,则中止这些步骤。
-
令 added primary sources 为一个新的 列表。
-
令 removed primary sources 为一个新的 列表。
-
令 added tracked sources 为一个新的 列表。
-
令 removed tracked sources 为一个新的 列表。
-
对每个发生变化的 XR 输入源:
-
令 oldInputSource 为 session 的 活动 XR 输入源列表中先前与该 XR 输入源关联的
XRInputSource
,然后: -
令 newInputSource 为 session 的 相关 realm 中的 新
XRInputSource
,然后:
-
-
排队一个任务,执行以下步骤:
-
移除 removed primary sources 中的每个
XRInputSource
,从 session 的 活动 XR 输入源列表中。 -
扩展 session 的 活动 XR 输入源列表,添加 added primary sources。
-
如果 added primary sources 或 removed primary sources 非空,在 session 上派发名为
XRInputSourcesChangeEvent
的inputsourceschange
事件,added
设为 added primary sources,removed
设为 removed primary sources。 -
移除 removed tracked sources 中的每个
XRInputSource
,从 session 的 活动 XR 跟踪源列表中。 -
扩展 session 的 活动 XR 输入源列表,添加 added tracked sources。
-
如果 added tracked sources 或 removed tracked sources 非空,在 session 上派发名为
XRInputSourcesChangeEvent
的trackedsourceschange
事件,added
设为 added tracked sources,removed
设为 removed tracked sources。
-
每个 XRSession
都有一个 visibility state(可见性状态) 值,是一个枚举。对于 内嵌会话,可见性状态 必须与 Document
的
visibilityState 保持一致。对于 沉浸式会话,可见性状态
必须设置为下列最能反映会话状态的值之一。
-
visible
状态表示XRSession
渲染的图像用户可以看到,且requestAnimationFrame()
回调按 XR 设备 的原生刷新率执行。输入也会被XRSession
正常处理。 -
visible-blurred
状态表示XRSession
渲染的图像用户可能能看到,但不是主要焦点。requestAnimationFrame()
回调可能会被限流。输入不会被XRSession
处理。 -
hidden
状态表示XRSession
渲染的图像用户无法看到。requestAnimationFrame()
回调在 可见性状态 改变前不会被处理。输入也不会被XRSession
处理。
visibilityState
属性返回 XRSession
的 可见性状态。onvisibilitychange
属性是 事件处理 IDL 属性,用于 visibilitychange
事件类型。
可见性状态 可以在除 XR 动画帧 处理期间外的任何时间被用户代理更改,并且用户代理在可能时应监控 XR 平台以观察会话可见性受外部影响时及时更新 可见性状态。
注意: XRSession
的 可见性状态 并不代表 HTML 文档的可见性。根据系统配置,页面在 沉浸式会话 活动时可能仍然可见。(例如,连接到 PC
的头显在头显显示沉浸式内容时,显示器上页面仍可见。)开发者应继续依赖 Page Visibility
判断页面可见性。
注意: XRSession
的 可见性状态 不影响或限制在有线连接(2D 内容仍可见)下的鼠标行为。内容如需更强的鼠标行为控制,可考虑使用 [pointerlock] API。
在 XRSystem
中,有几种描述帧率的定义:
-
nominal frame rate(名义帧率):
XRSystem
要求体验以该速率渲染帧以保持名义性能。即使体验掉帧,未必每秒都能获得requestAnimationFrame()
回调,但XRSystem
期望达到这个目标。 -
effective frame rate(实际帧率):体验实际每秒能处理多少次
requestAnimationFrame()
的性能度量。该值会随体验是否赶上XRSystem
的帧时而波动。 -
display frame rate(显示帧率):物理显示器实际绘制帧的速率,可能由体验的 名义帧率推导得来。该值属于硬件实现细节,对体验不可见。
每个 XRSession
可有一个 internal target frameRate(内部目标帧率),即 目标帧率。
每个 XRSession
可有一个 internal nominal frameRate(内部名义帧率),即 名义帧率。如果 实际帧率 低于 名义帧率,XR 合成器 可以用重投影等技术提升体验。inline
会话不可有该属性。
frameRate
属性反映 内部名义帧率。若 XRSession
无 内部名义帧率,则返回 null
。
onframeratechange
属性是 事件处理 IDL 属性,用于 frameratechange
事件类型。若 XRSession
的 名义帧率因任何原因变化,必须用新名义帧率和该 XRSession
应用名义帧率。
supportedFrameRates
属性返回支持的 目标帧率值列表。该属性是可选的,且
inline
会话或作者无法控制帧率的 XRSystem
不得有此属性。若 XRSession
支持该属性,也必须支持 frameRate
。
每个 XRSession
有一个 viewer reference space(观察者参考空间),即类型为 "viewer"
,带有
单位变换 原点偏移的 XRReferenceSpace
。
每个 XRSession
有一个 视图列表,是与 XR 设备 提供的视图对应的 view 的 list。如果 XRSession
的
renderState
的 composition enabled 布尔值为 false
,视图列表必须只包含一个 view。视图列表在 XRSession
生命周期内不可变,且必须包含会话期间可能出现的所有 view,包括初始时未激活的次视图。
onend
属性是 事件处理 IDL 属性,用于 end
事件类型。
oninputsourceschange
属性是 事件处理 IDL 属性,用于 inputsourceschange
事件类型。
onselectstart
属性是 事件处理 IDL 属性,用于 selectstart
事件类型。
onselectend
属性是 事件处理 IDL 属性,用于 selectend
事件类型。
onselect
属性是 事件处理 IDL 属性,用于 select
事件类型。
onsqueezestart
属性是 事件处理 IDL 属性,用于 squeezestart
事件类型。
onsqueezeend
属性是 事件处理 IDL 属性,用于 squeezeend
事件类型。
onsqueeze
属性是 事件处理 IDL 属性,用于 squeeze
事件类型。
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
都有一个 output canvas,即 HTMLCanvasElement
,初始为
null
。output canvas 是用于展示 "inline"
XRSession
渲染内容的 DOM 元素。
每个 XRRenderState
还有一个 composition enabled 布尔值,初始为 true
。当渲染命令针对 API
提供的表面执行,并由 XR 合成器 展示时,XRRenderState
被认为是composition enabled。如果 "inline"
XRSession
的渲染直接输出到 output canvas,则 XRRenderState
的
composition enabled 标志必须为 false
。
注意:目前只有当 composition
enabled 设为 false
时,XRRenderState
才有 output canvas;但未来规范版本可能会引入更多高级用法(如镜像、图层合成)下的 output canvas 设定方式,这些将需要开启 composition。
当为 XRRenderState
创建 XRSession
session 时,用户代理必须 初始化渲染状态,执行以下步骤:
-
令 state 为 session 的 新
XRRenderState
对象。 -
将 state 的
depthNear
初始化为0.1
。 -
将 state 的
depthFar
初始化为1000.0
。 -
将 state 的
passthroughFullyObscured
初始化为false
。 -
按如下方式初始化 state 的
inlineVerticalFieldOfView
:- 如果 session 是 内嵌会话:
-
初始化为
PI * 0.5
。 - 否则:
-
初始化为
null
。
-
将 state 的
baseLayer
初始化为null
。
depthNear
属性定义了近平面距离观察者的距离(米)。depthFar
属性定义了远平面距离观察者的距离(米)。
depthNear
和 depthFar
会用于 projectionMatrix
计算 XRView
时。渲染时使用
projectionMatrix
时,只有距离观察者在 depthNear
到 depthFar
之间的几何体才会被绘制。它们还决定了 XRWebGLLayer
深度缓冲区的取值解释方式。depthNear
可大于 depthFar
。
注意: 通常构建透视投影矩阵时,开发者会指定视椎体及近平面和远平面。对于沉浸式 XR 设备,视椎体由光学、显示和摄像头等因素共同决定。但近平面和远平面可由应用调整,因为合适的取值取决于渲染的内容类型。
passthroughFullyObscured
属性是作者给 XRSystem
的提示,表示其打算用虚拟内容完全覆盖视口。当视口不再被不透明像素覆盖时,作者应将该标志重新设为 false
。
注意: XRSystem
可以用此作为临时禁用透视通道的提示。对于透视光学设备,用户仍能看到环境,此标志无效。
inlineVerticalFieldOfView
属性定义了以弧度为单位的默认垂直视场角,用于计算 "inline"
XRSession
的投影矩阵。矩阵计算还会考虑output canvas 的宽高比。对于沉浸式会话,该值必须为 null
。
baseLayer
属性定义了 XRWebGLLayer
,XR 合成器将从中获取图像。
4.3. 动画帧
XRSession
提供 XR 设备
跟踪状态信息的主要方式,是通过在 XRSession
实例上调用 requestAnimationFrame()
安排回调。
callback =
XRFrameRequestCallback undefined (DOMHighResTimeStamp ,
time XRFrame );
frame
每个 XRFrameRequestCallback
对象都有一个 cancelled 布尔值,初始为
false
。
每个 XRSession
有
一个 动画帧回调列表,初始为空,
一个 当前运行动画帧回调列表,也初始为空,
以及一个 动画帧回调标识符,是一个初始为零的数字。
requestAnimationFrame(callback)
方法会将 callback 加入队列,以便下次用户代理要为设备运行动画帧时调用。
调用此方法时,用户代理必须执行以下步骤:
cancelAnimationFrame(handle)
方法用于根据 动画帧回调标识符 handle 取消已存在的动画帧回调。
调用此方法时,用户代理必须执行以下步骤:
-
令 session 为 this。
-
在 session 的 动画帧回调列表 或 session 的 当前运行动画帧回调列表 中查找与 handle 关联的条目。
renderState
state 检查图层状态,用户代理必须执行以下步骤:
-
如果 state 的
baseLayer
为null
,返回false
。 -
返回
true
。
注意: WebXR 图层模块将为该算法引入新语义。
XRSession
session,用户代理必须执行以下步骤:
-
如果用 session 的
renderState
检查图层状态 返回false
,则返回false
。 -
如果 session 的 mode 是
"inline"
且 session 的renderState
的 output canvas 为null
,则返回false
。 -
返回
true
。
当 XRSession
session 从 XR 设备 接收到时间戳 frameTime 的 观察者状态更新时,会运行一次 XR 动画帧,无论 动画帧回调列表 是否为空,都必须执行以下步骤:
-
排队一个任务,执行以下步骤:
-
令 now 为当前高精度时间。
-
令 frame 为 session 的 动画帧。
-
将 frame 的 time 设为 frameTime。
-
将 frame 的
predictedDisplayTime
设为 frameTime。 -
如果 session 的 mode 不是
"inline"
,将 frame 的predictedDisplayTime
设为 XR 合成器 预计显示该 XR 动画帧 的平均时间戳。 -
对 视图列表 中每个 view,将其 viewport modifiable 标志设为 true。
-
如果该帧对 session 应被渲染:
-
将 session 的 当前运行动画帧回调列表 设为 session 的 动画帧回调列表。
-
将 session 的 动画帧回调列表 设为一个空列表。
-
将 frame 的 active 设为
true
。 -
应用帧更新于 frame。
-
按顺序对 session 的 当前运行动画帧回调列表 中的每个 entry:
-
如果 entry 的 cancelled 为
true
,则继续下一个 entry。 -
调用 entry,参数为 « now, frame » 和 "
report
"。 -
将 session 的 当前运行动画帧回调列表 设为空 列表。
-
将 frame 的 active 设为
false
。
-
-
如果 session 的 pending render state 不为
null
,则应用待处理渲染状态。
-
Window
接口的 requestAnimationFrame()
方法不会因有活动的 XRSession
而改变,且在任何 XRSession
上调用
requestAnimationFrame()
也不会与 Window
的 requestAnimationFrame()
产生任何交互。活动沉浸式会话 可以影响 渲染时机,如果它导致页面被遮挡。如果在 活动沉浸式会话 期间 2D
浏览器视图可见(即会话运行在有线头显上),则 Window
的 requestAnimationFrame()
和 requestIdleCallback()
的回调时机可能不会与会话的 requestAnimationFrame()
同步,不应用于渲染 XR 内容。
注意:如果在 XRSession
的
requestAnimationFrame()
回调中调用了 Window
的 requestAnimationFrame()
,用户代理可能会在开发者控制台显示警告,因为如果
活动沉浸式会话
影响 渲染时机,这些回调可能不会被调用,即使调用了时机也可能不准确。
Window
requestAnimationFrame()
的回调,在会话活动期间可能不会被处理。这取决于所用设备类型,最有可能出现在移动或一体机设备上,此时沉浸内容会完全遮挡 HTML 文档。因此,开发者不能依赖 Window
requestAnimationFrame()
回调来调度 XRSession
requestAnimationFrame()
回调,反之亦然,即使它们共享渲染逻辑。未遵循该建议的应用在所有平台上可能无法正常运行。以下演示了希望在两种动画循环间切换的应用更有效的模式:
let xrSession= null ; function onWindowAnimationFrame( time) { window. requestAnimationFrame( onWindowAnimationFrame); // 这可能在某些设备(如有线头显桌面)运行沉浸式会话时被调用。 // 为防止两个循环并行渲染,会话未开始时才渲染。 if ( ! xrSession) { renderFrame( time, null ); } } // 页面加载即可启动 window 动画循环。 window. requestAnimationFrame( onWindowAnimationFrame); function onXRAnimationFrame( time, xrFrame) { xrSession. requestAnimationFrame( onXRAnimationFrame); renderFrame( xrFrame. predictedDisplayTime, xrFrame); } function renderFrame( time, xrFrame) { // 共享渲染逻辑。 } // 假定由用户手势事件调用。 async function startXRSession() { xrSession= await navigator. xr. requestSession( 'immersive-vr' ); xrSession. addEventListener( 'end' , onXRSessionEnded); // 这里做必要的会话设置。 // 启动会话动画循环。 xrSession. requestAnimationFrame( onXRAnimationFrame); } function onXRSessionEnded() { xrSession= null ; }
使用 "inline"
会话渲染到 HTML 文档的应用无需特别协调动画循环,因为用户代理会在 沉浸式会话活动时自动挂起所有 "inline"
会话的动画循环。
4.4. XR 合成器
用户代理必须维护一个 XR 合成器,用于处理 XR 设备的输出展示和帧时序。合成器必须使用独立渲染上下文,其状态与文档创建的任何图形上下文隔离。合成器必须防止页面破坏合成器状态或读取其他页面/应用的内容。合成器还必须运行在独立线程或进程中,使页面性能与向用户适时展示新图像解耦。合成器可以在渲染内容上叠加设备或用户代理 UI,比如设备菜单。
注意:该规范的未来扩展可能允许合成器复合来自同一页面的多个图层。
5. 帧循环
5.1. XRFrame
XRFrame
表示某个 XRSession
的所有被跟踪对象状态的快照。
应用可以通过在 XRSession
上调用
requestAnimationFrame()
并传入 XRFrameRequestCallback
获得 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
属性必须返回该 XRFrame
预计显示在设备上的平均时间点对应的 DOMHighResTimeStamp
。对于
"inline"
XRSession
,predictedDisplayTime
必须返回传递给 XRFrameRequestCallback
的时间戳值。
predictedDisplayTime
用于让渲染的 XR 场景状态能反映帧实际呈现时的状态,而不是 requestAnimationFrame()
被安排或执行的时刻。
predictedDisplayTime
并非用于推断应用有多少渲染时间,因为 XR 合成器
通常帧提交后还要做额外处理。如果体验假设可以一直处理到 predictedDisplayTime
,则
XR 合成器
无法利用已提交帧,应用也无法达到目标帧率。
每个 XRFrame
表示某一
时间点所有被跟踪对象的状态,并存储或能够查询该时间点的具体信息。
getViewerPose(referenceSpace)
方法返回 观察者 相对于 referenceSpace
的姿态(XRViewerPose
),其状态为
XRFrame
的 time。
调用该方法时,用户代理必须执行以下步骤:
-
令 frame 为 this。
-
令 session 为 frame 的
session
对象。 -
如果 frame 的 animationFrame 为
false
,抛出InvalidStateError
并中止这些步骤。 -
令 pose 为 session 的 新
XRViewerPose
对象。 -
填充 session 的 观察者参考空间 在 referenceSpace 下于 frame 表示的时间的姿态到 pose,
force emulation
设为true
。 -
如果 pose 为
null
,返回null
。 -
令 xrviews 为一个空的 列表。
-
令 offset 为
0
。 -
对于
session
上 视图列表中的每个激活的视图 view,执行以下步骤:-
初始化 xrview 的底层视图为 view。
-
初始化 xrview 的
index
为 offset。 -
初始化 xrview 的帧为 frame。
-
初始化 xrview 的会话为 session。
-
初始化 xrview 的参考空间为 referenceSpace。
-
令 viewtransform 为 session 的相关领域中新建、等于 view 的视图偏移的
XRRigidTransform
对象。 -
将 xrview 的
transform
属性设置为 将XRViewerPose
的transform
与 viewtransform 变换在 session 的相关领域中相乘的结果。 -
将 xrview 添加到 xrviews。
-
令 offset 增加
1
。
-
将 pose 的
views
设为 xrviews -
返回 pose。
getPose(space, baseSpace)
方法返回
space 相对于 baseSpace 的姿态(XRPose
),其状态为
XRFrame
表示的时间。
调用该方法时,用户代理必须执行以下步骤:
帧更新 是一个可以给定 XRFrame
执行的算法,意在每个
XRFrame
被执行时运行。
每个 XRSession
都有一个 帧更新列表,是一个 列表,元素为帧更新,初始为空列表。
要为 XRFrame
frame 应用帧更新,用户代理必须执行以下步骤:
注意:本规范未定义任何帧更新,但其他规范可能会添加。
6. 空间(Spaces)
WebXR Device API 的核心特性之一是能够提供空间跟踪。空间接口使应用能够推理被跟踪实体彼此之间及与用户物理环境的空间关系。
6.1. XRSpace
XRSpace
表示一个虚拟坐标系,其原点对应于一个物理位置。从 API 请求或提供的空间数据始终是相对于特定 XRSpace
及特定 XRFrame
的时刻来表达的。诸如姿态坐标等数值都是该空间内相对于原点的坐标。该接口设计为不可透明访问。
[SecureContext ,Exposed =Window ]interface :
XRSpace EventTarget { };
每个 XRSpace
有一个
session,其值为创建该 XRSpace
的 XRSession
。
每个 XRSpace
有一个
原生原点,即空间中的位置和朝向。该 XRSpace
的 原生原点
可能会被 XR
设备的底层跟踪系统更新,不同的 XRSpace
可以定义其 原生原点如何被跟踪和更新的不同语义。
每个 XRSpace
有
一个 有效原点,其作为 XRSpace
的 坐标系的基础。
从有效空间到原生原点空间的变换由 原点偏移定义,
即一个初始值为单位变换的 XRRigidTransform
。换句话说,
有效原点
可通过原点偏移与原生原点相乘获得。
有效原点
只能作为另一个 XRSpace
坐标系中的
XRPose
被观察到,可通过 XRFrame
的
getPose()
方法获得。不同 XRSpace
之间的空间关系可以在不同 XRFrame
之间发生变化。
要填充
姿态(pose),即在 XRSpace
space 相对于 XRSpace
baseSpace,在由 XRFrame
frame 表示的时刻,将结果填入 XRPose
pose,可选地带 force emulation 标志,用户代理必须执行以下步骤:
-
如果 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 相对于 baseSpace 在 frame 的 time 的姿态,然后按如下分支:
- 如果 limit 为
false
且跟踪系统为 space 相对于 baseSpace 提供了正在主动跟踪或静态已知的 6DoF 姿态: -
将 transform 的
orientation
设为 space 的 有效原点 在 baseSpace 的 坐标系 中的朝向。将 transform 的
position
设为 space 的 有效原点 在 baseSpace 的 坐标系 中的位置。如果支持,将 pose 的
linearVelocity
设为 space 的 有效原点 相对 baseSpace 坐标系 的线速度。如果支持,将 pose 的
angularVelocity
设为 space 的 有效原点 相对 baseSpace 坐标系 的角速度。将 pose 的
emulatedPosition
设为false
。 - 否则如果 limit 为
false
且跟踪系统为 space 相对于 baseSpace 提供了 3DoF 姿态或者 6DoF 但位置既未主动跟踪也非静态已知: -
将 transform 的
orientation
设为 space 的 有效原点 在 baseSpace 的 坐标系 中的朝向。将 transform 的
position
设为跟踪系统对 space 的 有效原点 在 baseSpace 坐标系 中位置的最佳估计。该估计可以包含如脖子或手臂模型等计算偏移。如无可用位置估计,必须使用上一个已知位置。将 pose 的
linearVelocity
设为null
。将 pose 的
angularVelocity
设为null
。将 pose 的
emulatedPosition
设为true
。 - 否则如果 space 相对于 baseSpace 的姿态曾被确定且 force emulation 为
true
: -
将 transform 的
position
设为 space 的 有效原点 在 baseSpace 坐标系 中的上一次已知位置。将 transform 的
orientation
设为 space 的 有效原点 在 baseSpace 坐标系 中的上一次已知朝向。将 pose 的
linearVelocity
设为null
。将 pose 的
angularVelocity
设为null
。将 pose 的
emulatedPosition
设为true
。 - 否则:
-
将 pose 设为
null
。
- 如果 limit 为
注意:XRPose
的 emulatedPosition
布尔值并不表示 baseSpace 的位置是否为模拟,只表示在计算 space 相对于 baseSpace 的位置时是否依赖于模拟。例如,具有
3DoF 跟踪的控制器,当其 targetRaySpace
或 gripSpace
相对于 XRReferenceSpace
查询时,会报告 emulatedPosition
为 true
;但如果 targetRaySpace
的姿态在 gripSpace
下查询,则会报告 emulatedPosition
为 false
,因为这两个空间之间的关系应当是精确已知的。
6.2. XRReferenceSpace
XRReferenceSpace
是应用可用于与用户物理环境建立空间关系的几种常见 XRSpace
之一。
XRReferenceSpace
通常在整个 XRSession
期间保持静态,最常见的例外是用户在会话中途重新配置。每个 XRReferenceSpace
的 原生原点
描述了一个坐标系,其中 +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
枚举值受支持,则会创建一个 XRReferenceSpace
实例(或其扩展接口)。type 指示参考空间的跟踪行为:
-
传入类型
viewer
时会创建XRReferenceSpace
实例。它表示一个跟踪空间,其 原生原点 跟踪 观察者 的位置和朝向。每个XRSession
必须支持"viewer"
XRReferenceSpace
。 -
传入类型
local
时会创建XRReferenceSpace
实例。它表示一个跟踪空间,其 原生原点 在创建时靠近观察者。具体位置和朝向会根据底层平台规范初始化。使用该参考空间时,用户不应大幅移动(或不移动),跟踪会为此优化。对于具备 6DoF 跟踪的设备,local
参考空间应强调保持原点相对用户环境的稳定。 -
传递类型为
local-floor
时,会创建一个XRReferenceSpace
实例。它表示一个跟踪空间,其原生原点位于地面上,处于用户可以安全站立的位置。Y
轴在地面高度等于0
,X
和Z
的位置和朝向根据底层平台惯例初始化。如果地面高度未知,则必须进行估算,采用某个估算地面高度。如 估算地面高度采用非默认值,必须舍入到足以防止指纹识别的程度。使用该参考空间时,用户一般不会离开初始位置或移动极小,跟踪为此目的进行了优化。对于具备6DoF跟踪的设备,local-floor
参考空间应强调使原点相对用户环境保持稳定。注意:如果
"local-floor"
参考空间的地面高度被调整以防止指纹识别,建议舍入到最接近的 1cm。 -
传入类型
bounded-floor
时会创建XRBoundedReferenceSpace
实例。它表示一个跟踪空间,其 原生原点 位于地面,用户应在已设定边界(boundsGeometry
)内活动。bounded-floor
跟踪优化为保持原点和boundsGeometry
相对环境的稳定。 -
传入类型
unbounded
时会创建XRReferenceSpace
实例。它表示用户可在环境中自由移动、甚至远离起点的跟踪空间。unbounded
跟踪优化为保持用户当前位置附近的稳定,因此 原生原点 可能随时间漂移。
注意:假设底层平台对参考空间 Y 轴的规范在不同 XRReferenceSpace
类型间保持一致。也就是说,若 XR 系统支持多种参考空间,则它们的 Y 轴应在该 XRSession
生命周期内保持平行且同向。但 "viewer"
不依赖底层平台朝向规范。"unbounded"
参考空间应在原点接近其他参考空间时对齐 Y 轴,用户远距离移动时可偏离。
支持 "local"
参考空间的设备必须支持 "local-floor"
参考空间(必要时可模拟),反之亦然。
onreset
属性是 事件处理 IDL 属性,用于 reset
事件类型。
当为 XRSession
session 按 XRReferenceSpaceType
type 请求 XRReferenceSpace
时,用户代理必须 创建参考空间,步骤如下:
-
初始化 referenceSpace:
- 如 type 为
bounded-floor
: -
令 referenceSpace 为 session 相关 realm 中的新
XRBoundedReferenceSpace
。 - 否则:
-
令 referenceSpace 为 session 相关 realm 中的新
XRReferenceSpace
。
- 如 type 为
-
将 referenceSpace 的 type 设为 type。
-
将 referenceSpace 的 session 设为 session。
-
返回 referenceSpace。
XRSession
session 是否支持参考空间,步骤如下:
-
如 type 不在 session 的 已授予功能集 中,返回
false
。 -
如 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)
方法被调用时必须按以下步骤执行:
-
令 base 为该方法被调用的
XRReferenceSpace
。 -
初始化 offsetSpace:
- 如 base 是
XRBoundedReferenceSpace
的实例: -
令 offsetSpace 为 base 相关 realm 中的新
XRBoundedReferenceSpace
,并将 offsetSpace 的boundsGeometry
设为 base 的boundsGeometry
,每个点都乘以 originOffset 的inverse
。 - 否则:
-
令 offsetSpace 为 base 相关 realm 中的新
XRReferenceSpace
。
- 如 base 是
-
将 offsetSpace 的 origin offset 设为 base 的 origin offset 与 originOffset 在 base 相关 realm 下 相乘的结果。
-
返回 offsetSpace。
注意:预期部分应用会用 getOffsetReferenceSpace()
实现基于鼠标、键盘、触摸或手柄输入的场景导航控制。这会导致 getOffsetReferenceSpace()
在活跃输入期间每帧调用一次。因此强烈建议 UA 让通过 getOffsetReferenceSpace()
创建新 XRReferenceSpace
的操作保持轻量。
6.3. XRBoundedReferenceSpace
XRBoundedReferenceSpace
扩展了 XRReferenceSpace
,包含
boundsGeometry
,用于指示用户空间的预设边界。
[SecureContext ,Exposed =Window ]interface :
XRBoundedReferenceSpace XRReferenceSpace {readonly attribute FrozenArray <DOMPointReadOnly >boundsGeometry ; };
XRBoundedReferenceSpace
的原点必须位于地面,Y
轴在地面处等于 0
。X
和 Z
的位置及朝向根据底层平台的规范初始化,通常应靠近房间中心,面向合理的前方。
注意:其他 XR 平台有时将 bounded-floor
参考空间的跟踪类型称为“房间级(room scale)”跟踪。XRBoundedReferenceSpace
并不适用于多房间空间、不平地面或非常大的开放区域。需要处理这些场景的内容应使用 unbounded
参考空间。
每个 XRBoundedReferenceSpace
有一个 原生边界几何,描述 XRBoundedReferenceSpace
的边界,用户可在此区域内安全移动。该多边形边界用 DOMPointReadOnly
数组表示,数组中的点构成安全空间边缘的环路。每个点表示相对于 原生原点 的偏移量(单位:米)。点必须以从上往下看时顺时针顺序排列,朝向 Y 轴负方向。每个点的 y
值必须为 0
,w
必须为 1
。边界可视为从地面起无限高,该形状可以是凸的也可以是凹的。
原生边界几何中的每个点必须限制在参考空间原生原点的合理距离内。
注意:建议 原生边界几何中的点限制在原点各方向 15 米以内。
原生边界几何中的每个点还必须量化到足以防止指纹识别。为保证用户安全,量化后的点值不得超出平台报告的边界。
boundsGeometry
属性是一个
DOMPointReadOnly
数组,
其中每个条目等于 XRBoundedReferenceSpace
的
原生边界几何 经过
inverse
原点偏移的预乘处理后的结果。换句话说,它在 XRBoundedReferenceSpace
坐标系下,
相对于 有效原点 提供了相同的边界。
如果 原生边界几何临时不可用(如 XR
设备初始化中、长时间丢失跟踪、或在多个预设空间间移动时),boundsGeometry
必须返回空数组。
注意:如果边界或地面高度在请求参考空间时尚未解析,但 XR 设备支持它们,仍可返回有界参考空间。
注意:内容不应要求用户移动到 boundsGeometry
之外。若用户物理空间允许,也可能移动到边界之外,导致位置值超出多边形边界。这不是错误,应由内容优雅处理。
注意:内容通常不应自行可视化 boundsGeometry
,因为向用户提供安全关键信息是用户代理的责任。
7. 视图(Views)
7.1. XRViewGeometry
包括 XRViewGeometry
接口混入体在内的对象,表示用于 XR 设备向用户呈现图像的显示,或用于收集现实世界视觉信息的传感器。这些对象包含一个视图几何。
视图几何对应于用于在观察者参考空间的坐标系与承载对象的屏幕空间之间转换的内外参集合。
视图几何有一个 承载对象,即该视图几何所包含数据的物理硬件。
视图几何的 承载对象有一个关联的 屏幕空间,即该承载对象读取数据或渲染数据的二维平面。
视图几何有一个关联的 视图偏移,即 XRRigidTransform
,描述承载对象在观察者参考空间的坐标系中的位置和朝向。
注意:视图偏移没有约束,视图允许有不同朝向。这可出现在眼部显示屏居中有角度的头显设备中,也可能出现在更极端的 CAVE 渲染等场景。由于这个原因,像 z 排序和剔除等技术可能需要针对每只眼做。
视图几何有一个关联的 投影矩阵,是一个矩阵,描述渲染到承载对象时所用的投影。投影矩阵可以包含如剪切等变换,可能无法用简单视锥体精确描述。
注意:该矩阵的逆可用于“读取”屏幕空间像素并将其转换回以承载对象为原点的坐标系中。
[SecureContext ,Exposed =Window ]interface mixin {
XRViewGeometry readonly attribute Float32Array projectionMatrix ; [SameObject ]readonly attribute XRRigidTransform transform ; };
每个 XRViewGeometry
有一个关联的 内部投影矩阵,存储其 投影矩阵。初始为
null
。
注意:transform
可用于许多渲染库中定位相机对象。如应用需要传统视图矩阵,可通过 transform.inverse.matrix
获得。
projectionMatrix
属性是底层 视图几何的投影矩阵。强烈建议应用直接使用该矩阵,勿修改或分解。渲染时未使用此投影矩阵可能导致画面畸变或错位,给用户带来不适。该属性必须通过获取投影矩阵算法计算。
transform
属性是该对象的 XRRigidTransform
,表示对象在用于获取该对象的
XRReferenceSpace
中的位置和朝向。
要为给定 XRViewGeometry
view geometry 获取投影矩阵:
7.2. XRView
视图对应于 XR
设备用于向用户呈现图像的显示或显示区域。它们用于获取与视图物理输出属性(包括视场、眼睛偏移和其他光学属性)高度对齐地渲染内容所需的全部信息。视图可以覆盖用户视野的重叠区域。不保证任何 XR 设备使用的视图数量或顺序有固定要求,也不要求在 XRSession
生命周期内 视图数量保持不变。
视图有一个关联的 eye,即 XREye
,描述该视图应显示给哪只眼。如果视图没有固有的眼睛关联(如显示为单目),该值必须为
"none"
。
视图有一个 active 标志,在 XRSession
生命周期内可能会变化。主视图必须始终将 active 标志设为 true
。
注意:许多头戴显示器(HMD)会请求渲染两个视图,分别对应左右眼,而大多数魔窗设备只请求一个视图,但应用不应假设具体的视图配置。例如:魔窗设备如支持立体输出可能会请求两个视图,若关闭该模式出于性能原因可能又只请求一个。类似地,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
属性描述底层视图的眼睛。该属性的主要作用是保证预渲染的立体内容能呈现正确的内容给正确的眼睛。
index
属性描述了当该 XRView
通过 getViewerPose()
返回的 views
数组中时的偏移量。
可选的 recommendedViewportScale
属性包含 UA
推荐的视口缩放值,应用可用其配置动态视口缩放。若系统未实现推荐缩放的启发式或方法,则为 null
。不为 null 时,该值必须大于 0.0 且小于等于 1.0 的数值,并且必须量化以避免泄漏详细性能或 GPU 利用率数据。
注意:建议通过从一组有限的可能缩放值中四舍五入推荐缩放值,并在接近边界时使用滞后,避免缩放值快速变化导致视觉不适。
每个 XRView
有一个 session,即产生该视图的 XRSession
。
每个 XRView
有一个 frame,即产生该视图的 XRFrame
。
每个 XRView
有一个 underlying view,即其所代表的底层视图。
requestViewportScale(scale)
方法请求用户代理将该视口的requested viewport scale 设为指定值。
当在 XRView
xrview 上调用该方法时,用户代理必须执行以下步骤:
-
如果 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 获取缩放视口,请执行以下步骤:
-
令 scale 为 view 的 当前视口缩放。
-
用户代理可以选择对 scale 进行限制,以施加最小视口缩放因子。
-
令 glViewport 为新的 WebGL 视口。
-
将 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 为 新
XRViewport
,位于 session 的相关 realm。 -
将 viewport 的
x
初始化为 glViewport 的x
分量。 -
将 viewport 的
y
初始化为 glViewport 的y
分量。 -
将 viewport 的
width
初始化为 glViewport 的width
。 -
将 viewport 的
height
初始化为 glViewport 的height
。 -
返回 viewport。
注意:具体整数值的计算特意留给 UA 自行决定。直接向下取整 width/height 并保留
x
、y
偏移的方法是有效的,但 UA 也可以在约束范围内选取稍作调整的值,例如为了效率将视口对齐到 2
的幂像素网格。缩放视口必须完全包含在全尺寸视口内,但可由 UA 自行决定放置在全尺寸视口内的任何位置。大小和位置计算必须具确定性,并在同一会话内对于相同输入返回一致结果。
7.3. 主视图和次视图
当渲染该视图对沉浸式体验来说是必要的时,该视图为主视图。主视图在整个 XRSession
生命周期内必须为active。
当内容可以选择不渲染该视图,但依然能提供可用的沉浸式体验时,则该视图为次视图。当内容选择不渲染这些视图时,用户代理可以通过重投影重建这些视图。除非启用了 "secondary-views" 功能,否则次视图不得active。
views
数组做出不正确假设,导致面对多于两个视图时出现问题。
由于用户代理可以通过重投影等机制渲染这些次视图, 有必要区分那些准备自行处理这些次视图的内容, 与那些要么意识不到存在次视图、要么不希望处理次视图的内容。
为此,暴露次视图的用户代理必须支持 "secondary-views" 功能描述符作为提示。启用此功能的内容应:
启用 "secondary-views" 时,用户代理可在需要时,将设备支持的任意次视图暴露给 XRSession
。
此时用户代理不得用重投影重建次视图,应依赖内容实际渲染结果。
注意:建议内容通过 optionalFeatures
启用 "secondary-views",以确保最大兼容性。
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
属性指定视口矩形左下角(像素),视口从该点向右width
像素,向上height
像素。该值可直接传给 WebGL viewport 方法。
XRViewerPose
的所有 XRView
,为每个视图从
XRWebGLLayer
查询 XRViewport
,并用其设置
WebGL 渲染用的WebGL 视口。
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 必须执行:
-
令 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)
构造函数调用时必须执行以下步骤:
-
令 transform 为 新
XRRigidTransform
,位于当前 realm。 -
令 transform 的
position
为新DOMPointReadOnly
,位于当前 realm。 -
如 position 或 orientation 中任一值为
NaN
或其他非有限数(如infinity
),抛出TypeError
并中止。 -
令 transform 的
orientation
为新DOMPointReadOnly
,位于当前 realm。 -
设置 transform 的
orientation
的x
、y
、z
、w
分别为 orientation 对应的字典成员。 -
令 transform 的 内部矩阵为
null
。 -
返回 transform。
position
属性是一个三维点(米),描述变换的平移分量。该 position
的 w
属性必须为 1.0
。
orientation
属性是描述旋转分量的四元数。orientation
必须归一化到长度 1.0
。
matrix
属性将 position
和 orientation
描述的变换作为矩阵返回。该属性必须通过获取矩阵算法计算。
注意:此矩阵左乘列向量时,会先用 orientation
对向量做三维旋转,再用 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
,其值为 translation * rotation 的结果(左乘),即translation * rotation
。数学上,矩阵为: -
返回 transform 的 内部矩阵。
inverse
属性返回 XRRigidTransform
在 transform 相关 realm 下的一个实例,若应用于被 transform 变换过的对象,则可将其还原到初始姿态。该属性应懒计算。由 inverse
返回的 XRRigidTransform
必须以 transform 作为其 inverse
。
若 XRRigidTransform
的 position
为 { x: 0, y: 0, z: 0, w: 1 }
,orientation
为 { x: 0, y: 0, z: 0, w: 1 }
,则称为单位变换。
要相乘两个 XRRigidTransform
B 和 A,位于 Realm realm,UA 必须执行:
-
令 result 为 新
XRRigidTransform
,位于 realm。 -
将 result 的
matrix
设为 realm中新建Float32Array
,值为 B 的matrix
左乘 A 的matrix
。 -
将 result 的
orientation
设为 realm中新建DOMPointReadOnly
,表示 resultmatrix
左上 3x3 子矩阵对应的旋转四元数。 -
将 result 的
position
设为 realm中新建DOMPointReadOnly
,取自 resultmatrix
的第四列向量。 -
返回 result。
result 表示从 A 源空间到 B 目标空间的变换。
注意:这等价于构造一个 XRRigidTransform
,其
orientation
为 A 与 B 的旋转的合成,position
为 A 的 position 经 B 的 orientation 旋转后,加上 B 的 position。
9. 姿态(Pose)
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
。
emulatedPosition
属性当 transform
表示基于传感器读数主动跟踪的 6DoF 姿态时为 false
;若其
position
包含如脖子或手臂模型等计算偏移时为 true
。估算地面高度在判断 XRPose
是否包含 计算偏移时不得考虑。
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
场景的各个视点,这些视点相对于查询 XRReferenceSpace
时使用的 XRViewerPose
。数组中的每个
XR 场景 视图 都必须被渲染,才能在 XR 设备 上正确显示。每个 XRView
包含用于指示视点和投影矩阵的刚性变换,并可用于在需要时从图层查询 XRViewport
。
注意: XRViewerPose
的 transform
可用于为场景旁观者视角或多用户交互定位观察者的图形表示。
10. 输入
10.1. XRInputSource
XRInputSource
表示一个XR 输入源,即任何允许用户在与观察者处于同一虚拟空间内执行定向操作的输入机制。XR
输入源的示例包括但不限于手持控制器、光学跟踪的手、以及基于凝视的输入方法(这些方法依赖观察者的姿态)。未明确与XR 设备关联的输入机制(如传统手柄、鼠标、键盘)不应视为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 Gamepads Module 所扩展
handedness
属性描述XR
输入源(如果有的话)关联的是哪只手。没有自然手型(如头戴设备上的控件)或当前无法确定手型的输入源,该属性必须设为 "none"
。
targetRayMode
属性描述用于产生目标射线的方法,并指示应用如何(如有需要)向用户展示该射线。
-
gaze
表示目标射线以观察者为起点,沿其面朝的方向发出。(在头戴显示器环境中常称为“凝视输入”设备。) -
tracked-pointer
表示目标射线来自手持设备或其它手部跟踪机制,代表用户用手或持握设备进行指向。目标射线相对被跟踪物体的朝向必须遵循平台的人体工学规范(如有)。如无平台规范,目标射线应指向用户食指自然伸直时的方向。如果XRSystem
判断手持设备某部位(如笔尖)用于接触真实表面,则目标射线必须以该点为起点。 -
screen
表示输入源来自与内联会话输出上下文关联的 canvas 元素的交互,例如鼠标点击或触摸事件。 -
transient-pointer
表示输入源由操作系统交互意图生成,而非具体硬件。一些例子包括基于敏感信息(如凝视)的用户意图、WebDriver 合成输入或辅助技术生成的输入。仅当辅助技术也作为主输入使用时才应用此模式,以避免无意中透露辅助技术的使用(符合 W3C 设计原则)。
targetRaySpace
属性是一个 XRSpace
,其原生原点用于跟踪
XRInputSource
的首选指向射线的空间位置和朝向(沿其 -Z
轴),定义见 targetRayMode
。
对于 targetRayMode
为 "transient-pointer"
的输入源,targetRaySpace
表示交互开始时指向目标的射线。其姿态应在该 XRInput 的 gripSpace 内保持静态。
gripSpace
属性是一个 XRSpace
,其原生原点用于跟踪用于渲染虚拟物体以看似被用户手持的姿态。如果用户手持一根直棒,该 XRSpace
会将原点放在其弯曲手指的中心,-Z
轴沿棒指向拇指。X
轴垂直于手背,右手背朝 +X
,左手背朝
-X
,Y
轴由 XZ 关系推断,+Y
大致沿手臂方向。
skipRendering
属性表示该输入源是可见的,且当前会话可能无需渲染它。如果 skipRendering
为 true 且 targetRayMode 为 "tracked-pointer",用户代理必须确保始终向用户展示 XR 输入源 的表示。
控制器被展示给用户的情形包括:控制器位于显示器与用户之间、显示器透明、或控制器由操作系统渲染。
skipRendering
是给开发者的提示,表示无需渲染输入源(如控制器)。但应继续渲染拾取射线和光标。
对于 targetRayMode
为 "transient-pointer"
的输入源,gripSpace
应为相关用户手势空间,如无则应为用户可控制的其它空间(如 ViewerSpace、其他 XRInput 的 gripSpace 或 targetRaySpace)。这样用户仍可操控 targetRaySpace。
gripSpace
必须为 null
,如果输入源本身不可被跟踪,例如 targetRayMode
为 "gaze"
或 "screen"
。
profiles
属性是一个列表,包含 输入配置文件名,指示输入源的优选视觉表现和行为。
输入配置文件名 是不含空格的 ASCII 小写 DOMString
,各单词用连字符(-
)连接。应优先用设备厂商推荐的描述性名称。如平台有适宜标识(如
USB 厂商与产品 ID)可使用,但不能用唯一标识单一设备的值(如序列号)。输入配置文件名不得包含手型信息。多个 UA 暴露同一设备时应尽量报告相同 输入配置文件名。WebXR Input
Profiles Registry 是推荐管理输入配置文件名的位置。
profiles 按特异性降序排列。列表中第一个之后的 输入配置文件名 应为设备的替代表现,如更通用/更早版本、足够类似的广为人知设备,或类型描述(如 "generic-trigger-touchpad")。如有多个 profile,布局必须互为超集/子集。
如 XRSession
的 mode 为 "inline"
,profiles
必须为空列表。
UA 可选择仅报告适当通用的 输入配置文件名 或空列表。例如无法可靠识别输入设备、无匹配 profile、或 UA 希望隐藏输入设备时可用此方式。
例如,三星 HMD Odyssey 控制器是标准 Windows Mixed Reality 控制器的变种,且两者输入布局相同。因此 Odyssey 控制器的
profiles
可以为:
["samsung-odyssey", "microsoft-mixed-reality", "generic-trigger-squeeze-touchpad-thumbstick"]
。
第一个 profile 最精确地描述外观,第二个为可接受的替代,最后一个为大致类型(带 trigger、squeeze、触摸板和摇杆的控制器)。
类似地,Valve Index 控制器兼容 HTC Vive
控制器,但 Index 多了按钮和轴,因此其 profiles
可为:
["valve-index", "htc-vive", "generic-trigger-squeeze-touchpad-thumbstick"]
。此时 "valve-index" 布局为
"htc-vive" 的超集,且 appearance 更精确。最后一项仍为通用 fallback。
(字符串仅为示例,实际 profile 由 WebXR Input
Profiles Registry 管理。)
注意: XRInputSource
在 XRSession
的 inputSources
数组中是“活的”,其值会原地更新。因此不能通过保存某帧属性引用并与后续帧对比来判断状态变化,开发者应复制属性内容进行帧间比较。
XR 输入源如果支持 主输入源,则它支持一个主操作。主操作是平台相关的动作,触发时会产生 selectstart
、selectend
及 select
事件。主操作可能是按下扳机、触摸板、按钮、语音指令、手势等。如平台规范定义了推荐主输入,应采用,否则 UA 可自由选择。设备必须至少支持一个主输入源。
XR 输入源若不支持主操作,则为跟踪输入源。这类输入主要用于提供姿态数据。 注:如用于腿部或道具的跟踪附件即为 跟踪输入源。如未做手势识别,跟踪手也视为 跟踪输入源。
每个 XR 输入源可定义一个主握持操作。主握持操作是平台相关的动作,触发时会产生 squeezestart
、squeezeend
、squeeze
事件。主握持操作通常用于模拟抓握。例如按下握把触发器或做出抓握手势。如平台规范定义了推荐主握持操作应采用,否则 UA 可自行选择。
有时平台特定行为会导致 主操作 或 主握持操作 被中断或取消。例如,XR 输入源在 主操作或主握持操作开始后但尚未结束时被从XR 设备移除。
当 XR 输入源
source 在 XRSession
session 的 主握持操作被取消时,UA 必须执行如下步骤:
-
令 frame 为 新
XRFrame
,属于 session 的 相关 realm,session
为 session,time 为动作发生时间。 -
排队一个任务,触发输入源事件,事件类型为
XRInputSourceEvent
,事件名为squeezeend
,frame 为 frame,source 为 source。
10.2. 瞬时输入
某些XR 设备可能支持瞬时输入源,即XR 输入源仅在执行瞬时操作期间有意义,该操作可以是主操作(针对主输入源),也可以是跟踪输入源的设备专属辅助操作。
一个例子是在"inline"
XRSession
中使用鼠标、触摸或手写笔输入,必须产生一个瞬时XRInputSource
,其targetRayMode
设为screen
,作为主指针的主操作,以及非主指针的辅助操作。
另一个例子是,操作系统发出的意图,其输入源于无法直接暴露的敏感信息(如基于凝视的交互)。这些会生成一个瞬时XRInputSource
,其targetRayMode
设为transient-pointer
,作为主操作处理。
瞬时输入源仅在会话的活跃 XR 输入源列表中保留于瞬时操作期间。
瞬时输入源在处理瞬时操作时,遵循下列流程,而不是非瞬时主操作的算法:
10.3. XRInputSourceArray
XRInputSourceArray
表示一个列表,其中包含若干
XRInputSource
。
它用于内容会随时间变动的列表场景(如 XRSession
的 inputSources
属性),优于frozen array type。
[SecureContext ,Exposed =Window ]interface {
XRInputSourceArray iterable <XRInputSource >;readonly attribute unsigned long length ;getter XRInputSource (unsigned long ); };
index
length
属性表示 XRInputSourceArray
中包含的 XRInputSource
数量。
索引属性 getter 用于按下标获取 XRInputSource
。
11. 图层(Layers)
注意:本规范目前仅定义了 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 = {}); // Attributes
layerInit 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 Methods
view static double getNativeFramebufferScaleFactor (XRSession ); };
session
每个 XRWebGLLayer
都有一个 context 对象,初始为 null
,类型为 WebGLRenderingContext
或 WebGL2RenderingContext
的实例。
每个 XRWebGLLayer
都有一个关联的 session,即创建它时用到的 XRSession
。
XRWebGLLayer(session, context, layerInit)
构造函数调用时必须执行以下步骤:
-
令 layer 为 新
XRWebGLLayer
,属于 session 的 相关 realm。 -
如果 session 的 ended 值为
true
,抛出InvalidStateError
并中止。 -
如果 context 已丢失,抛出
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 是 inline session:
-
初始化 layer 的 composition enabled 为
false
。 - 否则:
-
初始化 layer 的 composition enabled 为
true
。
-
- 如果 layer 的 composition enabled 为
true
: -
-
令 scaleFactor 为 layerInit 的
framebufferScaleFactor
。 -
UA 可根据需要对 scaleFactor 进行限制或取整,例如为了性能把缓冲区尺寸对齐到2的幂。
-
令 framebufferSize 为推荐 WebGL framebuffer 分辨率,其宽高分别乘以 scaleFactor。
-
初始化 layer 的
framebuffer
为 新WebGLFramebuffer
,在 context 的 相关 realm 下,是一个尺寸为 framebufferSize 的不透明 framebuffer,用 context 创建,session 初始化为 session,并用 layerInit 的depth
、stencil
和alpha
参数。 -
分配并初始化与 session 的 XR 设备 兼容的资源,包括 GPU 可访问内存缓冲区,以支持 layer 的合成。
-
若 layer 的资源因任何原因无法创建,抛出
OperationError
并中止。
- 否则:
-
-
初始化 layer 的
antialias
为 layer 的context
的 实际 context 参数antialias
。 -
初始化 layer 的
framebuffer
为null
。
-
- 如果 layer 的 composition enabled 为
-
返回 layer。
注意:如果 XRWebGLLayer
的
composition enabled 布尔值被设为
false
,则 XRWebGLLayerInit
对象上的所有值都会被忽略,因为 WebGLRenderingContext
的默认 framebuffer 已经使用 context 的 实际 context 参数 分配,无法被覆盖。
context
属性是创建 XRWebGLLayer
时所用的 WebGLRenderingContext
。
每个 XRWebGLLayer
有一个 composition enabled 布尔值,初始设为
true
。如设为 false
,表示该 XRWebGLLayer
不得分配自己的 WebGLFramebuffer
,
并且其所有反映 framebuffer
属性的内容都必须反映 context 的默认 framebuffer 的属性。
framebuffer
属性是 XRWebGLLayer
的一个 WebGLFramebuffer
实例,如果 opaque
framebuffer composition enabled 为 true
,否则为
null
。framebuffer
的尺寸在 XRWebGLLayer
创建后不可被开发者调整。
不透明 framebuffer 与标准
WebGLFramebuffer
功能一致,但有如下特性,使其更类似于 默认 framebuffer:
-
不透明 framebuffer 即使在 WebGL 1.0 中也可以支持抗锯齿。
-
不透明 framebuffer 的附件不可被检查或修改。对其调用
framebufferTexture2D
、framebufferRenderbuffer
、deleteFramebuffer
或getFramebufferAttachmentParameter
必须抛出INVALID_OPERATION
错误。 -
不透明 framebuffer 有一个相关的 session,即其为之创建的
XRSession
。 -
不透明 framebuffer 在
requestAnimationFrame()
回调外被认为是不完整的。在其 session 的requestAnimationFrame()
回调外,调用checkFramebufferStatus
必须抛出FRAMEBUFFER_UNSUPPORTED
错误,尝试对 不透明 framebuffer 清除、绘制或读取必须抛出INVALID_FRAMEBUFFER_OPERATION
错误。 -
用
depth
true
初始化的 不透明 framebuffer 会有附加的深度缓冲区。 -
用
stencil
true
初始化的 不透明 framebuffer 会有附加的模板缓冲区。 -
不透明 framebuffer 的颜色缓冲区只有在
alpha
为true
时才包含 alpha 通道。 -
XR Compositor 会假定 不透明 framebuffer 包含预乘 alpha 的颜色。这一行为与
premultipliedAlpha
在context
的 实际 context 参数 设置无关。
注意:用户代理必须遵循 true
的 depth
和 stencil
值,这与 WebGL 创建绘图缓冲区时的行为一致。
附加到 不透明 framebuffer
的缓冲区在首次创建或每次处理 XR
动画帧 前必须清除为下表值。这与 WebGL context 默认 framebuffer
的行为相同。不透明
framebuffer 总会被清除,与关联的 WebGL context 的 preserveDrawingBuffer
设置无关。
缓冲区 | 清除值 |
---|---|
颜色 | (0, 0, 0, 0) |
深度 | 1.0 |
模板 | 0 |
注意:只要能保证开发者无法访问其他进程的缓冲区内容,实现可以优化省略 不透明 framebuffer 所需的隐式清除操作。例如开发者显式清除时,隐式清除就不需要。
如果 XRWebGLLayer
创建时 alpha
为 true
,则 framebuffer
必须由 RGBA
格式纹理支持。
如果 XRWebGLLayer
创建时 alpha
为 false
,则 framebuffer
必须由 RGB
格式纹理支持。
但 XR Compositor
必须将 framebuffer
的像素视为 SRGB8_ALPHA8
或 SRGB8
colorFormat
格式。
注意:这意味着 XR Compositor 在处理 framebuffer
纹理数据时,不能做线性 RGBA
或 RGB
到 gamma 的转换,否则最终像素会过亮,不符合普通 2D WebGLRenderingContext
的渲染效果。
当 XRWebGLLayer
被设为 沉浸式会话 的 baseLayer
时,只要自上一个 XR 动画帧
以来发生过下述任一情况,不透明
framebuffer 的内容会在 XR 动画帧 完成后立即呈现到 沉浸式 XR 设备 上:
-
在 不透明 framebuffer 作为当前绑定 framebuffer 时,关联
WebGLRenderingContext
的XRWebGLLayer
调用过clear
、drawArrays
、drawElements
或其他影响 framebuffer 颜色值的渲染操作。
在 不透明 framebuffer 呈现给 沉浸式 XR 设备 之前,用户代理应确保所有渲染操作都已刷新到 不透明 framebuffer。
每个 XRWebGLLayer
有一个 目标 framebuffer,若 composition
enabled 为 true
,则为 framebuffer
,否则为
context 的默认
framebuffer。
framebufferWidth
和 framebufferHeight
属性分别返回 目标 framebuffer 附件的宽和高。
antialias
属性为 目标 framebuffer
是否支持 UA 选择的抗锯齿技术,true
表示支持,否则为 false
。
ignoreDepthValues
属性如为
true
,表示 XR
Compositor 渲染时不得使用深度缓冲区内容。如为 false
,则表示深度缓冲内容将被 XR Compositor 利用,且应能代表渲染到该图层的场景。
深度缓冲存储的值应在 0.0
到 1.0
之间,其中 0.0
代表 depthNear
距离,1.0
代表 depthFar
距离,中间值线性插值。这也是 WebGL 默认行为。(参见 depthRange 函数
文档。)
注意:让场景的深度缓冲可用于合成器,可让部分平台实现更高质量和舒适度的增强(如改进重投影)。
fixedFoveation
属性控制 XR Compositor 使用的注视点渲染(foveation)量。如 UA
或设备不支持此属性,get 时应返回 null
,set 为 no-op。设为 0
表示最小 foveation,1
表示最大。UA
可自行解释该值。如 fixedFoveation
级别变化,会在下一个 XRFrame
生效。
注意:固定注视点渲染(fixed foveation) 是一种在用户视野边缘以较低分辨率渲染内容的技术。可大幅提升受限于 GPU 填充性能的体验,降低功耗并提升眼部贴图分辨率。对低对比度背景图像等效果显著,对高对比度如文本效果有限。作者可每帧调整其级别以在性能与视觉质量间权衡。
每个 XRWebGLLayer
必须有一个 全尺寸视口列表,为 列表,包含 WebGL viewport
,每个 视图一个,包括当前未 active 但可能变为 active 的 次视图。视口必须 width
和 height
均大于 0,且描述的矩形不得超出 目标 framebuffer 边界,且视口不能重叠。如 composition
enabled 为 false
,该列表只含一个 WebGL
viewport,覆盖 context 的整个默认 framebuffer。
每个 XRWebGLLayer
必须有一个 视口对象列表,为 列表,包含每个当前 active 视图 的 XRViewport
。
getViewport()
用于查询给定 XRView
渲染到该图层时应使用的 XRViewport
。
getViewport(view)
方法在 XRWebGLLayer
layer 上调用时,必须执行以下步骤:
-
令 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
设置为通过 获取缩放视口,从与 view 关联的 全尺寸视口列表及 session 得到的XRViewport
。
-
将 view 的 viewport modifiable 标志设为 false。
-
令 viewport 为 视口对象列表中与 view 关联的
XRViewport
。 -
返回 viewport。
注意:viewport modifiable 标志即使
current viewport scale 没有更改也会被设为 false。这样保证
getViewport(view)
在同一动画帧内多次调用总是返回一致结果,第一次获取的值会被锁定用于该帧后续操作。如果应用在 getViewport()
之后调用 requestViewportScale()
,则请求的值只会在未来帧再次调用
getViewport()
时生效。
每个 XRSession
必须确定一个原生
WebGL 帧缓冲分辨率,即与 XR 设备 物理像素分辨率匹配所需的 WebGL 帧缓冲像素分辨率。
要确定 XRSession
session 的 原生 WebGL 帧缓冲分辨率,需执行以下步骤:
-
如果 session 的 mode 值不是
"inline"
, 则将 原生 WebGL 帧缓冲分辨率 设为,足以容纳所有XRView
的帧缓冲像素,与显示屏下最大放大倍率区域的物理像素一一对应的分辨率,并中止这些步骤。如无方法可得,则可用 推荐 WebGL 帧缓冲分辨率。 -
如果 session 的 mode 值为
"inline"
, 则将 原生 WebGL 帧缓冲分辨率 设为 session 的renderState
的 输出画布的物理显示像素尺寸,在画布尺寸变化或 输出画布 变化时应重新评估本步骤。
此外,XRSession
还必须确定一个推荐 WebGL 帧缓冲分辨率,即能容纳所有 XRView
,并在性能和质量间为一般应用提供较好平衡的
WebGL 帧缓冲像素分辨率。它可以小于、大于或等于原生 WebGL 帧缓冲分辨率。新建 不透明 framebuffer 时,将以该分辨率为基础,再分别按 XRWebGLLayerInit
的 framebufferScaleFactor
缩放宽高。
注意:UA 可以采用任何方式估算 推荐 WebGL
帧缓冲分辨率。如有平台特定的推荐尺寸查询方法建议采用,但不是必须。framebufferScaleFactor
和 getNativeFramebufferScaleFactor()
的缩放因子分别应用于宽和高,因此缩放为2时总像素数变为4倍。如平台暴露的是按像素数的面积缩放,UA 需取平方根以换算为 WebXR 缩放因子。
getNativeFramebufferScaleFactor(session)
方法调用时,必须执行以下步骤:
-
令 session 为 this。
-
如果 session 的 ended 值为
true
,则返回0.0
并中止这些步骤。 -
返回 session 的 推荐 WebGL 帧缓冲分辨率 的宽和高分别乘以的值,以得到 session 的 原生 WebGL 帧缓冲分辨率。
11.3. WebGL 上下文兼容性
为了让 WebGL 上下文可用作沉浸式 XR 图像的源,必须在该兼容图形适配器上创建,适配于沉浸式 XR 设备。什么是兼容图形适配器取决于平台,但通常指该适配器能够无明显延迟地向沉浸式 XR 设备提供图像。如果 WebGL 上下文不是在兼容图形适配器上创建,通常需要在该适配器上重新创建后,才能与XRWebGLLayer
一起使用。
注意:在只有一个 GPU 的 XR 平台上,可以安全地认为该 GPU 与平台公布的沉浸式 XR 设备兼容,因此任何硬件加速的 WebGL 上下文也兼容。在同时有集成显卡和独立显卡的 PC 上,独立显卡通常被认为是兼容图形适配器,因为它一般性能更强。在安装了多个图形适配器的台式机上,物理上连接有沉浸式 XR 设备的那块适配器很可能被视为兼容图形适配器。
注意:"inline"
会话使用与 canvas 一样的图形适配器渲染,因此不需要 xrCompatible
上下文。
partial dictionary WebGLContextAttributes {boolean =
xrCompatible false ; };partial interface mixin WebGLRenderingContextBase { [NewObject ]Promise <undefined >makeXRCompatible (); };
当用户代理实现本规范时,必须在每个 WebGLRenderingContextBase
上设置一个布尔值 XR
compatible,初始为 false
。一旦 XR compatible 布尔值设为 true
,该上下文即可用于来自当前沉浸式 XR 设备的任何
XRSession
的图层。
注意:此标志会引起同步阻塞行为,不推荐使用。建议改用 makeXRCompatible()
实现异步。
XR compatible
布尔值既可以在上下文创建时设置,也可以在创建后设置(可能导致上下文丢失)。如需在创建时设置 XR compatible,则在请求 WebGL 上下文时,xrCompatible
context 创建属性必须设为 true
。如果请求文档的origin 不被允许使用 "xr-spatial-tracking" 权限策略,则 xrCompatible
无效。
xrCompatible
标志在 WebGLContextAttributes
上如为 true
,则请求用户代理用 兼容图形适配器 创建 WebGL 上下文,适配于 沉浸式 XR 设备。如果用户代理成功,则创建的上下文 XR compatible 布尔值为 true。要获取
沉浸式 XR 设备,应调用 ensure an immersive XR device is selected。
注意:ensure an immersive XR device is selected
需要并行执行,这会导致主线程阻塞。UA 应在控制台给出警告,建议使用 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。如果是否使用 XR
不确定,建议只在即将启动沉浸式会话时调用
makeXRCompatible()
。
makeXRCompatible()
方法确保 WebGLRenderingContextBase
运行在兼容图形适配器上,适配于沉浸式 XR 设备。
调用此方法时,UA 必须执行以下步骤:
-
如果请求文档的origin 不被允许使用 "xr-spatial-tracking" 权限策略,resolve promise 并返回。XR 权限策略被禁用时,应表现为本地没有 XR 设备,因为
makeXRCompatible()
设计为一次性设置的方法。 -
令 promise 为 新建 Promise,属于该
WebGLRenderingContextBase
所在 Realm。 -
令 context 为 this。
-
以下步骤并行执行:
-
令 device 为 ensure an immersive XR device is selected 的结果。
-
设置 context 的 XR compatible 布尔值如下:
- 如果 context 的 WebGL context lost flag 已设:
-
排队一个任务,将 context 的 XR compatible 设为
false
,并用InvalidStateError
拒绝 promise。 - 如果 device 为
null
: -
排队一个任务,将 context 的 XR compatible 设为
false
,并用InvalidStateError
拒绝 promise。 - 如果 context 的 XR compatible 为
true
: - 如果 context 是在 兼容图形适配器上为 device 创建的:
-
排队一个任务,将 context 的 XR compatible 设为
true
,resolve promise。 - 否则:
-
-
强制 context 丢失。
-
-
令 canvas 为 context 的 canvas。
-
如果 context 的 webgl context lost flag 已设,终止这些步骤。
-
设 context 的 webgl context lost flag。
-
设每个由 context 创建的
WebGLObject
实例的invalidated 标志。 -
禁用所有扩展,除了 "WEBGL_lose_context"。
-
-
触发 WebGL context 事件 e,事件名 "webglcontextlost",目标 canvas,
statusMessage
设为 ""。 -
如果 e 的 canceled flag 未设,则用
AbortError
拒绝 promise,并终止。 -
以下步骤并行执行。
-
等待在 兼容图形适配器上的可恢复绘图缓冲区,适配于 device。
-
将一个任务排入队列 在 WebGL task source 上执行以下步骤:
-
将 context 的 XR compatible 设为
true
。 -
resolve promise。
-
-
-
-
-
-
返回 promise。
此外,任何 WebGL context 丢失时,在触发 "webglcontextlost" 事件前应执行下面步骤:
-
将 context 的 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 上下文与设备兼容,可能会导致丢失 context。 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()
时必须抛出异常。
当用户代理需要以名称 name、XRFrame
frame 和 XRInputSource
source 触发输入源事件时,必须执行以下步骤:
-
用
XRInputSourceEvent
、type
name、frame
frame 和inputSource
source 创建 event。 -
将 frame 的 active 布尔值设为
true
。 -
为 frame 应用帧更新。
-
将 frame 的 active 布尔值设为
false
。
12.3. XRInputSourcesChangeEvent
XRInputSourcesChangeEvent
用于指示 活动 XR 输入源列表 发生变化时被触发,该列表可用于 XRSession
。
[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
的事件后位置和朝向(在事件前坐标系中)。如果 XRSystem
无法确定新旧坐标系的差异,则该属性可以为 null
。
注意:当头显在两个不同位置之间摘下又戴上时,referenceSpace
或 referenceSpace
可能会发生变化。在此类情况下,如果体验依赖于世界锁定内容,应提醒用户并重置场景。
12.5. XRVisibilityMaskChangeEvent
由于视锥体并不总是与矩形显示器精确相交,XRLayer
的整个区域可能不会被显示。该事件将通知体验哪些 XRView
区域被展示给用户。
当用户代理需要告知体验 XRLayer
的显示区域发生变化时,会触发 XRVisibilityMaskChangeEvent
事件。
体验可以选择只绘制该区域,这有助于提升性能。
注意:体验必须在 requestSession
的 promise 解决期间注册该事件,否则事件可能会触发且遮罩信息会丢失。
[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
属性表示该遮罩所应用的 XREye
。
index
属性表示该遮罩应用到的 XRView
在 视图列表
中的偏移量。
vertices
属性是一个 列表,包含
X
、Y
坐标。体验必须假定 Z
坐标为 -1
。每组
X
、Y
、Z
坐标描述一个顶点。如果该数组为空,则应绘制 XRView
的整个区域。
indices
属性是一个 列表,用于描述 vertices
顶点列表中的索引。这些索引将描述该眼睛的 XRView
应绘制的区域。如果该数组为空,则应绘制 XRView
的整个区域。
该区域必须使用 projectionMatrix
(XRView
的 eye
)和默认
XRRigidTransform
进行绘制。
注意:这意味着该区域不得使用当前 XRView
的 XRRigidTransform
(eye
)进行绘制。
12.6. 事件类型
用户代理必须提供以下新事件。事件的注册和触发必须遵循 DOM 事件的常规行为。
除非文档的 origin 不被允许使用 "xr-spatial-tracking" 权限策略,用户代理必须在 XRSystem
对象上触发一个名为 devicechange
的事件,以指示沉浸式 XR
设备的可用性已发生变化。
每当 XRSession
的 可见性状态发生变化时,用户代理必须在 XRSession
上,使用 XRSessionEvent
触发名为 visibilitychange
的事件。
当会话结束时(无论是由应用程序还是用户代理),用户代理必须在 XRSession
上,使用 XRSessionEvent
触发名为 end
的事件。
当会话的 激活 XR 输入源列表 发生变化时,用户代理必须在 XRSession
上,使用 XRInputSourcesChangeEvent
触发名为 inputsourceschange
的事件。
当会话的 激活 XR 跟踪源列表 发生变化时,用户代理必须在 XRSession
上,使用 XRInputSourcesChangeEvent
触发名为 trackedsourceschange
的事件。
当其某个 XRInputSource
开始其 主操作时,用户代理必须在 XRSession
上,使用 XRInputSourceEvent
触发名为 selectstart
的事件。事件类型必须为 XRInputSourceEvent。
当其某个 XRInputSource
结束其 主操作或一个已开始主操作的 XRInputSource
被断开时,用户代理必须在 XRSession
上,使用 XRInputSourceEvent
触发名为 selectend
的事件。
当其某个 XRInputSource
完全完成一次 主操作时,用户代理必须在 XRSession
上,使用 XRInputSourceEvent
触发名为 select
的事件。
当其某个 XRInputSource
开始其 主挤压操作时,用户代理必须在 XRSession
上,使用 XRInputSourceEvent
触发名为 squeezestart
的事件。
当其某个 XRInputSource
结束其 主挤压操作或一个已开始主挤压操作的 XRInputSource
被断开时,用户代理必须在 XRSession
上,使用 XRInputSourceEvent
触发名为 squeezeend
的事件。
当其某个 XRInputSource
完全完成一次 主挤压操作时,用户代理必须在 XRSession
上,使用 XRInputSourceEvent
触发名为 squeeze
的事件。
当 XR 合成器 更改 XRSession
的
内部名义帧率时,用户代理必须在 XRSession
上,使用 XRSessionEvent
触发名为 frameratechange
的事件。
当 本地原点 或 有效原点
发生不连续(即原点相对于用户环境的位置或朝向发生显著变化)时,(例如:用户重新校准 XR 设备后,或 XR 设备在丢失并恢复跟踪后自动调整原点)用户代理必须在 XRReferenceSpace
上,使用 XRReferenceSpaceEvent
触发名为 reset
的事件。当 XRBoundedReferenceSpace
的 boundsGeometry
发生变化时,也必须分发 reset
事件。如果 viewer 的姿态经历不连续,但 XRReferenceSpace
的原点物理映射保持稳定(如 viewer
在同一跟踪区域内暂时丢失并恢复跟踪时),则不得分发 reset
事件。当 unbounded
参考空间随时间对其 本地原点 进行微调以保持用户附近的空间稳定时,如果未发生显著不连续,也不得分发此事件。事件必须在利用新原点的任何 XR 动画帧
执行之前分发。必须在所有作为参考空间偏移空间的对象上分发 reset
事件,并且这些偏移 XRBoundedReferenceSpace
的 boundsGeometry
也应重新计算。
注意: 这意味着会话需要对那些有 reset
监听器的 XRReferenceSpace
保持强引用。
注意: 应用可以通过观察 emulatedPosition
布尔值来处理 viewer 位置的跳变。如果 viewer 位置的跳变与 emulatedPosition
从 true
变为 false
同时发生,则表示 viewer
已恢复跟踪,其新位置反映了之前模拟值的修正。对于没有“瞬移”机制、用户只能物理移动的体验,这通常是应用期望的行为。但如果体验提供了“瞬移”机制,则在恢复跟踪后强行跳回 viewer 位置可能会令人不适。因此,这类应用在恢复跟踪时可以直接从 viewer 在虚拟世界的当前位置继续体验,将跳变吸收进瞬移偏移。为此,开发者可调用 getOffsetReferenceSpace()
创建一个新的参考空间,其 有效原点 按 viewer 自前一帧以来的位置跳变进行调整。
13. 安全性、隐私性与舒适性注意事项
WebXR 设备 API 提供了强大的新特性,同时也带来了多种独特的隐私、安全和舒适性风险,用户代理必须采取措施加以缓解。
13.1. 敏感信息
在 XR 场景下,敏感信息包括但不限于用户可配置的数据(如瞳距 IPD)和基于传感器的数据(如 XRPose
)。所有沉浸式会话都将暴露一定量的敏感数据,因为渲染内容需要用户的姿态信息。
但在某些情况下,相同的敏感信息也会通过 "inline"
会话暴露。
13.2. 用户意图
用户意图指的是用户对某个操作的主动行为和同意的信号。
在暴露敏感信息或允许对用户体验有重大影响的操作前,通常需要确保用户意图。这种意图可以通过多种方式传达或观察到。
注意:判断用户意图的常见方式是通过 UI 控件的瞬时激活,通常是“进入 VR”按钮。由于激活是瞬时的, 请求 XR 会话的浏览上下文必须是包含该 UI 控件的祖先或同源域的后代,并且最近是该浏览上下文的活动文档。
13.2.1. 用户激活
瞬时激活在某些场景下可以作为用户意图的标志。13.2.2. 启动 Web 应用
在某些环境下,页面可能以应用形式呈现,并明确用于运行沉浸式内容。在这种情况下,启动 Web 应用也可以作为用户意图的标志。13.2.3. 隐式与显式同意
隐式同意指的是用户代理在未明确询问用户的情况下,根据 Web 应用的安装状态、访问频率和最近访问情况,或用户代理定义的用户明确表示想进入沉浸式体验的行为,来判断用户同意。鉴于 XR 数据的敏感性,强烈建议在依赖隐式信号时保持谨慎。
显式同意指的是用户代理在明确询问用户后,根据用户的答复来判断同意。当收集显式同意时,用户代理会说明请求内容,并为用户提供拒绝选项。请求用户同意的方式可以根据受保护特性和用户代理的选择以多种视觉形式呈现。Web 应用的安装状态可以作为显式同意的信号,前提是在安装时请求了某种显式同意。
13.2.4. 同意的持续时间
建议一旦为特定显式同意授予了某个源,该同意应持续到浏览上下文结束。用户代理可根据用户意图的隐式或显式信号选择延长或缩短同意时长,但建议在偏离此建议时,尤其是依赖隐式信号时要谨慎。例如,专为运行沉浸式内容而安装的 Web 应用可持续保存用户同意,但如果沉浸式内容只是附加功能,则不应如此。无论用户代理选择保存用户同意的时长如何,敏感信息只能由尚未结束的XRSession
暴露。
13.3. 会话中途同意
有多种非 XR API 会导致用户代理请求显式同意以使用某项功能。如果用户代理在存在活动沉浸式会话时需要请求用户同意,则必须在向用户显示同意请求前关闭会话。如果该功能的用户同意在活动沉浸式会话创建前已获得,则无需终止会话。
注意:此限制旨在确保所有用户代理在达成共识前行为一致,关于如何管理会话中途的显式同意。这不是长期要求。
13.4. 数据调整
在某些情况下,可以通过数据调整(如节流、量化、舍入、限制或以其他方式处理来自XR 设备的数据)来缓解安全和隐私威胁。有时即使已确认用户意图,也需要这样做以避免指纹识别。但数据调整缓解措施只能在不会导致用户不适的情况下使用。
13.4.1. 节流
节流指的是以低于原本可能的频率报告敏感信息。这种缓解措施有助于降低网站推断用户意图、位置或进行用户画像的能力。但如果使用不当,节流有很大风险导致用户不适。此外,在许多情况下,节流并不能完全缓解风险。13.4.2. 舍入、量化与扰动
舍入、量化和扰动是三类修改原始数据的缓解措施。舍入通过减少用于表示数据的数字位数来降低精度。量化将连续数据限制为离散的值集合。扰动则是在数据中引入微小的随机误差。这些缓解措施有助于防止指纹识别,尤其是在不会对用户舒适性造成明显影响时特别有用。13.4.3. 限制
限制指的是仅在数据处于特定范围内时才报告。例如,可以在用户远离批准位置超过一定距离时,舒适地限制位姿数据的报告。采用此缓解措施时应确保不会对用户体验产生负面影响。通常应避免在范围末端出现“硬停止”,以免造成突兀的用户体验。13.5. 受保护的功能
API 暴露的敏感信息可分为若干类别,这些类别具有相似的威胁特征,并需要相应的保护措施。
13.5.1. 沉浸性
用户必须能够控制何时创建沉浸式会话,因为创建会话会对用户设备产生侵入性变化。例如,启动沉浸式会话会激活XR 设备的传感器,接管设备显示,并开始呈现沉浸式内容,这可能会终止其他应用对 XR 硬件的访问。在某些系统上,这还可能带来显著的电源或性能开销,或触发状态栏或商店的启动。要判断给定global object是否允许沉浸式会话请求,用户代理必须执行以下步骤:
启动"inline"
会话并不自动带有相同的要求,但根据会话的请求特性,可能会有额外要求。
要判断给定global object是否允许inline 会话请求,用户代理必须执行以下步骤:
13.5.2. 位姿
当基于传感器数据时,XRPose
和XRViewerPose
会暴露敏感信息,这些信息可能被用于输入嗅探、凝视追踪或指纹识别等多种方式。
要判断是否可以向XRSession
session报告位姿,用户代理必须执行以下步骤:
-
如果session的
visibilityState
为,返回
false
。 -
判断是否可以返回位姿数据:
注意:用户代理判断位姿数据不会暴露可指纹识别数据的方法由用户代理自行决定。
XRViewerPose
和 XRPose
的主要区别在于包含了 XRView
信息。当存在多个视图且这些视图之间的物理关系可由用户配置时,这些视图之间的关系被视为敏感信息,因为它可能被用于对用户进行指纹识别或画像。
如果 XRView
之间的关系可以唯一标识XR 设备,则用户代理必须对 XRView
数据进行匿名化处理,以防止指纹识别。匿名化的方法由用户代理自行决定。
注意:此外,如果 XRView
之间的关系受用户配置的瞳距(IPD)影响,则强烈建议用户代理在会话创建期间、报告任何 XRView
数据之前,要求明确同意。
13.5.3. 参考空间
根据所用参考空间的不同,应用可能会暴露多种敏感信息。-
在支持6DoF跟踪的设备上,
"local-floor"
参考空间也可用于步态分析,实现用户画像和指纹识别。此外,由于"local-floor"
参考空间提供了已知的地面高度,站点还可能推断用户身高,实现用户画像和指纹识别。 -
"bounded-floor"
参考空间在尺寸足够受限时,不会让开发者确定地理位置。但由于地面高度已知且用户可在空间内行走,站点仍可能推断用户身高或进行步态分析,实现用户画像和指纹识别。此外,还可能通过有界参考空间报告的边界进行指纹识别。 -
"unbounded"
参考空间暴露的空间数据最多,可能导致用户画像和指纹识别。例如,这些数据可能用于确定用户的具体地理位置或进行步态分析。
因此,各种参考空间类型的创建都受到限制,以确保暴露的敏感信息得到安全处理:
大多数参考空间要求使用该空间的用户意图已明确,无论是通过显式同意还是隐式同意。详情见特性要求表。
任何一组可相互关联的"local"
、
"local-floor"
和"bounded-floor"
参考空间,若能相互关联,则必须共享同一个原生原点;此限制仅在"unbounded"
参考空间的创建受限时适用。
要判断两个空间space和baseSpace之间位姿是否必须被限制,用户代理必须执行以下步骤:
-
如果space或baseSpace中有任意一个是
XRBoundedReferenceSpace
,且另一个空间的原生原点超出了原生边界几何的合理距离,则返回 true。 -
如果space或baseSpace中有任意一个是
XRReferenceSpace
,其type为"local"
或"local-floor"
,且两个空间的原生原点之间的距离大于用户代理确定的合理距离,则返回true
。 -
返回
false
。
注意:对文档可见性的要求基于[DEVICE-ORIENTATION]。
注意:建议相对于"local"
或"local-floor"
参考空间报告的位姿应限制在距XRReferenceSpace
的原生原点15米范围内。
注意:建议相对于XRBoundedReferenceSpace
报告的位姿应限制在XRBoundedReferenceSpace
的原生边界几何外1米范围内。
13.6. 可信环境
可信 UI是由用户代理呈现的界面,用户可以与之交互,但页面无法与之交互。用户代理必须支持显示可信 UI。
可信 UI必须具备以下属性:
-
不可被伪造
-
能够指示请求/内容的来源
-
如果依赖于与用户共享的密钥,该密钥不能被混合现实捕获(例如不能是摄像头可见的手势)
-
在同一用户代理的沉浸式体验中保持一致
广义上,用户代理支持可信 UI有两种方式。一种是可信沉浸式
UI,即不退出沉浸模式的可信 UI。实现可信沉浸式
UI具有挑战性,因为XRWebGLLayer
缓冲区会填满 XR 设备显示,用户代理通常不会“保留”像素用于自身。用户代理不要求支持可信沉浸式
UI,也可以临时暂停/退出沉浸模式,向用户显示非沉浸式可信
UI。
-
未处于沉浸模式时显示的默认 2D 模式浏览器
-
仅能通过保留硬件按钮交互、以防止伪造的沉浸模式内提示
-
暂停沉浸会话并显示某种原生系统环境,在其中可显示提示
读取输入信息(头部姿态、输入姿态等)的能力对受信任UI的完整性构成风险,因为页面可能利用这些信息在用户与受信任UI交互时,窥探用户的选择,包括猜测键盘输入。为防止这种风险,当用户与受信任UI(沉浸式或非沉浸式,如地址栏或系统对话框)交互时,用户代理必须将所有XRSession
的可见性状态设为或
"visible-blurred"
。此外,为防止恶意页面监视其他页面的输入,当当前聚焦区域不属于创建该XRSession
的文档时,用户代理必须将该XRSession
的可见性状态设为。
在为某个可信 UI实例选择或
"visible-blurred"
时,用户代理必须考虑头部姿态信息是否构成安全风险。例如,涉及文本输入(尤其是密码输入)的可信
UI,用户在输入时头部姿态可能泄露输入内容。此时,用户代理还应停止暴露任何与眼动追踪相关的信息。
用户代理必须使用可信 UI显示权限提示。
如果虚拟环境无法以低延迟和高帧率持续跟踪用户头部运动,用户可能会感到迷失或身体不适。由于无法强制页面始终高效且正确地渲染内容,用户代理必须提供可跟踪的可信环境和异步于页面内容运行的XR Compositor。Compositor 负责合成可信与不可信内容。如果内容性能不佳、不提交帧或意外终止,用户代理应能继续呈现响应式的可信 UI。
此外,页面内容还可能以非性能相关的方式让用户感到不适。例如,错误的跟踪、闪烁的颜色,以及有意冒犯、恐吓或惊吓用户的内容,都可能让用户希望快速退出 XR 体验。在这些情况下摘下 XR 设备可能并不总是快捷或可行。为此,用户代理必须为用户提供某种操作(如按下保留硬件按钮或执行手势),以便退出 WebXR 内容并显示用户代理的可信 UI。
13.7. 上下文隔离
可信 UI 必须由独立的渲染上下文绘制,其状态与页面使用的任何渲染上下文(如 WebGL 渲染上下文)隔离。这可防止页面破坏可信 UI 上下文的状态,避免其无法正确渲染跟踪环境,也防止页面捕获可信 UI 的图像,导致隐私信息泄露。
此外,为防止 CORS 相关漏洞,每个浏览上下文都会看到 API 返回对象的新实例,如XRSession
。如context等属性设置在某个XRWebGLLayer
上时,只有同一相关 realm才能读取,不能被其他相关 realm读取,除非它们具有同源。同样,API 方法不得导致其他浏览上下文的可观察状态变化。例如,不会暴露可用于系统级方向重置的方法,否则恶意页面可反复调用,导致其他页面无法正常跟踪。但用户代理必须响应用户手势或系统菜单触发的系统级方向重置。
注意:这不适用于由某个浏览上下文进入沉浸模式、获取设备锁并可能在其他浏览上下文上触发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
。
此类分组通常由运行的浏览器版本和平台/设备识别,但不能通过任何已连接外部设备的状态来区分。我们可以使用用户代理
中 在用户代理字符串上无法区分 的概念来
适当地评估指纹识别风险。
有些无法通过 user agent 字符串区分的用户代理将永不支持某种XRSessionMode
会话。
例如:运行在某型号手机上的用户代理,该型号手机已知不支持移动 AR。在这些情况下,isSessionSupported()
始终报告不支持该XRSessionMode
不会带来指纹识别风险,因为所有此类设备都会一致报告相同的值,且设备类型和型号可通过userAgent
等方式推断。因此,在这些系统上,用户代理应自动拒绝相关XRSessionMode
的"xr-session-supported"。
另一些无法通过 user agent 字符串区分的用户代理通常支持某种XRSessionMode
会话。
例如:仅在 VR 头显内运行且已知支持 WebXR 的用户代理,除非被用户明确阻止,否则很可能支持"immersive-vr"
会话。在这些情况下,报告XRSessionMode
不支持虽然准确,但会暴露更多可唯一标识用户的信息。因此,始终报告该XRSessionMode
可用,并允许requestSession()
失败,更能保护隐私且通常不会让用户困惑。在这些系统上,用户代理应自动授予相关XRSessionMode
的"xr-session-supported"。
对于 XR 能力高度可变的无法通过 user agent 字符串区分的用户代理(如支持 XR
外设的桌面系统),指纹识别风险最高。此类设备上的用户代理不应自动授予"xr-session-supported",以免isSessionSupported()
API 提供额外的指纹识别信息。
-
每次调用
isSessionSupported()
时,始终判断显式同意(可结合缓存的权限提示等)。 -
自动授予"xr-session-supported",但让
isSessionSupported()
始终返回true
,即使平台并不总有 XR 能力,无论相关硬件或软件是否存在。这样会影响用户体验,因为页面会向无法使用 XR 的用户展示 XR 内容。 -
当有合适硬件时,
isSessionSupported()
请求显式同意;无硬件时,延迟一段随机时间后返回false
。实现时,内容不得区分用户代理未连接 XR 硬件与用户拒绝显式同意的情况。
无论采用哪种技术,都不应在未获得显式同意的情况下,泄露更多关于已连接 XR 硬件的信息。
14. 集成
14.1. 权限策略
本规范定义了一个策略控制特性,用于控制是否允许任何需要空间跟踪的XRSession
通过requestSession()
返回,以及是否允许通过isSessionSupported()
或devicechange
事件指示支持需要空间跟踪的会话模式,在navigator.xr
对象上。
该特性的标识符为"xr-spatial-tracking"
。
该特性的默认允许列表为["self"]
。
注意:如果文档的源未被允许使用"xr-spatial-tracking"
权限策略,则所有沉浸式会话都将被阻止,因为所有沉浸式会话都需要空间跟踪。Inline 会话仍然允许,但仅限于使用"viewer"
XRReferenceSpace
。
14.2. 权限 API 集成
[permissions] API 为网站请求用户权限和查询已授予权限提供了统一方式。
"xr"
强特性的权限相关算法和类型定义如下:
- 权限描述符类型
-
dictionary
:XRPermissionDescriptor PermissionDescriptor {XRSessionMode
;mode sequence <DOMString >
;requiredFeatures sequence <DOMString >
; };optionalFeatures - 权限结果类型
-
[
Exposed =Window ]interface
:XRPermissionStatus PermissionStatus {attribute FrozenArray <DOMString >
; };granted - 权限查询算法
-
查询 "xr" 权限时,使用
XRPermissionDescriptor
descriptor 和XRPermissionStatus
status,用户代理必须执行以下步骤:-
如果 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"
,并中止这些步骤
- 权限请求算法
-
使用
XRPermissionDescriptor
descriptor 和XRPermissionStatus
status 请求 xr 权限时,用户代理必须执行以下步骤:-
将 status 的
granted
设为空FrozenArray
。 -
令 requiredFeatures 为 descriptor 的
requiredFeatures
。 -
令 optionalFeatures 为 descriptor 的
optionalFeatures
。 -
令 device 为获取当前设备的结果,参数为 mode、requiredFeatures 和 optionalFeatures。
-
令 result 为解析请求特性的结果,参数为 requiredFeatures、optionalFeatures 和
mode
。 -
如果 result 为
null
,执行以下步骤: -
令 (consentRequired, consentOptional, granted) 为 result 的字段。
-
此时用户代理可以询问用户是否允许调用算法使用 consentRequired 和 consentOptional 中的任意特性。提示结果应在判断是否有明确用户意图时纳入考虑。
-
对 consentRequired 中的每个 feature 执行以下步骤:
-
对 consentOptional 中的每个 feature 执行以下步骤:
-
将 status 的
granted
设为 granted。 -
将 granted 的所有元素添加到 device 的已授予特性集合中,针对 mode。
注意:用户代理可以在判断用户意图时,将所有请求特性的权限提示合并批量展示,也可以逐一展示。
注意:判断 Web 应用用户意图时,用户代理必须检查应用是否被用户明确以 Web 应用方式启动,而不能仅检查源是否与已安装 Web 应用一致。
-
要解析请求特性,给定 requiredFeatures 和
optionalFeatures 以及 XRSessionMode
mode,用户代理必须执行以下步骤:
-
令 device 为 获取当前设备(参数为 mode、requiredFeatures 和 optionalFeatures)的结果。
-
令 previouslyEnabled 为 device 针对 mode 的 已授予功能集合。
-
如果 device 为
null
或 device 的 支持模式列表不包含 mode,执行以下步骤:-
返回 元组 (consentRequired, consentOptional, granted)
-
-
对于 requiredFeatures 中的每个 feature,执行以下步骤:
-
对于 optionalFeatures 中的每个 feature,执行以下步骤:
-
返回 元组
(|consentRequired|, |consentOptional|, |granted|)
变更记录
自 2022 年 3 月 31 日候选推荐快照 的变更
-
暴露 XRSession 的 granted features(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 设备定义(GitHub #927)
-
纳入近期隐私讨论结论(GitHub #1124)
-
将 isSessionSupported 从使用用户意图切换为使用权限(GitHub #1136)
-
澄清最小 viewport scale 可能会变化(GitHub #1134)
-
改进指纹识别相关内容(GitHub #1133)
-
增加 requestViewportScale/recommendedViewportScale(GitHub #1132)
-
澄清 framebuffer scale factor 分别应用于宽度/高度(GitHub #1131)
-
确保 pending render state 总是被应用(GitHub #1128)
-
将 context XR 兼容性受 xr-spatial-tracking 权限策略控制(GitHub #1126)
-
更改 updateRenderState 应用时机(GitHub #1111)
自 2019 年 10 月 10 日工作草案 的变更
新增特性:
-
为 secondary views 增加特性(GitHub #1083)
-
用 layers sequence 更新 XRRenderStateInit(GitHub #999)
-
将输入源拆分为主/辅助(GitHub #929)
-
定义 squeeze 事件(GitHub #893)
变更:
-
主视图必须始终处于激活状态(GitHub #1105)
-
在 makeXRCompatible() 中正确处理上下文丢失(GitHub #1097)
-
引入 active view 概念(GitHub #1096)
-
修正 frame 和 viewport 缓存为显式(GitHub #1093)
-
允许缓存多种对象(GitHub #1088)
-
允许对 framebufferScaleFactor 进行裁剪(GitHub #1084)
-
澄清“确保选择沉浸式设备”的线程特性,弃用 xrCompatible(GitHub #1081)
-
澄清 native origins 相关内容(GitHub #1071)
-
将文档可见性检查改为 UA 自主选择(GitHub #1067)
-
新增“检查图层状态”算法(GitHub #1064)
-
关于 null 和 emulated poses 的多项变更(GitHub #1058)
-
说明 XRInputSource/frame 的输入帧语义(GitHub #1053)
-
为 XRRigidTransform 增加校验(GitHub #1043)
-
何时适用空输入 profile 数组的细微调整(GitHub #1037)
-
允许 trusted ui 使用 visible-blurred,并警告文本输入泄露风险(GitHub #1034)
-
澄清 window.rAF() 相关内容(GitHub #1033)
-
任务与 promise 处理方式优化(GitHub #1032)
-
未传 render state 时 updateRenderState() 直接返回(GitHub #1031)
-
移除 responsible/active/focused documents 的使用(GitHub #1030)
-
澄清 context isolation 下的 browsing context 和 realm(GitHub #1029)
-
明确 reset 事件在 offset spaces 上的行为(GitHub #1024)
-
明确每个对象创建在哪个 realm(GitHub #1023)
-
rAF() 回调参数使用当前时间戳(GitHub #1015)
-
会话特性请求不再需要 session 参数(GitHub #1012)
-
允许在 rAF 内取消 rAF 回调(GitHub #1005)
-
说明 opaque framebuffer 持有特定 session 的引用(GitHub #1004)
-
inputsourcechange 初次事件推迟到 promise resolve 后(GitHub #1002)
-
文档 framebufferScaleFactor 的效果(GitHub #993)
-
如果 depth||stencil 请求,则允许 depth&&stencil 结果(GitHub #987)
-
isSessionSupported 返回更灵活(GitHub #986)
-
澄清 inline 设备何时暴露跟踪/输入数据(GitHub #985)
-
增加 viewport 形状的常识性限制(GitHub #976)
-
说明 preserveDrawingBuffer 在此无效(GitHub #975)
-
澄清 inline 会话的 visiblityState 行为(GitHub #974)
-
定义 opaque framebuffer 何时视为 dirty(GitHub #970)
-
设备变更时可能更新 inline 设备(GitHub #947)
-
澄清有界参考空间行为(GitHub #938)
-
多项 XR 兼容性算法修复(GitHub #921)
-
补充 trusted UI 章节(GitHub #875)
-
更好定义 depthNear 和 depthFar 的用法(GitHub #888)
-
澄清 local-floor 空间使用估算高度时 emulatedPosition 不为 true(GitHub #871)
自 2019 年 2 月 5 日首次公开工作草案 的变更
新增特性:
-
为 XRInputSource 添加 profiles 属性,用于输入配置文件名称列表(GitHub #695)
-
为 XREye 添加 none 变体(GitHub #641)
-
添加显式的 inline XR 设备(GitHub #737)
-
姿态隐私考量,包含数据调整与受保护功能(GitHub #761)
-
参考空间隐私考量(GitHub #762)
-
定义敏感信息与用户意图(GitHub #757)
-
必需与可选特性(特性依赖)(GitHub #749)
-
跟踪丢失与恢复(GitHub #559)
-
将 blur/focus 事件改为 visibilitychange(GitHub #687)
-
定义输入源事件顺序(GitHub #629)
-
描述输入源列表的维护方式(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 改为单一,移除 XRPresentationContext(GitHub #656)
-
移除 XRLayer 基类(GitHub #688)
-
移除 XRWebGLLayer.requestViewportScaling()(GitHub #631)
-
移除 XRWebGLLayer 的 context 属性(GitHub #707)
-
移除仅反映请求值的属性(GitHub #574)
-
移除 XRSessionCreationOptions(GitHub #566)
变更:
-
描述 XRWebGLLayer 所需的清除行为(GitHub #866)
-
规定 XRWebGLLayer 帧缓冲始终使用预乘 alpha(GitHub #840)
-
澄清 reset 事件的变换方向(GitHub #843)
-
定义如何满足特性需求(GitHub #839)
-
在适当时检查 session 是 inline 而非 immersive(GitHub #834)
-
暂时禁止 stereo inline session(GitHub #829)
-
处理 projectionMatrix 中的分离缓冲区(GitHub #830)
-
确保 makeXRCompatible() 选择沉浸式设备(GitHub #809)
-
将 features 改为 'any' 序列(GitHub #807)
-
链接“触发输入源事件”算法,显式构造 frame(GitHub #797)
-
从规范和 explainer 中移除 Environment blend mode(GitHub #804)
-
为每个方法提供描述(GitHub #798)
-
要求 UA 显示手动设备激活步骤(GitHub #799)
-
进一步澄清合成器相关内容(GitHub #805)
-
澄清刚体变换矩阵的获取方式(GitHub #806)
-
允许 UA 限制裁剪平面(GitHub #802)
-
禁止在 getViewport() 中使用过期 XRView(GitHub #796)
-
明确 depth/alpha/stencil 值的使用方式(GitHub #800)
-
profiles 变更时触发输入源事件(GitHub #795)
-
澄清 reset 事件的触发时机(GitHub #637)
-
明确 requestReferenceSpace() 何时可拒绝请求(GitHub #651)
-
使 XRRay.matrix 唯一,补充获取步骤(GitHub #655)
-
采用 TAG 推荐的 Promise 返回方式(GitHub #700)
-
将 requestSession() 的竞态部分移至主线程(GitHub #706)
-
澄清独占访问下允许小型叠加 UI(GitHub #709)
-
合并“结束会话”与“关闭会话”,澄清并添加 onend 事件(GitHub #710)
-
inline session 不检查 XR compat 标志(GitHub #705)
-
校验 position DOMPointInit(GitHub #568)
-
明确原生原点(GitHub #621)
-
移除 buttonIndex(GitHub #741)
-
移除对 "immersive-ar" 和 XRRay 的引用(GitHub #784)
-
移除 XRInputSource.gamepad 的 explainer 和 index.bs 引用(GitHub #782)
-
getViewport 传入无效 view 时抛出错误(GitHub #771)
-
阻止会话中途请求同意(GitHub #767)
-
规范 XRPresentationContext 创建(GitHub #501)
-
将 inputSources getter 从方法改为属性(GitHub #624)
-
将必需的 gamepad index 改为 -1(GitHub #690)
-
处理分离数组(GitHub #684)
-
要求敏感 UI 隐藏 WebXR 内容(GitHub #742)
-
使 xr-standard gamepad 映射更为严格(GitHub #735)
-
修正 XRRay.matrix 算法(GitHub #728)
-
修正 XRRay.matrix 算法中的分离数组问题(GitHub #716)
-
简化 requestSession() 对不支持模式的处理(GitHub #714)
-
部分 XRRenderState 说明优化(GitHub #703)
-
将“待处理渲染状态列表”替换为“待处理渲染状态”(GitHub #701)
-
更好地定义 gamepad 占位按钮与轴(GitHub #661)
-
澄清未触摸时 touchpad 应报告的值(GitHub #660)
-
将 getPose 参数 referenceSpace 重命名为 baseSpace(GitHub #659)
-
修正变换的乘法顺序(GitHub #649)
-
澄清 local 与 local-floor 跟踪的假设(GitHub #648)
-
简化可用空间类型(GitHub #626)
-
requestSession:优先检查用户激活(GitHub #685)
-
使 boundsGeometry 相对于有效原点工作(GitHub #613)
-
明确 views 数组的填充方式(GitHub #614)
-
标识 Gamepad id 应为 unknown 的情况(GitHub #615)
-
重构 XRSpace、get(Viewer)Pose 定义(GitHub #609)
-
将 supportsSessionMode 重命名为 supportsSession(GitHub #595)
-
合并参考空间类型与接口(GitHub #587)
-
inverse 属性始终返回同一对象(GitHub #586)
-
会话关闭时拒绝所有未决 Promise(GitHub #585)
-
规定投影矩阵可包含切变(GitHub #575)
-
描述 updateRenderState 可能抛出的异常(GitHub #511)
-
编辑 requestSession() 并初始化会话(GitHub #601)
-
将 XRRigidTransform 的 inverse 从方法改为属性(GitHub #560)
-
指明合成时何时使用深度值(GitHub #563)
-
Stationary 子类型支持为全有或全无(GitHub #537)
-
将 outputContext 移至 XRRenderState(GitHub #536)
-
规定 getViewerPose 非 rAF XRFrame 时抛出错误(GitHub #535)
-
移除 viewMatrix 并添加 XRTransform.inverse()(GitHub #531)
-
将 XRHandedness 枚举的空字符串改为 'none'(GitHub #526)
-
指明 tracked-pointer 射线的推荐人体工学(GitHub #524)
-
澄清 XRRay 构造函数并定义归一化(GitHub #521)
-
规范 identity reference space 文本(GitHub #520)
-
澄清何时拒绝沉浸式会话(GitHub #360)
-
规定无 base layer 时不调用帧回调(GitHub #512)
15. 致谢
感谢以下个人对 WebXR 设备 API 规范的贡献:
特别感谢 Vladimir Vukicevic(Unity)为这一切的开启做出的贡献!