当前位置: 首页 > 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.lryc.cn/news/605626.html

相关文章:

  • langchain--1--prompt、output格式、LCEL示例
  • 【烧脑算法】Dijkstra 算法:解决最短路问题
  • 会议室预定系统核心技术:如何用一行SQL解决时间冲突检测难题
  • LLC电源原边MOS管DS增加RC吸收对ZVS的影响分析
  • ode with me是idea中用来干嘛的插件
  • 山东移动云主机:技术架构与特性解析
  • AI 安监系统:为工业园安全保驾护航
  • 1 机器学习概述 (第一天2025.7.31)
  • RabbitMQ 队列配置设置 RabbitMQ 消息监听器的并发消费者数量java
  • Java 大视界 -- Java 大数据在智能医疗远程健康监测与疾病预防预警中的应用(374)
  • Linux 进程管理与计划任务
  • linux git ssh配置过程
  • React中的this绑定
  • SpringMVC的核心架构与请求处理流程
  • PostgreSQL dblink 与 Spring Boot @Transactional 的事务整合
  • 网络层概述
  • AngularJS 事件
  • Web 开发 08
  • 智慧社区项目开发(四)——前后端登录认证相关功能实现解析
  • 网关 + MDC 过滤器方案,5分钟集成 日志 traceid
  • Gemini Fullstack LangGraph Quickstart(DeepSeek+Tavily版本)
  • 智慧园区通行效率↑68%!陌讯多模态融合算法的实战解析
  • 【Cpolar实现内网穿透】
  • 转码刷 LeetCode 笔记[1]:3.无重复字符的最长子串(python)
  • 解决宇道项目关于接收日期格式yyyy-MM-dd HH:mm:ss后端自动转为1970-01-01 00:00:00的问题
  • 计算机网络——UDP
  • TOC-Transformer-LSTM-ABKDE,计算机一区算法龙卷风优化算法应用到概率区间预测!Matlab实现
  • css 不错的按钮动画
  • Linux日志管理与时间同步
  • 【数据结构初阶】--二叉树(六)