Cesium实战:5分钟搞定3D地球可视化(附完整代码示例)
当我们需要在网页中展示全球地理数据时,Cesium无疑是最强大的选择之一。这个开源的JavaScript库让创建3D地球变得异常简单,无论是展示气象数据、物流轨迹,还是构建数字孪生应用,Cesium都能提供专业级的可视化效果。本文将带你快速上手,用最简单的代码实现一个功能完整的3D地球。
1. 环境准备:零配置起步
不需要复杂的开发环境,甚至不需要安装任何软件,我们就能开始Cesium之旅。现代前端开发的一大优势就是可以直接使用CDN引入库文件,这让我们能够快速验证想法。
创建一个空白HTML文件,添加以下基础结构:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Cesium快速入门</title> <!-- 引入Cesium核心CSS --> <link href="https://cesium.com/downloads/cesiumjs/releases/1.104/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> </head> <body> <!-- 3D地球容器 --> <div id="cesiumContainer"></div> <!-- 引入Cesium核心JS --> <script src="https://cesium.com/downloads/cesiumjs/releases/1.104/Build/Cesium/Cesium.js"></script> <script> // 我们的代码将写在这里 </script> </body> </html>提示:上述代码使用了Cesium官方CDN,确保你始终获取最新稳定版本。如需离线开发,可下载对应版本的库文件。
2. 初始化3D地球场景
有了基础HTML结构后,我们只需几行JavaScript代码就能创建一个完整的3D地球:
// 设置Cesium ion访问令牌(必需) Cesium.Ion.defaultAccessToken = 'your_access_token_here'; // 初始化3D地球 const viewer = new Cesium.Viewer('cesiumContainer', { terrainProvider: Cesium.createWorldTerrain(), // 使用Cesium世界地形 baseLayerPicker: false, // 简化界面,隐藏底图选择器 timeline: false, // 隐藏时间轴 animation: false, // 隐藏动画控件 }); // 设置初始视角 viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(116.4, 39.9, 10000000), // 经度,纬度,高度 orientation: { heading: Cesium.Math.toRadians(0), // 朝向 pitch: Cesium.Math.toRadians(-90), // 俯仰角 roll: 0.0 // 翻滚角 } });关键参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
| terrainProvider | Object | 地形数据提供者,createWorldTerrain使用Cesium全球地形 |
| baseLayerPicker | Boolean | 是否显示底图选择控件 |
| timeline | Boolean | 是否显示时间轴控件 |
| animation | Boolean | 是否显示动画播放控件 |
注意:使用Cesium世界地形需要注册Cesium ion账号获取访问令牌。免费账户每月有10GB的配额,对学习和测试完全够用。
3. 添加丰富的地理数据层
空白的3D地球只是开始,Cesium真正的强大之处在于它能轻松集成各种地理空间数据。让我们添加几个常见的数据类型:
3.1 加载高分辨率影像
// 添加高分辨率影像图层 viewer.imageryLayers.addImageryProvider( new Cesium.IonImageryProvider({ assetId: 3845 }) // Bing Maps影像 );3.2 显示3D建筑模型
// 启用3D建筑(仅在城市区域可见) viewer.scene.primitives.add( Cesium.createOsmBuildings() );3.3 添加自定义标记点
// 在地球上添加标记点 const entity = viewer.entities.add({ name: '北京', position: Cesium.Cartesian3.fromDegrees(116.4, 39.9), point: { pixelSize: 10, color: Cesium.Color.RED, outlineColor: Cesium.Color.WHITE, outlineWidth: 2 }, label: { text: '北京市', font: '14pt sans-serif', style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineWidth: 2, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, pixelOffset: new Cesium.Cartesian2(0, -10) } });4. 完整代码示例
将所有代码整合,我们得到一个功能完整的3D地球可视化示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Cesium 3D地球示例</title> <link href="https://cesium.com/downloads/cesiumjs/releases/1.104/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> <style> html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } </style> </head> <body> <div id="cesiumContainer"></div> <script src="https://cesium.com/downloads/cesiumjs/releases/1.104/Build/Cesium/Cesium.js"></script> <script> // 设置访问令牌(替换为你的实际令牌) Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; // 初始化3D地球 const viewer = new Cesium.Viewer('cesiumContainer', { terrainProvider: Cesium.createWorldTerrain(), baseLayerPicker: false, timeline: false, animation: false, }); // 设置初始视角(中国区域) viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(116.4, 39.9, 10000000), orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-90), roll: 0.0 } }); // 添加影像图层 viewer.imageryLayers.addImageryProvider( new Cesium.IonImageryProvider({ assetId: 3845 }) ); // 添加3D建筑 viewer.scene.primitives.add( Cesium.createOsmBuildings() ); // 添加标记点 viewer.entities.add({ name: '北京', position: Cesium.Cartesian3.fromDegrees(116.4, 39.9), point: { pixelSize: 10, color: Cesium.Color.RED, outlineColor: Cesium.Color.WHITE, outlineWidth: 2 }, label: { text: '北京市', font: '14pt sans-serif', style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineWidth: 2, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, pixelOffset: new Cesium.Cartesian2(0, -10) } }); </script> </body> </html>5. 进阶功能与性能优化
当基础功能实现后,你可能还想进一步提升用户体验和性能:
5.1 地形质量与性能平衡
// 调整地形细节层级 viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ url: Cesium.IonResource.fromAssetId(1), requestVertexNormals: true, // 启用顶点法线,增强光照效果 requestWaterMask: true // 启用水面效果 }); // 根据视距动态调整细节 viewer.scene.screenSpaceCameraController.minimumZoomDistance = 100; // 最小视距 viewer.scene.screenSpaceCameraController.maximumZoomDistance = 50000000; // 最大视距5.2 添加动态数据
// 添加动态飞行路径 const flightPath = viewer.entities.add({ name: '飞行轨迹', polyline: { positions: Cesium.Cartesian3.fromDegreesArray([ 116.4, 39.9, // 北京 121.47, 31.23 // 上海 ]), width: 5, material: new Cesium.PolylineGlowMaterialProperty({ glowPower: 0.2, color: Cesium.Color.BLUE }) } }); // 添加移动的飞机模型 const airplane = viewer.entities.add({ name: '飞机', position: Cesium.Cartesian3.fromDegrees(116.4, 39.9, 10000), model: { uri: 'https://assets.agi.com/models/airplane/CesiumAir/Cesium_Air.glb', minimumPixelSize: 64 }, path: { resolution: 1, material: new Cesium.PolylineGlowMaterialProperty({ glowPower: 0.1, color: Cesium.Color.YELLOW }), width: 10 } }); // 动画:飞机沿路径飞行 const start = Cesium.JulianDate.fromDate(new Date()); const stop = Cesium.JulianDate.addSeconds(start, 3600, new Cesium.JulianDate()); viewer.clock.startTime = start.clone(); viewer.clock.stopTime = stop.clone(); viewer.clock.currentTime = start.clone(); viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; viewer.timeline.zoomTo(start, stop); // 设置飞机位置随时间变化 airplane.position = new Cesium.SampledPositionProperty(); for (let i = 0; i <= 3600; i += 60) { const time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate()); const position = Cesium.Cartesian3.fromDegrees( 116.4 + (121.47 - 116.4) * (i / 3600), 39.9 + (31.23 - 39.9) * (i / 3600), 10000 ); airplane.position.addSample(time, position); }5.3 性能优化技巧
- 按需加载:使用Cesium的
CreditDisplay和ImageryLayerCollection控制数据加载 - 细节层级(LOD):为3D模型设置合适的
minimumPixelSize和maximumScale - 内存管理:定期调用
viewer.entities.removeAll()清理不再使用的实体 - 相机控制:合理设置
screenSpaceCameraController参数,避免过度渲染
// 性能优化配置示例 viewer.scene.globe.depthTestAgainstTerrain = true; // 启用深度测试 viewer.scene.fog.enabled = true; // 启用雾效,优化远处渲染 viewer.scene.sun.show = false; // 关闭太阳效果提升性能 viewer.scene.moon.show = false; // 关闭月亮效果6. 常见问题解决方案
在实际开发中,你可能会遇到以下典型问题:
6.1 跨域资源加载
当从不同域加载资源时,可能会遇到CORS限制。解决方案:
- 确保服务器配置了正确的CORS头
- 使用代理服务器中转请求
- 对于本地开发,可以禁用浏览器安全限制(仅限开发环境)
6.2 地形闪烁问题
地形或模型出现闪烁通常是由于Z-fighting引起,解决方法:
// 调整深度测试参数 viewer.scene.globe.depthTestAgainstTerrain = true; // 对于特定实体,设置高度偏移 entity.polygon.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND; entity.polygon.extrudedHeightReference = Cesium.HeightReference.RELATIVE_TO_GROUND;6.3 移动设备兼容性
在移动设备上使用时,注意:
- 简化场景复杂度
- 降低地形质量
- 禁用不必要的特效
- 优化触摸交互体验
// 移动设备优化配置 if (Cesium.FeatureDetection.isMobile()) { viewer.scene.globe.detailScalar = 0.5; // 降低地形细节 viewer.scene.fxaa = true; // 启用快速抗锯齿 viewer.scene.highDynamicRange = false; // 禁用HDR }7. 项目实战建议
掌握了基础功能后,可以考虑以下方向扩展你的Cesium项目:
- 数据可视化:将业务数据(如气象、交通、人口等)映射到3D地球上
- 时空分析:利用Cesium的时间轴功能展示数据随时间变化
- AR/VR集成:通过WebXR将Cesium场景扩展到增强/虚拟现实设备
- 数字孪生:构建真实世界的数字副本,用于模拟和预测
对于大型项目,建议采用模块化开发方式:
// 模块化示例:将Cesium功能封装为独立模块 class CesiumMap { constructor(containerId, options = {}) { this.viewer = new Cesium.Viewer(containerId, { terrainProvider: Cesium.createWorldTerrain(), ...options }); this._initCamera(); this._setupBaseLayers(); } _initCamera() { this.viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(116.4, 39.9, 10000000), orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-90), roll: 0.0 } }); } _setupBaseLayers() { this.viewer.imageryLayers.addImageryProvider( new Cesium.IonImageryProvider({ assetId: 3845 }) ); this.viewer.scene.primitives.add(Cesium.createOsmBuildings()); } addMarker(position, options = {}) { return this.viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude), point: { pixelSize: options.size || 10, color: options.color || Cesium.Color.RED, outlineColor: Cesium.Color.WHITE, outlineWidth: 2 }, label: { text: options.label || '', font: '14pt sans-serif', style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineWidth: 2, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, pixelOffset: new Cesium.Cartesian2(0, -10) } }); } } // 使用封装好的模块 const map = new CesiumMap('cesiumContainer'); map.addMarker({ longitude: 116.4, latitude: 39.9 }, { label: '北京' });