当前位置: 首页 > web >正文

Cesium 快速入门(四)相机控制完全指南

Cesium 快速入门(四)相机控制完全指南

看过的知识不等于学会。唯有用心总结、系统记录,并通过温故知新反复实践,才能真正掌握一二
作为一名摸爬滚打三年的前端开发,开源社区给了我饭碗,我也将所学的知识体系回馈给大家,助你少走弯路!
OpenLayers、Leaflet 快速入门 ,每周保持更新 2 个案例
Cesium 快速入门,每周保持更新 4 个案例

Cesium 快速入门(一)快速搭建项目
Cesium 快速入门(二)底图更换
Cesium 快速入门(三)Viewer:三维场景的“外壳”
Cesium 快速入门(四)相机控制完全指南
Cesium 快速入门(五)坐标系
Cesium 快速入门(六)实体类型介绍
Cesium 快速入门(七)材质详解
Cesium 快速入门(八)Primitive(图元)系统深度解析
Cesium 快速入门(九)Appearance(外观)系统深度解析
Cesium 快速入门(十) JulianDate(儒略日期)详解
Cesium 快速入门(十一)3D Tiles 大规模三维地理空间数据
Cesium 快速入门(十二)数据加载详解
Cesium 快速入门(十三)事件系统

Cesium 相机控制完全指南

相机(Camera)是 Cesium 场景交互的核心,负责定义观察者视角和运动方式。

核心概念

相机坐标系

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 为水平

基础配置

默认视角设置

相机初始化默认看向 0° 经线、0° 纬线区域,可通过以下方式修改默认视图:

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

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

相机参数获取

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)

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

// 方式一:使用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)

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

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)

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

// 方式一:使用Cartesian3偏移量
viewer.camera.lookAt(Cesium.Cartesian3.fromDegrees(116.404, 39.915, 0), // 目标点坐标new Cesium.Cartesian3(0, -500, 300) // 相对目标点的偏移量(右、后、上)
);// 方式二:使用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 // 距离目标点的距离(米))
);

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

交互控制

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

键盘控制实现

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

<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(() => {// 初始化Viewerviewer = new Cesium.Viewer(cesiumContainer.value, {geocoder: false, // 关闭地理编码搜索homeButton: false, // 关闭主页按钮sceneModePicker: false, // 关闭场景模式选择器baseLayerPicker: false, // 关闭底图选择器navigationHelpButton: false, // 关闭导航帮助animation: false, // 关闭动画控件timeline: false, // 关闭时间轴fullscreenButton: false, // 关闭全屏按钮baseLayer: false, // 关闭默认地图});// 清空logoviewer.cesiumWidget.creditContainer.style.display = "none";// 监听键盘事件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;}});initMap();
});// 加载天地图
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", // 天地图使用 Web 墨卡托投影(EPSG:3857),需确保 tileMatrixSetID: "w"subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"], // 子域名maximumLevel: 18,credit: new Cesium.Credit("天地图影像"),});// 添加地理标注const labelProvider = new Cesium.WebMapTileServiceImageryProvider({url:"http://{s}.tianditu.gov.cn/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&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);// 天地图地理标注(后添加的会覆盖前面的)viewer.imageryLayers.addImageryProvider(labelProvider);
};
</script>
<style scoped>
.container {width: 100vw;height: 100vh;
}
</style>

高级应用

相机事件监听

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

// 相机移动开始事件
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`);}
});

视角保存与恢复

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

// 视角状态管理
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"); // 恢复概览视角

性能优化

相机相关性能建议

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

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

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

// 相机移动节流
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;
});
http://www.xdnf.cn/news/16861.html

相关文章:

  • 项目中如何追踪项目进度,避免项目延期如何追踪项目进度
  • Java客户端连接Redis
  • Ⅹ—6.计算机二级综合题19---22套
  • 大数据平台数仓数湖hive之拉链表高效实现
  • 学习日志23 python
  • Spring MVC体系结构和处理请求控制器
  • 【linux驱动开发】Vscode + Remote SSH + clangd + bear=内核源码阅读环境搭建
  • 三维开放场景图助力机器人自主导航!Point2Graph:点云驱动的三维开放词汇场景图端到端机器人导航
  • 蓝牙设备配对:从机发现主机全过程
  • 《质光相济:Three.js中3D视觉的底层交互逻辑》
  • 嵌入式仿真教学的革新力量:深圳航天科技创新研究院引领高效学习新时代
  • 学习笔记《区块链技术与应用》第三天 网络 难度
  • 【01】大恒相机SDK C++开发 —— 初始化相机,采集第一帧图像、回调采集、关闭相机
  • TGD第九篇:三维应用——视频边缘检测
  • Excel 知识点汇总
  • 爱普生002墨水与004墨水基本参数及支持机型
  • 行业热点丨仿真历史数据难以使用?如何利用几何深度学习破局,加速汽车工程创新
  • Java 17 新特性解析与代码示例
  • Linux的库制作与原理
  • Haproxy调度算法 - 静态算法介绍与使用
  • 为什么Android主线程与java主线程不同,不会退出?
  • 全栈:怎么把IDEA和Maven集成一下?
  • 前端框架Vue3(四)——组件通信及其他API
  • 分布内侧内嗅皮层的层Ⅱ或层Ⅲ的网格细胞(grid cells)对NLP中的深层语义分析的积极影响和启示
  • 一万字讲解Java中的IO流——包含底层原理
  • QtConcurrent::run函数
  • Nginx反向代理负载均衡
  • 常用设计模式系列(十六)—策略模式
  • Ubuntu 24.04 LTS 保姆级教程:安装 NVIDIA 显卡驱动、CUDA 12.5 及 Docker 容器工具包
  • 【YOLOv1】