WebXR图像追踪结合ThreeJS实现AR拖拽时交集数组为空问题
核心问题1:模型矩阵更新未同步到世界矩阵
你设置了mesh.matrixAutoUpdate = false并手动赋值矩阵,但没有标记世界矩阵需要更新,导致射线检测时模型的世界位置还是初始值,无法被射线命中。
修改代码:
在设置mesh.matrix.fromArray(pose.transform.matrix)后,添加:
mesh.matrixWorldNeedsUpdate = true;
同理,若启用mesh2,也要做同样修改。
核心问题2:射线检测未递归子对象
GLTF加载的模型是THREE.Scene实例,实际可交互的网格是它的子对象。你当前的射线检测raycaster.intersectObjects(group.children, false)第二个参数为false,不会递归检测子对象,只能检测到空的Scene容器,无法命中实际网格。
修改代码:
将getIntersections函数里的检测代码改为:
return raycaster.intersectObjects(group.children, true);
核心问题3:OrbitControls干扰AR相机姿态
AR模式下,相机姿态由WebXR会话控制,初始化的OrbitControls会覆盖相机的位置和旋转,导致射线计算的相机/控制器姿态错误。
修改代码:
在init函数中初始化OrbitControls后,添加会话状态监听:
// 初始化OrbitControls后添加 controls.enabled = !renderer.xr.isPresenting; // 监听XR会话状态,自动禁用/启用控制器 renderer.xr.addEventListener('sessionstart', () => { controls.enabled = false; }); renderer.xr.addEventListener('sessionend', () => { controls.enabled = true; });
核心问题4:手持AR设备的射线适配错误
手机触屏的AR控制器,目标射线模式是screen,而非VR手柄的tracked-pointer,当前基于控制器世界矩阵计算射线的方式不适合触屏,需要适配屏幕触摸点生成射线。
修改代码:
修改getIntersections函数,根据控制器的targetRayMode调整射线计算逻辑:
function getIntersections(controller) { // 适配屏幕触摸模式 if (controller.userData.targetRayMode === 'screen') { const frame = renderer.xr.getFrame(); const inputSource = controller.userData.inputSource; if (frame && inputSource) { const pose = frame.getPose(inputSource.targetRaySpace, renderer.xr.getReferenceSpace()); if (pose) { raycaster.ray.origin.setFromMatrixPosition(pose.transform.matrix); raycaster.ray.direction.set(0, 0, -1).applyQuaternion(new THREE.Quaternion().fromArray(pose.transform.orientation)); } } } else { // VR手柄的射线计算逻辑 controller.updateMatrixWorld(); tempMatrix.identity().extractRotation(controller.matrixWorld); raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld); raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix); } return raycaster.intersectObjects(group.children, true); }
同时修正onSelectStart中targetRayMode的获取方式:
function onSelectStart(event) { const controller = event.target; controller.userData.targetRayMode = event.data.inputSource.targetRayMode; const intersections = getIntersections(controller); // ... 后续代码 }
额外优化:模型尺寸适配
GLTF模型可能尺寸过大或过小,导致射线检测难以命中,建议添加合适的缩放:
mesh.scale.set(0.1, 0.1, 0.1); // 根据模型实际尺寸调整 mesh.matrixAutoUpdate = false; mesh.updateMatrix(); // 手动更新矩阵
内容的提问来源于stack exchange,提问作者Wolfetto

