如果您已经使用过 WebXR Device API,那么您已经完成了大部分工作。
WebXR Device API 已于去年秋天在 Chrome 79 中推出。正如当时所述,Chrome 对该 API 的实现仍在开发中。Chrome 很高兴地宣布,部分工作已完成。Chrome 81 中新增了两项功能:
本文将介绍增强现实。如果您已经使用过 WebXR Device API,那么您会很高兴地知道,您需要学习的新内容非常少。进入 WebXR 会话的方式基本相同。运行帧循环在很大程度上是相同的。两者之间的区别在于配置,这些配置可让内容以适合增强现实的方式显示。如果您不熟悉 WebXR 的基本概念,请阅读我之前关于 WebXR Device API 的文章,或者至少熟悉其中介绍的主题。您应了解如何请求和进入会话,以及如何运行帧循环。
如需了解点击测试,请参阅随附文章在真实视图中放置虚拟对象。本文中的代码基于 Immersive Web Working Group 的 WebXR Device API 示例中的 Immersive AR 会话示例(演示 来源)。
在深入了解代码之前,您应该至少使用一次沉浸式 AR 会话示例。您需要一部搭载 Chrome 81 或更高版本的新型 Android 手机。
它有什么用途?
增强现实技术将成为许多现有或新网页的宝贵补充,让它们无需离开浏览器即可实现 AR 用例。例如,它可以帮助用户在教育网站上学习,并允许潜在买家在购物时直观了解家中物品。
我们来考虑一下第二种使用情形。假设您要模拟将虚拟对象的等身大小表示法放置在真实场景中。放置后,图片会保留在所选 Surface 上,显示的尺寸与实际内容在该 Surface 上的尺寸相同,并且允许用户围绕图片移动,以及向图片靠近或远离图片。与二维图片相比,这让观看者能够更深入地了解对象。
我有点领先我自己。若要实际执行我所描述的操作,您需要 AR 功能和一些检测表面的方法。本文将介绍前者。WebXR Hit Test API 中的相关文章(链接到上文)对后者进行了介绍。
请求会话
请求会话与之前的操作非常相似。首先,通过调用 xr.isSessionSupported()
确定当前设备上是否支持所需的会话类型。请改为请求 'immersive-ar'
,而不是像之前那样请求 'immersive-vr'
。
if (navigator.xr) {
const supported = await navigator.xr.isSessionSupported('immersive-ar');
if (supported) {
xrButton.addEventListener('click', onButtonClicked);
xrButton.textContent = 'Enter AR';
xrButton.enabled = supported; // supported is Boolean
}
}
与之前一样,这会启用“进入 AR”按钮。当用户点击该按钮时,调用 xr.requestSession()
,同时传递 'immersive-ar'
。
let xrSession = null;
function onButtonClicked() {
if (!xrSession) {
navigator.xr.requestSession('immersive-ar')
.then((session) => {
xrSession = session;
xrSession.isImmersive = true;
xrButton.textContent = 'Exit AR';
onSessionStarted(xrSession);
});
} else {
xrSession.end();
}
}
一个便捷属性
您可能已经注意到,我在上一个代码示例中突出显示了两行。XRSession
对象似乎有一个名为 isImmersive
的属性。这是一个我自己创建的便利属性,不属于规范的一部分。我稍后将用它来决定向查看者显示什么内容。为什么此属性不是 API 的一部分?由于您的应用可能需要以不同的方式跟踪此属性,因此规范作者决定让 API 保持简洁。
进入会话
回想一下我在之前的文章中提到的 onSessionStarted()
是什么样的:
function onSessionStarted(xrSession) {
xrSession.addEventListener('end', onSessionEnded);
let canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
xrSession.requestReferenceSpace('local-floor')
.then((refSpace) => {
xrRefSpace = refSpace;
xrSession.requestAnimationFrame(onXRFrame);
});
}
我需要添加一些内容来处理增强现实渲染。关闭背景 首先,我要确定是否需要背景这是我将首次使用便捷属性的地方。
function onSessionStarted(xrSession) {
xrSession.addEventListener('end', onSessionEnded);
if (session.isImmersive) {
removeBackground();
}
let canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
xrSession.requestAnimationFrame(onXRFrame);
});
}
参考空间
我之前的文章略去了参考空间。我要介绍的示例使用了其中两个,因此现在需要修正这一遗漏。
参照空间描述了虚拟世界与用户的物理环境之间的关系。具体方法如下:
- 为在虚拟世界中用于表示位置的坐标系指定原点。
- 指定用户是否应在该坐标系内移动。
- 该坐标系是否具有预先建立的边界。(此处展示的示例不使用具有预先确定边界的坐标系。)
对于所有参考空间,X 坐标表示左右,Y 坐标表示上下,Z 坐标表示前后。正值分别表示向右、向上和向后。
XRFrame.getViewerPose()
返回的坐标取决于请求的参考空间类型。我们在讲解帧循环时会详细介绍这一点。现在,我们需要选择适合增强现实的参考类型。同样,这使用了我的便捷属性。
let refSpaceType
function onSessionStarted(xrSession) {
xrSession.addEventListener('end', onSessionEnded);
if (session.isImmersive) {
removeBackground();
}
let canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
xrSession.requestAnimationFrame(onXRFrame);
});
}
如果您查看过沉浸式 AR 会话示例,就会发现场景最初是静态的,完全不是增强现实。您可以用手指拖动和滑动来在场景中移动。如果您点击“开始 AR”,背景就会消失,您可以通过移动设备在场景中移动。这些模式使用不同的参考空间类型。上面突出显示的文字展示了如何进行选择。它使用以下引用类型:
local
- 原点位于会话创建时观看者的所在位置。这意味着,体验不一定有明确定义的底价,并且起源的确切位置可能会因平台而异。虽然聊天室没有预先设定的边界,但预计用户在观看内容时除了旋转之外不会有其他移动。如您在我们自己的 AR 示例中看到的,您或许可以在空间内进行一些移动。
viewer
- 最常用于在页面中内嵌显示的内容,此间距会随观看设备而变化。传递给 getViewerPose 时,它不会提供跟踪,因此始终报告原点位置,除非应用使用 XRReferenceSpace.getOffsetReferenceSpace()
修改了位置位置。该示例使用它来启用基于触摸的相机平移。
运行帧循环
从概念上讲,我在之前的文章中描述的 VR 会话没有什么变化。将引用空间类型传递给 XRFrame.getViewerPose()
。
返回的 XRViewerPose
将用于当前的引用空间类型。使用 viewer
作为默认值可让网页在请求用户同意使用 AR 或 VR 内容之前显示内容预览。这说明了一点:内嵌内容使用与沉浸式内容相同的帧循环,从而减少了需要维护的代码量。
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(refSpaceType);
if (xrViewerPose) {
// Render based on the pose.
}
}
总结
本系列文章仅介绍了在网络上实现沉浸式内容的基础知识。沉浸式 Web 工作组的 WebXR Device API 示例介绍了更多功能和用例。我们还刚刚发布了一篇点击测试文章,其中介绍了用于检测表面和在真实相机视图中放置虚拟项的 API。请查看这些文章,并关注 web.dev 博客,以便在未来一年内阅读更多文章。
照片由 David Grandmougin 拍摄,选自 Unsplash