一、添加地形与添加自定义地形
在 Cesium 1.93 中添加地形可以通过配置terrainProvider实现。Cesium 支持多种地形数据源,包括 Cesium Ion 提供的全球地形、自定义地形服务以及开源地形数据。下面介绍几种常见的添加地形的方法:
使用 Cesium Ion 全球地形服务
这是最简单的方式,需要一个 Cesium Ion 账户和访问令牌:
// 设置Cesium Ion访问令牌
Cesium.Ion.defaultAccessToken = '你的Cesium Ion令牌';
// 初始化Viewer并启用全球地形
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain({
requestVertexNormals: true, // 启用地形光照
requestWaterMask: true // 启用水面效果
}),
baseLayerPicker: false, // 可选:禁用默认图层选择器
});
添加自定义地形
1、从地理空间数据云下载数据:
数据资源->公开数据->DEM 数字高程数据



2、从cesiumlab下载工具进行数据转换:
安装下载的工具,比如当前版本cesiumlab4_4.0.8.exe;
打开工具,安装以下方式设置提交即可:


将以上生成的瓦片本地部署,部署的方式很多种,只要保证能通过url在线访问即可:

在代码中加载:
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: new Cesium.CesiumTerrainProvider({
url: 'http://localhost:3000', // 替换为你的服务器地址
requestVertexNormals: true, // 请求法线以支持地形光照
requestWaterMask: true // 请求水掩码以支持水面效果
})
});
// 配置自定义地形服务
const customTerrainProvider = new Cesium.CesiumTerrainProvider({
url: 'http://localhost:3000', // 替换为你的服务器地址
requestVertexNormals: true,
requestWaterMask: true,
isSct: true // 若为 SuperMap iServer 服务需设为 true [6](@ref)
});
// 应用自定义地形
viewer.terrainProvider = customTerrainProvider;
常见问题排查
问题现象 | 解决方案 |
---|---|
地形加载失败 | 检查网络连接和 Cesium Ion 令牌 |
水体效果未显示 | 确认 requestWaterMask: true |
地形贴图模糊 | 增大 viewer.scene.maximumScreenSpaceError 值 |
内存泄漏 | 限制 viewer.scene.globe.tileCacheSize |
二、相机的方向和位置
在Cesium 1.93中,相机的方向和位置控制是三维场景交互的核心。
1、相机坐标系与关键概念
1.1 相机坐标系基础
将相机比喻成直立行走的人,镜头好比人的视野。
- 位置(Position):相机在三维空间中的笛卡尔坐标(Cartesian3),以地球质心为原点。
- 方向(Direction):相机的朝向,由视线向量(View Vector)表示,指向场景中的目标点。
- 上方向(Up Vector):相机的 "上方" 方向,默认与地球表面垂直(Z 轴正方向)。
- heading:绕Y轴旋转(正北为0°,向东为正方向)。
- pitch:绕X轴旋转(-90°为俯视地面,0°为平视,正值为仰视)。
- roll:绕Z轴旋转(默认0°,正值为右倾)。
- 参考系(Reference Frame):相机运动的参考坐标系,通常为ENU(东 - 北 - 上)或ECF(地心地固坐标系)。
const orientation = {
heading: Cesium.Math.toRadians(0), // 正北
pitch: Cesium.Math.toRadians(-90), // 俯视地面
roll: 0.0
};
2、相机控制的核心方法
2.1 setView:直接设置视角
特点:无动画,立即切换到目标位置和方向。
viewer.camera.setView({
destination: position, // 目标位置(Cartesian3)
orientation: orientation // 方向参数
});
const position = Cesium.Cartesian3.fromDegrees(116.3907917, 39.9158389, 500); // 故宫
const orientation = {
heading: Cesium.Math.toRadians(0), // 正北
pitch: Cesium.Math.toRadians(-90), // 俯视地面
roll: 0.0
};
viewer.camera.setView({
destination: position,
orientation
});
2.2 flyTo:动画飞行至目标
特点:支持平滑过渡,可设置飞行时长、视角偏移等。
关键参数:
-
duration:动画时间(秒)。
-
pitchAdjustHeight:高度超过此值时自动调整俯仰角。
viewer.camera.flyTo({
destination: position,
orientation: orientation,
duration: 5, // 5秒动画
pitchAdjustHeight: -90 // 强制俯视地面
});
2.3 lookAt:视角锁定目标点
特点:相机位置固定,始终朝向目标点。
参数:target(目标点)和offset(偏移量,支持HeadingPitchRange)。
const center = Cesium.Cartesian3.fromDegrees(116.4, 39.9);
viewer.camera.lookAt(center, new Cesium.HeadingPitchRange(0, -Math.PI/2, 1000));
2.4 viewBoundingSphere:环绕目标区域
适用场景:室内或小范围模型浏览。
const boundingSphere = new Cesium.BoundingSphere(center, radius);
viewer.camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(0, 0, 0));
2.5 方向控制的进阶应用
2.5.1 局部坐标系转换
使用Transforms.eastNorthUpToFixedFrame将局部坐标转换为全局坐标系:
const localPosition = new Cesium.Cartesian3(10, 20, 0);
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(localPosition);
const globalPosition = Cesium.Matrix4.multiplyByPoint(transform, localPosition);
2.5.2 动态方向控制
通过事件监听实时更新相机方向:
viewer.scene.preRender.addEventListener(() => {
const heading = viewer.camera.heading;
const pitch = viewer.camera.pitch;
console.log(`当前航向:${Cesium.Math.toDegrees(heading).toFixed(2)}°`);
});
2.5.3 实体跟随模式
使用trackedEntity让相机自动跟随移动目标:
viewer.trackedEntity = entity; // 实体ID或对象
2.5.4 多阶段飞行
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(116.39, 39.90, 1000000),
duration: 3,
orientation: { heading: 0, pitch: -Math.PI/2, roll: 0 },
complete: () => {
// 第一阶段完成后触发第二阶段
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(116.40, 39.91, 500000),
duration: 2,
easingFunction: Cesium.EasingFunction.CUBIC_IN_OUT
});
}
});
效果:分阶段飞行,首阶段俯冲至地面,第二阶段缓升至目标点。
2.6 常见问题与注意事项
- 坐标系一致性
确保位置和方向参数在同一坐标系下(如WGS84)。若使用局部坐标,需通过变换矩阵转换。
- 俯仰角限制
默认俯仰角范围为[-π/2, π/2],超出可能导致视角异常。可通过viewer.camera.pitchLimits调整。
- 性能优化
频繁调用flyTo或setView时,建议合并连续操作,避免卡顿。
2.7 完整示例:相机环绕目标点
// 定义目标点(北京天安门)
const target = Cesium.Cartesian3.fromDegrees(116.397, 39.908, 50);
// 设置相机初始位置和方向
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(116.397, 39.908, 1000),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-30),
roll: 0
}
});
// 启动环绕动画(每5秒绕目标一圈)
viewer.clock.onTick.addEventListener(() => {
const time = Cesium.JulianDate.now(viewer.clock.currentTime);
const angle = (time.secondsOfDay * 360) / 5; // 每5秒旋转360°
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(
116.397 + 10 * Math.cos(Cesium.Math.toRadians(angle)),
39.908 + 10 * Math.sin(Cesium.Math.toRadians(angle)),
1000
),
orientation: {
heading: Cesium.Math.toRadians(angle),
pitch: Cesium.Math.toRadians(-30),
roll: 0
}
});
});
2.8 相机动画与相机动态交互
Cesium 1.93 实现镜头飞向故宫的完整示例,包含了基础的场景设置、相机飞行动画以及简单的交互控制。
<template>
<div id="cesiumContainer"></div>
<div class="controls">
<button id="flyToPalaceBtn">飞向故宫</button>
<button id="flyToGreatWallBtn">飞向长城</button>
<button id="resetViewBtn">重置视角</button>
</div>
</template>
<script setup>
Cesium.Ion.defaultAccessToken = 'Cesium defaultAccessToken'
import { onMounted } from "vue";
import * as Cesium from "cesium";
import "./Widgets/widgets.css";
window.CESIUM_BASE_URL = "/"; // 设置Cesium静态资源路径(public目录)
onMounted(() => {
// 初始化Viewer
const viewer = new Cesium.Viewer('cesiumContainer', {
geocoder: false, //设置搜索框是否可见
homeButton: false, // 返回初始位置键是否可见
sceneModePicker: false, // 查看器选择模式选择键是否可见
baseLayerPicker: false, // 图层选择键是否可见
navigationHelpButton: false, // 帮助按钮是否可见
animation: false, // 播放控制按钮是否可见
timeline: false, // 时间轴是否可见
fullscreenButton: false, // 全屏按钮是否可见
terrainProvider: Cesium.createWorldTerrain()
});
// 故宫位置(经纬度和高度)
const palacePosition = {
destination: Cesium.Cartesian3.fromDegrees(116.3907917, 39.9158389, 500), // 经度、纬度、高度(米)
orientation: {
heading: Cesium.Math.toRadians(0.0), // 偏航角(向东)
pitch: Cesium.Math.toRadians(-30.0), // 俯仰角(向下倾斜)
roll: 0.0 // 翻滚角
},
duration: 5, // 飞行持续时间(秒)
maximumHeight: 2000, // 飞行过程中最大高度(米)
curveAmount: 0.5 // 飞行曲线弯曲程度(0-1)
};
// 长城位置(慕田峪段)
const greatWallPosition = {
destination: Cesium.Cartesian3.fromDegrees(116.6558, 40.4139, 500),
orientation: {
heading: Cesium.Math.toRadians(90.0),
pitch: Cesium.Math.toRadians(-20.0),
roll: 0.0
},
duration: 5,
maximumHeight: 3000
};
// 初始视角
const initialView = {
destination: Cesium.Cartesian3.fromDegrees(116.3907917, 39.9158389, 15000),
orientation: {
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-30.0),
roll: 0.0
}
};
// 设置初始视角
viewer.camera.setView(initialView);
// 飞向故宫按钮事件
document.getElementById('flyToPalaceBtn').addEventListener('click', function () {
viewer.camera.flyTo(palacePosition);
});
// 飞向长城按钮事件
document.getElementById('flyToGreatWallBtn').addEventListener('click', function () {
viewer.camera.flyTo(greatWallPosition);
});
// 重置视角按钮事件
document.getElementById('resetViewBtn').addEventListener('click', function () {
viewer.camera.setView(initialView);
});
})
</script>
<style scoped>
* {
margin: 0;
padding: 0;
}
#cesiumContainer {
width: 100wh;
height: 100vh;
}
.controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 100;
}
button {
padding: 8px 16px;
background-color: #007BFF;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background-color: #0056b3;
}
</style>