Cesium | Camera类介绍及其业务场景的简单实现

前言

在二维地图中,我们只需要提供经纬度坐标即可定位到指定地点,而在三维世界中,经纬度(即二维坐标系)是无法描述一个要素的位置,所以需要使用笛卡尔坐标系来描述空间要素的位置。具体介绍这里不啰嗦,在之前的文章中已经大致介绍过,这篇主要来讲解一下 camera 相关参数及利用camera能够做出的效果及其实现思路。

camera

image.png
我在 blender 中截了一张图模拟我们的相机对象,球体便是 cesium 的地球 🌏,个人觉得是很生动形象的。有助于我们了解相机的视角,方便后续讲解调整相机的相关参数。

官网的 api 中是这么介绍的:相机类由位置、方向和视锥体定义。 视锥体(viewing frustum)由6个(上、下、左、右、近、远)平面限定,每个平面可 由Cartesian4对象表示,其中x,y和z分量定义垂直于平面的单位向量,w分量是平面距原点/相机位置的距离。

1
2
3
4
5
6
7
8
//创建相机对象。
var camera = new Cesium.Camera(scene);
camera.position = new Cesium.Cartesian3();
camera.direction = Cesium.Cartesian3.negate(Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3());
camera.up = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_Y);
camera.frustum.fov = Cesium.Math.PI_OVER_THREE;
camera.frustum.near = 1.0;
camera.frustum.far = 2.0;

说的什么鸟语,目前我们不需要了解这么复杂的自定义实现,只需要专注于它给我们提供的变换视角方法即可!

操作 camera

cesium 中提供了多种方式对相机进行操作:setViewflyTolookAt,主要介绍一下这三种方式的使用及异同

setView(options)

viewer.camera.setView(options)

设置相机位置、方向和变换。

opitons<Object>参数列表:

名称 类型 描述
destination Cartesian3 | Rectangle 相机在 wgs84 坐标系中的最终位置,或自顶向下视图可见的矩形区域
orientation Object 包含方位(direction),上方向(up)及方位角(heading),仰角(pitch),滚动角(roll)属性的对象
endTransform Matrix4 相机参考坐标系的变换矩阵
convert Boolean Whether to convert the destination from world cool。(这参数我也不知道干嘛的……)

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 1. 通过top-down视图设置位置。
viewer.camera.setView({
destination : Cesium.Cartesian3.fromDegrees(-117.16, 32.71, 15000.0)
});

// 2 通过方位角、俯仰角、滚动角设置视图。
viewer.camera.setView({
destination : cartesianPosition,
orientation: {
heading : Cesium.Math.toRadians(90.0), // east, default value is 0.0 (north)
pitch : Cesium.Math.toRadians(-90), // default value (looking down)
roll : 0.0 // default value
}
});

// 3. 相机空间位置不变,改变方位角、俯仰角和滚动角。.
viewer.camera.setView({
orientation: {
heading : Cesium.Math.toRadians(90.0), // east, default value is 0.0 (north)
pitch : Cesium.Math.toRadians(-90), // default value (looking down)
roll : 0.0 // default value
}
});


// 4. 通过自顶向下视图查看矩形。
viewer.camera.setView({
destination : Cesium.Rectangle.fromDegrees(west, south, east, north)
});

// 5. 通过使用单位向量的方向(orientation)设置相机位置。
viewer.camera.setView({
destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0),
orientation : {
direction : new Cesium.Cartesian3(-0.04231243104240401, -0.20123236049443421, -0.97862924300734),
up : new Cesium.Cartesian3(-0.47934589305293746, -0.8553216253114552, 0.1966022179118339)
}
});

flyTo(options)

viewer.camera.flyTo(options)

相机从当前位置飞行到新的空间位置。

opitons<Object>参数列表(其余参数请查阅官方文档):

名称 类型 描述
destination Cartesian3 | Rectangle 相机在 wgs84 坐标系中的最终位置,或自顶向下视图可见的矩形区域
orientation Object 包含方位(direction),上方向(up)及方位角(heading),仰角(pitch),滚动角(roll)属性的对象
duration Number 飞行持续时间(以秒为单位)。如果省略,由飞行距离计算合理的持续时间。
complete Camera~FlightCompleteCallback 飞行完成时执行的功能。
cancel Camera~FlightCompleteCallback 飞行取消时执行的功能。
endTransform Matrix4 飞行完成时相机将处于的参考系的变换矩阵。
maximumHeight Number 飞行中的最大高度。
easingFunction EasingFunction | EasingFunction~Callback 释放时调用功能。
…… …… ……

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 1. 飞向通过top-down视图表示的位置
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(-117.16, 32.71, 15000.0)
});

// 2. 飞向通过top-down视图表示的矩形
viewer.camera.flyTo({
destination : Cesium.Rectangle.fromDegrees(west, south, east, north)
});

// 3. 飞向利用单位向量表示方向(orientatin)的位置
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0),
orientation : {
direction : new Cesium.Cartesian3(-0.04231243104240401, -0.20123236049443421, -0.97862924300734),
up : new Cesium.Cartesian3(-0.47934589305293746, -0.8553216253114552, 0.1966022179118339)
}
});

// 4. 飞向利用方位角(heading)、俯仰角(pitch)、滚动角(roll)表示方向(orientatin)的位置
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0),
orientation : {
heading : Cesium.Math.toRadians(175.0),
pitch : Cesium.Math.toRadians(-35.0),
roll : 0.0
}
});

lookAt(target,offset)

通过目标(target)和偏移(offset)设置相机位置、方向。 目标(target)以世界坐标方式表示;偏移(offset)是在以目标为中心的本地“东-北-上”参考系中的笛卡尔坐标或“方位角/俯仰角/范围(heading/pitch/range)”。 如果偏移(offset)由笛卡尔坐标表示,它表示距离由变换矩阵定义的参考系中心的偏移量。 如果偏移(offset)由“heading/pitch/range”表示,方位角(heading)和俯仰角(pitch)的角度由根据变换矩阵定义的参考系确定,方位角(heading)从 y 轴开始朝向 x 轴增加,俯仰角(pitch)为从 xy 平面的旋转角度,正的俯仰角位于平面下方,负的俯仰角位于平面上方。 范围(range)为距中心点的距离。在 2D 中必须为自上而下视图,相机位于俯视目标的上方,目标上方的高度值是偏移量的大小。方位角根据偏移确定,如果不能从偏移确定方位角,则方位角为北方向。

viewer.camera.lookAt(target,offset)

设置相机位置、方向和变换。

opitons<Object>参数列表:

名称 类型 描述
target Cartesian3 目标在世界坐标中的空间位置。
offset Cartesian3 | HeadingPitchRange 距以目标为中心的本地“东-北-上”参考系的偏移。

用法:

1
2
3
4
5
6
7
8
9
10
// 1. 利用笛卡尔坐标设置偏移
var center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0);
viewer.camera.lookAt(center, new Cesium.Cartesian3(0.0, -4790000.0, 3930000.0));

// 2. 利用HeadingPitchRange设置偏移
var center = Cesium.Cartesian3.fromDegrees(-72.0, 40.0);
var heading = Cesium.Math.toRadians(50.0);
var pitch = Cesium.Math.toRadians(-20.0);
var range = 5000.0;
viewer.camera.lookAt(center, new Cesium.HeadingPitchRange(heading, pitch, range));

三者异同

首先三者基本都是通过调整相机对象的destinationorientation达到定位到指定位置的目的,不同点在于flyTo是带有动画效果的飞入目的地,其余二者是直接定位到对应位置(如图)接下来我们详细说明一下参数中的方位角/俯仰角/滚动角,因为这和我们想要实现的效果息息相关。

setView flyTo lookAt

Heading/Pitch/Roll

上文中已经对这三个角进行了中文注释

  • Heading:方位角

  • Pitch:俯仰角

  • Roll:滚动角

现在以你聪明的小脑袋瓜为中心,heading类似于左右环顾也就是左右方向的改变,pitch相当于低头抬头也就是上下方向的改变,而roll可以想象你在侧翻,而你的头始终朝前看,滚动一圈也就相当于roll了360°。

下图直观的展现了视角与三个角的关系:

image.png

HeadingPitchRange与HeadingPitchRoll

这两者都是用来定义一个局部使用的角对象,二者区别在HeadingPitchRoll主要用在模型变换上,而HeadingPitchRange更多用于相机、视角相关操作的时候。下边简单将二者有区别的参数进行展示。

  • range:代表一个半径值,也就是锁定了相机距离目标的距离。

  • roll:滚动分量(以弧度为单位)

实战案例

相机能做的效果非常多,而最经典的因该就是绕点飞行和漫游了,接下俩就两个实现效果来简单实现。

绕点飞行

绕点飞行.gif

我们首先创建一个点将绕点飞行的中心点可视化出来,不多介绍了就是基本的创建实体:

1
2
3
4
5
6
7
var entity = this.GLOBAL.Viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(116.391935, 39.907123),
point: {
color: Cesium.Color.YELLOW,
pixelSize: 10,
},
})

接下来利用zoomTo将视角定位到我们创建的点,在then中利用onTick绑定一个每帧运行的方法,在方法中调整我们的heading(方位角),实现绕着点位旋转:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var heading = 0 // 朝向
var offset = new Cesium.HeadingPitchRange(
Cesium.Math.toRadians(heading),
-Cesium.Math.toRadians(30),
1000
)
this.GLOBAL.Viewer.zoomTo(entity, offset).then(() => {
this.GLOBAL.Viewer.clock.onTick.addEventListener(() => {
heading += 0.1
offset = new Cesium.HeadingPitchRange(
Cesium.Math.toRadians(heading),
-Cesium.Math.toRadians(30),
1000
)
this.GLOBAL.Viewer.zoomTo(entity, offset)
this.GLOBAL.Viewer.scene.screenSpaceCameraController.enableInputs = false
})
})

第一人称视角漫游

在之前的文章【Cesium | 利用Property机制实现轨迹回放】中我们已经实现了轨迹回放,现在我们可以利用相机和变换来实现固定视角。

addRoamLine方法中,我们添加固定视角方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 添加漫游路线
addRoamLine(positions, speed) {
// ……
this.viewer.trackedEntity = this.animateEntity;
if (this.isFirstPerson) {
this.clockEvent = this.viewer.clock.onTick.addEventListener(
this.firstPersonEvent()
);
}
// ……
}
firstPersonEvent() {
return () => {
if (!this.animateEntity) return;
if (!this.viewer.trackedEntity) return;

let center = this.animateEntity.position.getValue(
this.viewer.clock.currentTime
);
let orientation = this.animateEntity.orientation.getValue(
this.viewer.clock.currentTime
);
if (center) {
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
transform = Cesium.Matrix4.fromRotationTranslation(
Cesium.Matrix3.fromQuaternion(orientation),
center
);
this.viewer.camera.lookAtTransform(
transform,
new Cesium.Cartesian3(-100, 0, 50)
);
}
};
}

关于transform相关的知识等我统一梳理一下在分享吧,本来想在这篇文章里写,写着写着感觉自己的理解还不够到位……太弟弟了/(ㄒoㄒ)/~~

第一人称视角漫游.gif


Cesium | Camera类介绍及其业务场景的简单实现
https://moewang0321.github.io/2022/02/18/CesiumCamera类介绍及其业务场景的简单实现/
作者
Moe Wang
发布于
2022年2月18日
许可协议