十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Qt Creator + MSVC2017 + OSG 3.4 + OSGEarth 2.8 三维GIS环境搭建实战指南

Qt Creator + MSVC2017 + OSG 3.4 + OSGEarth 2.8 三维GIS环境搭建实战指南 简介面向Qt环境下开展OSG/OsgEarth三维数字地球开发的工程师这份资源提供了一个可直接落地的纯Qt工程框架。工程采用Qt 5.12与MSVC2017编译器对应OSG 3.4、OsgEarth 2.8版本组合解决了在Qt Creator中集成OsgEarth的常见配置与调用问题源码结构清晰适合作为三维地球应用开发的起始模板也可用于学习Qt与OSG/OsgEarth的混合渲染机制。包体为ZIP格式共113个文件约65MB涵盖dll运行库、qm翻译文件、cpp/h源码、pro工程配置、ui界面文件及可执行程序等解压后即可查看工程结构并尝试编译运行。已有1168人学习下载作者在描述中表示若运行不成功可提供远程协助对初次搭建环境的学习者较为友好。通过该项目可快速获得基于Qt的高帧率OsgEarth渲染框架实测在2060显卡下帧率可达150以上有助于后续在此基础上扩展业务功能和数字地球交互场景。 最近在折腾一个三维GIS项目需要在Qt Creator环境下把osgEarth跑起来选型定的是Qt 5.12 MSVC2017编译器 OSG 3.4 OSGEarth 2.8。这套组合网上零散资料不少但能从头到尾走通的文章不多尤其是在Qt Creator下用CMake组织工程、处理Debug/Release库切换、解决运行时崩溃这些环节坑比想象中多。这篇文章把我从零搭建到跑出第一个地球的完整过程、踩过的坑和思路梳理出来给正在做同样事的你一个参考。不管你之前用过OSG还是完全新手只要熟悉C和Qt基础按这篇文章的思路操作基本能在半天内把环境跑通。我尽量把每个为什么这么做都讲清楚避免你照着抄完却不知道怎么改。1. 为什么是这个组合Qt 5.12 MSVC2017 OSG 3.4 OSGEarth 2.8的选型逻辑1.1 版本搭配的合理性先说结论这套组合在Windows x64平台下是经过大量项目验证的稳定搭配。Qt 5.12是Qt官方最维护时间最长的LTS版本之一官方安装包直接提供msvc2017_64的预编译库不需要自己从源码构建Qt省掉一大半麻烦。MSVC2017对应Visual Studio 2017其C11/14标准支持完善和OSG 3.4、OSGEarth 2.8的代码完全兼容。OSG 3.4是目前市面上用得最广的稳定分支虽然OSG 3.6.5在2019年就发布了但很多第三方库、插件和教程仍以3.4为基准。OSGEarth 2.8是osgEarth加入地形引擎重构前的最后一个大版本其接口稳定性和文档完备程度都处在高峰而且这个版本对OSG 3.4的适配非常成熟。如果选更老的2.6缺少很多新特性选更新的3.x接口变化大踩坑成本高。1.2 我在几个方案之间的对比取舍在定这套方案前我也对比过另外两种组合一是MinGW Qt OSG二是Visual Studio 2019 Qt 5.15 OSG 3.6。第一种在Windows下用MinGW编译OSG和osgEarth会遇到很多细节问题比如第三方库curl、gdal在MinGW下的编译脚本不完善可能折腾一两天都过不去第二种组合本身没问题但如果你需要闪退定位、内存分析这些环节VS2019自带的工具链和Qt 5.15的适配虽然更好但很多公司项目历史代码还在用VS2017保持工具链统一反而省心。还有一点容易被忽略OSGEarth 2.8的CMake配置对MSVC2017的识别非常友好生成工程时基本不会出现编译错误。考虑到项目组其他人可能也要加入开发选这套通用性最好的组合能减少很多协作成本。注意如果你拿到的是别人编译好的OSG/OSGEarth库务必确认对方用的编译器和运行库是MD还是MT。MSVC2017下默认是动态CRT/MD如果你编成静态的后面和Qt耦合时会出各种奇怪的链接错误。2. 环境搭建依赖库获取与Qt Creator的MSVC Kit配置2.1 OSG和OSGEarth库的两种获取方式获取OSG 3.4和OSGEarth 2.8的Windows预编译库主流有两种方式直接下载第三方编译好的包或者自己用CMake源码编译。我个人建议如果纯粹是想把应用跑起来优先下载预编译包但版本号要严格对应。OSG 3.4.1和OSGEarth 2.8有社区维护的VC2017 x64版本解压后目录结构一般是D:\OSG\ include\ bin\ lib\ share\ D:\OSGEarth\ include\ bin\ lib\检查预编译包是否可用有个很简单的办法打开OSG的bin目录如果里面有osgviewer.exe直接命令行运行osgviewer cow.osg这个模型通常在share目录里能找到看到一头牛就说明OSG运行环境没问题。osgEarth相关工具同理。如果这步都过不了就别指望Qt工程里能跑起来。如果想自己编译参考以下CMake关键项OSG 3.4.1CMake里勾选BUILD_OSG_PLUGINS和BUILD_OSG_EXAMPLES把CMAKE_INSTALL_PREFIX设为你的安装目录生成后全选ALL_BUILD再INSTALL。OSGEarth 2.8需要提前装好curl、sqlite3、zlib等依赖库。CMake里把OSGEARTH_BUILD_SAMPLES可选打开方便学习但注意如果你不需要读在线瓦片地图可以关掉OSGEARTH_USE_CURL这能省去很多库依赖。2.2 Qt Creator Kit配置的关键点Qt 5.12安装时选择MSVC2017 64-bit组件带Qt Creator。安装完成后打开Qt Creator在工具 - 选项 - Kits里检查编译器是否识别到Microsoft Visual C Compiler 15.x即MSVC2017。如果Kits页面里Compiler一栏是空的说明没装VS2017或者Qt Creator没自动探测到需要手动添加cl.exe路径通常是C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\cl.exe很多人忽略的一步Debug和Release跑不起来往往不是代码问题而是Kit里Qt版本的qmake路径对应错了。确认Qt Version一栏选的是D:\Qt\Qt5.12.12\5.12.12\msvc2017_64\bin\qmake.exe而不是MinGW版的qmake。MSVC Kit配错qmake编译能过但运行时库版本一定是错的。提示Qt 5.12版本号后面的小版本建议选5.12.12这是5.12系列最后的补丁版修复了很多Windows平台的崩溃和GL问题。环境变量方面把以下几项加入系统PATH注意等等都要用D:\Qt\Qt5.12.12\5.12.12\msvc2017_64\bin D:\OSG\bin D:\OSGEarth\bin D:\OSG\share D:\OSGEarth\share最后两项不是必须的但如果存在相对引用的模型和纹理文件加入后查找资源会省很多事。环境变量修改后如果是在Qt Creator里启动程序需要重启Qt Creator才能生效否则启动时仍然加载不到dll。3. CMakeLists.txt怎么写链接OSG/OSGEarth库的完整模板3.1 核心CMake逻辑在Qt Creator里建工程推荐直接用CMake而不是qmake。原因很简单OSG和OSGEarth官方都是基于CMake构建的用CMake会有更成熟的查找机制而且从Qt Creator 4.8开始对CMake工程的支持已经很完善调试体验和qmake工程没区别。我这里给出一个可以直接用的CMakeLists.txt模板cmake_minimum_required(VERSION 3.5) project(OsgEarthQtDemo) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 设置Qt路径按你的安装位置改 set(CMAKE_PREFIX_PATH D:/Qt/Qt5.12.12/5.12.12/msvc2017_64) # 手动指定OSG和OSGEarth路径 set(OSG_PATH D:/OSG) set(OSGEARTH_PATH D:/OSGEarth) find_package(Qt5 COMPONENTS Widgets OpenGL REQUIRED) find_package(OpenSceneGraph REQUIRED COMPONENTS osgDB osgUtil osgViewer osgGA osg) find_package(osgEarth REQUIRED) include_directories( ${OSG_PATH}/include ${OSGEARTH_PATH}/include ${OpenSceneGraph_INCLUDE_DIRS} ${OSGEARTH_INCLUDE_DIRS} ) link_directories( ${OSG_PATH}/lib ${OSGEARTH_PATH}/lib ) add_executable(${PROJECT_NAME} WIN32 main.cpp ) target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::OpenGL ${OpenSceneGraph_LIBRARIES} ${OSGEARTH_LIBRARIES} )如果你的预编译库没有提供CMake配置文件即没有osgEarthConfig.cmakefind_package会失败此时用传统的include_directories link_directories target_link_libraries方式会更稳妥include_directories( ${OSG_PATH}/include ${OSGEARTH_PATH}/include ) link_directories( ${OSG_PATH}/lib ${OSGEARTH_PATH}/lib ) target_link_libraries(${PROJECT_NAME} osgViewerd osgDBd osgUtild osgGAd osgd osgEarthd osgEarthUtild )3.2 Debug和Release库自动选择上面模板里暴露了两个问题一是Qt的CMake会自动区分debug和release但OSG/osgEarth的链接写法要注意库名。预编译包的lib目录下通常有带d后缀的debug版如osgViewerd.lib和不带d的release版如osgViewer.lib。如果懒得在CMake里写复杂判断最简单的做法是Debug和Release都链接不带d的release库。虽然debug下用了release库但实际测试中OSG系库的debug/release混用崩溃率并不高只是调试时看不到内部变量。但如果追求严谨可以这样写set(OSG_LIBRARIES osgViewer osgDB osgUtil osgGA osg ) set(OSGEARTH_LIBRARIES osgEarth osgEarthUtil ) target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::OpenGL ${OSG_LIBRARIES} ${OSGEARTH_LIBRARIES} )然后在Kit构建步骤里把Debug构建的链接选项加上d后缀。不过大部分项目都没这么讲究毕竟预编译包的lib文件往往只有一种版本直接用release链接即可。还有个小技巧如果你想在CMake里控制运行时dll自动拷贝可以用add_custom_command配合$TARGET_FILE_DIR:${PROJECT_NAME}把OSG、osgEarth的bin目录里的部分dll拷到输出目录。实际上更推荐直接把D:\OSG\bin和D:\OSGEarth\bin写进系统PATH一劳永逸但如果发布给客户时还得用windeployqt dll拷贝。4. 踩坑实录从能编译到能跑起来的完整排查链路这一部分是本文的精华。我在跑通过程中遇到了三大必现问题每个都卡了半小时以上按排查链路写出来你可以对照定位。4.1 windows no qt platform plugin could be initialized 问题这是Qt程序迁移到新环境后最经典的问题。现象是程序编译通过双击启动时报错This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.排查链路如下第一步检查编译用的Qt库目录和运行时找的Qt路径是否一致。Qt Creator里点左侧项目按钮看构建环境里的PATH是否包含了D:\Qt\Qt5.12.12\5.12.12\msvc2017_64\bin。如果CtrlR运行时路径没问题但双击exe报错多半是系统PATH里没配或者配了多个版本的QtWindows加载dll时优先加载了其他目录下的Qt5Core.dll。第二步检查platforms插件目录是否存在。正常情况bin目录下应该能找到一个platforms文件夹里面有qwindows.dll。如果只有Qt的MinGW版本或者拷贝exe发布时忘了带上platforms文件夹就会出现上面的错误。第三步如果上面都对还报错用依赖分析工具或直接用Process Explorer检查exe实际加载的Qt5Core.dll和qwindows.dll路径。我见过有人电脑上装了Anaconda其目录下的Qt dll被优先加载导致版本错乱。针对Qt Creator内部运行时的这种问题有一个很稳妥的兜底办法在main.cpp的最前面加一段#include QApplication #include QLibraryInfo int main(int argc, char *argv[]) { qputenv(QT_QPA_PLATFORM_PLUGIN_PATH, D:/Qt/Qt5.12.12/5.12.12/msvc2017_64/plugins/platforms); QApplication app(argc, argv); // ... }这个环境变量让Qt强制从指定目录加载platform插件可以绕开因为PATH顺序或者注册表导致的路径混乱。4.2 osgEarth初始化时的dll缺失问题OSG和osgEarth用的是插件式架构就算你链接了正确的lib运行时如果找不到插件dll会出现这样的错误Warning: Could not find plugin to read objects from file xxx.earth先说插件的查找机制。OSG运行时通过OSG_LIBRARY_PATH环境变量和相对路径去搜索插件目录插件文件在OSG的bin目录下名字形如osgdb_earth.dll、osgdb_osgearth.dll。如果在Qt Creator里启动时报插件找不到第一反应应该是环境变量没配好。排查链条确认osgdb_osgearth.dll确实存在于D:\OSGEarth\bin下。确认环境变量OSG_LIBRARY_PATH指向了D:\OSG\bin和D:\OSGEarth\bin。如果还不行直接在代码里临时指定#include osgDB/Registry #include osgDB/FileUtils #include osgEarth/Registry osgDB::FilePathList pathList osgDB::Registry::instance()-getLibraryFilePathList(); pathList.push_back(D:/OSG/bin); pathList.push_back(D:/OSGEarth/bin);还有一种隐蔽情况同时装了OSG 3.6和OSG 3.4两个版本的osgdb_osgearth.dll混在一起插件加载顺序导致版本错配。这种问题很难一眼看出来因为报错信息不明确。我当时是通过挨个dll比对版本号才定位的你可以用Process Explorer的查看加载的dll功能快速排查。4.3 运行时崩溃OpenGL上下文和帧缓冲问题第三个高频坑是在Windows上跑osgEarth时启动后黑屏或者直接崩溃错误堆栈往往指向osg::GraphicsContext或osgViewer::Viewer::realize。本质原因是Qt的OpenGL窗口和OSG的OpenGL上下文创建方式有冲突。我采用的稳定方案是让OSG自己创建图形窗口Qt只负责加载事件循环。osgEarth官方示例里基本也是这个模式代码如下osgViewer::Viewer viewer; viewer.setUpViewInWindow(100, 100, 800, 600);如果你想嵌入到Qt的QWidget窗口里就需要用osgViewer::GraphicsWindowEmbedded并手动处理paintEvent。这个方案能用但要注意在Qt 5.12开启Qt::AA_ShareOpenGLContexts属性的情况下OSG的上下文策略要设置为osg::DisplaySettings::setMaxNumberOfGraphicsContexts(1)否则多个上下文争抢会随机崩溃。如果只是先验证功能不建议一上来就搞嵌入先把独立窗口跑通再做Qt集成。这样可以把问题隔离避免把OSG本身的初始化问题和Qt嵌入逻辑问题混在一起排查。4.4 MSVC2017和Qt Creator的字符编码问题这个坑隐蔽但非常常见。Qt Creator默认源文件编码是UTF-8但Windows下MSVC编译器默认对没有BOM的UTF-8会当成GBK处理。如果你在main.cpp里写了中文字符串比如地形路径D:/地形数据/xxx.earth编译时会有警告或运行时路径不对。我的处理方式是要么在Qt Creator的编辑 - 编码里把文件显式改为UTF-8 with BOM要么不用中文路径所有项目文件路径全用英文和数字。三维GIS的模型文件、地形文件动辄几个G文件名里的中文字符很容易在Windows API和OSG的文件解析层之间出问题直接用英文名最省心。5. 跑通第一个demo从空窗口到渲染出地球5.1 最小可运行代码的完整逻辑环境配好、坑清掉后我建议先写一个最精简的demo只做一件事读一个earth文件并在OSG窗口里显示出来。这个demo能跑起来你的工程链路就通了。.earth文件是osgEarth的地图描述文件内容类似map namesimple version2 options profileglobal-geodetic/profile /options image layertrue nameBlue Marble urlD:/OSGEarth/data/blue_marble.jpg/url /image /map对应的main.cpp#include osgViewer/Viewer #include osgEarth/MapNode #include osgEarth/Map #include osgEarth/Registry #include osgDB/ReadFile #include QtWidgets/QApplication int main(int argc, char** argv) { QApplication app(argc, argv); osgEarth::Registry::initialize(); osg::ref_ptrosgViewer::Viewer viewer new osgViewer::Viewer; viewer-setUpViewInWindow(100, 100, 1024, 768); osg::ref_ptrosg::Node node osgDB::readNodeFile(D:/OSGEarth/data/map.earth); if (!node.valid()) { return -1; } viewer-setSceneData(node.get()); return viewer-run(); }这一段代码看着简单但有几个细节要注意。osgEarth::Registry::initialize()必须调用否则很多内部注册器没准备好。setUpViewInWindow在Windows下会创建一个原生窗口和QApplication的消息循环能共存前提是QApplication先创建。如果想让OSG的渲染循环和Qt的事件循环都跑起来简单做法是在viewer.run()之前开一个线程跑Qt事件或者反过来但最省事的是直接把OSG的帧循环放在主线程Qt事件循环如果暂时用不到可以不用app.exec()。5.2 数据文件和earth文件的组织方式工程跑通后你大概率会去加载真实的地形、影像数据而不是单张图片。osgEarth最灵活的地方在于它的URL可以是本地文件也可以是HTTP链接。为了快速验证我建议放一小块带地理参考的高程数据在旁边比如GeoTIFF格式的DEM然后在.earth文件里加一个高程层elevation layertrue nameDEM urlD:/OSGEarth/data/dem.tif/url /elevation如果预编译包里没有测试数据用GDAL工具或OSGEarth自带的osgearth_conv工具把一块普通的分区灰度图转成带坐标的GeoTIFF也行。这一步是验证osgEarth地形引擎能不能正常生成地形网格的关键因为很多编译问题在读一张图片显示看不出来一旦启用高程层就会触发大量代码路径。还有一点如果加载后地球是黑的或者纹理花屏大概率是显卡驱动或OpenGL版本的问题。OSG 3.4默认用的OpenGL 2.1路径理论上Windows 10的显卡驱动都支持。但如果你在虚拟机或者远程桌面里跑必须要检查是否启用了显卡的硬件加速否则性能惨不忍睹甚至直接崩溃。6. 性能调优和发布打包的个人经验6.1 运行性能从卡到顺的调整osgEarth工程跑起来后下一步就是让它流畅。我在这套组合里做了几处性能调整效果很明显。第一在构造osgEarth Map时关掉不必要的显示特性。很多示例默认开了大气散射、阴影和光照这些效果会大幅拖慢帧率。如果你的业务只是看地形和标注建map这样写osgEarth::MapOptions mapOpt; mapOpt.enableLighting(false); osg::ref_ptrosgEarth::Map map new osgEarth::Map(mapOpt);第二设置Viewer的线程模型。Windows上多线程渲染虽然利用率高但在只有普通独显的机器上反而容易卡顿。简单做法是viewer-setThreadingModel(osgViewer::Viewer::SingleThreaded);牺牲一点帧率上限换来的是事件处理和渲染的稳定性。这个取舍在很多行业应用里是值得的尤其是后续还要叠加Qt控件和交互逻辑。第三文件缓存。如果你的高程和影像数据比较稳定打开osgEarth::CacheOptions配置一个本地文件缓存路径二次启动时加载速度能提升好几倍。一定要放在固态硬盘上效果差距非常大。6.2 发布给其他人时怎么用windeployqt和dll拷贝工程跑通后总有那么一天要交给别人用。Qt程序发布的核心就是你的exe加上Qt的dll和插件加上OSG和osgEarth的dll和插件。我一般是手动写一个批处理windeployqt --release --no-system-d3d-compiler --no-opengl-sw --no-translations OsgEarthQtDemo.exe xcopy /Y D:\OSG\bin\*.dll . xcopy /Y D:\OSGEarth\bin\*.dll . xcopy /Y /E /I D:\OSG\bin\osgPlugins-3.4.1 plugins xcopy /Y /E /I D:\OSGEarth\bin\osgPlugins-3.4.1 plugins但这里有个很关键的坑windeployqt只会拷贝Qt本身的dll和插件不会管你自己链接的第三方库。所以OSG和osgEarth的压缩包迭代后插件目录里的dll会越积越多一些插件依赖的外部dll如curl.dll、libsqlite.dll如果系统里没有还需要额外拷贝。稳妥的做法是把OSG/OSGEarth的bin目录整体拷贝一份到发布目录再把系统没有的依赖也放进去。最后发布前一定在干净的虚拟机或无Qt环境的机器上测试一遍。我在自己电脑上跑得好好的拷到同事电脑上就报找不到qt plugin十次里有八次是PATH环境变量或platforms插件缺失。提前用干净环境验证能帮你在交付现场省下大量时间。回头再看整个搭建过程最大的感悟就是版本匹配决定下限环境配置决定上限。Qt Creator MSVC2017 OSG 3.4 OSGEarth 2.8这套组合只要版本对齐环境变量配好CMake工程结构理清楚剩下的都是体力活。我后来把同样的逻辑移植到Qt 5.15 VS2019 OSG 3.6 OSGEarth 3.0上也异常顺利原理都是通用的。希望这篇分享能帮你少走一些弯路尽快看到那个旋转的地球出现在自己窗口里。本文还有配套的精品资源点击获取
返回列表