1. 为什么我要从源码编译 Cesium for Unreal
Cesium for Unreal 这个插件在 UE 生态里做地理空间可视化的地位,用过的人心里都有数。它能把真实地形、影像、3D Tiles 直接搬进 UE 场景,做数字孪生、智慧城市、飞行模拟这类项目基本绕不开。但官方发布的版本有个很现实的问题:它是个"黑盒"。你拿到的是编译好的二进制,想改一行逻辑、加一个自定义的 Pawn 行为、调整相机和地球的交互方式,门都没有。
我这次的目标很明确——定制GlobePawn。官方自带的 GlobePawn 提供了基础的旋转、缩放、平移操作,但实际项目里往往需要更细的控制,比如限制俯仰角范围、自定义飞行速度曲线、增加键盘快捷键、或者把相机绑定到某个经纬高坐标上做定点巡航。这些需求靠蓝图能凑合一部分,但涉及底层 Tick 逻辑和输入映射的改动,还是得动 C++ 源码。
所以整个流程就是:拉源码 → 改代码 → 编译 → 在 UE 里验证。听起来简单,但 Cesium for Unreal 的编译链路比普通 UE 插件复杂得多,它依赖 Cesium Native 这个 C++ 库,涉及 CMake 构建、第三方依赖下载、vcpkg 包管理,中间任何一环出问题都会卡住。我前后折腾了差不多两天,踩了不少坑,这里把完整过程整理出来,给同样想改源码的人省点时间。
这篇文章适合两类人:一是已经会用 Cesium for Unreal 但想深入定制功能的开发者,二是想了解 UE 插件从源码到可用完整链路的工程师。不需要你是编译专家,但基本的 C++ 和 UE 项目结构得熟悉。
2. 编译前的环境准备与依赖梳理
2.1 版本匹配是第一步,别在这栽跟头
Cesium for Unreal 对 UE 版本、Visual Studio 版本、Cesium Native 版本都有严格要求。我这次用的是 UE 5.3,对应的 Cesium for Unreal 版本是 2.x 系列。这里有个关键点:不同版本的 Cesium for Unreal 对应的 Cesium Native commit 是不一样的,你不能随便拉个最新版就往上套。
我的建议是直接去 GitHub 的 release 页面,找到标注支持 UE 5.3 的那个 tag,然后 checkout 到对应分支。别用 main 分支,main 分支经常在适配新版本 UE,编译到一半报错是常事。
Visual Studio 这边,UE 5.3 官方要求 VS 2022,工作负载需要勾选"使用 C++ 的桌面开发"和"使用 C++ 的游戏开发",另外 Windows SDK 版本要匹配。我一开始用的是 VS 2019,结果 CMake 配置阶段就报了一堆工具链不兼容的错,换成 VS 2022 之后顺畅很多。
注意:如果你机器上装了多个 VS 版本,CMake 可能会挑错。可以在 CMake 命令里显式指定
-G "Visual Studio 17 2022"来强制使用 VS 2022。
2.2 依赖清单和下载策略
Cesium for Unreal 的编译依赖大致分三层:
| 依赖层级 | 具体内容 | 获取方式 |
|---|---|---|
| UE 引擎层 | Unreal Engine 5.3 源码或安装版 | Epic Launcher 或 GitHub |
| 插件层 | Cesium for Unreal 源码 | GitHub clone |
| 原生库层 | Cesium Native 及其第三方依赖 | CMake 自动下载 + vcpkg |
最麻烦的是第三层。Cesium Native 依赖一堆第三方库,包括但不限于draco、ktx2、libjpeg-turbo、openssl、sqlite3等。这些库的下载和编译是通过 CMake 的ExternalProject机制自动完成的,但国内网络环境下,从 GitHub 拉这些依赖经常超时。
我的做法是提前配置好 Git 的代理(这里说的是 Git 的 HTTP 代理配置,用于加速代码拉取),或者手动把依赖包下载到 CMake 的缓存目录里。具体路径在cesium-native/extern下面,CMake 会先检查本地有没有,没有再下载。
另外,vcpkg 的集成也是个坑。Cesium Native 用 vcpkg 管理部分依赖,如果你机器上没装 vcpkg,CMake 会尝试自动 clone 并 bootstrap。这个过程在 Windows 上会编译 vcpkg 自身,大概需要十几分钟。建议提前手动装好 vcpkg,并设置好VCPKG_ROOT环境变量。
2.3 磁盘空间和编译时间的心理预期
整个编译过程产生的中间文件非常大。我实测下来,Cesium Native 的 build 目录加上 UE 插件的中间文件,轻松超过 30GB。如果你的 C 盘空间紧张,建议把 build 目录设置到其他盘。
编译时间方面,首次完整编译(包括所有第三方依赖)在 16 核机器上大概需要 40 分钟到 1 小时。如果只是改了插件层的代码重新编译,增量编译大概 2-3 分钟。所以第一次编译要有耐心,别看到卡住就以为死了。
3. 源码获取与工程结构解析
3.1 拉取源码的正确姿势
Cesium for Unreal 的仓库地址在 GitHub 上,直接 clone 就行。但要注意,这个仓库用了 submodule,cesium-native是作为子模块引入的。所以 clone 的时候要加--recursive:
git clone --recursive https://github.com/CesiumGS/cesium-unreal.git cd cesium-unreal git checkout v2.0.0 # 换成你需要的版本 tag git submodule update --init --recursive如果你已经 clone 了但忘了加--recursive,可以补一句git submodule update --init --recursive。这一步很关键,少了子模块,后面 CMake 配置会直接报找不到 cesium-native 的错。
拉下来之后,目录结构大概是这样:
cesium-unreal/ ├── Source/ │ ├── CesiumRuntime/ │ ├── CesiumEditor/ │ └── ThirdParty/ ├── cesium-native/ # 子模块 │ ├── CMakeLists.txt │ ├── extern/ │ └── src/ ├── Build/ └── CesiumForUnreal.upluginSource/CesiumRuntime是我们主要改代码的地方,GlobePawn相关的类就在这里面。cesium-native是底层地理空间计算库,一般不需要动,除非你要改数据加载逻辑。
3.2 GlobePawn 在源码中的位置和继承关系
在Source/CesiumRuntime/Public和Private下面,你能找到CesiumGlobeAnchorComponent、CesiumGeoreference、GlobeAwareDefaultPawn这些类。GlobePawn实际上对应的是AGlobeAwareDefaultPawn,它继承自ADefaultPawn,然后混入了ICesiumGeoreferenceable接口。
这个类的核心逻辑在GlobeAwareDefaultPawn.cpp里,主要做了几件事:
- 重写
Tick,每帧根据当前 Pawn 的 UE 世界坐标反算出经纬高 - 处理输入绑定,把鼠标、键盘操作转换成相机的旋转和位移
- 维护一个
CesiumGeoreference的引用,用来做坐标转换
你要改功能,基本就是在这个文件里动刀。比如我想加一个"限制最大俯仰角"的功能,就得在Tick或者输入处理函数里加角度钳制逻辑。
3.3 编译入口:Build.cs 和 CMake 的关系
UE 插件的编译入口是CesiumRuntime.Build.cs,它定义了模块的依赖、源文件列表、编译选项。但 Cesium for Unreal 特殊的地方在于,它还要先编译cesium-native这个原生库,然后通过ThirdParty模块把编译好的静态库链接进来。
所以整个编译链路是:
- CMake 配置
cesium-native,下载依赖,生成 VS 工程 - 编译
cesium-native,产出.lib和.dll - UE 的 UnrealBuildTool 编译
CesiumRuntime模块,链接上一步的产物 - 打包成插件,供 UE 项目使用
这个链路里,第 1、2 步是最容易出问题的。UE 的构建系统不会自动帮你做这些,你需要手动先跑一遍 CMake。
4. 编译 cesium-native 的完整实操
4.1 CMake 配置阶段的参数选择
进入cesium-unreal/cesium-native目录,创建一个 build 目录,然后跑 CMake。我用的命令大概是这样:
mkdir build cd build cmake .. -G "Visual Studio 17 2022" -A x64 ^ -DCMAKE_BUILD_TYPE=Release ^ -DCESIUM_USE_LTO=OFF ^ -DVCPKG_TARGET_TRIPLET=x64-windows这里几个参数值得说明:
-G "Visual Studio 17 2022":指定生成器,避免 CMake 挑到旧版 VS-A x64:目标架构,UE 5.3 在 Windows 上默认是 x64-DCMAKE_BUILD_TYPE=Release:编译类型,Debug 版本体积巨大且慢,一般用 Release-DCESIUM_USE_LTO=OFF:关闭链接时优化,LTO 会显著增加编译时间,调试阶段没必要开-DVCPKG_TARGET_TRIPLET=x64-windows:vcpkg 的目标三元组,要和 UE 的架构匹配
配置阶段 CMake 会去拉一堆第三方依赖。如果卡在某个ExternalProject下载上,可以看build/CMakeFiles/下面的日志,找到具体是哪个库超时,然后手动下载放到extern对应目录。
实操心得:我遇到过一次
draco下载卡了半小时,后来发现是 GitHub 的 raw 文件访问不稳定。解决办法是手动 clone draco 仓库到extern/draco,然后在 CMake 里把对应的ExternalProject_Add注释掉,改成add_subdirectory。这样虽然麻烦,但比干等强。
4.2 编译过程中的常见报错与处理
配置成功后,用 VS 打开生成的cesium-native.sln,或者直接用命令行:
cmake --build . --config Release --parallel 16--parallel 16是并行编译的线程数,根据你 CPU 核心数调整。我 16 核机器开 16 线程,内存占用大概 12GB,如果你内存只有 16GB,建议降到 8 线程,不然容易爆内存。
编译过程中我遇到几个典型报错:
报错一:error MSB6006: "cmd.exe" 已退出,代码为 3
这个错误信息非常笼统,实际原因可能是某个ExternalProject的自定义命令失败了。排查方法是看 VS 的输出窗口,往上翻找到具体是哪个项目、哪条命令。我那次是因为openssl的 Perl 脚本执行失败,机器上没装 Perl。装个 Strawberry Perl 就好了。
报错二:找不到sqlite3.h
这是 vcpkg 集成没生效。检查VCPKG_ROOT环境变量是否设置,以及 CMake 配置时有没有正确传入CMAKE_TOOLCHAIN_FILE。如果 vcpkg 是手动装的,toolchain 文件路径一般是%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake。
报错三:链接阶段报LNK2019未解析的外部符号
这种一般是某个第三方库没编译成功,或者链接顺序不对。先确认cesium-native的 build 目录下有没有生成对应的.lib文件,没有的话说明那个子项目编译失败了,回去单独编译它。
4.3 编译产物的整理和验证
编译完成后,build/Release下面会有一堆.lib和.dll。你需要把这些产物放到 UE 插件能识别的位置。Cesium for Unreal 的ThirdParty模块会去特定路径找这些库,具体路径在CesiumRuntime.Build.cs里定义。
我一般会检查这几个关键文件是否存在:
cesium-native.libdraco.libktx2.libsqlite3.lib- 对应的
.dll文件
如果缺了,UE 编译阶段会报链接错误。这时候别急着改 UE 的代码,先回去把cesium-native编译完整。
5. 修改 GlobePawn 源码实现自定义功能
5.1 定位关键代码:Tick 和输入处理
打开Source/CesiumRuntime/Private/GlobeAwareDefaultPawn.cpp,找到Tick函数。这个函数每帧执行,核心逻辑是:
void AGlobeAwareDefaultPawn::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); // 获取当前 Pawn 的 UE 世界坐标 FVector UECoords = GetActorLocation(); // 通过 Georeference 转换成经纬高 glm::dvec3 LongitudeLatitudeHeight = _georeference->TransformUnrealPositionToLongitudeLatitudeHeight(UECoords); // 更新内部状态 // ... }我要加的功能是"限制相机俯仰角在 -80 度到 +80 度之间"。这个逻辑应该加在输入处理之后、应用到相机之前。找到处理旋转输入的地方,一般是在MoveRight、MoveUp或者Turn这类函数里。
5.2 添加自定义角度钳制逻辑
我在GlobeAwareDefaultPawn.h里加了一个成员变量:
UPROPERTY(EditAnywhere, Category = "Cesium|GlobePawn") float MaxPitchAngle = 80.0f;然后在Tick里,获取当前控制器的旋转,钳制 Pitch 值:
if (AController* Controller = GetController()) { FRotator CurrentRotation = Controller->GetControlRotation(); CurrentRotation.Pitch = FMath::Clamp(CurrentRotation.Pitch, -MaxPitchAngle, MaxPitchAngle); Controller->SetControlRotation(CurrentRotation); }这段代码看起来简单,但有个细节:SetControlRotation会触发控制器的旋转更新,如果每帧都调用,可能会和输入系统的旋转叠加产生抖动。我的做法是只在 Pitch 超出范围时才钳制,正常范围内不干预。
注意:
MaxPitchAngle我用了UPROPERTY(EditAnywhere),这样在 UE 编辑器里可以直接调,不用每次改代码重新编译。这个技巧在调试阶段特别有用,建议所有可调参数都这么处理。
5.3 重新编译 UE 插件模块
改完代码后,不需要重新编译整个cesium-native,只需要重新编译 UE 的CesiumRuntime模块。如果你是用 UE 编辑器打开的项目,直接点编译按钮就行。如果是命令行,可以用:
UnrealBuildTool.exe CesiumForUnrealEditor Win64 Development -Project="你的项目.uproject" -WaitMutex增量编译很快,一般两三分钟。编译成功后,在 UE 编辑器里重新加载插件,就能看到效果。
这里有个坑:如果你改了.h文件里的UPROPERTY,UE 需要重新生成反射代码。有时候增量编译不会触发这个,导致新属性在编辑器里不显示。解决办法是删掉Intermediate/Build下面对应的缓存,强制全量编译一次。
6. 常见问题排查与避坑经验
6.1 编译类问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| CMake 配置卡在下载依赖 | 网络问题 | 手动下载依赖放到 extern 目录 |
| MSB6006 cmd.exe 退出代码 3 | 某个 ExternalProject 命令失败 | 看输出日志定位具体项目,补装缺失工具 |
| LNK2019 未解析外部符号 | 第三方库未编译或路径不对 | 检查 build 目录产物,确认 Build.cs 路径 |
| UE 编译报找不到 cesium-native.h | 头文件搜索路径没配 | 检查 Build.cs 里的 PublicIncludePaths |
| 编辑器里新属性不显示 | 反射代码未重新生成 | 删 Intermediate 缓存,全量编译 |
| 运行时崩溃在坐标转换 | Georeference 未初始化 | 确认场景里有 CesiumGeoreference Actor |
6.2 几个我踩过的坑
坑一:Debug 版本编译出来的库不能用
我一开始图省事,CMake 配置时用了 Debug 模式,编译出来的cesium-native.lib是 Debug 版。结果 UE 那边是 Development 配置,链接时报了一堆_ITERATOR_DEBUG_LEVEL不匹配的错。后来统一用 Release 才解决。记住:UE 的 Development 配置对应 CMake 的 Release,别搞混。
坑二:改了源码但 UE 没重新编译
有次我改了GlobeAwareDefaultPawn.cpp,保存后直接在编辑器里点播放,发现行为没变。折腾半天才想起来,UE 编辑器不会自动检测 C++ 源码变化,必须手动触发编译。后来我养成了习惯,改完代码先按Ctrl+Alt+F11编译,再测试。
坑三:vcpkg 版本冲突
我机器上之前装过一个旧版 vcpkg,用于其他项目。Cesium Native 编译时用了这个旧版,结果某些库的版本对不上,报了一堆奇怪的错。解决办法是给 Cesium Native 单独指定一个 vcpkg 实例,通过-DVCPKG_ROOT=参数传入,别用全局的。
6.3 性能相关的注意事项
编译出来的插件在运行时,GlobePawn的Tick每帧都在做坐标转换,这个转换涉及双精度浮点运算,有一定开销。如果你的场景里同时有多个 Pawn 或者频繁切换,建议把坐标转换的结果缓存起来,不要每帧都算。
另外,cesium-native默认会开多线程加载地形和影像数据。如果你在低配机器上跑,可以在Cesium3DTileset的设置里限制并发请求数,避免把网络和 CPU 占满。
7. 功能验证与后续扩展思路
7.1 在 UE 里验证自定义功能
编译成功后,在 UE 编辑器里创建一个测试关卡,放一个CesiumGeoreference,再放一个GlobeAwareDefaultPawn。把 Pawn 的Auto Possess Player设为Player 0,然后播放。
测试我的角度钳制功能:用鼠标上下拖动,观察相机是否在 -80 度到 +80 度之间停住。如果没生效,先检查MaxPitchAngle的值是不是设成了 0,再确认Tick里的代码有没有被执行到(可以加个UE_LOG输出调试)。
验证通过后,把MaxPitchAngle暴露到蓝图里,这样策划也能调。UE 的UPROPERTY加上BlueprintReadWrite就行。
7.2 还能改哪些地方
GlobePawn只是个起点,Cesium for Unreal 里可定制的地方很多:
- 自定义飞行速度曲线:在
Tick里根据高度动态调整移动速度,低空慢速、高空快速 - 经纬高定点巡航:加一个
TArray<FCesiumGeographicCoordinates>,让 Pawn 按顺序飞过这些点 - 输入映射重构:把硬编码的按键改成
UInputAction,适配 UE 5 的 Enhanced Input 系统 - 碰撞和地形贴合:让 Pawn 自动贴合 Cesium 地形高度,而不是悬空
这些改动都遵循同样的流程:改源码 → 增量编译 → 验证。熟悉之后,一次改动大概十几分钟就能看到效果。
7.3 关于版权显示的处理
有朋友问过 Cesium for Unreal 默认显示的版权信息怎么处理。这个在CesiumCreditSystem里控制,可以在Cesium3DTileset的设置里关掉ShowCreditsOnScreen。但要注意,Cesium 的数据源本身有版权要求,关掉显示之前先确认你的使用场景是否合规。我一般建议保留,除非项目有明确的界面规范要求。
整个流程走下来,最深的体会是:编译 Cesium for Unreal 的难点不在改代码,而在环境配置和依赖管理。代码本身结构清晰,改起来不难,但 CMake、vcpkg、第三方库这一套组合拳,第一次接触的人很容易懵。我的建议是先把官方文档的编译指南过一遍,然后严格按照版本对应关系来,别跳步。遇到报错先看日志,定位到具体是哪个环节,再针对性解决。实在卡住了,去 GitHub 的 issue 区搜一下错误信息,大概率有人遇到过同样的问题。