【Unity3D】Cesium加载大地图

1 前言

Cesium 是一个地球可视化平台和工具链,具有数据切片、数据分发、三维可视等功能。

Cesium 支持 JS、Unity、Unreal、O3DE、Omniverse 等平台,框架如下。

Cesium 相关链接如下:

本文实验完整资源见→Cesium加载大地图案例

2 环境搭建

本节主要参考 Cesium for Unity Quickstart

1)创建 3D (URP) 或 3D (HDRP) 项目

说明:官方建议 Unity Editor 最低版本为 2021.3.2f1,笔者 Unity Editor 版本为 2021.3.11f1c2;Cesium for Unity 可与通用渲染管线(URP)和高清渲染管线(HDRP)配合使用,但是,它不支持 Unity 的内置渲染器,如果选择 3D 项目作为模板,Cesium 加载的数据集将无法正确渲染。

2)添加作用域注册表

依次点击【Edit→Project Settings→Package Manager】,添加注册表信息如下。

Name: Cesium
URL: https://unity.pkg.cesium.com
Scope(s): com.cesium.unity

3)下载 Cesium For Unity

依次点击【Window→Package Manager】打开包管理器窗口,在 Packages 菜单中选择 My Registries,下载 Cesium for Unity,如下。

下载成功后, 菜单栏会多出一个 Cesium 菜单,如下。

4)连接到 Cesium ion

在 Cesium 窗口点击 Connect to Cesium io 按钮,跳转到 Web 网页,点击 Allow。

5)添加 Token

在 Cesium 窗口点击 Token 按钮,添加 token,如果没有 token,就创建一个 token,如果有 token,就使用已创建的 token。

6)创建世界地图

在 Cesium 窗口点击 Cesium World Terrain + Bing Maps Aerial imagery 添加世界地图。

场景显示如下。

如果看不见地图,可能是相机的位置比较远,被裁剪了,可以调整远裁剪平面的位置,如下。

7)添加动态相机

屏蔽场景中默认的相机,在 Cesium 窗口添加 Dynamic Camera,如下。此时,在 Hierarchy 窗口可以看到 CesiumGeoreference 对象下面自动生成了一个名为 DynamicCamera 的对象,该对象上挂了 Camera 组件。

运行后,可以通过鼠标和方向按键控制相机位置和姿态,如下。

说明:视觉的变化通过调整 CesiumGeoreference 组件下的 Latitude 和 Longitude 属性实现,如下。

补充:读者也可以下载 Cesium 官方 Deom 体验一下,详见→cesium-unity-samples

3 地图切片

本节主要介绍 fbx 文件切片流程,对于其他文件的切片,可以参考 CesiumLab地理信息基础数据处理平台使用手册

3.1 切片

下载并安装 CesiumLab 后,打开 CesiumLab 客户端,fbx 文件的切片如下。

在处理日志中可以查看任务是否处理成功,如下。如果切片失败,可能是 fbx 文件中包含相机或灯光,使用 Blender 删除相机和灯光,再重新导出 fbx 文件。

生成的文件如下,加载切片时,需要用到 tileset.json 文件。

3.2 预览切片

在分发服务中可以查看切片,步骤如下。

点击预览后,会跳转到另一个网页,显示切片如下。

用户在浏览器中输入:http://localhost:9003/viewer/index.html,再按以下步骤添加切片,也可以预览切片。

3.3 上传切片到 Cesium ion

Cesium ion 官网登录账号后,按以下步骤上传切片。

选择文件后,上传切片。

在 My Assets 窗口可以查看已经上传的切片,如下。

注意:第一列的 ID 在加载资源时会用到;用户也可以将 Asset Depot 中的资源添加到 My Assets 中。

4 Unity 中加载切片

4.1 加载 Cesium ion 中切片

在 Cesium 窗口单击 Add 按钮,在底部 Console 右边会出现 Cesium ion Assets 窗口,选择地图添加到场景中,如下。

在 Hierachy 窗口双击 CesiumGeoreference 下面的地图对象,使相机聚焦到地图,地图显示如下,通过修改 ion Asset ID 可以加载不同地图。

4.2 加载 CesiumLab 服务中切片

在 Cesium 窗口点击 Blank 3D Tiles Tileset,在 Hierarchy 窗口会生成 CesiumGeoreference 和 Cesium3DTileset 对象,选中 Cesium3DTileset 对象,设置切片的 url,如下。

url 来自 CesiumLab,如下。

在 Hierachy 窗口双击 Cesium3DTileset 对象,使相机聚焦到地图,地图显示如下。

可以看到,地图方位异常,在 4.4 节将介绍调整地图方位的方法。

4.3 加载本地切片

将切片拷贝到项目目录下的 Resources / city 目录下。在 Cesium 窗口点击 Blank 3D Tiles Tileset,在 Hierarchy 窗口会生成 CesiumGeoreference 和 Cesium3DTileset 对象,选中 Cesium3DTileset 对象,设置切片的 url,如下。

在 Hierachy 窗口双击 Cesium3DTileset 对象,使相机聚焦到地图,地图显示同 4.2 节。

4.4 调整地图方位

1)通过 CesiumLab 预览参数调整地图

使用 3.2 节中方法预览切片,调整好方位后,将鼠标放在屏幕正中间,记录底部的经度、纬度、高度,如下。

选中 CesiumGeoreference 对象,设置经度、纬度、高度为上面记录的值,如下。

在 Hierachy 窗口双击 CesiumGeoreference 对象,使相机聚焦到地图,地图显示如下。

可以看到,地图的方位已正确显示,用户如果对该方位还是不满意,在调整好视觉后,点击 Place Origin Here 按钮重置该视觉下的经度、纬度、高度,如下。

2)通过 tileset 文件中 transform 参数调整地图

CesiumLab地理信息基础数据处理平台使用手册 的 2.2.2.1 节 “输出数据的空间参考” 中,我们可以看到以下公式:

其中,3dtiles 里的 transform 矩阵是指切片文件中的 tileset.json 文件,如下。

可以看到 transform 矩阵是一个 4x4 的矩阵,并且最后一列的列向量是 [0, 0, 0, 1]',类似于平移矩阵的形式(平移矩阵详见空间和变换中 2.1.1 节,这篇文章的变换是列向量右乘,Cesium 中是行向量左乘),因此 transform 矩阵的最后一行是平移偏移量,我们将该偏移量设置到 Cesium Georeference 中,如下,地图显示效果同 4.4 1)节。

5 添加对象

5.1 CesiumGlobalAnchor

给场景中添加 DynamicCamera,并屏蔽掉 Main Camera,添加热气球对象,如下。

运行后,发现气球跟随相机一起运动,如下。

将气球对象移到 CesiumGeoreference 下面,并添加 CesiumGlobalAnchor 组件,如下。

运行效果如下,可以看到气球对象没有跟随相机一起运动了。

5.2 参考子场景

选中 CesiumGeoreference 对象,点击 Create Sub-Scene Here 添加子场景,CesiumGeoreference 对象下面会生成一个挂有 CesiumSubScene 组件的对象,重命名为 Sub-Scene。

调整 CesiumSubScene 组件中的 Activation Radius、Latitude、Longitude、Height 属性,如下。Latitude、Longitude、Height 确定了子场景的中心,Activation Radius 为子场景的半径,只有相机在子场景范围内,才激活子场景内的物体(子对象)。

将气球对象拖拽到 Sub-Scene 对象下面,如下(气球对象不需要挂 CesiumGlobalAnchor 组件)。

运行效果如下,可以看到,只有相机进入子场景范围内,才激活子场景内的物体(子对象)。

6 Native 编译

本节主要参考 Building Cesium for Unity

1)编译环境准备

dotnet --version # 6.0 or later
cmake --version # 3.15 or later
Visual Studio 2022
Git Bash # 拉代码和编译命令都可以在 Git Bash里执行

2)拉 cesium-unity-samples 代码

cesium-unity-samples 源码。

git clone --recurse-submodules git@github.com:CesiumGS/cesium-unity-samples.git

3)拉 cesium-unity 代码

cesium-unity 源码。

cd cesium-unity-samples/Packages
git clone --recurse-submodules git@github.com:CesiumGS/cesium-unity.git com.cesium.unity

clone 时如果忘记添加 --recurse-submodules,可以通过以下命令递归拉取子模块依赖。

cd cesium-unity-samples/Packages/com.cesium.unity
git submodule update --init --recursive

注意:不要通过浏览器下载 cesium-unity 源码的 zip 文件,因为这样不会拉子模块代码,也不要试 图把所有子模块的 zip 文件下载下来后再合并,因为子模块太多了,有的子模块里面又包含子模块,很容易漏掉。cesium-unity 源码比较大,大概 1.89 GB(包含所有子模块),如果下载比较慢,可以使用 Git常用命令总结 中方法配置代理来加速下载。

以下是子模块的依赖文件。

com.cesium.unity\.gitmodules
com.cesium.unity\native~\extern\cesium-native\.gitmodules
com.cesium.unity\native~\extern\cesium-native\extern\draco\.gitmodules
com.cesium.unity\native~\extern\cesium-native\extern\earcut\.gitmodules
com.cesium.unity\native~\extern\cesium-native\extern\rapidjson\.gitmodules

com.cesium.unity.gitmodules 内容如下,可以看到 cesium-nativecesium-unity 的一个子模块,这正是我们要编译的 native 子模块。

[submodule "native~/extern/cesium-native"]
    path = native~/extern/cesium-native
    url = ../cesium-native.git
[submodule "native~/extern/tidy-html5"]
    path = native~/extern/tidy-html5
    url = https://github.com/htacg/tidy-html5.git
[submodule "native~/extern/enum-flags"]
    path = native~/extern/enum-flags
    url = https://github.com/grisumbras/enum-flags.git

4)构建 Reinterop

Reinterop 是一个 Roslyn(C# 编译器)源代码生成器,在编译 cesium-unity C# 代码时由 Unity 自动调用,并生成 C# 与 C++的交互层。

cd cesium-unity-samples/Packages/com.cesium.unity
dotnet publish Reinterop~ -o .

5)打开 cesium-unity-samples 项目

使用 Unity Editor 打卡 cesium-unity-samples 项目,Unity 将自动编译 cesium-unity C# 源代码,同时调用 Reinterop 生成 C# 和 C++ 源代码。此时 Cesium 的功能还不能正常使用,会抛出以下异常。这是因为 C++ 的代码还没编译。

DllNotFoundException: CesiumForUnityNative assembly:<unknown assembly> type:<unknown type> member:(null)
NotImplementedException: The native implementation is missing so OnValidate cannot be invoked.

生成的 C++ 源码地址为:com.cesium.unity/native~/build,用户可以通过 Visual Studio 打开 CesiumForUnityNative.sln 文件来查看源码。

6)编译 C++ 代码

关闭 cesium-unity-samples 项目,执行完以下命令行再打开项目。

构建 Debug 版本。

# compile the C++ code for use in the Editor
cd cesium-unity-samples/Packages/com.cesium.unity/native~
# 在 build 目录中生成 CMake 构建配置, 并将构建类型设置为 Debug, 以便在构建项目时启用调试信息(只需执行一次, 第二次编译时不需要执行该命令)
cmake -B build -S . -DCMAKE_BUILD_TYPE=Debug
# 在 build 目录中执行构建操作, 使用 14 个构建线程并生成 Debug 版本的可执行文件或库, 将构建结果安装到 install 指向的目录中
cmake --build build -j14 --target install --config Debug

构建 Release 版本。

# build a release build
cd cesium-unity-samples/Packages/com.cesium.unity/native~
# 在 build 目录中生成 CMake 构建配置, 并将构建类型设置为 RelWithDebInfo, 它允许在 Release 版本中包含调试信息(只需执行一次, 第二次编译时不需要执行该命令)
cmake -B build -S . -DCMAKE_BUILD_TYPE=RelWithDebInfo
# 在 build 目录中执行构建操作, 使用 14 个构建线程并生成带有调试信息的 Release 版本的可执行文件或库, 将构建结果安装到 install 指向的目录中
cmake --build build -j14 --target install --config RelWithDebInfo

若在后面的编译过程中,报错:could not find any instance of Visual Studio,可以参考博客→解决CMake时“could not find any instance of Visual Studio”的问题

7)查看安装目录

打开 com.cesium.unity/native~/CMakeLists.txt 文件,搜索 "CMAKE_INSTALL_PREFIX",如下。

# When building for the Editor, both Runtime and Editor assemblies are
# written to the Editor directory so that Unity won't load them in
# a standalone build.
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_DIR}/../Editor" CACHE PATH "Installed to the Editor directory." FORCE)
endif()

可以看到,安装目录为 "${CMAKE_CURRENT_LIST_DIR}/../Editor",即 com.cesium.unity/Editor 目录,由此得构建和安装的目录如下。

com.cesium.unity\native~\build\Editor\Debug\CesiumForUnityNative-Editor.dll ->
com.cesium.unity\Editor\CesiumForUnityNative-Editor.dll

com.cesium.unity\native~\build\Runtime\Debug\CesiumForUnityNative-Runtime.dll ->
com.cesium.unity\Editor\CesiumForUnityNative-Runtime.dll

7 修改 Native 代码案例

1)打开 native 源码工程

使用 Visual Studio 打开 CesiumForUnityNative.sln 文件来查看源码,如下。

2)修改源码

修改 CesiumForUnityNative-Runtime 模块下的 CesiumCreditSystemImpl.cpp 文件(源码位置:com.cesium.unity/native~/Runtime/src/CesiumCreditSystemImpl.cpp),如下。

3)编译源码

# compile the C++ code for use in the Editor
cd cesium-unity-samples/Packages/com.cesium.unity/native~
# 在 build 目录中生成 CMake 构建配置, 并将构建类型设置为 Debug, 以便在构建项目时启用调试信息(只需执行一次, 第二次编译时不需要执行该命令)
cmake -B build -S . -DCMAKE_BUILD_TYPE=Debug
# 在 build 目录中执行构建操作, 使用 14 个构建线程并生成 Debug 版本的可执行文件或库, 将构建结果安装到 install 指向的目录中
cmake --build build -j14 --target install --config Debug

编译完成后,可以看到 com.cesium.unity/Editor/CesiumForUnityNative-Runtime.dll 文件修改日期发生变化。

4)修改前后对比

修改前底部有一行文字。

修改后底部文字消失。

声明:本文转自【Unity3D】Cesium加载大地图

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容