Pico Ultra VR 适配 — 调试过程记录¶
背景¶
truth-lab 最速降线实验需要支持 WebXR VR,在 Pico Ultra 头显上运行。项目使用 React Three Fiber v9.6.1 + Three.js r174 + react-three/xr v6。
调试时间线¶
第 1 轮:极简测试页 → 场景不工作¶
症状:纯 Three.js 测试页(xr-test.html)能在 Pico 正常进入 VR 并看到方块,但完整 truth-lab 场景不行。
行动: - 从 @react-three/xr v6 方案切换到原生 WebXR API - onCreated 中设置 gl.xr.enabled = true - 使用 navigator.xr.requestSession('immersive-vr', { requiredFeatures: ['local-floor'] }) - 使用 gl.xr.setSession(session) + gl.xr.setReferenceSpaceType('local-floor')
结果:❌ 失败。报错 "failed to execute requestReferenceSpace on 'XRSession', this device does not support the requested reference space type"
第 2 轮:双 Agent 根因分析¶
方法:用 delegate_task 启动两个子 Agent: - Agent A:深度分析所有可能性(分析 R3F 源码、Three.js WebXRManager、pmndrs/xr 源码) - Agent B:审阅 Agent A 结论,评估可靠性
Agent A 核心发现: 1. R3F v9.6.1 自带 XR 支持(handleXRFrame + sessionstart/sessionend 监听) 2. @pmndrs/xr(@react-three/xr 的内核)硬编码 'local-floor' 为 required feature 3. R3F 的 loop() 在 isPresenting=true 时自动跳过
Agent B 审阅结论(高可靠性原因筛选): - ✅ 最可能:Pico 不支持 'local-floor' reference space type - ✅ 遗漏发现:@pmndrs/xr 内部 calls session.requestReferenceSpace('local-floor') - ✅ 遗漏发现:gl.xr.setReferenceSpaceType() 多次被覆盖的时序问题
行动: 1. 移除 requiredFeatures: ['local-floor'] 2. 删除 gl.xr.setReferenceSpaceType('local-floor') 错用 3. 改用 alert() 替换为页面内错误提示(VR 浏览器屏蔽 alert) 4. 添加 rendererRef 清理
结果:❌ 还是失败。"requestReferenceSpace failed" — @pmndrs/xr 内部依然用 'local-floor'
第 3 轮:猴子补丁 setReferenceSpaceType¶
发现:@pmndrs/xr 库在 store.js 和 init.js 中硬编码 'local-floor',无论外部怎么配置。
行动: 1. 在 onCreated 中对 gl.xr.setReferenceSpaceType 做猴子补丁,拦截 'local-floor' → 'local' 2. 使用 customSessionInit: { requiredFeatures: [] } 绕过 session 创建时的 feature 检查
结果:❌ 虽然 session 创建成功(不闪退了),但场景全黑——坐标问题
第 4 轮:VR 场景坐标偏移¶
发现:桌面场景在 z=0 平面,VR 相机在原点看向 -z,场景在用户**身后**。场景中心在 y=-4,在用户**脚下**。
行动: 1. 为 SceneContent 添加 <group position={vrOffset}> 2. VR 模式偏移 [-5, 2.8, -3](场景中心移到 [0, -1.2, -3],用户面前 3 米)
结果:❌ 还是全黑
第 5 轮:去掉 react-three/xr,纯 R3F 原生方案¶
发现:R3F v9.6.1 自带完整 XR 支持。@react-three/xr 添加了大量不必要的功能(手柄建模、手部追踪、teleport 等),可能干扰基本渲染。
行动: 1. 完全移除 @react-three/xr 和 <XR> 组件 2. 移除 createXRStore() 3. 自己实现 VrButton 组件,直接调用 navigator.xr.requestSession() + gl.xr.setSession(session) 4. 用全局 _rendererRef 传递 renderer 引用
结果:❌ "requestReferenceSpace failed, device does not support requested reference space type" —— 猴子补丁没有阻止 THREE 内部使用 'local-floor'
第 6 轮:紧挨着 setSession 前设置 referenceSpaceType ✅¶
发现:之前在 onCreated 中设置 setReferenceSpaceType('local'),但在 R3F 初始化过程中可能有其他代码覆盖了这个设置。
行动: 1. 保留猴子补丁 2. 在 handleClick 中,gl.xr.setSession(session) 紧挨着之前 再调一次 gl.xr.setReferenceSpaceType('local') 3. 消除一切时序窗口
结果:✅ 成功进入 VR!场景正确显示
最终方案总结¶
3 个关键修复¶
| 修复 | 解决什么问题 | 方法 |
|---|---|---|
移除 requiredFeatures | Pico 不支持 'local-floor' 作为 session 必需特性 | navigator.xr.requestSession('immersive-vr') 无参数 |
猴子补丁 setReferenceSpaceType | Three.js 和第三方库内部默认为 'local-floor' | 拦截方法调用,'local-floor' → 'local' |
| 紧挨着 setSession 前设置 | 消除初始化过程中的覆盖时序 | gl.xr.setReferenceSpaceType('local') 紧贴 setSession() |
最终架构¶
- 不使用
@react-three/xr/@pmndrs/xr - 完全依赖 R3F v9.6.1 内置 XR 支持(sessionstart/sessionend + handleXRFrame)
- Vendor 体积:2.0 MB(vs 4.3MB 使用 react-three/xr 时)
代码要点¶
待改进¶
- VR 模式下的控制点拖拽交互(需手柄支持)
- VR 场景位置可能需要微调
- 去掉 console.log 调试输出