Skip to content

Cesium 相机控制完全指南

相机(Camera)是 Cesium 场景中的"眼睛",负责定义观察者在 3D 世界中的位置和视角方向。所有渲染到屏幕上的内容都由相机的当前状态决定,包括:

  • 观察点位置
  • 视线方向
  • 视野范围
  • 透视效果

核心概念

相机坐标系

Cesium 相机系统基于右手坐标系:

  • 位置(Position):相机在世界坐标系中的三维坐标(Cartesian3)
  • 方向(Direction):相机视线方向向量(默认指向-Z 轴)
  • 姿态(Orientation):由 heading(方位角)、pitch(俯仰角)、roll(翻滚角)定义

重要区别

  • 本地坐标系:以相机为中心,X 轴向右,Y 轴向上,Z 轴向后
  • 世界坐标系:以地球中心为原点的固定坐标系

相机状态参数

参数类型描述取值范围
positionCartesian3世界坐标位置任意三维坐标
positionCartographicCartographic经纬度高度表示经度[-180,180],纬度[-90,90],高度 ≥0
headingNumber方位角(绕 Y 轴)[-π, π]弧度,0 为正北
pitchNumber俯仰角(绕 X 轴)[-π/2, π/2]弧度,-π/2 为俯视
rollNumber翻滚角(绕 Z 轴)[-π, π]弧度,0 为水平
frustumPerspectiveFrustum, PerspectiveOffCenterFrustum, OrthographicFrustum视锥体参数-

基础配置

默认视角设置

js
// 在创建Viewer前设置全局默认视图矩形
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
  89.99, // 西经度
  39.9, // 南纬度
  116.41, // 东经度
  39.92 // 北纬度
);

注意DEFAULT_VIEW_RECTANGLE必须在 Viewer 创建前设置才有效,适用于需要固定初始范围的场景(如特定区域监控)

相机参数获取

js
const camera = viewer.camera;

// 获取相机位置(笛卡尔坐标)
const position = camera.position;
// 获取相机方向矩阵
const directionMatrix = camera.direction;
// 获取相机方位角
const heading = camera.heading;
// 获取相机俯仰角
const pitch = camera.pitch;
// 获取相机滚动角
const roll = camera.roll;
// 获取相机高度(米)
const height = camera.positionCartographic.height;

相机控制方法

1. 直接定位(setView)

瞬时定位到目标位置,无过渡动画,适用于需要精确定位的场景:

js
// 方式一:使用heading/pitch/roll定义姿态(推荐)
viewer.camera.setView({
  destination: Cesium.Cartesian3.fromDegrees(
    116.404, // 经度
    39.915, // 纬度
    1000 // 高度(米)
  ),
  orientation: {
    heading: Cesium.Math.toRadians(0), // 方位角:0°(正北)
    pitch: Cesium.Math.toRadians(-30), // 俯仰角:-30°(俯视)
    roll: 0, // 翻滚角:0°(水平)
  },
});

// 方式二:使用方向向量定义姿态
viewer.camera.setView({
  destination: Cesium.Cartesian3.fromDegrees(116.404, 39.915, 1000),
  orientation: {
    direction: new Cesium.Cartesian3(0.1, -0.2, -1), // 视线方向向量
    up: new Cesium.Cartesian3(0, 1, 0), // 相机上方向向量
  },
});

应用场景:初始化定位、按钮快速跳转、精确坐标定位

2. 平滑飞行(flyTo)

带过渡动画的相机移动,提供更好的用户体验:

js
viewer.camera.flyTo({
  destination: Cesium.Cartesian3.fromDegrees(116.404, 39.915, 1000),
  orientation: {
    heading: Cesium.Math.toRadians(0),
    pitch: Cesium.Math.toRadians(-30),
    roll: 0,
  },
  duration: 5, // 动画持续时间(秒),默认3秒
  easingFunction: Cesium.EasingFunction.CUBIC_IN_OUT, // 缓动函数
  maximumHeight: 2000, // 飞行路径最高点限制(米)
  complete: function () {
    console.log("飞行完成!");
  },
  cancel: function () {
    console.log("飞行被取消!");
  },
  endTransform: Cesium.Matrix4.IDENTITY, // 结束时的变换矩阵
});

常用缓动函数对比

  • LINEAR_NONE:匀速运动
  • CUBIC_IN_OUT:先加速后减速(默认)
  • QUADRATIC_IN:匀加速
  • ELASTIC_OUT:弹性效果

3. 锁定目标(lookAt)

固定相机看向目标点,适用于跟踪移动目标:

js
// 方式一:使用HeadingPitchRange(推荐)
viewer.camera.lookAt(
  Cesium.Cartesian3.fromDegrees(116.404, 39.915, 0),
  new Cesium.HeadingPitchRange(
    Cesium.Math.toRadians(30), // 方位角:30°(东北方向)
    Cesium.Math.toRadians(-20), // 俯仰角:-20°(俯视)
    1000 // 距离目标点的距离(米)
  )
);

// 方式二:使用Cartesian3偏移量
viewer.camera.lookAt(
  Cesium.Cartesian3.fromDegrees(116.404, 39.915, 0), // 目标点坐标
  new Cesium.Cartesian3(0, -500, 300) // 相对目标点的偏移量(右、后、上)
);

// 恢复自由控制
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);

注意:使用 lookAt 后,相机将锁定目标点,需调用camera.lookAtTransform(Cesium.Matrix4.IDENTITY)恢复自由控制

交互控制

禁用默认交互

代表地图无法实现拖拽、缩放、旋转等交互

js
const controller = viewer.scene.screenSpaceCameraController;

// 禁用默认控制
controller.enableRotate = false;
controller.enableTranslate = false;
controller.enableZoom = false;

自定义交互控制

方法描述单位:米
moveForward向前移动
moveBackward向后移动
moveLeft向左移动
moveRight向右移动
moveUp向上移动
moveDown向下移动
---------------------
lookLeft向左旋转
lookRight向右旋转
lookUp向上旋转
lookDown向下旋转
---------------------
twistLeft向左倾斜
twistRight向右倾斜

下面是完整的相机键盘控制方案,支持移动、旋转和倾斜:

展开代码
vue
<template>
  <div ref="cesiumContainer" class="container"></div>
</template>

<script setup>
import { ref, onMounted } from "vue";
import * as Cesium from "cesium";
const cesiumContainer = ref(null);
let viewer = null;

// 天地图TOKEN
const token = "05be06461004055923091de7f3e51aa6";

onMounted(() => {
  // 初始化Viewer
  viewer = new Cesium.Viewer(cesiumContainer.value, {
    geocoder: false, // 关闭地理编码搜索
    homeButton: false, // 关闭主页按钮
    sceneModePicker: false, // 关闭场景模式选择器
    baseLayerPicker: false, // 关闭底图选择器
    navigationHelpButton: false, // 关闭导航帮助
    animation: false, // 关闭动画控件
    timeline: false, // 关闭时间轴
    fullscreenButton: false, // 关闭全屏按钮
    baseLayer: false, // 关闭默认地图
  });
  // 清空logo
  viewer.cesiumWidget.creditContainer.style.display = "none";
  initMap();

  // 定位到北京
  viewer.camera.setView({
    destination: Cesium.Cartesian3.fromDegrees(116.404, 39.915, 1000),
    orientation: {
      heading: Cesium.Math.toRadians(0),
      pitch: Cesium.Math.toRadians(-90),
      roll: 0.0,
    },
  });

  // 键盘控制相机交互
  document.addEventListener("keydown", function (event) {
    const camera = viewer.camera;
    const distance = 100; // 每次移动的距离,单位:米
    switch (event.key) {
      case "ArrowUp": // 上按键
        camera.moveForward(distance); // 向前移动
        break;
      case "ArrowDown": // 下按键
        camera.moveBackward(distance); // 向后移动
        break;
      case "ArrowLeft": // 左按键
        camera.moveLeft(distance); // 向左移动
        break;
      case "ArrowRight": // 右按键
        camera.moveRight(distance); // 向右移动
        break;
      case "w": // w按键
        camera.lookUp(distance); // 向上旋转
        break;
      case "s": // s按键
        camera.lookDown(distance); // 向下旋转
        break;
      case "a": // a按键
        camera.lookLeft(distance); // 向左旋转
        break;
      case "d": // d按键
        camera.lookRight(distance); // 向右旋转
        break;
      case "q": // q按键
        camera.twistLeft(distance); // 向左倾斜
        break;
      case "e": // e按键
        camera.twistRight(distance); // 向右倾斜
        break;
      default:
        break;
    }
  });
});

// 加载天地图
const initMap = () => {
  // 以下为天地图及天地图标注加载
  const tiandituProvider = new Cesium.WebMapTileServiceImageryProvider({
    url:
      "http://{s}.tianditu.gov.cn/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=" +
      token,
    layer: "img",
    style: "default",
    format: "tiles",
    tileMatrixSetID: "w",
    subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"], // 子域名
    maximumLevel: 18,
    credit: new Cesium.Credit("天地图影像"),
  });
  // 天地图影像添加到viewer实例的影像图层集合中
  viewer.imageryLayers.addImageryProvider(tiandituProvider);
};
</script>
<style scoped>
.container {
  width: 100vw;
  height: 100vh;
}
</style>

高级应用

相机事件监听

监听相机状态变化,实现动态响应:

js
// 相机移动开始事件
viewer.camera.moveStart.addEventListener(function () {
  console.log("相机开始移动");
  // 可在此处暂停其他动画或更新UI状态
});

// 相机移动结束事件(常用)
viewer.camera.moveEnd.addEventListener(function () {
  console.log("相机移动结束");
  // 获取新视域范围
  const viewRectangle = viewer.camera.computeViewRectangle();
  if (Cesium.defined(viewRectangle)) {
    const west = Cesium.Math.toDegrees(viewRectangle.west).toFixed(2);
    const east = Cesium.Math.toDegrees(viewRectangle.east).toFixed(2);
    const south = Cesium.Math.toDegrees(viewRectangle.south).toFixed(2);
    const north = Cesium.Math.toDegrees(viewRectangle.north).toFixed(2);
    console.log(`当前视域范围: ${west}°E-${east}°E, ${south}°N-${north}°N`);
  }
});

解决相机移动时的画面抖动

js
// 调整惯性参数
const controller = viewer.scene.screenSpaceCameraController;
controller.inertiaSpin = 0.8;
controller.inertiaTranslate = 0.8;
controller.inertiaZoom = 0.8;

// 启用平滑相机移动
viewer.camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z;

碰撞检测

防止相机穿过地形或模型

javascript
// 启用地形碰撞检测
viewer.scene.screenSpaceCameraController.enableCollisionDetection = true;

// 设置最小碰撞高度
viewer.scene.screenSpaceCameraController.minimumCollisionTerrainHeight = 2.0;

视角保存与恢复

实现视角状态的保存和快速切换:

js
// 视角状态管理
const cameraStates = {
  savedStates: {}, // 存储视角状态的对象

  // 保存当前视角
  saveState: function (key) {
    const camera = viewer.camera;
    this.savedStates[key] = {
      position: camera.position.clone(),
      orientation: {
        heading: camera.heading,
        pitch: camera.pitch,
        roll: camera.roll,
      },
    };
    console.log(`已保存视角: ${key}`);
  },

  // 恢复视角
  restoreState: function (key, duration = 1.5) {
    const state = this.savedStates[key];
    if (!state) {
      console.error(`未找到保存的视角: ${key}`);
      return;
    }

    viewer.camera.flyTo({
      destination: state.position,
      orientation: state.orientation,
      duration: duration,
    });
  },

  // 删除视角
  deleteState: function (key) {
    if (this.savedStates[key]) {
      delete this.savedStates[key];
      console.log(`已删除视角: ${key}`);
    }
  },
};

// 使用示例
cameraStates.saveState("overview"); // 保存概览视角
cameraStates.restoreState("overview"); // 恢复概览视角

性能优化

相机相关性能建议

使用相机视锥体剔除:自动隐藏视域外对象

js
viewer.scene.globe.enableLighting = true;
viewer.scene.globe.depthTestAgainstTerrain = true;

避免过度相机移动:高频相机移动会导致帧率下降

js
// 相机移动节流
let isCameraMoving = false;
viewer.camera.moveEnd.addEventListener(function () {
  isCameraMoving = false;
  // 恢复高帧率渲染
  viewer.scene.maximumRenderTimeChange = 0.0;
});

viewer.camera.moveStart.addEventListener(function () {
  isCameraMoving = true;
  // 移动时降低渲染频率
  viewer.scene.maximumRenderTimeChange = 0.2;
});