简介:一套适用于Visual Studio 2019环境的OSG3.6.5与OSGEarth2.10预编译64位三方依赖库包,面向需要编译这两款图形引擎的C++开发者,解决第三方库获取难、版本不匹配等常见问题。压缩包共2000个文件,约137.76MB,以1351个h头文件为主体,搭配56个lib导入库、32个cmake配置脚本,以及c/inl/hpp等源文件,另有少量dll动态库供运行时调用;头文件、库文件与配置脚本分类存放,便于在VS2019工程中直接配置包含目录和库目录。依赖范围覆盖OSG与OSGEarth编译链接所需的常用组件,省去手动下载、编译和维护第三方库的繁琐步骤。资源还包含完整时区数据库文件,适合实现全球范围的时间区域渲染与地理信息可视化场景。离线环境下可直接解压使用,避免网络受限时无法拉取依赖。目前已有619人学习下载,适合正在搭建或升级OSG/OSGEarth开发环境的中高级图形开发者,可有效缩短环境配置周期,让开发精力更集中在三维功能与场景的实现上。 开始写之前,先交代一下背景:这几天后台好多人在问同一件事——拿到一份“OSG3.6.5和OSGEarth预编译好的3rdParty.zip三方库”之后,到底怎么配置才能顺利编译出项目,跑通一个最简单的demo。问的人多了,我决定把整个流程和自己踩过的坑一次性整理出来,顺便把最近搜得比较多的“osgearth如何计算点是否在featurenode内”也一起捋一遍,这篇尽量把从零到能跑的通路讲清楚,少走几天弯路。
1. 3rdParty.zip里到底打包了什么:一份依赖清单,也是一张避坑地图
1.1 为什么OSG/OSGEarth绕不开这些三方库
OpenSceneGraph本身是一个纯C++的3D渲染引擎,负责场景图管理、渲染状态、节点遍历、动画更新这一整套东西。但它并不打算自己去实现“读图片、连网络、解压数据、解析地理坐标”这些琐碎又繁重的基础能力,而是采用插件机制把外部库接进来。比如你加载一张jpg贴图,引擎会去找osgdb_jpeg.dll,而这个插件背后就是libjpeg;你要加载高程tif,背后就是GDAL;你要从网络加载瓦片,背后就是libcurl。
OSGEarth是在OSG之上做地形和地理数据渲染的库,它把OSG插件的这场“接力赛”又拉高了一档:地形数据、矢量要素、标注、影像金字塔,全都要跟GDAL、GEOS、CURL、Freetype这些底层库打交道。所以编译OSG的时候没有三方库,编译能过,但很多插件是空的;编译OSGEarth的时候没有三方库,CMake直接列出十几个红叉,根本走不下去。
这就是3rdParty.zip存在的意义:它们是Windows下把所有依赖预先编译好,打包成一份“开箱即用”的库集合。用户拿到后,只需要在CMake里给它指个路径,就能让头文件、lib、dll全部对上号,不需要自己动手编译十来个第三方库。
1.2 核心依赖逐个拆解
我见过的3rdParty.zip一般是include、lib、bin三件套的目录结构,不同压缩包的名字略有差别,但内容基本围绕下面这张表展开。
| 三方库 | 作用 | 在OSG/OSGEarth中被谁使用 |
|---|---|---|
| zlib | 数据压缩解压 | 压缩纹理、OSG的slave压缩、部分模型格式 |
| libpng / libjpeg / libtiff | 常用图像格式解码 | 贴图加载插件 osgdb_png/jpeg/tiff |
| jasper / openexr | JPEG2000、HDR高动态图像 | 高质量贴图、雷达影像等 |
| freetype | TrueType字体渲染 | 文字标注、屏幕文字、片尾字幕 |
| libcurl | HTTP/FTP网络传输 | 加载在线瓦片服务、WMS、TMS |
| libxml2 | XML解析 | 读取OSGEarth的earth文件、样式表 |
| gdal | 栅格和矢量地理数据抽象 | 地形高程、影像、矢量要素的读写 |
| geos | 空间几何拓扑算法 | OSGEarth矢量空间分析与几何操作 |
| sqlite3 / spatialite | 嵌入式数据库 | TMS/MBTiles本地瓦片库、矢量存储 |
| glut/freeglut | OpenGL工具库 | 部分官方示例窗口程序 |
其中,GDAL和GEOS是所有依赖里最“重”的两个。GDAL自己要编依赖,GEOS又是一套C++库,手动编译时往往都要单独折腾。你要是用的3rdParty.zip里有它们,恭喜,最难啃的骨头已经有人帮你啃完了。
1.3 版本与平台匹配:解压前先对着检查表看一眼
拿到zip第一件事不是解压,是看它的版本和你的工具链是不是同一个“世界线”。OSG 3.6.5和OSGEarth的版本匹配比较常规,常见的组合是OSG 3.6.5搭配osgEarth 2.10.2或2.9。
真正容易出问题的,是VS版本和架构。
第三方库的lib和dll内部都绑定了编译时用的CRT(C运行时库)。用VS2015编出来的静态lib,拿到VS2019的项目里链接,十有八九报LNK2038: mismatch detected for RuntimeLibrary。64位和32位更不用说,库目录直接对不上。
所以解压前一定确认三件事:
- VS版本:VS2015(vc14)、VS2017(vc15)、VS2019(vc16)对应不同的预编译包,别混用。
- 架构:x64还是Win32,检查包内
lib目录下是x64子目录还是一个扁平的lib,要看清楚。 - Debug/Release:好的3rdParty包会在lib文件上区分调试版和发布版,比如
zlibd.lib是debug,zlib.lib是release。如果你的包只有一个版本,编译时就必须严格用对应配置。
建议在解压根目录先建一个README.txt,把上面三项信息写进去,免得过两个月自己都忘了这份包是干什么用的。
2. 拿到预编译3rdParty.zip后,从解压到出Demo的完整操作流
2.1 解压、目录结构与PATH环境变量的安排
假设下载到的包是3rdParty-VS2017-x64.zip,我的习惯是解压到D:/3rdParty这样没有空格和中文的路径下,避免一些老牌库的预处理器被路径里的空格搞得精神错乱。解压后目录大致是这个样子:
D:/3rdParty/ include/ zlib.h gdal/ curl/ ... lib/ debug/ release/ bin/ debug/ release/接下来处理环境变量。
OSG运行时主要靠OSG_ROOT定位主程序目录,PATH里的dll目录则决定了能不能快速加载三方库。如果你把OSG和OSGEarth都安装到D:/OSG,给系统变量或用户变量追加下面几项:
OSG_ROOT = D:/OSG PATH += D:/OSG/bin;D:/3rdParty/bin/release注意Windows系统环境变量修改后,需要重新打开命令行或重启VS才能生效。这一步很多新手会忽略,结果运行示例程序报“找不到DLL”,先把环境变量刷新一遍再怀疑其他问题。
2.2 CMake里最关键的3个配置区域
用CMake-gui配置OSG时,我的做法是先把源码目录和构建目录填好,点Configure一次,让所有缓存变量暴露出来,然后再重点看下面三个区域。
第一个是三方库路径。最直接的方式是把ACTIVE_3RD_PARTY_DIR指向D:/3rdParty。有些版本的3rdParty包还需要额外设置CMAKE_PREFIX_PATH,填D:/3rdParty也能让CMake的find_package和find_library在搜索时自动找到对应目录里的include和lib。
第二个是插件的开关。OSG的CMake里有大量OSG_USE_XXX、BUILD_OSG_PLUGINS_BY_DEFAULT这类选项。如果你用的是预编译3rdParty包,通常所有依赖都能找到,直接把默认值留着就行。但如果某些选项显示NOT-FOUND,先别急着“三连关”,看一下是不是路径没指对。关插件虽然省时间,但牺牲的是功能。
第三个是安装路径。OSG和OSGEarth都需要执行INSTALL步骤,把头文件、库、dll统一安装到指定目录,所以我会先改CMAKE_INSTALL_PREFIX,设置成D:/OSG。这样后续OSGEarth的CMake找OSG时,只指这一个目录就够了。
配置完成后点Generate,生成VS工程文件前,最后确认一次构建配置和生成器平台是x64还是Win32。之前见过有人CMake选的是Win32,三方库是x64,编译到一半冒出几百个无法解析的外部符号,怎么查都是库没对上。
2.3 编译OSG和OSGEarth的顺序与验证
顺序是严格的:先OSG,再OSGEarth。
在VS里打开生成的OpenSceneGraph.sln,把配置从Debug或Release二选一。我推荐先用Release跑通全流程,因为Debug版的三方库在预编译包里有时候不齐全,而Release几乎永远是齐的。右键ALL_BUILD,生成,然后右键INSTALL,生成。OSG编译大约需要10到20分钟,取决于机器性能。
编完后验证一下:
osgversion命令行输出3.6.5,然后运行示例:
osgviewer cow.osg能看到奶牛模型旋转起来,说明OSG基本OK。
接着用CMake配置osgEarth源码。构建目录指向需要写可写的目录,配置时指定OSG的安装前缀D:/OSG,通常会自动找到OSG的头文件与库。同样点击Configure,看到所有依赖项都变白(没有红色NOT-FOUND),再Generate、ALL_BUILD、INSTALL。
osgEarth编完,个人建议去examples下找osgearth_viewer,让它加载一个简单earth文件。看到地球转起来,才算整个链路完整跑通。
3. 常见编译/运行坑排查实录:从报错信息反推根因
3.1 VS版本错配的LNK2038:一次典型的Runtime Library冲突
这个坑我犯过,网上也最多人问。现象是链接.lib的时候报:
error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MDd_DynamicDebug' doesn't match value 'MTd_StaticDebug'翻译成人话:你给三方的lib是拿/MDd(动态运行时)编的,但当前项目用的是/MTd(静态运行时)编的,两边不想跟对方一起玩。
排查链路也很清晰:
- 先用
dumpbin /headers xxx.lib查看三方库的DLL characteristics或者直接用VS的属性页打开项目,看到“运行库”选的是“多线程调试 (/MTd)”。 - 比对三方包里的说明,确认它是用哪个VS版本和运行库方式编译的。
- 如果是VS版本错,重新找对应版本的3rdParty.zip,或者用vcpkg自己编一套,不要试图在项目属性里硬把运行库改成和三方库里一样的设置,强行改会引发新的运行时崩溃。
注意预编译三方库通常默认用的是动态库/MDd或/MD,OSG和OSGEarth的CMake默认也是动态库,所以最稳妥的方案是保持默认不动,别去折腾静态运行库。
3.2 GDAL版本不对:影像加载不出来时的检查顺序
还有种情况是编过了、跑起来了,但OSGEarth加载tif地形或影像时整块地形是空的,或者屏幕显示一片黑。
别急着去改代码。先怀疑数据源,再怀疑OSGEarth配置,最后怀疑三方库里的GDAL。
OSGEarth对GDAL的版本要求比较敏感,如果3rdParty里打包的GDAL太老,或者你自己手动装了另一个GDAL并被PATH提前搜到,会导致运行时加载的dll不是你CMake配置时指定的那个。程序加载GDAL插件后调用一些新接口直接失败,表面看就是“影像加载不出来”。
我的检查顺序是:
- 在命令行执行
osgearth_viewer --caps,看输出里GDAL的版本号。 - 用
Process Explorer或listdlls查看进程实际加载的gdalXXXX.dll路径。 - 按路径找到dll,确认它来自3rdParty包,而不是系统盘里某个GIS软件自带的GDAL。
如果确认是被系统路径污染了,把3rdParty的bin目录挪到PATH更靠前的位置,或者把项目工作目录下的dll都统一清理掉。
3.3 动态库加载失败:先分清是PATH问题还是依赖链问题
运行时弹窗“无法定位程序输入点 XXXX 于动态链接库 gdalXXX.dll 上”,这是另一个高频问题。
它跟“找不到DLL”不一样。“找不到”是路径没配好;而“无法定位程序输入点”99%是dll版本混用,比如A.exe运行时找到了B.dll,但B.dll内部又依赖C.dll,而C.dll是另一个更老或更新的版本,里面缺少某个导出函数。
我自己的排查方式分两步:
第一步,先确认OSG的bin目录和3rdParty的bin目录都在PATH里,并且只用一套。
第二步,用Dependencies工具(就是以前Dependency Walker的替代品)打开出问题的exe或dll,它会列出完整的依赖树,能看到哪一层依赖断掉了。这类问题很多时候是因为你把不同版本的3rdParty混合使用,比如GDAL用A包、curl用B包,A包里的GDAL调用了B包curl没有的函数,崩溃就来了。所以三方库包尽量不要混搭,最好整套来自同一个作者或同一个版本。
3.4 定位问题的通用排查链路
把上面几种坑合并成一个通用套路,可以解决九成问题:
- 拿到报错,先看是编译期(链接错误)还是运行期(dll错误)。
- 链接错误,去项目属性页看附加依赖项里的lib路径,确认是不是指向3rdParty目录,再看VS版本和平台。
- 运行错误,先跑
osgversion、osgearth_version,这类工具能加载基础插件,如果它都崩,说明环境变量没配好。 - 用Process Explorer检查实际加载的dll,确认没有“串包”。
- 最小化复现:新建一个空项目,只调OSG和OSGEarth基础接口,逐步加功能,找到崩溃触发点。
这套排查链路看起来朴素,但每一条都能单独干掉一类坑。遇到问题别盲目重装,先定位是哪一层出的错。
4. 不用这套zip也能编译的备选路线
4.1 vcpkg自动化依赖管理
如果你不想用别人打包的预编译库,或者你手头的VS版本和网上的3rdParty包匹配不上,我的建议是用vcpkg。
vcpkg install osg osgearthvcpkg会从源码编译OSG、OSGEarth以及它们需要的三方依赖,整个过程自动化程度高,而且会按你当前的VS版本生成对应库。劣势是耗时长——首次编译这几百个依赖可能要一两个小时,而且vcpkg默认的triplet是x86,需要指定--triplet=x64-windows或者x64-windows-release。
用vcpkg编完后,CMake里只要设置:
CMAKE_TOOLCHAIN_FILE = D:/vcpkg/scripts/buildsystems/vcpkg.cmakeOSG和OSGEarth的依赖会自动被找到,省心程度比手动配置3rdParty高很多。
4.2 Linux下的系统包方案
如果你是在Ubuntu这类发行版上编译,其实根本不需要第三方zip。直接用系统包:
sudo apt install libopenscenegraph-dev libosgearth-dev系统包仓库已经把三方依赖全部处理好了,你只用写业务代码。缺点是版本通常比官方最新版老,而且有些新特性没有,但如果只是学习和业务开发,完全够用。
4.3 官方安装包与混用注意
OSG官方在GitHub的Release页面会发布Windows安装包,比如OpenSceneGraph-3.6.5-VC2017-x64-release.exe,装上以后自带OSG和一部分三方库。但注意,官方安装包并不包含OSGEarth的三方库完整依赖。
如果你想只装官方包,然后自己单独编译OSGEarth,最稳妥的做法是:先确认官方包带的GDAL、curl能跑通,再去编译OSGEarth。之前有同事这么干过,结果OSGEarth的CMake找不到GDAL头文件,后来还是回去找第三方包补全。
所以我的建议是,既然你手里已经有编译好的3rdParty.zip,就老老实实用整套,别和官方安装包混着来,混用dll是出问题最多的场景。
5. 附:判断点是否落在FeatureNode内的两种落地做法
5.1 为什么“点是否在Feature内”看起来简单却容易搞错
最近搜“osgearth如何计算点是否在featurenode内”的人不少。这个需求的典型场景是:屏幕上某个坐标点点击下去,要判断是否命中了某个矢量要素标注区域,比如一块多边形的行政区划、一个封闭建筑轮廓。
很多人第一反应是拿点去跟Feature的几何坐标做射线法判断,但实际做起来往往会遇到两个问题:
- FeatureNode里的Feature几何坐标通常是经纬度,直接按平面坐标算射线法,在高纬度地区误差会变大。
- Feature可能有洞,可能有多边形,而不是简单一个外轮廓,这导致判断逻辑要写完整。
想避开这些坑,有两种落地做法。
5.2 射线法:从Feature取出几何并在平面坐标系内判定
先讲最直观的射线法。
通过featureNode->getFeature()拿到osgEarth::Features::Feature,再拿到它的几何集合。对每个多边形做“从点向任意方向引一条射线,统计与多边形边界相交次数,奇数次在内部,偶数次在外部”。
关键点是要先把经纬度坐标投影到平面。我通常用Feature自带SRS的transform方法,把点坐标转换到适合当前区域的投影坐标,再做射线法,避免直接用经纬度当平面坐标点算。
代码逻辑大致是这个骨架:
osgEarth::Features::Feature* feature = featureNode->getFeature(); const osgEarth::Features::Geometry* geom = feature->getGeometry(); for (auto it = geom->getComponents().begin(); it != geom->getComponents().end(); ++it) { const osgEarth::Features::Polygon* poly = dynamic_cast<const osgEarth::Features::Polygon*>(*it); if (poly && pointInPolygon(projectedPoint, poly)) { return true; } }这段代码里,pointInPolygon就是标准的射线法实现。边界情况要处理:点在多边形边界上、点与顶点重合,这些在实际点击场景里经常发生。我的建议是给“相交”判断加一个很小的容差epsilon,避免因为浮点精度漏判。
5.3 更省事的拾取思路:用Intersector处理三维场景
如果你只是想实现“鼠标点到屏幕上的某个Feature”,没必要手动做点与多边形的关系计算,直接用OSG的相交检测更靠谱。
做法是把屏幕坐标转换成射线,用osgUtil::LineSegmentIntersector对场景求交,然后遍历交点,检查相交节点路径里是否包含目标FeatureNode。这种思路天然支持三维地形和相机视角变化,代码量也更少。
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector = new osgUtil::LineSegmentIntersector(near, far); osgUtil::IntersectionVisitor visitor(intersector.get()); node->accept(visitor); if (intersector->containsIntersections()) { // 遍历 intersection.nodePath 查找 FeatureNode }这种方法有个好处,就是不关心Feature到底是面还是线还是点,只要它被渲染成了可拾取节点,就能命中。如果你是在做点击选中的交互功能,优先考虑这种方案。
射线法适合你已经有明确的点坐标和Feature,想独立做算法判断的场景;Intersector适合鼠标拾取交互。两个思路搭配起来,基本能覆盖所有“点在FeatureNode内”的判断需求。
最后说句实在话:预编译三方库这玩意儿,关键不是“下载下来用”,而是“知道它解决了什么问题”。你理解了OSG和OSGEarth对第三方库的依赖关系,理解了版本匹配的原理,后面无论是换VS版本,还是换Linux环境,都能举一反三。上面这些坑我基本都亲自踩过,写出来就是希望你能直接绕过去,省下来的时间多跑几个demo,比啥都值。
本文还有配套的精品资源,点击获取