理解 Cesium 中的相机(1)

背景

基于 Cesium 的二次开发中经常会用到相机(Camera)对象, 相机对象经常用来处理和地图视角相关的功能,比如设置地图初始视角、飞行到指定位置、控制地图最小缩放高度等。虽然经常会用到,但是还没有仔细梳理过相机相关的内容,本文试着梳理一部分。

一、如何理解 Cesium 中的相机

生活中的相机是一个用来拍照的设备,而这里的相机应该理解成一个人机交互的媒介。地图的缩放、平移、旋转,以及相关的鼠标操作都是由相机作为媒介来实现的。相机的位置和姿态参数决定了我们能看到的地图的样子。

可以把使用 Cesium 浏览地图的过程想象成这样一个场景:

一个带有相机的能实时传回拍摄画面的无人机在地球上空飞翔。当我们在 Cesium 程序上操作鼠标的时候,无人机就会执行相关的飞行动作,并实时拍摄照片回传到程序,于是我们看到了预期的地图。在这个过程中,无人机的姿态、位置决定了相机的位置,从而决定了我们能看到什么样的地图。

二、理解相机常用参数

相机由位置(position)、姿态(orientation)和视锥体(frustum)定义。^[1]^

相机的三个关键参数中 position、orientation 最常用,其中 orientation 不太好理解。

orientationheadingpitchroll 三个参数构成,他们的单位都是弧度。在 Camera API 中没有 orientation 属性,只有 headingpitchroll 三个相关的属性。这三个属性怎么理解呢?看一张图就明白了。

假设把人绑在相机上,差不多就是这个效果。

三个姿态参数的含义以及取值范围如下:

heading: 相机的偏航角 。取值范围 [-π/2, π/2]。

取值及其效果:

  • -π/2 :向左旋转 90 度;
  • -π/4 :向左旋转45度;
  • 0 :不旋转;
  • π/2 :向右旋转 90 度;
  • π/4 :向右旋转45度;

pitch: 相机的俯仰角 。取值范围 [-π/2, 0]。

取值及其效果: - -π/2 :俯视地面; - -π/4 :斜向下45度俯视地面; - 0 :平视前方,将看不到地图;

roll: 相机的翻滚角。取值范围 [0, π]。

取值一般都是 0 。

三、调用相机功能的方式

1. 直接调用

可以从 Viewer 和 Scene 的实例上获取到相机实例,然后实现相关功能。

  • 从 Viewer 实例上获取:viewer.camera
  • 从 Scene 实例上获取:scene.camera

测试发现从 Viewer 和 Scene 的实例中获取到的相机对象是一样的。

Cesium 中有相机类,但是使用时一般不会去执行 new Camera()

2. 间接调用

有些时候,不用获取相机实例也可调用相机的功能。例如viewer.flyToviewer.zoomTo 等。

四、相机的常见使用场景

1.设置地图视角

js 复制代码
// 设置视图为指定位置
viewer.scene.camera.setView({  
  destination: new Cesium.Cartesian3(-2081468.7298130617, 6669909.259494101, 3527719.400741791),  
  orientation: {  
    heading: 6.2643994123185305,  
    pitch: -1.565578025920228,  
    roll: 0,  
  }
});

2.飞行到指定位置或目标

js 复制代码
// 飞行到指定位置
viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(-117.16, 32.71, 15000.0), });

// 飞行到指定目标
const rectangle = viewer.entities.add({ 
  rectangle: { 
    coordinates: Cesium.Rectangle.fromDegrees(103, 25, 106, 27)
  }
}); 
viewer.flyTo(rectangle);
// 默认飞行时间 3 秒。
// 飞行完成之后,默认朝北,同时倾斜45度向下俯视。

3.缩放到指定目标

js 复制代码
// 缩放到新建的实体
const entity = viewer.entities.add({
  polyline: {
    positions: Cesium.Cartesian3.fromDegreesArray([-77, 35, -77.1, 35]),
    width: 5,
    material: Cesium.Color.RED,
  },
});
// 缩放到 entity 之后,默认朝北,同时倾斜45度向下俯视。
viewer.zoomTo(entity);
// 虽然此处没有直接使用相机对象,但是其内在原理是调整相机视角

4.追踪动态的实体

js 复制代码
const entity = viewer.entities.add({
  polyline: {
    positions: Cesium.Cartesian3.fromDegreesArray([-77, 35, -77.1, 35]),
    width: 5,
    material: Cesium.Color.RED,
  },
});
viewer.trackedEntity = wyoming;
// 当 entity 位置发生变化时,地图视角会跟着变化并保持让视角对准 entity

// 取消追踪 
viewer.trackedEntity = undefined;

// 双击实体之外的地方或者调用 viewer.zoomTo 、viewer.flyTo 时,追踪会被取消

关于追踪动态的实体的更详细的示例可参考动态增长的线 - Cesium Sandcastle

5. 限制地球缩放高度范围

js 复制代码
// 缩放时相机位置的最小高度(以米为单位)。默认为1.0。  
viewer.scene.screenSpaceCameraController.minimumZoomDistance = 50;
// 缩放时相机位置的最大高度(以米为单位)。默认为正无穷大。  
viewer.scene.screenSpaceCameraController.maximumZoomDistance = 20000000;

6. 获取当前视角的高度(根据高度更新的渲染状态)

js 复制代码
camera.positionCartographic.height

7. 获取屏幕位置对应的球面坐标

js 复制代码
// 获取从鼠标位置到相机位置的射线
const ray = viewer.scene.camera.getPickRay(event.endPosition)

// 获取射线和球面的交点(笛卡尔坐标)
const position = viewer.scene.globe.pick(ray, viewer.scene)

8. 视角锁定在一个点

js 复制代码
// 将相机锁定在一个点
const center = Cesium.Cartesian3.fromRadians(
  2.4213211833389243,
  0.6171926869414084,
  3626.0426275055174
);
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
viewer.scene.camera.lookAtTransform(
  transform,
  new Cesium.HeadingPitchRange(0, -Math.PI / 8, 2900)
);

9. 绕点旋转

js 复制代码
// 将相机锁定在一个点
const center = Cesium.Cartesian3.fromRadians(
  2.4213211833389243,
  0.6171926869414084,
  3626.0426275055174
);
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
viewer.scene.camera.lookAtTransform(
  transform,
  new Cesium.HeadingPitchRange(0, -Math.PI / 8, 2900)
);

// 绕点旋转
viewer.clock.onTick.addEventListener(function (clock) {
  viewer.scene.camera.rotateRight(0.005);
});

10. 监听地图视图变化

js 复制代码
const camera = viewer.camera
const removeStart = camera.moveStart.addEventListener( () => {
  //todo
})
const removeEnd = camera.moveEnd.addEventListener( () => {
  //todo
})

const removeChanged = camera.changed.addEventListener(() => {
  //todo
})

// 测试发现 changed 事件不太灵敏

参考资料

[1] . Camera - Cesium Documentation

相关推荐
用你的胜利博我一笑吧4 天前
vue3+ts+supermap iclient3d for cesium功能集合
前端·javascript·vue.js·3d·cesium·supermap
涛涛英语学不进去18 天前
3D Tiles的4x4的仿射变换矩阵
线性代数·3d·矩阵·cesium·3d tiles
GIS瞧葩菜18 天前
Cesium.ScreenSpaceEventHandler是 CesiumJS 中用于处理屏幕空间事件(如鼠标点击、移动、滚轮等)的工具
前端·javascript·cesium
BJ-Giser23 天前
cesium 水波纹扩散圆材质
前端·javascript·cesium
激动的兔子25 天前
使用Vue创建cesium项目模版该如何选择?
vue.js·cesium
BJ-Giser1 个月前
Cesium 视频纹理
前端·javascript·cesium
BJ-Giser1 个月前
Cesium 视频投射
前端·javascript·cesium
柳晓黑胡椒1 个月前
cesium 实现批量divpoint气泡,及气泡碰撞测试与自动避让
cesium·divpoint·碰撞测试
undefined&&懒洋洋1 个月前
【Cesium】Cesium图层请求完成的回调
开发语言·前端·javascript·cesium
undefined&&懒洋洋1 个月前
Cesium倾斜相机视角观察物体
前端·gis·相机·cesium