简介:在Visual Studio 2019环境下编译OSG 3.6.5与OSGEarth 2.10时,第三方依赖库往往是最大的拦路虎。这份预编译好的64位三方库压缩包专门面向此类开发者,已包含用于编译OSG 3.6.5和OSGEarth 2.10的第三方依赖组件,可直接用于CMake配置与工程链接。整个zip包约137.76MB,包含约2000个文件,其中以h头文件(1351个)、lib导入库(56个)、dll动态库(4个)及cmake配置文件(32个)为主,还配套有inc、inl、hpp等辅助源码文件与少量时区数据,结构上基本覆盖常见构建需求。目前已有619人下载学习,适合有一定C++基础、希望跳过繁琐三方库编译流程的OSG/OSGEarth使用者。拿到包后可按目录整合进工程,通过CMake快速识别依赖路径,省去手动编译和配置的时间,让开发者将更多精力集中在场景渲染与地球可视化业务本身。 写过 OSG 的人都知道,编译不是最大的坑,依赖才是。OSG3.6.5 配合 OSGEarth 开发的时候,光 3rdParty 就能让人卡住好几天。后来我直接用了别人预编译好的 3rdParty.zip 三方库,一下子顺了。这里我把整个使用过程、踩过的坑和配置细节整理出来,给想跳过编译地狱的朋友一份可以直接抄的作业。顺便把最近在项目里遇到的一个问题——怎么判断一个空间点是否落在地形的 FeatureNode 里——也一并讲了,因为很多人在跑通 OSGEarth 之后都会撞上这个点。
1. 为什么OSG和OSGEarth离不开3rdParty三方库
1.1 官方源码的“缺胳膊少腿”问题
OSG 官方源码包只包含核心场景管理、渲染状态机和节点遍历逻辑,真正干活的图像解码、网络请求、字体渲染、地理数据解析全部依赖外部库。编译的时候你会发现在 CMake 配置阶段报出一堆XXX_INCLUDE_DIR NOT-FOUND,比如找不到zlib.h、curl.h、freetype.h,这就是因为没有准备好第三方依赖。
OSGEarth 对第三方库的依赖更多。它要做地形数据组织、影像纹理加载、矢量边界解析,所以除了 OSG 本身需要的 zlib、libpng、libjpeg、tiff、freetype、curl、glut 这类基础库之外,还要求 GDAL、GEOS 这种地理空间库。缺少这些,源码下载下来连 Configure 都过不去,更别提生成 Visual Studio 工程文件了。
这些库如果全靠手工下载源码再分别编译,折腾一两天很正常,而且很容易出现版本对不上、编译选项不一致的情况。预编译好的 3rdParty.zip 就是为了解决这个问题:把 OSG 和 OSGEarth 编译时需要的绝大部分三方库提前编好,按 include、lib、bin 分类打包,让我们在 CMake 里直接指过去就行。
1.2 手动编译第三方库的代价
可能有人觉得“第三方库而已,自己编也不难”,但真做起来会非常痛苦。以 GDAL 为例,它本身依赖 PROJ、CURL、TIFF、GEOS 等一堆小库,每个库都有自己的一套 CMake 或 configure 逻辑;编译选项一旦不同,比如一个用了/MD一个用了/MT,后面链接时就会出现LNK2038之类的冲突错误,排查起来让人头大。
还有一个经典场景:你用 VS2015 编译了 OSG,结果下载的三方库是 VS2019 编译出来的,链接阶段一堆无法解析的外部符号。不同 MSVC 版本的 C++ 运行库和 STL 实现都有差异,二进制根本不通用。预编译的 3rdParty.zip 通常会明确标注对应的 MSVC 版本(比如 vc14、vc15、vc16),就是为了避免这类问题。
用现成的预编译包,本质上就是“专业配好的料包”替代“从种菜开始做晚饭”。虽然不能保证和你的工程 100% 契合,但至少能把编译环境搭建时间从几天压缩到半小时,这对做项目或者学习源码的人来说都是最优解。
1.3 预编译 zip 能解决什么
这类 zip 压缩包一般按目录组织好,解压后能看到 include、lib、bin 三个目录。CMake 配置时只要让 OSG 和 OSGEarth 找到对应的头文件和库文件目录,就可以继续走后面的生成流程。
更重要的是,预编译好的包通常会选择与 OSG 3.6.5 匹配的版本组合,比如 GDAL 2.x、GEOS 3.7、CURL 7.x 等。这些版本之间互相兼容,不容易出隐藏冲突。我实际用下来,只要下载的包没有解压损坏、路径里没有中文和空格,OSG 3.6.5 和 OSGEarth 的编译配置阶段基本一步到位,不用再手动改太多变量。
2. 预编译好的3rdParty.zip里到底有什么
2.1 目录结构速览
解压之后,一般会看到这样的结构:
3rdParty ├── include │ ├── zlib │ ├── png │ ├── jpeg │ ├── tiff │ ├── curl │ ├── freetype │ ├── gdal │ ├── geos │ └── ... ├── lib │ ├── debug │ ├── release │ └── ... └── bin ├── debug └── release有些包把 debug 和 release 的库分开放,有些则在 lib 目录下用_d后缀区分。OSG 源码在查找三方库时,会优先找不带_d的 release 库,如果只有 debug 版本,需要手动改变量名。
我比较建议拿到包先打开看看 lib 目录里是哪种风格,心里有数。否则 CMake 配置时看到一堆红颜色变量,容易懵。还有一点要注意:bin 目录里的 DLL 最终也需要拷贝到可执行文件旁边,或者加入系统 PATH,不然编译通过也跑不起来。
2.2 怎么辨认这个包适不适合自己
首先要看包名里的 MSVC 版本标识。常见的对应关系是:
| 包名标记 | VS 版本 |
|---|---|
| vc14 | VS2015 |
| vc15 | VS2017 |
| vc16 | VS2019 |
| vc17 | VS2022 |
OSG 3.6.5 官方发布较久,很多预编译包是针对 VS2015/VS2017 的。如果你用 VS2019,也不是完全不能用,但必须确定包里没有混用旧版运行时依赖,最好先在一台机器上做快速链接测试。
其次要看平台,x64 的包不能用在 Win32 工程里。这个看着简单,但很多新手下载时只看 “3rdParty.zip” 就解压了,最后 CMake 配置总是失败。另外还要留意 OSG 版本,OSG 3.6.x 的源码和 3.4 的 API 差异很大,三方库本身可能没有版本依赖,但配套的头文件可能不同,所以尽量找明确标注 “OSG 3.6.5” 的包。
2.3 一些包里不会写但是你要知道的事
预编译包虽然方便,但内部细节经常是黑盒。比如有些包把 debug 库和 release 库放在同一个 lib 目录下,用zlibd.lib和zlib.lib区分;有些包则不带 debug 版本,只在 release 下可用。如果之后想用 Debug 模式调试 OSGEarth 代码,就可能出现找不到zlibd.lib的情况。
我常用的一个探测方法:在解压目录里搜一下*.lib文件数量,如果明显只有 release 的那一套,就说明这个包不适合做完整调试。另一个隐藏坑是一个包里可能同时包含不同版本的 GDAL 或者 CURL,这通常是因为打包者把多个库的 release 和 debug 混在一起,但没清理干净。使用时尽量只让 CMake 指向这个包的一个统一前缀目录,不要手动把 include 和 lib 指向不同位置,否则容易出现头文件版本和库版本不一致的诡异错误。
3. 用预编译包编译OSG3.6.5和OSGEarth的完整配置
3.1 环境准备
先准备好四样东西:OSG 3.6.5 源码、OSGEarth 源码、3rdParty.zip、Visual Studio。OSG 源码建议从 GitHub 上openscenegraph/OpenSceneGraph的OpenSceneGraph-3.6.5标签下载,OSGEarth 则可以从gwaldron/osgearth仓库拿稳定版,注意要和 OSG 3.6.5 兼容,一般 2.9 之后的版本都可以。
解压路径我建议用纯英文,比如D:\dev\3rdParty和D:\dev\OSG。之前我在C:\Users\张三\...这种带中文的路径下配置,CMake 虽然能生成工程,但后续编译频繁出奇怪问题,最后所有路径都改成英文才消停。
环境上不需要提前装太多东西,因为三方库都包含了。只需要确认编译器,比如 VS2017 的 v141 工具集,然后打开 CMake-GUI 就可以开始了。
3.2 CMake 配置 OSG 的关键选项
打开 CMake-GUI,设置源码路径和构建路径(注意构建路径不要和源码路径一样,避免污染源码目录),点 Configure 后第一次选择编译器,会出现大量红色变量。
这时要找的开头是ACTUAL_3RDPARTY_DIR。有些版本的 OSG 支持直接设置这一个变量,指向你的 3rdParty 根目录,然后会自动去下面找 include/lib。实测这种方法最省事,只要包结构标准,基本能一次过。如果这个变量不生效,就需要逐个设置依赖项,比如:
ZLIB_INCLUDE_DIR: D:/dev/3rdParty/include ZLIB_LIBRARY: D:/dev/3rdParty/lib/zlib.lib CURL_INCLUDE_DIR: D:/dev/3rdParty/include CURL_LIBRARY: D:/dev/3rdParty/lib/libcurl.lib这种手工方式比较繁琐,而且变量名在不同版本里可能略有差异。我更推荐用一个取巧的办法:把3rdParty/bin加到系统的PATH,把3rdParty/lib加到CMAKE_PREFIX_PATH,然后重新 Configure,很多依赖项会自动找到。
Configure 无红色报错后,把BUILD_OSG_EXAMPLES打开,可以顺手测试例子能不能跑通。Generate 生成 VS 工程,打开.sln后直接生成 ALL_BUILD。首次编译时间比较长,建议把配置设为 Release,Debug 的检查和优化会拖慢速度。
3.3 OSGEarth 的 CMake 配置
OSG 编译安装完成后,才开始配置 OSGEarth。同样打开 CMake-GUI,指定 OSGEarth 源码目录,构建目录放另一个文件夹。
OSGEarth 会查找 OSG 的安装位置,需要把CMAKE_PREFIX_PATH指向 OSG 的安装根目录,或者在 GUI 里设置OSG_DIR这样变量。源码目录下的CMakeLists.txt会自动调用 OSG 的配置文件来找头文件。
三方库部分,和前面的思路一样,只要确保ACTUAL_3RDPARTY_DIR被正确设置,OSGEarth 就能从同一个 3rdParty 里找到 GDAL、GEOS、CURL。记得把OSGEARTH_USE_GDAL设为 ON,这是加载在线影像和 DEM 的关键。
Configure 成功后,重点检查有没有GDAL_INCLUDE_DIR和GEOS_INCLUDE_DIR都指向 3rdParty 路径。如果包里有多个 GDAL 版本,CMake 可能找到旧版本,这时需要手动把变量指向新版本路径。Generate 后编译 OSGEarth,同样建议选 Release。
3.4 编译完成后的工程集成
编译成功后,你的 OSG 和 OSGEarth 都会生成一套自己的 lib 和 dll。开发自己的应用时,VS 工程需要设置几项:
- C/C++ 附加包含目录:同时包含 OSG 的 include 和 3rdParty 的 include;
- 链接器附加库目录:包含 OSG 的 lib、OSGEarth 的 lib 和 3rdParty 的 lib;
- 链接器输入依赖库:把
osg*.lib、osgEarth*.lib、zlib.lib等都填进去。
我自己的项目就因为这步没做干净,折腾半天链接错误。建议在“属性管理器”里建一个公共配置文件,一次性配好,后续项目直接导入。
还要记得把3rdParty/bin里的 DLL 和 OSG、OSGEarth 生成的 DLL 拷到 exe 输出目录,或者统一放到一个目录再把这个目录加入 PATH。运行时如果提示找不到zlib.dll或者gdal.dll,基本就是这步漏了。
4. 常见问题与排查技巧实录
4.1 高频错误速查表
下面这些是我和几个朋友实践中遇到的典型问题,整理成表方便快速对照。
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| CMake 找不到 ZLIB_INCLUDE_DIR | 3rdParty 路径没设置对 | 检查 ACTUAL_3RDPARTY_DIR,或手动指定 include 路径 |
| LNK2038: RuntimeLibrary 不匹配 | Debug/Release 或 MT/MTd 不一致 | 统一使用同一 3rdParty 包,保持 vs 工程和库一致 |
| 无法解析的外部符号 __imp_curl_easy_init | 没有链接 CURL 库,或 lib 路径错误 | 在链接器输入里增加 libcurl.lib,确认路径 |
| 运行时找不到 zlib.dll | bin 目录未加入 PATH | 将 3rdParty/bin 和 OSG/bin 加入 PATH |
| GDAL 版本冲突导致崩溃 | 多个 GDAL 库混用 | 检查 CMake 变量,确保所有项目指向同一个 GDAL |
| OSGEarth 找不到地球文件 | OSGEARTH_DATA 环境变量未设置 | 设置 OSGEARTH_DATA 指向 osgearth 的 data 目录 |
4.2 调试和 Release 库混用的坑
最隐蔽的问题是 debug 和 release 混用。OSG 的 debug 库通常带d后缀,比如osgLib_d.lib,三方库也可能有类似规则。如果你用 Release 配置编译,但 CMake 里某个变量指向了 debug 库,链接时会出现“堆损坏”这类运行时错误,而错误出现的地方可能根本不是真正的问题。
我的建议是:在你正式开发前,先分别用 Debug 和 Release 配置编译一次官方例子,比如 OSG 的osgviewer,跑通了再往后走。这样就提前暴露三方库是否有缺失版本的问题,不用等到后面集成时再排查。
4.3 一条独家排查经验
如果 CMake 配置阶段变量反复报错,试过路径设置后都没用,那就把 CMakeCache.txt 删掉,重新 Configure。这个文件非常顽固,会记住旧路径,导致你改了变量它还用旧值。我遇到过三次因为旧缓存导致 GDAL 路径一直指向不存在的目录,删掉缓存重来立马正常。
另一个技巧是,下载 3rdParty.zip 后先做一个完整性检测,右键属性看是否有“解除锁定”按钮。Windows 默认会拦截从网上下载的 dll 文件,如果不解除锁定,CMake 能配置成功,但运行时会提示“不是有效的 Win32 应用程序”。这个问题几乎没人写,但非常常见。
5. 跑通了之后:如何判断点是否在FeatureNode内
5.1 这个需求从哪来
当你把 OSGEarth 跑起来,加载了矢量边界,就会遇到“某个点到底在不在这个区域内”的需求。比如飞行器是否进入管制区、鼠标点击是否能选中建筑边界、或者判断实时位置有没有越界。
这里提到的 FeatureNode,是 OSGEarth 里用于渲染矢量要素的节点,内部封装了几何体,但并不是简单的一个包围盒。如果你直接取节点的包围盒来判断点是否在里面,会得到大量误判,尤其是图形边界是不规则多边形时。
5.2 常见思路和坑
一种思路是用 OSG 的求交器:osgUtil::IntersectionVisitor和osgUtil::LineSegmentIntersector。将点往上下各拉一条射线,判断射线与 FeatureNode 的几何体相交次数,奇数次点在多边形内,偶数次是在外面。这个方法非常经典,而且可以在三维场景里直接用,不需要你自己解析顶点。
坑在于:如果 FeatureNode 没有开启或者不可见,求交器可能直接跳过它,导致永远返回 0 次相交。另外,如果点正好落在边界上,浮点数误差会让结果不稳定。这种情况下我会把点在原有基础上稍微偏移一点点,比如 0.001 米,再重新判断。
5.3 一种可落地的判断方法
我实际项目里,不使用三维射线,而是直接拿到 FeatureNode 的几何数据,做二维平面上的点在多边形内测试。大致流程是:
- 通过 FeatureNode 的
getFeature()拿到要素数据; - 把要素的顶点坐标提取出来,可以使用 WKT 或 GeoJSON 形式输出,方便后续计算;
- 将经纬度坐标当作二维平面坐标,使用射线法做包含判断;
- 如果有高程信息,忽略高程,只判断平面位置。
射线法的伪代码比较简单:
def point_in_polygon(pt, polygon_vertices): n = len(polygon_vertices) inside = False j = n - 1 for i in range(n): xi, yi = polygon_vertices[i] xj, yj = polygon_vertices[j] if ((yi > pt.y) != (yj > pt.y)) and \ (pt.x < (xj - xi) * (pt.y - yi) / (yj - yi) + xi): inside = not inside j = i return inside这个方法没有用到 OSG 的求交器,但准确性很高,而且当你要对大量点做批量判断时,性能比场景级求交好很多。唯一要注意的是多边形顶点必须按顺序排列,否则结果会乱。
5.4 为什么这个也跟三方库有关
做点在多边形内测试,其实完全可以用 GEOS 库来完成。GEOS 提供了成熟的intersects接口,你只需要把点坐标和多边形顶点封装成 GEOS 几何对象,调用一下即可,省去自己写射线法的时间。这也正好体现 3rdParty 里 GEOS 库的价值,它不只是给 OSGEarth 编译时垫底用的,运行时也能直接为我们提供空间计算能力。
如果你已经在使用 OSGEarth,并且加载了矢量数据,那么利用 GDAL 读取要素,再结合 GEOS 做空间关系判断,是一条非常顺畅的技术路线。而这一切的基础,就是我们最开始配置好的那套三方库。没有这套库,GDAL、GEOS 都得自己折腾,很可能走到一半就放弃了。
最后再分享一个自己的习惯:下载预编译包后,我会先建一个文本文档,记录下载来源、对应 VS 版本、是否带 debug 库和测试结果。这个方法帮我省了很多回头查版本的时间,特别是在同时维护多个旧项目的时候。希望这篇分享能帮大家少踩几个坑,把时间花在真正想做的事情上。
本文还有配套的精品资源,点击获取