Cesium拾取三维物体坐标源码分析


前言

坐标拾取有几种方式,这里只讲scene.pickPosition。大体思路是:先获取点击的屏幕坐标,然后读取fbo中的深度缓存信息,有了屏幕坐标和深度信息,即可根据矩阵变换,重新计算得到顶点的世界坐标。


流程图大体图下:

深度信息保存过程
在这里插入图片描述

1.深度信息的存储

获取屏幕坐标这个就不讲了,直接讲如何获取深度信息,在此之前,需要先知道深度信息保存在哪里。

在初始化view的时候,会创建GlobeDepth,GlobeDepth会创建两个fbo:_pickColorFramebuffer和_outputFramebuffer,这两个fbo都有深度缓存纹理

function GlobeDepth() {
   
   
  ...
  this._pickColorFramebuffer = new FramebufferManager({
   
   
    depthStencil: true,
    supportsDepthTexture: true,
  });
  this._outputFramebuffer = new FramebufferManager({
   
   
    depthStencil: true,
    supportsDepthTexture: true,
  });
}

为什么要创建GlobeDepth呢,因为cesium并不是直接把场景渲染到屏幕,而是先渲染到fbo,再从fbo到屏幕。如果我们看Scene里面的流程,会发现通常情况下使用的fbo正是GlobeDepth创建的fbo。

 if (environmentState.isSunVisible && scene.sunBloom && !useWebVR) {
   
   
    passState.framebuffer = scene._sunPostProcess.update(passState);
    scene._sunPostProcess.clear(context, passState, clearColor);
  } else if (useGlobeDepthFramebuffer) {
   
   
    passState.framebuffer = view.globeDepth.framebuffer;//通常是使用这个
  } else if (usePostProcess) {
   
   
    passState.framebuffer = view.sceneFramebuffer.framebuffer;
  }

知道深度信息保存在哪里后,我们后面就能通过context.readPixels读取深度信息了。

2.获取坐标

获取坐标的核心方法是pickPositionWorldCoordinates,下面具体说明每一句的作用(仅展示核心代码),大体分两步,先通过pickDepth.getDepth获取深度值,再通过 SceneTransforms.drawingBufferToWgs84Coordinates计算世界坐标

Picking.prototype.pickPositionWorldCoordinates = function(
  scene,
  windowPosition,
  result
) {
   
   
 ...
  scene.view = defaultView;
  const drawingBufferPosition 
### Cesium 中实现地理坐标和空间位置拾取的方法 在 Cesium 中,可以通过多种方式来拾取地理坐标以及三维场景中的空间位置。以下是几种常见的实现方法及其原理: #### 方法一:通过 `ScreenSpaceEventHandler` 和 `getPickRay()` 获取交点 可以利用鼠标事件监听器 `ScreenSpaceEventHandler` 来捕获用户的交互行为(如左键单击),并通过 `camera.getPickRay()` 将屏幕上的二维像素坐标转换为三维射线(Ray)。接着使用 `globe.pick()` 计算该射线与地球表面的交点。 具体代码如下: ```javascript var viewer = new Cesium.Viewer('cesiumContainer'); var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); handler.setInputAction(function (event) { var ray = viewer.camera.getPickRay(event.position); var cartesian = viewer.scene.globe.pick(ray, viewer.scene); if (cartesian) { // 笛卡尔坐标转经纬度 var cartographic = Cesium.Cartesian3.toCartographic(cartesian); var longitude = Cesium.Math.toDegrees(cartographic.longitude); // 经度 var latitude = Cesium.Math.toDegrees(cartographic.latitude); // 纬度 var height = cartographic.height; // 高程 console.log(`经度: ${longitude}, 纬度: ${latitude}, 高程: ${height}`); } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); ``` 上述代码实现了从屏幕坐标三维笛卡尔坐标的映射,并进一步将其转化为地理坐标系下的经纬度值[^1]。 --- #### 方法二:直接使用 `viewer.scene.pickPosition` 对于更简单的应用场景,可以直接调用 `pickPosition` 函数完成相同功能。需要注意的是,在某些情况下可能需要启用实验性的扩展支持。 示例代码如下: ```javascript var viewer = new Cesium.Viewer('cesiumContainer'); // 启用 pickPosition 功能 if (!Cesium.defined(Cesium.SceneTransforms.wgs84ToWindowCoordinates)) { throw '当前浏览器不支持 pickPosition'; } var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); handler.setInputAction(function (event) { var earthPosition = viewer.scene.pickPosition(event.position); if (earthPosition) { var cartographic = Cesium.Cartesian3.toCartographic(earthPosition); var longitude = Cesium.Math.toDegrees(cartographic.longitude); var latitude = Cesium.Math.toDegrees(cartographic.latitude); var height = cartographic.height; console.log(`经度: ${longitude}, 纬度: ${latitude}, 高程: ${height}`); } else { console.log('未检测到有效的位置'); } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); ``` 这种方法相比前一种更加简洁明了,但在处理复杂几何体时可能会存在一定的局限性[^2]。 --- #### 方法三:基于椭球模型计算最近距离 如果仅需快速估算某一点相对于地球中心的距离而无需考虑地形起伏,则可采用 `camera.pickEllipsoid()` 方便地获得近似解。 样例如下所示: ```javascript var viewer = new Cesium.Viewer('cesiumContainer'); var ellipsoid = viewer.scene.globe.ellipsoid; var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); handler.setInputAction(function (movement) { var windowPosition = movement.endPosition; var earthPosition = viewer.scene.camera.pickEllipsoid(windowPosition, ellipsoid); if (Cesium.defined(earthPosition)) { var cartographic = Cesium.Cartesian3.toCartographic(earthPosition); var lon = Cesium.Math.toDegrees(cartographic.longitude); var lat = Cesium.Math.toDegrees(cartographic.latitude); console.log(`纬度=${lat.toFixed(7)}, 经度=${lon.toFixed(7)}`); } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); ``` 此方案适用于实时反馈用户操作需求场合,比如拖拽标记物或者绘制路径等功能开发过程中非常实用[^3]。 --- ### 坐标变换注意事项 当涉及到不同类型的坐标系统之间的互相转化时,请务必注意单位一致性问题;另外还需留意是否存在特殊边界条件影响最终结果准确性等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值