专栏名称: 小猿猴GISer
GIS遥感交流学习
目录
相关文章推荐
51好读  ›  专栏  ›  小猿猴GISer

Cesium 之 Camera 实践

小猿猴GISer  · 公众号  ·  · 2025-01-17 18:10

正文

请到「今天看啥」查看全文


前言

之前我在 Cesium 中的 Camera(一) 一文中复现了官方的示例,演示了如何通过 监听键盘按键 或通过 监听鼠标事件 来控制 Camera,当时为了演示方便,修改了 ScreenSpaceCameraController 部分属性以禁用默认的处理程序。其实 Cesium 已经定义了默认的鼠标动作。具体如下图所示,通过单击鼠标 左键  + 拖拽 平移 视图,通过 右键单击 + 拖拽 鼠标滚轮滚动 缩放 视图,还有通过鼠标 中键 + 拖拽 或者 Ctrl + 左键或右键 + 拖拽 旋转 视图。

鼠标操纵视图的方法

本文将继续借助官方的示例来演示 Camera 的一些常用方法的实现效果,并对个别地方进行了修改,方便更好地理解。废话不多说,下面直入正题。

Camera 可视化

Cesium 提供了可视化 Camera 的专门类 DebugCameraPrimitive ,具体如下所示:

DebugCameraPrimitive 类

为了方便演示,我们需要更改一下 Camera 默认视图范围,就是地球的 默认视图区域 ,具体如下所示:我们需要在初始化 Viewer 对象前更改 Camera 的 DEFAULT_VIEW_RECTANGLE 属性。

注: 需要在初始化 Viewer 对象前修改Camera的上述属性,否则无法生效。另外下述区域范围参考的第三方库定义的一个中国区域的四至范围,具体参考: https://github.com/wandergis/coordtransform/blob/master/index.js#L144

Camera.DEFAULT_VIEW_RECTANGLE = Rectangle.fromDegrees(
  73.66,
  3.86,
  135.05,
  53.55
);

最终实现效果如下:

实现代码如下所示:

注:我们通过 viewer.camera viewer.scene.camera 都可以获取 camera 对象,实际上根据我测试没有什么区别。

/**
 * 绘制出中国范围四至
 */

function drawChinaExtent({
if (chinaExtentEntity) {
    viewer.entities.remove(chinaExtentEntity);
  }
  viewer.entities.add({
    rectangle: {
      coordinates: Cesium.Rectangle.fromDegrees(73.663.86135.0553.55),
      fill false,
      outlinetrue,
      outlineColor: Cesium.Color.RED,
    },
  });
}

/**
 * 可视化相机视锥体
 */

function visualCameraFrustum({
//开启地表透明
  scene.globe.translucency.enabled = true;
  scene.globe.translucency.frontFaceAlpha = 0.5;

//绘制是椎体
  camera.frustum.near = 200000.0;
  cameraPrimitive = scene.primitives.add(
    new Cesium.DebugCameraPrimitive({
      camera,
      updateOnChangefalse,
    })
  );
}

我们曾在 Cesium 中的 Camera(一) 一文中展示了键盘操作Camera的 moveXcc 一系列的移动方法,方法使用很简单,但这里我们详细对比一下 moveXcc() lookXcc() rotateXcc() twistXcc() 系列方法的区别,我们以 left 方向为例:

  • moveLeft :沿摄像机的左侧方向平移(直线移动),相当于 沿摄像机的局部坐标系 X 轴负方向移动 ,其参数单位为 ,表示移动的距离。
  • lookLeft :让摄像机 围绕其上向量 (up 向) 向左旋转 ,相当于 改变摄像机的视线方向 ,但 位置保持不变 ,参数单位为 弧度
  • rotateLeft :让摄像机围 绕其参考框架的中心 (通常是地球中心或地面上的某个目标点) 向左旋转 ,实现绕参考点的圆弧旋转,参数单位为 弧度

注意:上面括号里说通常是地球的中心,可以认为相机初始的目标点位于地球中心,因此可以认为其参考框架中心就是目标点。

  • twistLeft :让摄像机 围绕自身的视线方向 (视轴,即下图的 direcition 方向) 逆时针旋转 (从摄像机视角看是向左扭转),参数单位为 弧度

为了方便理解,我们顺便更正一下曾在 Cesium 中的 Camera(二) 一文中的手势图,其实我们文末评论处做了更正,即使用如下图所示的左手坐标系来标明 Camera 的方向。

左手坐标系

另外,关于上述方法还有不带方向的对应方法,如下图所示:不管是 axis 还是 direction 都可以用一个 Cartesian3 来表示,例如 Cartesian3.fromDegrees(-117.16, 32.71, 15000.0) ,表示这个轴或方向为 连接笛卡尔坐标系原点与该点(位于地表上方)的一个向量

Camera 常用方法

下面的演示是使用的官方示例,个别地方稍加修改,疑难的地方都作了注释,请注意看代码中添加的注释,这里仅放几个比较典型的效果,其余的代码请点击文末 阅读原文 链接访问即可。

在一个城市飞行

效果如下图所示:


camera.flyTo({
  destination: Cesium.Cartesian3.fromDegrees(
    -73.98580932617188,
    40.74843406689482,
    363.34038727246224
  ),
duration2,
complete() => {
    setTimeout(() => {
      camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(
          -73.98585975679403,
          40.75759944127251,
          186.50838555841779
        ),
        orientation: {
          heading: Cesium.Math.toRadians(200.0),
          pitch: Cesium.Math.toRadians(-50.0),
        },
        easingFunction: Cesium.EasingFunction.LINEAR_NONE,
      });
    }, 1000);
  },
});

飞往我的位置

效果如下所示:

这里用的定位图标使用的官方提供的,其实源码中有图标名称:官方的PinBuilder类提供了相应的添加方法。


另外,这里教大家一个技巧:有些类官方在 api 文档处附了对应的示例链接,我们直接点击学习即可,具体如下所示:

navigator.geolocation.getCurrentPosition(
  (pos) => {
    //绘制一个定位图标
    viewer.entities.add({
      position: Cesium.Cartesian3.fromDegrees(
        pos.coords.longitude,
        pos.coords.latitude,
        0.0
      ),
      billboard: {
        image: pinBuilder.fromMakiIconId("hospital", Cesium.Color.RED, 48),
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 图标对齐方式
      },
    });
    camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(
        pos.coords.longitude,
        pos.coords.latitude,
        1000.0
      ),
    });
  },
  (err) => {
    ElMessage.error("错误(" + err.code + "): " + err.message);
  },
  {
    enableHighAccuracytrue,
  }
);

飞向一个矩形

效果如下图所示:


注:这里的这个效果,我们可以直接调用 Viewer flyTo 方法也可,具体参考官方入门教程的 Fly to an asset https://cesium.com/learn/cesiumjs-learn/cesiumjs-camera#fly-to-an-asset ,具体如下图所示:[图片]

const west = -90.0;
const south = 38.0;
const east = -87.0;
const north = 40.0;
const rectangle = Cesium.Rectangle.fromDegrees(west, south, east, north);

camera.flyTo({
destination: rectangle,
});

//这里是为了展示方便,所以绘制了一个矩形
viewer.entities.add({
rectangle: {
    coordinates: rectangle,
    material: Cesium.Color.RED.withAlpha(0.5),
  },
});

给相机设置一个局部坐标系——东北天坐标系

效果如下所示:


//局部坐标系(ENU)原点的坐标
const center = Cesium.Cartesian3.fromDegrees(-75.5977740.03883);
/**
 * 计算从ENU坐标系转换为全球通用坐标系的转换矩阵
 * ENU,全称为:East north up coordinate,即东北天坐标系,参考:
 * https://en.wikipedia.org/wiki/Local_tangent_plane_coordinates#Local_east,_north,_up_(ENU)_coordinates
 */

const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);

camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z;
//使用目标和变换矩阵设置摄像机位置和方向
camera.lookAtTransform(
  transform,
new Cesium.Cartesian3(-120000.0-120000.0120000.0)
);

referenceFramePrimitive = scene.primitives.add(
new Cesium.DebugModelMatrixPrimitive({
    modelMatrix: transform,
    length100000.0,
  })
);

//计算当前局部坐标系的原点转为世界坐标系坐标
const enuOrigin = Cesium.Matrix4.multiplyByPoint(
  transform,
new Cesium.Cartesian3(000),
new Cesium.Cartesian3()
);
viewer.entities.add({
position: enuOrigin,
billboard: {
    image: pinBuilder.fromMakiIconId("hospital", Cesium.Color.RED, 48),
    verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 图标对齐方式
  },
});

地球实时旋转

效果如下所示:


let removePostUpdate;
function viewInICRF({
//摄像机飞往默认的主视图(即Camera#.DEFAULT_VIEW_RECTANGLE),参数为持续时间,这里设置为0秒
  camera.flyHome(0);
//multiplier默认值为1.0,获取或设置调用 Clock#tick 时前进的时间量,由于单位为秒,因此这里设置每次时间的前进时长为3小时
  clock.multiplier = 3 * 60 * 60;
//添加在更新场景后和渲染场景之前执行的事件监听器。
//事件的订阅者接收 Scene 实例作为第一个参数,将当前时间作为第二个参数。
  removePostUpdate = scene.postUpdate.addEventListener((scene, time) => {
    console.log("time参数类型为JulianDate: ", time instanceof Cesium.JulianDate);
    if (scene.mode !== Cesium.SceneMode.SCENE3D) {
      return;
    }

    //计算旋转矩阵,以在给定时间将点或矢量从国际天体参考系 (GCRF/ICRF) 惯性系轴变换为地球固定系轴 (ITRF)。如果尚未加载执行转换所需的数据,则此函数可能会返回 undefined。
    const icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(time);
    if (Cesium.defined(icrfToFixed)) {
      const offset = Cesium.Cartesian3.clone(camera.position);
      //从表示旋转的 Matrix3 和表示平移的 Cartesian3 计算 Matrix4 实例。这里只指定了第一个旋转参数,第二个平移参数默认值为:Cartesian3.ZERO
      const transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed);
      //使用目标和变换矩阵设置摄像机位置和方向。
      camera.lookAtTransform(transform, offset);
    }
  });
}

好了,以上就是本文的所有内容,源码点击文末 阅读原文 链接访问即可,文中若存在不足或错误,还望在评论处指出,我们下期见喽!

参考资料

  1. https://cesium.com/learn/cesiumjs/ref-doc/Camera.html?classFilter=camera
  2. https://sandcastle.cesium.com/index.html?src=Camera.html&label=All
  3. https://cesium.com/learn/cesiumjs/ref-doc/DebugCameraPrimitive.html?classFilter4. https://cesium.com/learn/cesiumjs/ref-doc/PinBuilder.html?classFilter=pinbuilder
  4. https://cesium.com/learn/cesiumjs-learn/cesiumjs-camera







请到「今天看啥」查看全文