Cesium快速入门到精通系列教程二:添加地形与添加自定义地形、相机控制

news2025/6/6 17:56:16

一、添加地形与添加自定义地形

在 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 轴正方向)
    1. heading​​:绕Y轴旋转(正北为0°,向东为正方向)。
    2. ​​pitch​​:绕X轴旋转(-90°为俯视地面,0°为平视,正值为仰视)。
    3. ​​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 设置默认视角

// 设置Cesium默认视角
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
  89.5, // 西边经度
  20.4, // 南边维度
  110.4, // 东边经度
  61.2) // 北边维度

2.2 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.3 flyTo:动画飞行至目标​​

特点​​:支持平滑过渡,可设置飞行时长、视角偏移等。

关键参数​​:

  • duration:动画时间(秒)。
  • pitchAdjustHeight:高度超过此值时自动调整俯仰角。
viewer.camera.flyTo({
    destination: position,
    orientation: orientation,
    duration: 5,  // 5秒动画
    pitchAdjustHeight: -90  // 强制俯视地面
});

2.4 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.5 viewBoundingSphere:环绕目标区域​​

适用场景​​:室内或小范围模型浏览。

const boundingSphere = new Cesium.BoundingSphere(center, radius);
viewer.camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(0, 0, 0));

2.6 方向控制的进阶应用

2.6.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.6.2 动态方向控制​​

通过事件监听实时更新相机方向: 

viewer.scene.preRender.addEventListener(() => {
    const heading = viewer.camera.heading;
    const pitch = viewer.camera.pitch;
    console.log(`当前航向:${Cesium.Math.toDegrees(heading).toFixed(2)}°`);
});
2.6.3 实体跟随模式​​

使用trackedEntity让相机自动跟随移动目标: 

viewer.trackedEntity = entity;  // 实体ID或对象
2.6.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.7 常见问题与注意事项

  • 坐标系一致性​​

确保位置和方向参数在同一坐标系下(如WGS84)。若使用局部坐标,需通过变换矩阵转换。

  • 俯仰角限制​​

默认俯仰角范围为[-π/2, π/2],超出可能导致视角异常。可通过viewer.camera.pitchLimits调整。

  • ​​性能优化​​

频繁调用flyTo或setView时,建议合并连续操作,避免卡顿。

2.8 完整示例:相机环绕目标点

// 定义目标点(北京天安门)
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.9 相机动画与相机动态交互

 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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2399156.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python学习打卡day43

DAY 43 复习日 作业&#xff1a; kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 浙大疏锦行 数据集使用猫狗数据集&#xff0c;训练集中包含猫图像4000张、狗图像4005张。测试集包含猫图像1012张&#xff0c;狗图像1013张。以下是数据集的下…

Microsoft Word使用技巧分享(本科毕业论文版)

小铃铛最近终于完成了毕业答辩后空闲下来了&#xff0c;但是由于学校没有给出准确地参考模板&#xff0c;相信诸位朋友们也在调整排版时感到头疼&#xff0c;接下来小铃铛就自己使用到的一些排版技巧分享给大家。 注&#xff1a;以下某些设置是根据哈尔滨工业大学&#xff08;威…

windows安装多个版本composer

一、需求场景 公司存在多个项目&#xff0c;有的项目比较老&#xff0c;需要composer 1.X版本才能使用 新的项目又需要composer 2.X版本才能使用 所以需要同时安装多个版本的composer二、下载多个版本composer #composer官网 https://getcomposer.org/download/三、放到指定目…

【办公类-22-05】20250601Python模拟点击鼠标上传CSDN12篇

、 背景需求: 每周为了获取流量券,每天上传2篇,获得1500流量券,每周共上传12篇,才能获得3000和500的券。之前我用UIBOT模拟上传12篇。 【办公类-22-04】20240418 UIBOT模拟上传每天两篇,获取流量券,并删除内容_csdn 每日任务流量券-CSDN博客文章浏览阅读863次,点赞18…

贪心算法应用:边着色问题详解

贪心算法应用&#xff1a;边着色问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择&#xff0c;从而希望导致结果是全局最优的算法策略。边着色问题是图论中的一个经典问题&#xff0c;贪心算法可以有效地解决它。下面我将从基础概念到具体实现&#xff0c;全…

ck-editor5的研究 (2):对 CKEditor5 进行设计,并封装成一个可用的 vue 组件

前言 在上一篇文章中—— ck-editor5的研究&#xff08;1&#xff09;&#xff1a;快速把 CKEditor5 集成到 nuxt 中 &#xff0c;我仅仅是把 ckeditor5 引入到了 nuxt 中&#xff0c;功能还不算通用。 这一篇内容将会对其进行设计&#xff0c;并封装成可复用的 vue 组件&…

Java-redis实现限时在线秒杀功能

1.使用redisson pom文件添加redisson <!--redisson--><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.23.4</version></dependency> 2.mysql数据库表设…

simulink mask、sfunction和tlc的联动、接口

这里全部是讲的level2 sfunction&#xff08;用m语言编写&#xff09;&#xff0c;基于matlab 2020a。 1.mask的参数操作 1&#xff09;mask通过set_param和get_param这2个函数接口对mask里面定义的Parameters&Dialog的参数的大部分属性进行读写&#xff0c;一般是Value值…

VMWare安装常见问题

如果之前安装过VMWare软件&#xff0c;只要是 15/16 版本的&#xff0c;可以正常使用的&#xff0c;不用卸载&#xff01;&#xff01;&#xff01; 如果之前安装过&#xff0c;卸载了&#xff0c;一定要保证通过正常的渠道去卸载&#xff08;通过控制面板卸载软件&#xff09…

【北邮 操作系统】第十二章 文件系统实现

一、文件的物理结构 1.1 文件块、磁盘块 类似于内存分页&#xff0c;磁盘中的存储单元也会被分为一个个“块/磁盘块/物理块”。很多操作系统中&#xff0c;磁盘块的大小与内存块、页面的大小相同 内存与磁盘之间的数据交换(即读/写操作、磁盘I/0)都是以“块”为单位进行的。即…

Docker 插件生态:从网络插件到存储插件的扩展能力解析

Docker 容器技术以其轻量、快速、可移植的特性,迅速成为构建和部署现代应用的核心工具。然而,尽管 Docker Engine 自身功能强大,但在面对多样化的生产环境和复杂业务需求时,仅靠核心功能往往无法满足所有场景。 例如,跨主机的容器网络通信、异构存储系统的持久化数据管理…

WordPress搜索引擎优化的最佳重定向插件:进阶指南

在管理网站时&#xff0c;我们经常需要调整网页地址或修复错误链接。这时&#xff0c;通过重定向不仅能有效解决这些问题&#xff0c;还能显著提升网站在搜索引擎中的排名。对于熟悉基础重定向插件的用户来说&#xff0c;一些功能更强大的工具可以帮助你更全面地管理网站&#…

org.junit.runners.model.InvalidTestClassError:此类问题的解决

不知道大家是否遇见过以上这种情况&#xff0c;我也是今天被这个错误搞得很烦&#xff0c;后来通过网上查找资料终于找到了问题所在————就是简单的Test注解的错误使用 Test注解的注意情况 &#xff1a;1 权限必须是public 2 不能有参数 3 返回值类型是void 4 本类的其他的…

用户管理页面(解决toggleRowSelection在dialog用不了的隐患,包含el-table的plus版本的组件)

新增/编辑/删除/分配角色&#xff0c;图片上传在此文章分类下另一个文章 1.重点分配角色&#xff1a; <template><!-- 客户资料 --><div class"pageBox"><elPlusTable :tableData"tableData" :tablePage"tablePage" onSi…

Linux-GCC、makefile、GDB

GCC gcc -E test.c -o test.i预处理(-o指定文件名) gcc -S test.i -o test.s编译gcc -c test.s -o test.o汇编gcc test.o -o test链接(生成一个可执行程序的软连接) gcc test.c -o test一条指令可以完成以上所有内容 gcc *.c -I(大写的i) include由于在main.c中找不到当前文件…

[MySQL初阶]MySQL(7) 表的内外连接

标题&#xff1a;[MySQL初阶]MySQL(7)表的内外连接 水墨不写bug 文章目录 一. 内连接 (INNER JOIN)二. 外连接 (OUTER JOIN)关键区别总结 三、 如何选择 在 MySQL 中&#xff0c;连接&#xff08;JOIN&#xff09;用于根据两个或多个表之间的相关列组合行。内连接&#xff08;I…

【R语言编程绘图-mlbench】

mlbench库简介 mlbench是一个用于机器学习的R语言扩展包&#xff0c;主要用于提供经典的基准数据集和工具&#xff0c;常用于算法测试、教学演示或研究场景。该库包含多个知名数据集&#xff0c;涵盖分类、回归、聚类等任务。 包含的主要数据集 BostonHousing 波士顿房价数据…

检索器组件深入学习与使用技巧 BaseRetriever 检索器基类

1. BaseRetriever 检索器基类 在 LangChain 中&#xff0c;传递一段 query 并返回与这段文本相关联文档的组件被称为 检索器&#xff0c;并且 LangChain 为所有检索器设计了一个基类——BaseRetriever&#xff0c;该类继承了 RunnableSerializable&#xff0c;所以该类是一个 …

【Doris基础】Doris中的Replica详解:Replica原理、架构

目录 1 Replica基础概念 1.1 什么是Replica 1.2 Doris中的副本类型 2 Doris副本架构设计 2.1 副本分布机制 2.2 副本一致性模型 3 副本生命周期管理 3.1 副本创建流程 3.2 副本恢复机制 4 副本读写流程详解 4.1 写入流程与副本同步 4.2 查询流程与副本选择 5 副本…

【中国·广州】第三届信号处理与智能计算国际学术会议 (SPIC2025) 即将开启

第三届信号处理与智能计算国际学术会议 (SPIC2025) 即将开启 在信息技术飞速发展的当下&#xff0c;信号处理与智能计算作为前沿科技领域&#xff0c;正深刻改变着我们的生活与产业格局。为汇聚全球顶尖智慧&#xff0c;推动该领域进一步突破&#xff0c;第三届信号处理与智能…