已知投影矩阵,如何计算Three.js相机矩阵?
解决Google Maps WebGL API + Three.js高海拔物体射线检测失效问题
问题核心
当前射线检测逻辑在近地面场景正常,但高海拔(400米+)物体无法被选中,根源在于Google Maps投影矩阵对应的NDC空间与自定义射线的Z值不匹配;纯Three.js原生方法依赖相机位置,但Google Maps未开放该参数。
方案1:修正射线生成逻辑(优先尝试)
你之前用0和0.5作为NDC的Z轴值,不符合标准透视投影的NDC范围(标准NDC的Z轴是-1到1,对应近/远平面)。Google Maps的投影矩阵大概率遵循标准NDC规则,修改射线生成代码即可覆盖整个视锥体:
raycast(normalizedScreenPoint: three.Vector2): three.Intersection[] { // 用标准NDC近/远平面Z值(-1/1)创建点 const ndcNear = new three.Vector3(normalizedScreenPoint.x, normalizedScreenPoint.y, -1); const ndcFar = new three.Vector3(normalizedScreenPoint.x, normalizedScreenPoint.y, 1); // 计算投影矩阵的逆矩阵 this.projectionMatrixInverse.copy(this.camera.projectionMatrix).invert(); // 将NDC坐标转换到相机空间 const cameraSpaceNear = ndcNear.applyMatrix4(this.projectionMatrixInverse); const cameraSpaceFar = ndcFar.applyMatrix4(this.projectionMatrixInverse); // 生成射线:原点为近平面点,方向为远-近并归一化 this.raycaster.ray.origin.copy(cameraSpaceNear); this.raycaster.ray.direction.subVectors(cameraSpaceFar, cameraSpaceNear).normalize(); // 执行检测并返回结果 return this.raycaster.intersectObjects(yourSceneObjectList, true); }
补充说明:高海拔物体处于视锥体的远平面区域,之前的0.5Z值无法覆盖到该范围,导致射线无法命中。
方案2:从投影矩阵推导相机位置
如果必须获取Three.js空间中的相机位置,可通过以下两种方式:
方式1:利用射线原点间接推导
相机在相机空间中的原点是(0,0,0),将该点通过「投影逆矩阵 + 视图逆矩阵」转换到世界空间即可得到相机位置。但Google Maps未开放视图矩阵,可通过以下间接方式获取:
getCameraWorldPosition(): three.Vector3 { // 取NDC空间原点(0,0,-1),转换为相机空间点 const ndcOrigin = new three.Vector3(0, 0, -1); const projectionInverse = new three.Matrix4().copy(this.camera.projectionMatrix).invert(); const cameraSpacePos = ndcOrigin.applyMatrix4(projectionInverse); // 结合anchor经纬度反推世界空间位置,需自行推导相机高度 const cameraLatLngAlt = { ...this.anchor, altitude: this.getCameraAltitude() }; const cameraWorldPos = new three.Vector3().fromArray(transformer.fromLatLngAltitude(cameraLatLngAlt, this.rotation, this.scale)); return cameraWorldPos; }
方式2:屏幕坐标对比法(无需射线)
绕过射线检测,直接将物体世界坐标转换为屏幕坐标,与点击位置做距离判断,适合高海拔物体:
checkObjectClick(object: three.Object3D, clickScreenPos: {x: number, y: number}, renderer: three.WebGLRenderer): boolean { const worldPos = new three.Vector3(); object.getWorldPosition(worldPos); // 将世界坐标转换为NDC坐标 worldPos.project(this.camera); // 转换为屏幕坐标(适配渲染器容器尺寸) const screenX = (worldPos.x + 1) * renderer.domElement.clientWidth / 2; const screenY = (-worldPos.y + 1) * renderer.domElement.clientHeight / 2; // 距离阈值根据物体大小调整(示例为10像素) const distance = Math.sqrt(Math.pow(screenX - clickScreenPos.x, 2) + Math.pow(screenY - clickScreenPos.y, 2)); return distance < 10; }
额外排查点
如果修正射线逻辑后仍失效,可检查:
- Google Maps的Z轴方向:部分地图引擎Z轴向下,而Three.js默认Z轴向外,需反转射线的Z分量。
- 相机的
near/far参数:确保与Google Maps投影矩阵的近远平面范围一致,可尝试将near设为0.1,far设为10000。
内容的提问来源于stack exchange,提问作者Dragonsheep
相关产品推荐
相关产品推荐

