简介:面向在VS2019平台下开展OSG3.6.5与OSGEarth2.10相关开发的程序员,这份压缩包提供了一套预编译好的64位第三方依赖库,使得原本繁琐的依赖构建过程变得开箱即用,可显著缩短环境配置周期。包内共收录2000个文件,其中以1351个h头文件、56个lib库文件和32个cmake配置文件为主体,同时包含少量c源码、inl内联定义以及proto协议文件,整包大小约137.76MB。已有619人浏览学习,适合需要快速搭建三维渲染与地形开发环境的中高级C++开发者。资源不仅覆盖了完整的头文件与静态库,还附带必要的dll动态库、时区数据文件与HTML说明文档,可帮助开发者跳过第三方库的编译配置环节,直接将精力集中在OSG场景渲染及OSGEarth地理空间应用的核心功能开发上。无论从事三维仿真、地形构建还是地球可视化,这款预编译三方库都能作为可靠基础,有效提高项目开发效率。 搞三维GIS和视景仿真的开发者,基本都躲不过OSG(OpenSceneGraph)和OSGEarth这对组合。OSG负责底层渲染,OSGEarth负责加载地形影像、矢量数据、高程模型,两个配合起来能搭出相当完整的数字地球可视化框架。但有一个坎,几乎每个人都会卡一下:编译依赖。OSGEarth依赖OSG,而OSG本身又依赖一堆第三方库,比如GDAL、CURL、GEOS、SQLite、ZLIB、LIBPNG之类。手动挨个编译这些库,配置CMake路径,处理不同版本的兼容问题,少说也得折腾两三天,运气不好能磨一周。
我自己第一次搞这套东西的时候,就是栽在依赖上。后来拿到了一个别人打包好的OSG3.6.5和OSGEarth配套的预编译3rdParty.zip三方库,把整个编译时间从“以天为单位”压缩到了“以小时为单位”。这篇文章就围绕这个zip,把环境搭建、源码编译、常见问题一次讲透,最后再顺手聊一下最近群里问得比较多的“点是否在FeatureNode内”的判断思路,算是给正在折腾这套工具链的朋友一份能直接抄作业的实操笔记。
1. 3rdParty.zip到底是什么,为什么能省这么多事
1.1 OSG和OSGEarth的依赖关系拆解
很多初学者不理解,为什么编译OSG会牵扯出一堆第三方库。其实道理很简单:OSG本身是一个渲染引擎,它只负责把模型画出来,但模型文件的格式千奇百怪,图片纹理有PNG、JPG、TGA,模型有三DS、OBJ、FBX,高程数据又有各种DEM格式——OSG不可能每种格式都在内核里实现读取器,所以它把解析工作外包给了第三方库。
具体到OSGEarth这里更麻烦。OSGEarth的地形数据来源通常是GDAL,网络资源加载依赖CURL,矢量分析依赖GEOS,空间数据存储依赖SQLite3和Spatialite。这些库之间还有传递依赖,比如GDAL内部又依赖TIFF、JPEG、PROJ等。如果自己从源码编译GDAL,得先编译它的一堆子依赖,而且GDAL的CMake选项特别多,选错一个后面OSGEarth直接编不出来。用预编译的3rdParty.zip,等于把这条依赖链一次性包圆了,里面的include和lib库文件都是按OSG 3.6.5对应的VS版本编译好的。
1.2 预编译zip和源码包的区别
打开3rdParty.zip,里面通常是三个核心目录:
- include:所有第三方库的头文件,编译OSG和OSGEarth时,CMake会从这里找GDAL/hdr、curl/curl.h等定义。
- lib:静态库和导入库文件,链接阶段使用,比如gdal_i.lib、libcurl.lib。
- bin:运行时DLL文件,编译出来的exe运行时依赖它们。
有经验的朋友看到这个结构应该就懂了,这跟OSG源码包自带的第三方依赖目录结构是对齐的。所以拿到zip后,直接把内容解压到工作目录(一般和OSG源码同级),然后在CMake里指定路径,就能被正确识别。
提示:注意zip对应的编译器版本。3.6.5这个版本一般对应VS2015和VS2017的预编译结果,如果你本机装的是VS2013或者VS2022,要么换编译器版本,要么自己重新打包依赖,否则会出现“resolved magic number mismatch”这类的库文件不匹配错误。
2. 环境准备:解压、摆放、配环境变量
2.1 目录安排
我习惯的目录结构是:
D:\OSG\ ├── OpenSceneGraph-3.6.5\ # OSG源码 ├── osgearth-master\ # OSGEarth源码 └── 3rdParty\ # 预编译三方库 ├── include\ ├── lib\ ├── bin\ └── CMake\这里有一个小细节:路径里不要带中文,不要带空格。OSG的构建系统虽然比早年好一些,但CMake在解析带空格的路径时还是偶尔抽风,尤其是涉及到第三方库的查找环节。为这种问题排查半天,纯属浪费时间。
另外,建议把OSG源码、OSGEarth源码和3rdParty放在同一根目录下,这并不是必须的,但后面写CMake缓存文件、相对路径配置时会更方便,尤其是你想把整套环境复制到另一台机器上的时候,相对路径会减少很多麻烦。
2.2 环境变量配置
编译安装完之后,需要添加以下环境变量到系统PATH:
- OSG_DIR:指向OSG的安装目录(里面要能看到bin、lib、include)。
- OSG_FILE_PATH:指向OSG自带的数据资源目录,方便运行示例程序。
- 3rdParty的bin目录:这个容易漏。编译OSGEarth程序时,运行时需要GDAL的DLL、CURL的DLL,如果PATH里没有,程序启动会直接报“找不到gdal203.dll”。
配置完环境变量,开一个新的命令行窗口,输入osgversion,如果能看到版本号,说明OSG环境已经通了。
2.3 如何快速验证三方库是否可用
这一步很多教程不会特意提,但我建议在编译OSGEarth之前先做一次小验证。用CMake构建OSG时,如果三方库路径正确,CMake的配置输出里会列出“Found ZLIB”、“Found GDAL”等字样。如果看到“GDAL NOT FOUND”,先别急着继续,回头检查路径或者版本。
我用这套预编译包时的经验是:最好检查两次。第一次在OSG的CMake配置阶段,第二次在OSGEarth的CMake配置阶段。因为OSGEarth对GDAL的版本敏感度更高,经常出现“OSG编译过了,但OSGEarth报告找不到GDAL”的情况,原因多半是三方库的bin目录没进PATH,或者CMake缓存的变量路径不对。
3. 用CMake把OSG和OSGEarth编译出来
3.1 编译OSG:模式参数和构建目录
准备工作完成后,打开cmake-gui,源码目录选D:/OSG/OpenSceneGraph-3.6.5,构建目录建议新建一个build文件夹,不要和源码混在一起。点“Configure”之后,选择对应的VS版本和平台,这里的平台一定要和3rdParty匹配。
如果三方库路径正确,CMake会自动找到大部分依赖。有些版本需要在分组里手动把ACTUAL_3RDPARTY_DIR变量指向3rdParty根目录。这里有个细节:这个变量名的具体含义是“actual third party directory”,因为OSG的构建脚本里有些变量是通过缓存从旧版继承的,不手动指定时可能悬空。
Configure通过后,把CMAKE_INSTALL_PREFIX设为D:/OSG/install,然后点Generate,用VS打开生成的解决方案,在“解决方案管理器”里对ALL_BUILD选择“生成”。这一步耗时较长,取决于CPU核心数,一般半小时到一小时。
编译过程中最常遇到的报错是“无法打开包括文件:zlib.h”或“无法打开文件:zlib.lib”。这时候不要慌,多半是3rdParty的include和lib路径没有正确传给CMake——回到cmake-gui里检查变量ZLIB_INCLUDE_DIR和ZLIB_LIBRARY,把它们手动修正到3rdParty目录下的对应位置,再重新Configure。
3.2 编译OSGEarth:版本分支和依赖项确认
OSGEarth的编译需要注意版本分支。主分支(master)通常是对应最新OSG的,如果你用的是3.6.5,建议直接切到对应的release分支,比如osgearth-2.10或osgearth-2.9,避免出现API接口对不上的问题。
CMake阶段,关键看三个变量:
- GDAL_INCLUDE_DIR / GDAL_LIBRARY:指向3rdParty里的路径。
- CURL_INCLUDE_DIR / CURL_LIBRARY:同上。
- GEOS_INCLUDE_DIR / GEOS_LIBRARY:矢量功能依赖。
把这些都配置好之后,Configure和Generate,然后像OSG一样编译ALL_BUILD。我实测过,只要三方库版本一致、路径正确,OSGEarth的编译非常顺利,不会像手动编GDAL时遇到那么多“暗坑”。
3.3 建议的编译顺序和环境测试
编译顺序有讲究:必须先编OSG并安装,再编OSGEarth。因为OSGEarth的CMake脚本会查找已安装的OSG库。如果你把两个放到同一个解决方案里一起编,也不是不行,但依赖顺序一旦错乱,链接阶段会出现一堆“unresolved external symbol”的错误,排查起来特别头疼。
安装完成后,强烈建议运行一个OSGEarth自带的示例程序,比如osgearth_viewer。如果它加载示例earth文件后能正常显示三维地形,说明整套环境已经通了。我当年的第一反应是:原来编译这玩意儿真能这么顺,早知道直接找预编译包,不自己死磕依赖了。
4. 进阶问题:如何判断点是否在FeatureNode内
4.1 FeatureNode是什么
编译环境搭好之后,实际开发中有一个出现频率很高的问题,也是OSGEarth用户群里经常被问到的热搜话题:怎么判断一个点是否在FeatureNode内。
先解释下FeatureNode。OSGEarth把矢量数据源(Shapefile、GeoJSON等)加载后,会解析成Feature对象,每个Feature里包含一个Geometry(点、线、面)。这些Feature通过FeatureNode节点添加到场景图中,用于渲染道路、行政区划、地块等。
常见的需求场景是:点击地图,判断点击位置是否落在某个行政区划(即某个Feature)内;或者判断车辆当前坐标是否在某个禁飞区内。这里涉及的是“点与面”的空间包含关系,属于典型的地理空间分析问题。
4.2 几种可行的判断方案
方案一:基于osgUtil的相交检测
FeatureNode本身是一个Drawable节点,可以直接挂给osgUtil::IntersectionVisitor做拾取。这个方法比较适合“鼠标点击选区域”的场景——你把鼠标点击位置转化成屏幕坐标,构造一条拾取射线,用IntersectionVisitor对地形和FeatureNode做求交,返回命中的节点。如果命中的是某个FeatureNode,就说明点击点在这个节点上。
但这个方案有一个明显的坑:FeatureNode的几何体是三角形剖分后的渲染面片。一个凹多边形或者带空洞的行政区,在三角剖分后,三角形的集合并不严格等于原始多边形的内部区域,边界附近会出现误差。所以相交检测适合“粗匹配”,不适合精确判断。
方案二:通过Feature的Geometry做空间判断
更准确的思路是:先拿到被点击或待判断位置的坐标,将其转换为FeatureNode对应的空间参考,然后遍历FeatureNode中的Feature,对每个Feature的Geometry调用空间包含判断。
关键代码逻辑大致如下:
- 获取FeatureNode中的Feature集合。
- 通过
featureNode->getFeature()或遍历成员拿到osgEarth::Features::Feature。 - 从Feature中取出
Geometry,一般是Polygon类型。 - 判断坐标点是否在Polygon内部。
这里的“判断点是否在Polygon内部”,我建议直接用射线法(Ray Casting algorithm)实现,不依赖额外的GIS库。射线法的思路很简单:从这个点引一条水平射线,统计它与多边形边的交点数,如果是奇数,说明点在内部,偶数则在外部。代码量不大,而且对于任意形态多边形(包括凹多边形)都有效。
方案三:借用GEOS库
上面提到3rdParty里有GEOS,它的geos::geom::Geometry::contains方法可以直接做精确判断。如果项目里已经链了GEOS,就不用自己写射线法了,直接把Feature的Geometry转换成GEOS的Geometry对象,然后调用contains方法即可。这个方案最省事,精度也高,前提是你能确保坐标转换无误。
4.3 我踩过的坑:坐标系匹配比判断本身更重要
在我自己的项目里,最初死磕的是“contains”写不对,后来排查半天才发现,问题是出在坐标系不统一。
OSGEarth场景里的坐标默认是经纬度(WGS84经纬度),但FeatureNode内部可能已经映射到不同的投影坐标系,比如墨卡托投影或UTM。如果直接拿经纬度坐标去和投影坐标系下的Polygon做包含判断,结果肯定不对。所以正确做法是:先获取Feature的SRS(空间参考系),把待判断点坐标通过SRS::transform转换到Feature的坐标系,再做包含判断。
另外,在鼠标拾取的时候,屏幕上拿到的是视口坐标,要先反算到世界坐标,再反算到经纬度,最后转换到Feature坐标系。这一串转换链中任何一环出错,判断结果都会飘。
关于性能,如果场景里有几千个Feature,每帧都遍历所有Feature做点包含判断是不现实的。常规做法是:
- 先用FeatureNode的包围盒做粗过滤,把明显不相交的区域排掉。
- 再对剩余Feature做精确判断。
- 如果数据量特别大,可以给Feature集合建R树索引,OSGEarth内部的FeatureSource理论上也支持空间过滤。
5. 常见问题与排查技巧实录
5.1 编译期问题速查
| 问题现象 | 可能原因 | 处理办法 |
|---|---|---|
| CMake找不到ZLIB | 3rdParty路径未正确指定 | 手动设置ZLIB_INCLUDE_DIR和ZLIB_LIBRARY |
| 编译报错“无法打开包括文件:gdal.h” | GDAL头文件路径没进CMake搜索目录 | 检查GDAL_INCLUDE_DIR,指向3rdParty的include |
| 链接时报“unresolved external symbol” | 编译选项(Debug/Release)或库版本不匹配 | 统一使用Release x64,确保OSG和OSGEarth配置一致 |
| 运行时提示“找不到gdal203.dll” | 未把3rdParty的bin目录加入系统PATH | 添加PATH后重启命令行/IDE |
| osgEarth运行时崩溃或黑屏 | 三方库的DLL版本冲突,比如系统里已有其他版本的GDAL | 把3rdParty的bin目录放在PATH最前面,确保优先加载 |
5.2 环境常见坑
第一是IDE缓存问题,这个特别容易误导人。你在命令行下配好了环境变量,Visual Studio却检测不到。这不是环境变没有生效,而是VS在启动时读取了旧的环境变量。解决办法是重启VS,或者干脆重启系统。别问我怎么知道的,有一回我在这上面耗了一个下午,最后发现重启就好了。
第二是Debug和Release混用。很多三方库的预编译包只提供了Release版,如果你用Debug模式编译OSGEarth,链接阶段会报错,提示找不到“gdal_i.lib”的Debug版本。解决办法很简单:项目统一用Release模式编译,除非你自己能把整套三方库的Debug版也编译出来。
第三是GDAL的版本覆盖问题。如果电脑上装过QGIS、Anaconda或者其他GIS软件,系统里很可能有多个版本的GDAL DLL。程序启动时,Windows按PATH顺序加载DLL,万一加载到旧版本,OSGEarth可能直接崩溃或者加载高程数据异常。解决方案是把3rdParty的bin目录置于PATH最前面,确保优先加载我们指定的版本。
5.3 点判断问题排查顺序
关于第4节中的“点是否在FeatureNode内”,如果判断结果一直是错的,我建议按这个顺序排查:
- 先确认FeatureNode是否加载成功,数据源是否正常解析。
- 再确认坐标系的转换,把待判断点转换到和Feature完全一样的SRS里。
- 接着单独测试Geometry数据是否正确,打印一下Feature的边界范围,目测点是否在范围内。
- 最后才怀疑射线法代码本身——用一个矩形或三角形做单元测试,能迅速定位问题出在算法还是在坐标链路上。
6. 一点个人体会
编译OSG和OSGEarth,本质上是一个“配置环境”的过程,它本身不产生业务价值,但环境搭不好,后面的所有工作都卡在起点。预编译的3rdParty.zip这种资源,虽然在一些老派开发者看来属于“走捷径”,但说实话,能用现成的可靠依赖,完全没必要去重复造轮子——尤其在项目排期紧张的时候,快速把环境跑通,把精力投入到功能开发上,才是更明智的选择。
我在这次环境搭建里比较深的体会是:编译错的不是库,而是版本。只要保证OSG源码版本、OSGEarth版本、编译器版本、三方库预编译版本四者对齐,整个过程其实非常机械。反过来,任何一环出了偏差,报错信息都会把你带到沟里去——所以拿到任何一个预编译包,第一件事不是开工,而是确认版本匹配。
如果你手上正好也在搭这套环境,或者遇到点判断的疑难杂症,欢迎拿来讨论。我自己也是踩了不少坑才把这些老版本的脾气摸透,写出来就是希望后人能少走几步弯路。
本文还有配套的精品资源,点击获取