WebXR 是一组反对将渲染 3D 场景用来出现虚拟世界(虚拟现实,也称作 VR)或将图形图像增加到事实世界(加强事实,也称作 AR)的规范。通过该 API 能够拜访 VR/AR 虚构设施和跟踪用户姿势动作。它用于替换曾经废除的 WebVR API。
Khronos 的 OpenXR API 根本笼罩了 WebXR API 根底性能,然而它们与 WebGL 和 OpenGL 的关系不同,WebGL 和 OpenGL 关系是 1:1 的映射,而 WebXR 和 OpenXR 是由两个不同的组织开发,所以它们之间会有一些雷同概念用不同的形式来示意。
- AR 全称 Augmented Reality,加强事实。能够让屏幕上的虚拟世界可能与事实世界场景进行联合与交互。能够参考游戏 Pokémon GO。
- VR 全称 Virtual Reality,虚拟现实。由电脑创立虚构的 3D 世界,用户看不到事实环境,齐全沉迷在虚拟世界中。能够参考电影 头等玩家。
- MR 全称 Mixed Reality,混合事实。能够看成 AR 和 VR 的交融,用户能够看见事实环境,和额定的虚构物件,并能够进行交互。能够参考 Quest 的 MR 头盔。
- XR 全称 Extended Reality,扩大事实。它是一个总称,任何虚构和事实等技术,如 AR、VR 和 MR 都能够看成 XR 的一部分。
WebXR 中的 X 并不是作为首字母缩略词的一部分,而是作为某种代数变量来示意,你能够将它看成任何你心愿的词,例如 Extended 或 Cross。
历史
在 2014 年,Mozilla 工程总监 Vladimir Vukicevic 首次提出 WebVR 概念,它能够兼容 PC、挪动设施、VR 等各种设施,无需下载和装置在浏览器即可运行 3D VR 内容。
Mozilla 提出 WebVR 后,谷歌 Chrome 团队在 2016 年也退出开发,单干推出了 WebVR API 1.0。起初局部微软员工也退出,帮忙欠缺 WebVR API 2.0。
到了 2018 年,谷歌、Mozilla 等巨头组成的 W3C Immersive Web Working Group,推动标准化 WebXR 取代 WebVR,它被设计整合 AR、VR 以及将来可能呈现的事实和设施。
兼容性
目前 WebXR 并没有正式公布,反对 WebXR 的浏览器并不多,如下所示。
能够发现不是齐全不反对就是须要通过试验 flag 来开启该性能,只有少数几个浏览器才反对,不过没有关系,WebXR 次要用在 VR 头盔设施上,而头盔浏览器个别都是反对 WebXR 的,例如 Firefox Reality 浏览器。
目前 Firefox Reality 浏览器曾经交给了 Igalia 持续开发,Igalia 基于 Firefox Reality 持续开发新的 Wolvic 浏览器。Mozilla 则并将业务重心转向 Web VR 社交平台 Hubs。
疾速体验
要将画面渲染到 VR 设施上,须要搭配应用 WebXR 和 WebGL API,WebXR 负责拜访 VR 设施和获取传感器数据,WebGL 负责渲染画面。
要最简略的将画面渲染到 VR 设施上须要以下几步。
- 查看以后环境是否反对 WebXR
- 创立 XR Session
- 创立 XR 兼容的 WebGLContext
- 在 XRSession 的 requestAnimationFrame 回调中一直的渲染新的一帧画面
- 用户退出或本人完结 XRSession
上面代码演示如何创立一个最简略的 VR 场景。
if (navigator.xr) {
// 1. 查看是否反对 immersive-vr 模式
navigator.xr.isSessionSupported('immersive-vr').then((supported) => {if (supported) {const btn = document.createElement('button')
btn.textContent = '进入 VR'
btn.onclick = onBtnClick
document.body.appendChild(btn)
}
});
}
let gl
function onBtnClick() {navigator.xr.requestSession('immersive-vr').then(session => {
// 2. 申请 VR 会话
const canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true});
// 3. 与创立一般 WebGL 不同,这里须要设置 xrCompatible 参数
session.updateRenderState({baseLayer: new XRWebGLLayer(session, gl) });
// 更新会话的渲染层,后续渲染会渲染在该层上
session.requestAnimationFrame(onXRFrame);
})
}
function onXRFrame(time, frame) {
const session = frame.session;
// 4. 这个 session 是下面申请的 session
// 须要应用 session 上的 requestAnimationFrame
// 而不是 window 上的
session.requestAnimationFrame(onXRFrame);
const glLayer = session.renderState.baseLayer;
// 绑定 framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
// 随着工夫变动革除色
gl.clearColor(Math.cos(time / 2000),
Math.cos(time / 4000),
Math.cos(time / 6000), 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
navigator.xr 是 WebXR 的入口,它是一个 XRSystem 对象,它只有两个办法,isSessionSupported 查看指标模式是否反对和 requestSession 申请指标模式会话。模式分为 ar 和 vr。
首先须要通过 isSessionSupported 办法检测是否反对指标模式,如果反对就能够提醒用户能够进入 VR 模式。这里不能间接进入 VR 会话,须要在用户交互的回调函数中申请进入,相似于音频的自动播放限度。
用户点击按钮后,就能够通过 requestSession 办法申请指标模式的 XRSession,有了 XRSession 后,须要给它设置一个渲染层,后续渲染的画面会渲染到该渲染层上,和创立 WebGL 上下文一样,这里通过 canvas 元素的 getContext 办法获取,惟一不同的是须要传入 xrCompatible 参数,让 GL 上下文由 XR 适配器创立,这样能力与 XR 兼容。
最初就是利用 XRSession 上的 requestAnimationFrame 办法来渲染画面到 VR 设施。与 window.requestAnimationFrame 相似,不过它多接管一个 XRFrame 参数,下面保留了这一帧的信息,接下来渲染和 WebGL 中是一样的,不过须要将画面渲染到 XRSession 渲染层的 framebuffer 中。
要运行下面这个 Demo 会十分艰难,在桌面浏览器中不反对 VR,须要一个 VR 头盔设施,另外 WebXR 还须要 https 环境。
不过能够装置 WebXR 模拟器插件来调试开发 XR 利用,下图是在装置 XR 插件后,运行在 Chrome 浏览器中的成果。
可能有人认为 WebXR 性能相比 Native 利用会慢很多,其实大量指标和统计数据分析显示 WebXR 并不比 Native 应用程序慢。如果有 VR 设施能够点击下方链接来体验下 Web 中的 VR 切箭头。
https://moonrider.xyz/
WebXR Device API
下面 WebXR 例子中,首先通过 XRSystem 对象申请创立一个新的 XRSession 对象,XRSession 对象在渲染循环中一直创立 XRFrame 对象。上面就来近距离好好看一看 WebXR 中的这些对象。
XRSystem
XRSystem 是 WebXR 的入口,能够通过 navigator.xr 拜访到 XRSystem 对象。该对象下面只有两个办法和一个事件,其签名如下所示。
[SecureContext, Exposed=Window] interface XRSystem : EventTarget {
// Methods
Promise<boolean> isSessionSupported(XRSessionMode mode);
[NewObject] Promise<XRSession> requestSession(XRSessionMode mode, optional XRSessionInit options = {});
// Events
attribute EventHandler ondevicechange;
};
签名中的 XRSessionMode 是一个枚举字符串,它能够是上面这 3 个字符串。
- inline 渲染画面到页面上,就和应用一般 WebGL 渲染是一样的,浏览器应该反对该模式
- immersive-vr 渲染到 VR 设施
- immersive-ar 渲染到 AR 设施,该模式定义在 WebXR Augmented Reality Module 中
isSessionSupported 办法用于查问给定模式在以后环境下是否反对,该办法并不能给出 100% 的检测后果,然而它会十分疾速,也不会激活 VR 设施。
requestSession 该办法申请创立 XRSession 并进入指定模式的会话,前面所有的渲染,用户地位信息等都是基于该 XRSession 对象,它的第一个参数是会话模式字符串,第二个参数是可选的性能字符串,因为不是所有 XR 设施都反对所有性能,另外有些性能会输入敏感信息,与其让用户在应用时进行权限提醒,不如在一开始就一次性提醒,改参数签名如下。
dictionary XRSessionInit {
sequence<any> requiredFeatures;
sequence<any> optionalFeatures;
};
它有两个字段,一个必选和一个可选性能,它们都是接管字符串的数组。
默认状况会主动蕴含 “viewer” 性能,如果是 AR 或 VR 会话还会主动蕴含 “local” 性能。另外还有 “local-floor”、”bounded-floor” 和 “unbounded” 性能,它们都须要用户批准能力应用,这些性能字符串代表的意思将在上面章节中解说。
ondevicechange 事件会在设施发生变化时触发,例如原本没有 VR 设施,然而前面接入了,或者有 VR 设施然而连贯断掉了。触发该事件,之前的 XRSession 会完结,渲染上下文也会被革除,都须要从新创立。
XRSession
XRSession 示意一个 XR 会话,与 XR 设施互动就是通过该对象,所以该对象比拟重要,同时也比较复杂,其签名如下。
enum XRVisibilityState {
"visible",
"visible-blurred",
"hidden",
};
[SecureContext, Exposed=Window] interface XRSession : EventTarget {
// Attributes
readonly attribute XRVisibilityState visibilityState;
readonly attribute float? frameRate;
readonly attribute Float32Array? supportedFrameRates;
[SameObject] readonly attribute XRRenderState renderState;
[SameObject] readonly attribute XRInputSourceArray inputSources;
// Methods
undefined 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();
// Events
attribute 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;
};
可见性
visibilityState 属性是 XR Session 以后显示的状态,一共有上面 3 个状态。
- visible XR 渲染的画面失常展现个用户
- visible-blurred 用户能够看见 XR 渲染的画面,然而失焦了,此时渲染的帧率可能会被限度,画面也可能被含糊解决
- hidden 用户看不到以后画面,requestAnimationFrame 回调将不会被解决
能够通过 onvisibilitychange 事件来监听可见性变换。
帧率
frameRate 属性是设施的名义帧率,它并不是实在渲染的帧率。
supportedFrameRates 属性是设施反对的指标帧率。
名义帧率发生变化时会触发 onframeratechange 事件。
updateTargetFrameRate 办法能够更新会话指标帧率,如果会话没有名义帧率或者设置的帧率不在 supportedFrameRates 中,会间接报错 reject。
输出
inputSources 属性是摄入设施列表,例如 VR 手柄。当输出设施发生变化时会触发 oninputsourceschange 事件。
onselect 和 onsqueeze 等相干事件会在用户按下主性能按键或主挤压按键时触发。
空间
requestReferenceSpace 办法会返回 XRReferenceSpace 对象,次要用于跟踪空间信息,前面章节将会具体解说该对象。
渲染循环
requestAnimationFrame 办法中须要产生新的帧给用户,它根本与 window.requestAnimationFrame 相似,惟一不同的是,它的回调函数的第二个参数是一个 XRFrame 对象。
cancelAnimationFrame 办法与 window.cancelAnimationFrame 相似。
渲染状态
renderState 属性是 XRSession 可配置渲染参数的值,例如配置远或近的深度、FOV 等属性,该对象签名如下。
dictionary XRRenderStateInit {
double depthNear;
double depthFar;
double inlineVerticalFieldOfView;
XRWebGLLayer? baseLayer;
sequence<XRLayer>? layers;
};
[SecureContext, Exposed=Window] interface XRRenderState {
readonly attribute double depthNear;
readonly attribute double depthFar;
readonly attribute double? inlineVerticalFieldOfView;
readonly attribute XRWebGLLayer? baseLayer;
};
depthNear 是透视矩阵的近裁切面,默认为 0.1
depthFar 是透视矩阵的远裁切面,默认为 1000
inlineVerticalFieldOfView 是 inline 模式下的垂直 FOV,默认为 90 度弧度(其余模式为 null)
baseLayer 渲染层,是 XR 合成器获取图片的中央
layers 自定义合成层,目前还不反对,配置将间接报错
通过 updateRenderState 办法能够更新这些参数,在获取到 XRSession 后,必须更新的一个属性是 baseLayer,它是一个 XRWebGLLayer 对象,该对象上面会具体解说,能够通过 new XRWebGLLayer(XRSession, WebGLRenderingContext) 来构建一个。
这里须要留神 WebGLRenderingContext 须要是 XR 兼容的,有两种办法来创立 XR 兼容的上下文。第一种是在创立的时候间接传入 xrCompatible 参数。
const canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true});
另一种形式是 XR 性能并不是必须的,在反对 XR 的状况才应用,不反对则应用一般 WebGL 渲染。这时候能够应用 makeXRCompatible 办法。
const canvas = document.createElement('canvas');
gl = canvas.getContext('webgl');
gl.makeXRCompatible().then(() => {xrSession.updateRenderState({ baseLayer: new XRWebGLLayer(xrSession, gl) });
});
应用该办法还须要利用解决上下文失落问题,如果不监听 makeXRCompatible 将会间接 reject。
// 监听上下文失落
canvas.addEventListener("webglcontextlost", (event) => {
// 表明本人解决上下文复原
event.preventDefault();});
canvas.addEventListener("webglcontextrestored", () => {
// 上下文复原,从新加载必要资源
loadSceneGraphics();});
XRWebGLLayer
XRWebGLLayer 提供用于渲染的 WebGL framerbuffer,并且启用 XR 设施硬件加速 3D 渲染。签名如下。
typedef (WebGLRenderingContext or
WebGL2RenderingContext) XRWebGLRenderingContext;
dictionary XRWebGLLayerInit {
boolean antialias = true;
boolean depth = true;
boolean stencil = false;
boolean alpha = true;
boolean ignoreDepthValues = false;
double framebufferScaleFactor = 1.0;
};
[SecureContext, Exposed=Window]
interface XRWebGLLayer: XRLayer {
constructor(XRSession session,
XRWebGLRenderingContext context,
optional XRWebGLLayerInit layerInit = {});
// Attributes
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;
// Methods
XRViewport? getViewport(XRView view);
// Static Methods
static double getNativeFramebufferScaleFactor(XRSession session);
};
构造函数能够承受 3 个,除了下面章节讲的 XRSession 和 WebGL 上下文,还承受一个 XRWebGLLayerInit 对象,其中的 antialias、depth、stencil 和 alpha 与 WebGL 中的意义一样,这里不再具体解说。
ignoreDepthValues 示意 XR 合成器是否能够读取深度缓存信息来帮忙合成器渲染,如果深度缓存中存储的不是以后的场景深度缓存,那么合成器如果读取该值,可能会造成画面呈现伪影。该参数或属性示意合成器是否疏忽读取深度缓存。
framebufferScaleFactor 属性示意对 framebuffer 的缩放,UA 会有个缩放为 1 的默认 framebuffer 大小,该大小可能和 native 大小不统一,例如有些设施举荐应用低分辨率来保障性能。通过 framebufferScaleFactor 参数能够设置 UA 创立 framebuffer 的大小,例如缩放 0.5 将创立宽高是默认一半的 framebuffer。
如果想创立和 native 一样大的 framebuffer,能够应用 getNativeFramebufferScaleFactor 静态方法获取 native 大小的缩放,如下所示。
const nativeScaleFactor = XRWebGLLayer.getNativeFramebufferScaleFactor(xrSession);
const glLayer = new XRWebGLLayer(xrSession, gl, { framebufferScaleFactor: nativeScaleFactor});
xrSession.updateRenderState({baseLayer: glLayer});
framebuffer 缩放每个 XRSession 只能配置一次,如果想再次配置须要从新创立一个 XRWebGLLayer。
function rescaleWebGLLayer(scale) {let glLayer = new XRWebGLLayer(xrSession, gl, { framebufferScaleFactor: scale});
xrSession.updateRenderState({baseLayer: glLayer});
});
这样重新配置会比拟重,对性能有影响,不应该频繁做该操作。
fixedFoveation 属性示意固定注视点渲染级别,0 示意最小,1 示意最大,该技术会在用户凝视的中央应用高分辨率,眼帘边缘应用低分辨率,进步性能,如果设施不反对,该属性则为 null。该值能够动静批改,批改过后的下一帧将利用最新的值。
framebuffer 属性示意是最终画面要渲染到的中央,如果是 inline 模式该值为 null。该 framebuffer 不能被检查和操作,如果对它执行 deleteFramebuffer、getFramebufferAttachmentParameter 等办法将会间接报错,如果在 session.requestAnimationFrame 回调函数里面操作也会报错。
framebufferWidth 和 framebufferHeight 属性别离示意 framebuffer 的宽高。
XRFrame
XRFrame 示意 XRSession 在给定工夫点所有被跟踪状态的快照。XRFrame 能够在 XRSession 的 requestAnimationFrame 回调函数中获取,它的签名如下。
[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 只在 requestAnimationFrame 回调函数中无效,一旦控制权返回给浏览器,XRFrame 就会被标记为生效,这时候再去拜访下面的办法将会间接报错。
session 属性是创立它的 XRSession 对象。
predictedDisplayTime 是预测的该帧在设施上显示的工夫点,对于 inline 模式,该值与 requestAnimationFrame 第一个参数雷同。
getViewerPose 办法返回以后 XRFrame 工夫点,XR 设施关联的 referenceSpace 中观察者的空间姿态信息。
getPose 办法返回在以后 XRFrame 工夫点,给定空间绝对于 baseSpace 空间的姿态信息。
XRReference
SpaceXRReferenceSpace 继承于 XRSpace(空对象)用于关联用户的物理空间,XRReferenceSpace 中的坐标系与 WebGL 中统一,+X 向右,+Y 向上,+Z 向后。XRReferenceSpace 签名如下。
enum XRReferenceSpaceType {
"viewer",
"local",
"local-floor",
"bounded-floor",
"unbounded"
};
[SecureContext, Exposed=Window]
interface XRReferenceSpace : XRSpace {[NewObject] XRReferenceSpace getOffsetReferenceSpace(XRRigidTransform originOffset);
attribute EventHandler onreset;
};
目前 XRReferenceSpaceType 分为 5 种类型,别离如下。
- viewer 示意具备原生原点的跟踪空间,个别用于不进行任何跟踪场景,任何设施都应该反对该类型
- local 示意只跟踪用户旋转,不跟踪地位,能够了解为坐下,只用头部来观看场景
- local-floor 与 local 类型类似,然而它是站立着的
- bounded-floor 示意在平安区内跟踪旋转和地位,用户能够齐全与场景进行交互
- unbounded 示意用户能够自在在场景中挪动和旋转,没有安全区限度
XRReferenceSpaceType 对象个别通过 XRSession 的 requestReferenceSpace 办法获取,对于 bounded-floor 类型,返回的是 XRBoundedReferenceSpace 对象,该对象继承于 XRReferenceSpaceType,签名如下。
[SecureContext, Exposed=Window]
interface XRBoundedReferenceSpace : XRReferenceSpace {readonly attribute FrozenArray<DOMPointReadOnly> boundsGeometry;};
多进去的 boundsGeometry 属性用于示意安全区,它是顺时针的点的数组。
getOffsetReferenceSpace 办法用于对空间进行调整,例如用手柄对现有空间进行一些旋转调整。
onreset 事件在空间被重置时触发,例如,用户校准 XR 设施或 XR 设施重连后主动切回原点。
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 属性用于形容地位和旋转。
linearVelocity 属性用于形容线速度,米每秒。
angularVelocity 属性用于形容角速度,弧度每秒。
emulatedPosition 属性示意 transform 属性中的地位信息是否是模仿预计进去的。
XRViewerPose
XRViewerPose 继承与 XRPose,形容用户在跟踪的 XR 场景中的状态。其签名如下。
[SecureContext, Exposed=Window] interface XRViewerPose : XRPose {[SameObject] readonly attribute FrozenArray<XRView> views;
};
它只多出一个 views 属性,它示意用户左眼或右眼看到的场景,必须每个 XRView 能力在 XR 设施上正确展现场景。
XRRigidTransform
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;
};
position 属性用于形容地位信息。
orientation 属性用于形容旋转信息,它是四元数。
matrix 属性是形容地位和旋转的矩阵,和 WebGL 一样是列主序。
inverse 属性返回以后 XRRigidTransform 对象逆对象。
XRView
XRView 示意单个视口,XR 设施向用户出现的图像。其签名如下。
enum XREye {
"none",
"left",
"right"
};
[SecureContext, Exposed=Window] interface XRView {
readonly attribute XREye eye;
readonly attribute Float32Array projectionMatrix;
[SameObject] readonly attribute XRRigidTransform transform;
readonly attribute double? recommendedViewportScale;
undefined requestViewportScale(double? scale);
};
eye 属性用于示意该 XRView 对应的眼睛,如果设施不辨别左右眼则为 ‘none’。
projectionMatrix 属性为投影矩阵。
transform 属性示意 getViewerPose() 办法中提供的旋转和地位信息。
recommendedViewportScale 属性为设施举荐缩放。
requestViewportScale 办法能够批改该 XRView 缩放,该办法能够频繁调用,直到 xrWebGLLayer.getViewport(xrView) 获取它的 viewport 时才失效。
XRViewport
XRViewport 用于示意单个 XRView 示意的视口,其签名如下。
[SecureContext, Exposed=Window] interface XRViewport {
readonly attribute long x;
readonly attribute long y;
readonly attribute long width;
readonly attribute long height;
};
个别利用 XRWebGLLayer 获取该对象,获取到后间接设置 WebGL 的 viewport。
xrSession.requestAnimationFrame((time, xrFrame) => {const viewer = xrFrame.getViewerPose(xrReferenceSpace);
gl.bindFramebuffer(xrWebGLLayer.framebuffer);
for (xrView of viewer.views) {const xrViewport = xrWebGLLayer.getViewport(xrView);
gl.viewport(xrViewport.x, xrViewport.y, xrViewport.width, xrViewport.height);
}
});
XRInputSource
XRInputSource 示意一个输出源,例如 VR 手柄,其签名如下。
enum XRHandedness {
"none",
"left",
"right"
};
enum XRTargetRayMode {
"gaze",
"tracked-pointer",
"screen"
};
[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;
};
handedness 属性示意该输出设施是哪个手握持,如果不辨别左右手或不晓得则为 ‘none’。
targetRayMode 属性用于形容如何出现指标射线,gaze 类型为用户凝视输出,tracked-pointer 类型为手柄输出的激光射线,个别为从食指射出,screen 为 inline 模式下的鼠标或触屏输出。
targetRaySpace 是 XRSpace 对象,用于跟踪该输出源的光线的旋转和地位信息。
gripSpace 是 XRSpace 对象,用于跟踪该输出设施(VR 手柄)的旋转和地位信息。
profiles 输出源的形容信息,通过它能够获取是哪个平台的 VR 手柄,这样就能够加载不同的 VR 手柄模型。
全景 VR 图片
理解完了 WebXR,上面来利用它来实现一个全景 VR 图片查看器,它能够在 VR 设施中全景查看 VR 图片。
目前有很多 VR 看房利用,它们是在桌面浏览器或手机上通过鼠标或手指滑动来查看全景图片,在 VR 设施中查看全景图片根本与在桌面浏览器中查看差不多,在 VR 设施中全景图片会包裹用户,带来更好的沉迷感,用户通过旋转头部来查看全景图片。
避免代码太多,上面 Demo 代码中将疏忽 WebGL 相干代码,WebGL 相干代码和只在桌面浏览器中渲染没有区别。
function main() {
const xr = navigator.xr
let refSpace
// 第一步查看以后环境
if (xr) {xr.isSessionSupported('immersive-vr').then((supported) => {if (supported) {const btn = document.createElement('button')
btn.textContent = '进入 VR'
btn.onclick = onBtnClick
document.body.appendChild(btn)
} else {document.body.innerHTML = '以后设施不反对 VR'}
}).catch(() => {document.body.innerHTML = '检测失败'})
} else {document.body.innerHTML = '以后浏览器不反对 WebXR'}
// 以后反对 VR 并且用户无意进入 VR
function onBtnClick () {
// 申请进入 VR 会话
xr.requestSession('immersive-vr').then(session => {initWebGL() // 初始化 WebGL,创立 gl 上下文 等
session.updateRenderState({baseLayer: new XRWebGLLayer(session, gl) });
// 设置渲染层
// 申请 local 空间,咱们只须要跟踪用户头部旋转
session.requestReferenceSpace('local').then(s => {
refSpace = s
session.requestAnimationFrame(onXRFrame); // 开始渲染
})
})
}
function onXRFrame(time, frame) {
const session = frame.session;
session.requestAnimationFrame(onXRFrame);
const glLayer = session.renderState.baseLayer;
gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
// 设置渲染 framebuffer
const pose = frame.getViewerPose(refSpace)
// 获取旋转和视图信息
if (pose) {
pose.views.forEach(v => {
// 渲染每一个 view,左眼和右眼
const vp = glLayer.getViewport(v)
gl.viewport(vp.x, vp.y, vp.width, vp.height)
// 设置 gl 的 viewport
gl.uniform1f(eyeLoc, v.eye === 'right' ? 1 : 0)
// 通知着色器是左眼还是右眼,v.transform.matrix[12] = 0
v.transform.matrix[13] = 0
v.transform.matrix[14] = 0
// local 类型,也可能传递地位信息,这里将它去除
gl.uniformMatrix4fv(martixLoc, false, mat4.mul(mat4.create(), v.transform.matrix, mat4.invert(mat4.create(), v.projectionMatrix)));
// 通知着色器矩阵信息
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
// 渲染模型
})
}
}
}
}
下面例子中次要分为 3 步。
- 查看以后环境是否反对 VR。
- 用户点击按钮示意要进入 VR,创立好 XRSession 会话和 XRReferenceSpace 空间。循
- 环渲染画面,获取用户左眼和右眼视图和用户头部旋转信息别离渲染不同画面。
下面例子中渲染全景图片形式应用的是 equirectangular-3d 投影,这部分代码和本篇文章关联不大,这里就疏忽相干的代码。
原始图如下。
最终的渲染成果如下。
总结
利用 WebXR Device API 能够在 Web 环境拜访到 XR 设施,它次要分为 3 种模式 inline、immersive-vr 和 immersive-ar,inline 模式还是在渲染在浏览器页面中,而 immersive-vr 则是拜访 VR 设施,将画面渲染到 VR 设施中,整体渲染过程与一般 WebGL 程序差不多,只不过画面要渲染到 XRWebGLLayer 的 framebuffer 中,并且辨别左右眼。