深色模式
拾取:从屏幕回到地理空间
渲染把三维世界压缩到二维屏幕;鼠标点击则试图逆向找回含义。一个屏幕像素可能对应建筑、天气面、地形或天空,因此“获取点击经纬度”不是唯一确定的操作。先决定要选中哪个对象、哪一种表面,再选择拾取方法。
点击确定方向,表面求交确定距离;这需要向量的概念。若想理解深度反算,可继续读齐次坐标与投影。
射线(ray)可写成 p(t)=o+t*d:o 是相机原点,d 是长度为 1 的观察方向,t≥0 是沿射线走过的距离。o 和 p 用米,t 也用米。一个二维像素只确定一条方向线;还需要与表面求交(intersection)才能选出具体三维位置。
三种问题对应不同路径
scene.pick 返回被拾取的对象或其标识,用来回答“点中了谁”。scene.pickPosition 利用深度缓冲重建三维位置,回答“此像素记录的表面在哪里”。globe.pick 则让相机射线与当前地球表面求交,适合明确要求地表位置的功能。
以下是接在第二课初始化之后的功能片段,沿用已经导入的 Cesium 命名空间和已创建的 viewer,省略容器与初始化代码。
js
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(({ position }) => {
const picked = viewer.scene.pick(position);
const ray = viewer.camera.getPickRay(position);
const ground = ray && viewer.scene.globe.pick(ray, viewer.scene);
if (!ground) return; // 点击天空等情况没有地表交点。
const c = Cesium.Cartographic.fromCartesian(ground);
console.log({
object: picked?.id,
longitude: Cesium.Math.toDegrees(c.longitude),
latitude: Cesium.Math.toDegrees(c.latitude),
ellipsoidHeight: c.height,
});
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 功能卸载时:handler.destroy();这里同时获取对象和地表位置,二者可能来自不同表面。例如点击高楼时,对象是建筑,地表交点位于建筑下方。若产品要显示楼顶坐标,应另选合适的深度拾取路径,不能把地表结果当作建筑表面。
深度读数并不覆盖所有对象
使用 pickPosition 前需检查 pickPositionSupported,还应处理返回空值。透明对象是否进入相应深度拾取过程,与场景设置和渲染方式有关;pickTranslucentDepth 也会增加额外工作。屏幕上看得见的半透明天气场,不保证可以直接得到其科学高度。
气象场查询更常见的做法是:先得到地理位置,再按网格定义采样原始数值。最终显示的 RGB 色值不能替代温度、风速等数据。查询时应同时返回单位、层次、时间与缺测状态,避免给一个漂亮的数字却没有可解释语义。
对象如何被画出
比较 Entity 与 Primitive,查看几何与材质的边界。
在浏览器本地运行 · 无需账号 · 鼠标拖动旋转,滚轮缩放
底图:Natural Earth II(公共领域);其余几何和地形为教学合成。 数据说明
查看运行源码
此代码直接读取实际运行的实验模块。按 kind 分支定位当前实验,也可查看数据公式和 GLSL。
读取源码…观察任务: 单击不同盒子,查看“拾取结果”,再单击空白区域。切换 Entity 与 Primitive 后重复操作。实验展示的是对象拾取;若进一步查询地表,天空区域和地平线附近又会有什么困难?
展开解释
天空通常没有地表交点。地平线附近射线与表面接近相切,少量屏幕偏移可能造成较大地理位置变化;这叫误差放大(error amplification),不是增加显示小数位就能解决。工具提示应允许无命中,并根据数据精度显示合理的小数位数;多显示几位小数不会提高实际定位精度。
官方延伸:Scene 拾取接口、Globe 射线求交。