【Unity3D】Cesium加载大地图

发布时间 2023-10-14 01:08:02作者: little_fat_sheep

1 前言

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

img

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

img

​ Cesium 相关链接如下:

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

2 环境搭建

​ 本节主要参考 Cesium for Unity Quickstart

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

img

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

2)添加作用域注册表

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

img

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,如下。

img

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

img

4)连接到 Cesium ion

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

img

img

5)添加 Token

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

img

6)创建世界地图

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

img

​ 场景显示如下。

img

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

img

7)添加动态相机

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

img

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

img

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

img

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

3 地图切片

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

3.1 切片

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

img

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

img

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

img

3.2 预览切片

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

img

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

img

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

img

3.3 上传切片到 Cesium ion

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

img

​ 选择文件后,上传切片。

img

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

img

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

4 Unity 中加载切片

4.1 加载 Cesium ion 中切片

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

img

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

img

4.2 加载 CesiumLab 服务中切片

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

img

​ url 来自 CesiumLab,如下。

img

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

img

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

4.3 加载本地切片

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

img

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

4.4 调整地图方位

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

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

img

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

img

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

img

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

img

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

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

img

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

img

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

img

5 添加对象

5.1 CesiumGlobalAnchor

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

img

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

img

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

img

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

img

5.2 参考子场景

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

img

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

img

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

img

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

img

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 文件来查看源码,如下。

img

2)修改源码

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

img

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)修改前后对比

​ 修改前底部有一行文字。

img

​ 修改后底部文字消失。

img

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