ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OSG与OSGEarth及Qt环境编译搭建实战指南

OSG与OSGEarth及Qt环境编译搭建实战指南 1. 为什么折腾这套环境需求与选型前后1.1 这套组合到底能干什么做三维GIS或者数字孪生相关项目的时候很多人第一个想到的就是WebGL方案Cesium、Three.js这些确实上手快。但如果你的项目需要处理大规模地形、影像、矢量数据或者要跑比较重的场景调度逻辑浏览器方案往往会在性能和内存上卡脖子。这时候OSGOpenSceneGraph加OSGEarth的组合就体现出价值了。OSG本质上是一个高性能的开源三维渲染引擎它的场景图管理、渲染状态优化、多线程渲染调度都非常成熟。OSGEarth则是在OSG之上构建的一套地形与GIS数据可视化框架专门解决全球尺度地形加载、影像切片调度、矢量数据叠加这些问题。至于OSGQt它是连接OSG渲染窗口和Qt界面框架的桥梁让你能在Qt应用程序里嵌入三维场景配合各种业务界面做桌面端GIS工具。这套技术栈的典型使用场景包括离线地理信息系统、军事仿真推演、油田管网可视化、智慧城市管理平台、航空航天任务演示等等。如果你是做桌面端三维可视化的这套组合基本上是绕不开的经典方案。1.2 为什么必须自己动手从源码编译很多人会问OSG不是有安装包吗直接下载不就行了但真实情况是OSG官方提供的预编译包版本通常比较保守不带OSGEarth和OSGQt而且预编译包往往默认编译了所有插件体积臃肿还可能出现与你本机环境不匹配的问题。更关键的是OSGEarth和OSGQt这两个重要库官方基本不提供Windows预编译版本必须自己拉源码编译。自己编译还有几个实际好处可以选择只编译你需要的插件模块减小库的体积可以开启或关闭某些高级特性比如调试信息、多线程支持可以针对自己机器的CPU指令集做优化编译最重要的是你后面调试代码的时候需要带有调试符号PDB文件的库预编译包根本不给你这些。我在VM 2019的环境里编译这套组合前后折腾了将近两个星期踩了不少坑也总结出一套比较顺的流程这文章就是希望帮你跳过那些坑。2. 编译前的准备工作工具链与依赖项梳理2.1 各核心组件版本选型与理由版本选择是整个环境搭建中最容易出问题的一步。我使用的环境组合是经过反复测试后相对稳定的一组组件版本说明Visual Studio2019 16.11.x支持C17完整特性对CMake支持完善CMake3.22至少需要3.15以上推荐用最新3.2x版本Qt5.15.2推荐使用msvc2019_64预编译包OSG3.6.5长期稳定版本社区使用面最大OSGEarth2.10.3与OSG 3.6.5兼容文档和示例丰富OSGQt官方GitHub最新master配合OSG 3.6.5使用需要小改动GDAL3.4.x读GIS数据时强烈建议开启GEOS3.10.xOSGEarth地形分析功能需要Curl7.x访问网络地图服务时使用要特别注意OSG的不同版本对第三方库的接口兼容性差异很大。比如OSG 3.6.x使用OpenThreads作为线程抽象层而到了3.7.x之后开始逐步迁移到std::thread这会影响OSGQt的编译方式。所以如果你打算用OSGQt建议先用3.6.x版本社区反馈最丰富问题也最容易搜到解决方案。2.2 第三方依赖库准备一劳永逸的做法OSG和OSGEarth编译都依赖一批第三方库。最让人头疼的就是这些库的版本匹配问题。我的做法是下载第三方库预编译包然后用CMake直接指向而不是自己再编译一遍第三方库。这里有一个非常实用的方案使用OSG社区的第三方库编译包。在GitHub上可以找到openscenegraph/3rdparty仓库里面有Windows版本的预编译包包含zlib、libpng、libjpeg、libtiff、freetype等基础依赖。我使用的是其中对应VS2019的x64版本。需要注意的一点是这个第三方库包只解决了OSG的基础依赖。OSGEarth还额外需要GDAL、GEOS、Curl、libzip等库。这些库我建议使用vcpkg安装因为它会自动处理依赖关系vcpkg install gdal:x64-windows vcpkg install geos:x64-windows vcpkg install libzip:x64-windows vcpkg install curl:x64-windows不过vcpkg编译这些库的时间比较长GDAL如果完整编译需要半小时以上。如果你不想等那么久也可以直接去GIS Internals或者OSGeo4W下载预编译的GDAL开发包然后把路径配置给CMake。这里还要提醒一下Qt的版本与OSGQt的兼容性非常敏感。OSGQt目前官方支持的主要是Qt5用Qt6编译会报一堆错。Qt 5.15.2安装时建议只勾选MSVC 2019 64-bit模块不要混装MinGW否则后面CMake配置时容易路径冲突。2.3 目录规划一个好习惯省去大量麻烦我建议先把目录结构规划好这样后面配置CMake和管理文件会轻松很多。我本机的规划如下D:\3DDev\ ├── src\ # 所有源码目录 │ ├── osg\ # OSG源码 │ ├── osgEarth\ # OSGEarth源码 │ └── osgQt\ # OSGQt源码 ├── build\ # 编译中间目录 │ ├── osg\ │ ├── osgEarth\ │ └── osgQt\ ├── install\ # 安装目录所有编译产物都放到这里 │ └── 3rdParty\ # 第三方库 └── vcpkg\ # vcpkg源码与安装包把源码、构建目录、安装目录彻底分开是多年被教训出来的经验。如果你把源码和构建目录混在一起换VS版本或者切换Debug/Release配置时只能把整个目录删掉重新来非常浪费时间。3. 核心组件编译实操过程3.1 编译OSG核心库CMake配置的每一步OSG编译是整个环境的基石。打开CMake GUI按下述步骤操作第一步指定源码目录和构建目录。源码目录选择D:\3DDev\src\osg构建目录选择D:\3DDev\build\osg。这里有个小陷阱不要图方便把构建目录放在源码目录下否则后续CMake缓存管理会很混乱。第二步点击Configure选择生成的VS版本为Visual Studio 16 2019平台选择x64。这里很多新手容易忽略的是平台选择默认可能是Win32如果你后面要编译64位的OSGEarth和Qt库这里选错就得全部重来非常浪费时间。第三步配置关键参数。配置选项很多我挑几个容易出错的重点说明BUILD_OSG_EXAMPLES: ON BUILD_OSG_PLUGINS: ON CMAKE_INSTALL_PREFIX: D:/3DDev/install/osg ACTIVE_3RD_PARTY_DIR: D:/3DDev/install/3rdParty这里有一个特别需要注意的选项是OSG_WINDOWING_SYSTEM默认是Win32不要改。如果你要用OSGQt实际上文件还是会走Win32窗口系统Qt只是提供一个容纳OSG渲染窗口的容器。还有一个OPENGL_PROFILE选项默认是GL2推荐保持默认或者选择GL3。如果选GL3需要你的显卡驱动支持OpenGL 3.x核心模式对显卡要求会高一些但现代显卡基本都没问题。我使用的是GL2兼容性最好后面做集成测试时不用考虑太多显卡差异。配置完后第一次Configure等待CMake查找完毕然后Generate生成VS工程文件。第四步打开生成的OpenSceneGraph.sln在Solution Explorer中右键ALL_BUILD选择Build。编译前记得把配置切换成Release。第一次编译耗时较长大约需要20到40分钟取决于机器性能。我建议先编译一次Release版本不着急编译Debug。因为Debug版本的OSG库体积非常大编译时间更是成倍增加等环境完全验证OK之后再补编译Debug也不迟。3.2 编译OSGEarthGIS能力的接入OSGEarth编译的前提是OSG已经正确编译并安装。在CMake中配置OSGEarth时关键是指定OSG的安装路径和第三方库路径。CMake配置参数参考CMAKE_PREFIX_PATH: D:/3DDev/install/osg;D:/3DDev/install/3rdParty OSGEARTH_BUILD_SAMPLES: ON GDAL_INCLUDE_DIR: D:/3DDev/vcpkg/installed/x64-windows/include/gdal GEOS_INCLUDE_DIR: D:/3DDev/vcpkg/installed/x64-windows/include这里有个常见的麻烦点CMake需要找到OSG的osgPlugins目录这通常是通过OpenSceneGraph_DIR或者CMAKE_PREFIX_PATH来指定的。如果CMake提示找不到检查一下你的OSG是否执行了Install步骤。只编译ALL_BUILD不执行INSTALL是不行的必须右键INSTALL生成安装目录。GDAL和GEOS的路径需要注意vcpkg安装的库文件在installed/x64-windows/目录下但CMake有时候会搜索到错误的子目录。我在配置时遇到过CMake找到了GDAL的C绑定库libgdal_cxx但实际我们只需要C接口的libgdal。OSGEarth编译时间大概10到20分钟跟VS2019的并行编译能力有关。编译完成后执行INSTALL。另外2.10.3版本的OSGEarth使用了一种基于Pimpl的API设计有些示例程序在编译时会需要额外的宏定义如果某几个示例编译失败不代表库本身有问题可以跳过失败的项目继续编译。3.3 编译OSGQt连接Qt的封装层OSGQt是这套环境里最特殊的一个组件因为它不是OSG官方发布的而是社区维护的。打开的是官网的仓库地址但要注意分支选项。我使用的是master分支相对活跃但需要小改一处才能适配OSG 3.6.5。OSGQt的CMake配置相对简单CMAKE_PREFIX_PATH: D:/Qt/5.15.2/msvc2019_64 CMAKE_INCLUDE_PATH: D:/3DDev/install/osg/include但有一个非常容易踩的坑OSGQt源码中有一个文件使用了osg::ref_ptr的某个内部接口在OSG 3.6.5中该接口已经被标记为废弃直接编译会报错。解决办法是打开报错对应的头文件找到相关调用把get()方法改成get()的显式调用或者直接使用智能指针的-运算符访问。这类小问题谷歌一搜就有解决方案不用太慌。编译OSGQt时注意Qt的编译配置必须与OSG一致都是Release x64。如果你之前编译了OSG的Debug版本那OSGQt也得用Debug版本否则链接时会出现符号不匹配的错误。实测下来这种release/debug混用导致的链接错误是最难排查的因为报错信息往往是签名不一致让人莫名其妙。还有个细节OSGQt编译完成后会生成一个osgQt.dll动态库同时还有Qt对应的插件。使用的时候记得把osgQOpenGLWidget这个类所在模块的库路径添加到项目的链接器中。4. 工程级环境集成配置4.1 环境变量配置与路径规划编译安装完成后还要配置系统的环境变量这样后面新建项目才能自动找到对应的库和插件。需要添加的系统环境变量如下OSG_ROOT指向OSG安装目录比如D:/3DDev/install/osgOSG_FILE_PATH指向OSG示例数据目录通常位于源码目录下的data文件夹OSG_NOTIFY_LEVEL通知级别设为WARN可以控制调试信息的输出量PATH添加%OSG_ROOT%\bin以及OSGEarth、OSGQt的bin目录这里要特别提醒一下PATH变量的问题。如果你的系统里装了多个版本的OSG或者有ArcGIS这类软件自带的OSG库PATH里路径的先后顺序会直接决定程序运行时加载的是哪一个版本的DLL。我在项目里遇到过程序运行时崩溃排查了很长时间最后发现是PATH里有个旧版本的osg.dll把新版的给覆盖了。所以配置完成后建议在命令行里输入where osg.dll确认一下当前生效的是哪个路径下的库。4.2 创建第一个VS2019测试项目环境配置好了拿一个能跑的最小示例验证整个链路是否畅通。打开VS2019创建一个Qt Widgets Application项目然后在项目属性里配置附加包含目录和附加库目录。包含目录需要添加D:/3DDev/install/osg/include D:/3DDev/install/osgEarth/include D:/Qt/5.15.2/msvc2019_64/include库目录添加D:/3DDev/install/osg/lib D:/3DDev/install/osgEarth/lib D:/Qt/5.15.2/msvc2019_64/lib然后在QTMoc的加载过程中有个小技巧在main.cpp里加上全局场景变量初始化#include osgViewer/Viewer #include osgQt/GraphicsWindowQt #include osg/Node #include osgDB/ReadFile #include QVBoxLayout int main(int argc, char** argv) { QApplication app(argc, argv); QWidget* mainWidget new QWidget; QVBoxLayout* layout new QVBoxLayout(mainWidget); osg::ref_ptrosg::Node scene osgDB::readNodeFile(cow.osg); osgViewer::Viewer* viewer new osgViewer::Viewer; viewer-setSceneData(scene.get()); QWidget* osgWidget new osgQt::GLWidget(0, viewer); layout-addWidget(osgWidget); viewer-setCameraManipulator(new osgGA::TrackballManipulator); viewer-realize(); mainWidget-resize(800, 600); mainWidget-show(); return app.exec(); }编译这个测试项目如果链接成功且运行时能弹出窗口显示一个牛的模型说明OSG和OSGQt的基础链路已经通了。接下来再测试OSGEarth的地球加载。#include osgEarth/MapNode #include osgEarth/EarthManipulator #include osgEarth/Map #include osgEarth/TerrainOptions // 创建一个简单的地球 osg::ref_ptrosgEarth::Map map new osgEarth::Map; osgEarth::MapNode* mapNode new osgEarth::MapNode(map.get()); viewer-setSceneData(mapNode);注意OSGEarth运行时需要读写一些临时文件需要确保当前账户对安装目录有写权限否则运行时会报错。4.3 初始化配置中的几个典型问题快查这部分是实际配置过程中高频出现的问题整理成表格方便排查问题现象可能原因解决方案编译时报找不到头文件osg/Node包含目录未配置或配置错误检查项目属性中的C/C附加包含目录链接时报LNK2019无法解析的外部符号库文件路径不对或Debug/Release混用确认库目录指正确所有库版本一致运行时提示缺少osg80-osg.dllPATH未包含OSG的bin目录添加OSG_ROOT/bin到PATH加载地球时崩溃提示osgEarth未找到OSGEarth的DLL不在运行目录将OSGEarth的bin目录加入PATHOSGQt控件黑屏或白屏OpenGL初始化失败或渲染线程异常检查显卡驱动将QSurfaceFormat设为OpenGL 3.2 CoreQt插件加载失败Qt版本与编译环境不匹配删除缓存重新编译OSGQt还有一个非常隐蔽的问题OSG默认使用GL2渲染路径而Qt 5.15.2的QOpenGLWidget默认请求OpenGL 3.2 CoreProfile。直接混合使用会导致场景渲染失效。解决办法是设置QSurfaceFormat强制使用OpenGL 2.1兼容模式QSurfaceFormat format; format.setRenderableType(QSurfaceFormat::OpenGL); format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setVersion(2, 1); QSurfaceFormat::setDefaultFormat(format);这个设置必须在创建QApplication之前完成。5. 进阶链路OSGEarth完整集成与性能调优贴士5.1 接入真实地形数据验证OSGEarthOSGEarth真正强的地方是对GIS数据的加载能力。我这里用一个天地图或者本地切片的方式来验证整个OSGEarth链路是否正常。以加载本地GDAL影像为例写一段测试代码#include osgEarth/Map #include osgEarth/ImageLayer #include osgEarth/TMS osg::ref_ptrosgEarth::Map map new osgEarth::Map; osg::ref_ptrosgEarth::ImageLayer layer new osgEarth::ImageLayer(); layer-setDriver(tms); layer-setURL(D:/Data/dom/tms.xml); map-addLayer(layer.get());如果你有本地的GDAL影像也可以直接用gdal驱动叠加。OSGEarth参数格式和加载逻辑设计得比较好底层会把影像数据均匀切块并按需调度。这里说一个实际运行中非常容易出现的问题OSGEarth默认的日志等级是INFO如果你加载了大范围影像控制台会疯狂输出切片调度日志拖慢整体性能。可以在初始化设置osgEarth::Registry::instance()-setDefaultLogLevel(osgEarth::Log::WARN);日志等级设为WARN后控制台瞬间清净程序运行也流畅不少。这个设置往往被文档忽略但对实际项目非常关键。5.2 性能调优的几个实操经验OSGEarth在桌面端的性能优化我总结出几个对项目影响最大的参数第一地形坡度与误差设置。OSGEarth默认使用LODLevel of Detail分层调度地形数据。段差与渲染质量成正比与性能成反比。在TerrainOptions中可以设置minLOD和maxLOD来控制加载层级osgEarth::TerrainOptions terrainOptions; terrainOptions.minLOD 3; terrainOptions.maxLOD 19;第二纹理压缩。对于有大量影像数据的地球模型开启纹理压缩能显著减少显存占用。在加载影像层时加上compression选项layer-setOption(compression, dxt5);第三线程模型设置。OSGEarth的多线程调度默认会根据CPU核心数自动配置但如果你的场景里有大量动态对象建议使用osgViewer::Viewer::SingleThreaded模式先把逻辑跑通再切到多线程模式优化性能。这个调优顺序能避免大量因线程同步导致的难以排查的bug。5.3 从示例到业务集成我踩过的坑把OSG和OSGEarth集成到真正的Qt业务应用时有几个跟纯Demo完全不同的坑。第一个是事件的传递。OSG的图像事件在默认情况下不会自动传给Qt需要在OSGQt的GLWidget中重写事件处理。我在项目中实现了一个自定义的事件过滤器把Qt的鼠标事件转换成OSG的GUIEventAdapter事件核心代码如下void MyGLWidget::mouseMoveEvent(QMouseEvent* event) { osgGA::GUIEventAdapter::ScopedLocalCoordEvent coord(_viewer-getEventQueue(), event-x(), event-y()); _viewer-getEventQueue()-mouseMotion(coord.getX(), coord.getY()); QWidget::mouseMoveEvent(event); }第二个是渲染和UI线程的冲突。当场景数据较大时如果操作过于频繁UI线程会被阻塞。我的解决方案是单独开一个数据加载线程把耗时操作通过信号槽传回UI线程执行。第三个是崩溃恢复。桌面端应用长时间运行图形驱动偶尔会出问题。我这里没有特别好的解决方案能分享的就是尽量降低OpenGL版本的硬件要求有些机器集显环境下用GL2的兼容性远好于强制使用GL3。这也是我不推荐一上来就在OSG里开启GL3的原因默认GL2也许性能上吃亏但对业务型桌面应用而言稳定压倒一切。6. 最终检查清单与投入使用建议整个环境搭建完成后我建议按这个清单做一遍检查确认所有组件工作正常OSG核心渲染正常可以加载cow.osg、glider.osg等示例模型OSGEarth可以加载三维地球并叠加影像层OSGQt窗口可以嵌入Qt Widgets鼠标事件能正常交互Debug和Release两种配置的库都编译完毕项目切换配置时链接不出错PATH环境变量中没有多个版本的库冲突编译好的插件在osgPlugins-3.6.5目录下运行目录或PATH能正确找到。如果你后续要开发正式项目还有两个建议第一个建议是使用增量编译策略。OSG和OSGEarth的独立模块可以通过设置CMake的BUILD_OSG_DEPRECATED_SERIALIZERS等选项来控制编译面积减少无谓的编译时间。第二个建议是建立一套自动化的环境配置脚本。用批处理或者CMake脚本把环境变量、目录复制、路径设置这些操作固化下来这样换机器或者新同事加入时十几分钟就能恢复整个编译环境不用再靠记忆去配。这套环境我前后搭了不下五遍最早踩坑记录密密麻麻现在写出来发现流程其实很清楚。关键就是三件事版本不要乱配、目录安排要有条理、遇到问题先怀疑路径。把这三点做到了整个搭建过程基本就不会卡住。
返回列表