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

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.dtcms.com/a/307495.html

相关文章:

  • 【Django】-1- 开发项目搭建
  • Java Matcher对象中find()与matches()的区别
  • sqli-labs:Less-15关卡详细解析
  • 10.C 语言内存划分,static,字符串
  • MFC CChartCtrl编程
  • 逻辑回归的应用
  • 【人工智能】当AI智能体遇上安全与伦理:一场技术与人性的对话
  • 3DXML 转换为 UG 的技术指南及迪威模型网在线转换推荐
  • arm架构系统打包qt程序--麒麟操作系统为例
  • 递归混合架构(MoR)在医疗领域的发展应用能力探析
  • 网络编程(一)TCP编程和UDP编程
  • Kubernetes集群中滚动更新失败与资源配置错误的深度解析及应对策略
  • 机器学习03——数据与算法初步2
  • Git之本地仓库管理
  • 第一篇:【Python-geemap教程(三)上】3D地形渲染与Landsat NDVI计算
  • 学习 java web 简单监听器
  • 《能碳宝》AI辅助开发系统方案
  • ES 工业网关:比德国更适配,比美国更易用
  • 编程语言Java——核心技术篇(六)解剖反射:性能的代价还是灵活性的福音?
  • Ubuntu/Debian 搭建 Nginx RTMP 服务器全攻略
  • 使用的IDE没有内置MCP客户端怎么办?
  • [源力觉醒 创作者计划]_文心4.5开源测评:国产大模型的技术突破与多维度能力解析
  • 数据库中使用SQL作分组处理01(简单分组)
  • Web3.0 和 Web2.0 生态系统比较分析:差异在哪里?
  • Web3:在 VSCode 中使用 Vue 前端与已部署的 Solidity 智能合约进行交互
  • Kotlin -> 普通Lambda vs 挂起Lambda
  • Astra主题WooCommerce如何添加可变产品Astra variation product
  • tplink er2260t配置vlan透传iptv
  • python学智能算法(二十九)|SVM-拉格朗日函数求解中-KKT条件理解
  • 数据结构: 双向列表