CesiumJS 高频功能 —— 坐标与相机

484 阅读8分钟

坐标系统

Cesium 呈现了一个基于真实地理坐标的三维球面,用户通过二维界面与之互动。无论是常见的鼠标拾取,还是即将探讨的相机控制,都与 Cesium 的坐标系统紧密相关。精通 Cesium 的坐标系统对于开发者而言是至关重要的,它构成了空间交互和数据操作的基石。

坐标系

Cesium 支持多种坐标系统,以适应不同的应用场景:

  1. WGS84 经纬度坐标系:这是基于全球定位系统的标准地理坐标系统,但在 Cesium 中没有直接对应的对象表示。
  2. WGS84 弧度坐标系:与 WGS84 经纬度坐标系相似,区别在于使用弧度而非角度表示经纬度,在 Cesium 中通过 Cartographic 类实现。
  3. 笛卡尔空间直角坐标系:通过 Cartesian3 类表示,用于定位三维空间中的点。
  4. 平面坐标系:也称为屏幕坐标系,通过 Cartesian2 类表示,适用于二维平面或屏幕空间内的点定位。
  5. 4D 笛卡尔坐标系:通过 Cartesian4 类表示,虽然使用较少,但在需要额外维度信息时非常有用。

在实际应用中,Cartesian4 类的使用较为有限,可以暂时不予考虑。将上述坐标类型主要可以分为以下两类:

  1. 地理坐标系:通过 Cartographic 类实现,用于处理基于经纬度的地理信息。
  2. 笛卡尔坐标系:包括 Cartesian3Cartesian2,分别用于三维和二维空间的坐标定位。

WGS84 弧度坐标系

WGS84 坐标系是全球定位系统(GPS)的基础,其经度范围从 -180 度到 +180 度,纬度范围从 -90 度到 +90 度。作为应用最广泛的地理坐标参照系统,WGS84 不仅是众多遥感影像的采用标准,也是地理信息科学领域的通用语言。在 Cesium 中,我们使用 new Cesium.Cartographic(longitude, latitude, height) 来描述地理坐标,其中 longitudelatitude 是以弧度为单位的经度和纬度值。

笛卡尔空间直角坐标系

在计算机图形学领域,直接采用经纬度坐标进行绘图并不便捷。因此,我们通常将地理坐标转换为笛卡尔空间直角坐标系,以便应用计算机图形学的渲染技术。它将地球视为一个完美的椭球体,并以地球的质心作为坐标系的原点。构造函数为 new Cesium.Cartesian3(x, y, z)

平面坐标系(屏幕坐标系)

平面坐标系采用二维笛卡尔坐标系统,Cesium 通过 Cartesian2 类来定义这一系统。该类通过构造函数 new Cesium.Cartesian2(x, y) 来创建,其中 xy 分别代表相对于 canvas 左上角的像素距离。在这一坐标系中:

  • 屏幕的左上角被定义为坐标原点 (0, 0)
  • 水平方向为 X 轴,向右移动表示 X 值增加,即 X 轴正方向指向右侧。
  • 垂直方向为 Y 轴,向下移动表示 Y 值增加,即 Y 轴正方向指向下方。

坐标转换

在 Cesium 中,坐标转换是实现不同坐标系统间互操作的关键。以下是一些常用的坐标转换方法:

原数据类型目标数据类型转换方法
经纬度+高度地心坐标Cesium.Cartesian3.fromDegrees(longitude, latitude, height?)
地心坐标经纬度+高度Cesium.Cartographic.fromCartesian(cartesian3)
地心坐标屏幕坐标Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, cartesian3)
角度弧度Cesium.Math.toRadians(degrees)
弧度角度Cesium.Math.toDegrees(radians)

相机系统

在二维地理信息系统(GIS)中,视角的移动或漫游通常只需调整视域中心的经纬度坐标。但在三维空间中,我们不仅需要确定观察点的位置,还必须设定视线的指向。可以想象一下,在实际观察物体时,如果视线方向不正确,我们可能就无法看到目标物体。

Cesium 借助相机来控制场景中的视域,旋转、缩放以及移动等操作皆由相机来实现。当用户拖动地球时,实际上是相机在进行移动,从而产生了场景移动的效果。

相机的位置和朝向

CesiumJS 的 viewer.camera 对象是与相机交互的主要接口。相机的位置和朝向可以通过以下关键属性来定义:

  1. 位置(Position) :相机的位置由 destination 属性指定,通常以笛卡尔空间坐标(Cartesian3)来表示。直观来说,destination 与地球表面的距离越远,视场中的地球所占的比例就越小。
  2. 姿态(Orientation) :相机的朝向由 orientation 属性控制,可以通过设置 HeadingPitchRollDirectionUp 来调整。

HeadingPitchRoll

在三维空间中,我们使用右手笛卡尔坐标系来描述相机的姿态:

  • 俯仰角(Pitch) :绕 X 轴旋转,控制相机视角的上下偏移。
  • 偏航角(Yaw) :绕 Y 轴旋转,控制相机视角的左右偏移。
  • 翻滚角(Roll) :绕 Z 轴旋转,控制相机视角的倾斜。

在 Cesium 中,HeadingPitchRoll 的设置遵循这一坐标系,其中 yawheading 替代,以更符合航空术语。

DirectionUp

DirectionUp 是另一种设置相机姿态的方法,它直接指定了相机的“上”方向,通常是一个单位向量。这种方法简化了姿态设置,例如,当你需要相机直接指向上方时,只需将 DirectionUp 设置为 (0, 0, 1) 即可。

在 Cesium 中,仅指定 DirectionUp 时,相机会自动调整 headingpitch 以匹配指定的上方向,这在创建特定视觉效果时非常方便。

使用场景

  • 当你需要精确控制相机的方向,比如在模拟飞行或者创建特定的视角时,HeadingPitchRoll 是一个很好的选择。
  • 当你更关心相机的上方向而不是具体的方位和俯仰角度时,DirectionUp 提供了一种更直观的方式来定义相机的姿态。

常用相机方法

瞬时定位

在某些应用场景中,我们可能需要让相机立即跳转到一个全新的视角和位置。此时可以通过 setView 方法来实现这一需求。

示例代码如下所示:

viewer.scene.camera.setView({
    destination: Cesium.Cartesian3.fromDegrees(
      -75.5847,
      40.0397,
      1000.0
    ),
    orientation: {
      heading: -Cesium.Math.PI_OVER_TWO,
      pitch: -Cesium.Math.PI_OVER_FOUR,
      roll: 0.0,
    },
});

在这个示例中,相机瞬间移动到指定的地心坐标。destination 参数指定相机的位置,而 orientation 参数设置相机的朝向。

平滑飞行动画

为了提升用户体验,避免视角的突然变化,flyTo 方法提供了一种平滑过渡的动画效果。这种方法模拟了空中飞行的视角渐变,允许用户享受到更加自然和流畅的视觉过渡。通过设置飞行的持续时间,flyTo 能够实现从当前位置到目标位置的平滑飞行动画。

viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(-122.22, 46.12, 5000.0),
    orientation: {
      heading: Cesium.Math.toRadians(20.0),
      pitch: Cesium.Math.toRadians(-35.0),
      roll: 0.0,
    },
    duration: 3, // 飞行动画持续3秒
});

在这段代码中,相机将从当前位置平滑地“飞向”指定的地心坐标,同时调整其姿态以匹配新的视角。destination 参数定义了飞行的目的地,orientation 参数则确定了到达目的地时相机的朝向。通过添加 duration 参数,我们可以控制整个飞行动画的持续时间,从而创造出更加平滑和逼真的视觉效果。

固定视角观察

lookAt 方法会直接将视角跳转到指定的目的地。与 setView 方法不同,使用 lookAt 后,即使用户通过鼠标操作旋转视角,相机的相对位置仍然保持不变。运行示例代码后,通过左键拖动视口,可以发现相机绕着目标点旋转,而不会改变位置。这一方法常用于对某一特定对象进行固定视角观察。

const center = Cesium.Cartesian3.fromDegrees(116.39, 39.91);
const heading = Cesium.Math.toRadians(50);
const pitch = Cesium.Math.toRadians(-90);
const range = 2500;

viewer.camera.lookAt(center, new Cesium.HeadingPitchRange(heading, pitch, range));

视角绑定

viewBoundingSphere 方法的视角切换效果类似于 setView 方法,都是直接将视口切换到指定目的地,且不具备飞行过渡效果。使用此方法时,相机的视口似乎被绑定到了目标物体上,无论用户如何操作,视角始终围绕该物体。这种方法非常适合于需要从不同角度细致观察物体或进行建筑漫游的场景。

const position = Cesium.Cartesian3.fromDegrees(116.39, 39.91, 1500);
const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(-90, 0, 0));

viewer.entities.add({
    position,
    orientation,
    model: {
        uri: '../SampleData/models/CesiumAir/Cesium_Air.glb',
        minimumPixelSize: 100,
        maximumScale: 10000,
        show: true
    }
});

viewer.camera.viewBoundingSphere(
    new Cesium.BoundingSphere(position, 20),
    new Cesium.HeadingPitchRange(0, 0, 0)
);

总结

CesiumJS 通过精确的相机控制,为开发者提供了构建动态且交互性丰富的三维地图应用的强大功能。掌握常用的坐标系系统和相机操控技巧,不仅能够显著提升用户体验,还能增强应用的视觉表现力。CesiumJS 提供了一系列灵活的控制选项,从即时定位到平滑动画,再到视角的锁定,这些都使得三维可视化效果更加栩栩如生。

上述示例可以在 Camera - Cesium Sandcastle 中找到,并可直接运行体验。