跳到主要内容

XR 支持

PlayCanvas Web Components 可以便捷地为应用添加虚拟现实(VR)和增强现实(AR)。

基础配置​

启用 XR 需要:

  1. 使用 WebGL 2 渲染的 <pc-app>,详见下文。
  2. XR 专用脚本,来自引擎 NPM 包。
  3. 挂载 XR 脚本的摄像机 rig。
  4. 进入 XR 的界面,因为 WebXR 要求用户手势启动。
  5. 安全上下文,通过 HTTPS 提供页面,开发时可使用 http://localhost。

<pc-app> 默认使用 WebGPU,但只有浏览器提供 WebXR 与 WebGPU 之间的 XRGPUBinding 时,引擎才能使用 WebGPU 运行 XR。原文撰写时 Chrome 等尚未提供,导致引擎报告 XR 不可用。因此提供 XR 的页面应指定 WebGL 2:

<pc-app backend="webgl2">

嵌入 iframe 时还需 allow="xr-spatial-tracking",否则浏览器不会向页面暴露 XR。

XR 脚本​

引擎 scripts/esm/xr 包含一组脚本,可通过 <pc-asset> 声明:

<pc-asset src="/node_modules/playcanvas/scripts/esm/xr/xr-controllers.mjs"></pc-asset>
<pc-asset src="/node_modules/playcanvas/scripts/esm/xr/xr-menu.mjs"></pc-asset>
<pc-asset src="/node_modules/playcanvas/scripts/esm/xr/xr-navigation.mjs"></pc-asset>
<pc-asset src="/node_modules/playcanvas/scripts/esm/xr/xr-session.mjs"></pc-asset>

路径假设从项目根目录直接提供文件,未使用打包器,和入门中的 npm 方式相同。打包器不会发布 node_modules,因此需要将脚本复制到静态目录,或从 CDN 加载:

<pc-asset src="https://cdn.jsdelivr.net/npm/playcanvas@latest/scripts/esm/xr/xr-controllers.mjs"></pc-asset>
<pc-asset src="https://cdn.jsdelivr.net/npm/playcanvas@latest/scripts/esm/xr/xr-menu.mjs"></pc-asset>
<pc-asset src="https://cdn.jsdelivr.net/npm/playcanvas@latest/scripts/esm/xr/xr-navigation.mjs"></pc-asset>
<pc-asset src="https://cdn.jsdelivr.net/npm/playcanvas@latest/scripts/esm/xr/xr-session.mjs"></pc-asset>
CDN 与导入映射

CDN 加载时,import map 中 playcanvas 应指向与入门一致的 CDN 和版本。生产环境应固定版本,不使用 latest。

  • xr-session.mjs:管理会话生命周期,默认响应 ar:start 和 vr:start,收到 xr:end 或 Escape 时结束,并自动处理 rig 变换、AR 透明及清理。
  • xr-controllers.mjs:为检测到的控制器,包括手,动态下载并渲染 GLB 模型。
  • xr-navigation.mjs:通过指向选择传送、左摇杆平滑移动、右摇杆分段或平滑转向及分段垂直移动。默认落到 ground-height 指定平面,默认 0;也可自行设置 castRay 选择地面。
  • xr-menu.mjs:通过掌心向上手势或控制器按钮显示菜单,条目触发应用事件,适合退出 XR 等操作。

还有第五个脚本 xr-manipulation.mjs,支持双手抓住场景进行移动、旋转和缩放。它操作包含场景内容的目标实体,而非摄像机 rig。

摄像机配置​

XR 脚本应挂载到摄像机实体的父项,xrSession 移动 rig 根节点,头显直接驱动摄像机自身:

<!-- Camera (with XR support) -->
<pc-entity name="camera root">
<pc-entity name="camera" position="0 1.7 5">
<pc-camera></pc-camera>
</pc-entity>
<pc-script>
<pc-script-instance name="xrControllers"></pc-script-instance>
<pc-script-instance name="xrMenu" attributes='{
"menuItems": [{"label": "Exit XR", "eventName": "xr:end"}],
"fontAsset": "asset:arial-font"
}'></pc-script-instance>
<pc-script-instance name="xrNavigation"></pc-script-instance>
<pc-script-instance name="xrSession"></pc-script-instance>
</pc-script>
</pc-entity>
备注

xrMenu 渲染文本,需要与其他资源一起声明 font 类型 <pc-asset>,示例使用 arial.json:

<pc-asset src="assets/fonts/arial.json" type="font" id="arial-font"></pc-asset>

字体由 JSON 和旁边同名 PNG 组成,此处为 arial.png,两者都需复制。menuItems 是嵌套数组,因此 xrMenu 通过 attributes JSON 配置,而非逐属性配置,详见脚本行为。

进入 XR 的界面​

WebXR 仅允许用户手势启动会话,因此需要按钮等界面。下面创建两个简单按钮:

<button id="enterAR">Enter AR</button>
<button id="enterVR">Enter VR</button>

rig 已挂载 xrSession 时,按钮只需发送它监听的事件:

import { whenReady } from '@playcanvas/web-components';

const { app } = await whenReady('pc-app');

document.getElementById('enterAR').addEventListener('click', () => app.fire('ar:start'));
document.getElementById('enterVR').addEventListener('click', () => app.fire('vr:start'));
备注

示例按包名导入 whenReady,因此 import map 必须包含 @playcanvas/web-components,详见代码访问。

事件名由 xrSession 属性 start-ar-event、start-vr-event 和 end-event 指定,与自身事件冲突时可以重命名。无需额外结束代码,xr:end 或 Escape 会结束会话。

还可仅在设备支持相应模式时显示按钮。引擎在启动后异步检测,并在设备连接或断开时再次检测,因此先读取当前值,再监听 available:

import { XRTYPE_AR, XRTYPE_VR } from 'playcanvas';

// Continuing the module above, where app is the running application
const buttons = {
[XRTYPE_AR]: document.getElementById('enterAR'),
[XRTYPE_VR]: document.getElementById('enterVR')
};

for (const [type, button] of Object.entries(buttons)) {
button.hidden = !app.xr.isAvailable(type);
}

app.xr.on('available', (type, available) => {
if (buttons[type]) {
buttons[type].hidden = !available;
}
});

摄像机元素 API​

简单场景可直接调用 <pc-camera> 元素 API,无需脚本。startXr 必须直接从点击处理函数调用,满足用户手势要求:

import { whenReady } from '@playcanvas/web-components';

const camera = await whenReady('pc-camera');

document.getElementById('enter-vr').addEventListener('click', () => {
camera.startXr('immersive-vr', 'local-floor');
});

// ...and later, to leave the session:
camera.endXr();

startXr(type, space) 接受 immersive-ar 或 immersive-vr 会话类型,以及 viewer、local、local-floor、bounded-floor 或 unbounded 参考空间。

arAvailable 和 vrAvailable 分别报告支持情况,两者相互独立。Android ARCore 手机通常只支持 AR,因此各按钮应按自身模式判断。启动后短时间内两者仍可能为 false,需随引擎结果变化更新:

const { app } = await whenReady('pc-app');

const updateButtons = () => {
document.getElementById('enter-vr').hidden = !camera.vrAvailable;
document.getElementById('enter-ar').hidden = !camera.arAvailable;
};

updateButtons();
app.xr.on('available', updateButtons);

startXr 也会检测对应模式,不可用时不执行,因此误显示按钮不会产生明显错误。完整体验优先使用自动管理 rig、AR 透明和清理的 xrSession,快速测试和简单查看器可用元素 API。直接使用 API 开 AR 时,需要自行设置摄像机 clear-color="0 0 0 0" 并移除 pc-sky,以显示现实世界。

许多示例支持 XR,包括基本形状、GLB 加载器和级联阴影,源码展示了完整摄像机 rig。

后续步骤​

PlayCanvas 引擎提供完整 XR 支持和丰富选项,详见 XR 文档。