Qt Creator + MSVC2017 + OSG 3.4 + OSGEarth 2.8 三维GIS环境搭建实战指南
2026/9/8 18:47:34 网站建设 项目流程

简介:面向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,其C++11/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.1:CMake里勾选BUILD_OSG_PLUGINSBUILD_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.cmake),find_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。如果Ctrl+R运行时路径没问题,但双击exe报错,多半是系统PATH里没配或者配了多个版本的Qt,Windows加载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.dllosgdb_osgearth.dll。如果在Qt Creator里启动时报插件找不到,第一反应应该是环境变量没配好。

排查链条:

  1. 确认osgdb_osgearth.dll确实存在于D:\OSGEarth\bin下。
  2. 确认环境变量OSG_LIBRARY_PATH指向了D:\OSG\bin和D:\OSGEarth\bin。
  3. 如果还不行,直接在代码里临时指定:
#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::GraphicsContextosgViewer::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 name="simple" version="2"> <options> <profile>global-geodetic</profile> </options> <image layer="true" name="Blue Marble"> <url>D:/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_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer; viewer->setUpViewInWindow(100, 100, 1024, 768); osg::ref_ptr<osg::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 layer="true" name="DEM"> <url>D:/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_ptr<osgEarth::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上,也异常顺利,原理都是通用的。希望这篇分享能帮你少走一些弯路,尽快看到那个旋转的地球出现在自己窗口里。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询