资讯动态

Windows三维GIS环境:MSVC2017+OSG+osgEarth+Qt

发布时间:2026/10/13 8:59:49 来源:尧图企业网站定制
简介面向 MSVC2017 环境编译的 OSG 3.6.3 与 osgEarth 2.10 开发库整合包同时附带一个基于 Qt 5.14 的简单示例工程适用于三维地形渲染、虚拟仿真、GIS 可视化等场景的开发者。整合后的库文件可直接用于搭建 QtCreator 下的 OSG/osgEarth 开发环境省去自行源码编译、依赖配置的繁琐环节。压缩包约 530MB共 2000 个文件主体为 hpp/h 头文件、dll 动态库与 lib 导入库另含 exe 演示工具、earth 场景描述文件、osg 模型文件、着色器与纹理图片等已覆盖库引用、运行依赖、示例场景所需的典型文件类型。示例 Qt 工程展示了 QtWidget 与三维地球窗口的集成方式可帮助理解图形窗口适配与渲染线程的基本写法。已有 532 人学习下载适合需要快速搭建 OSGosgEarth 开发环境并上手三维可视化开发的中高级读者。1. MSVC2017OSG3.6.3osgEarth2.10Qt5.14Windows 三维 GIS 开发可以直接落地的环境快照拿到这个打包好的 msvc2017osg3.63osgearth2.10qt5.14.7z第一反应是省事。Windows 上做三维 GIS 开发最折磨人的不是业务代码而是把 OSG、osgEarth 和 Qt 三个库的版本、编译器、插件路径对齐。这个压缩包把三套库用同一套 MSVC2017 工具链编好解压后在 Qt Creator 里配置一下就能用省去自己编译 OSG 和 osgEarth 的半天时间。适合两类人一类是要在 Qt 界面里快速嵌入三维地图的从业者另一类是刚接手 osgEarth 2.x 项目、需要补环境的开发新手。它解决的核心问题是 ABI 一致性和插件配套这也是后续所有排错的主线。2. 版本选型逻辑为什么这套组合要绑在 MSVC2017 上2.1 工具链决定 ABIABI 决定能不能混用Windows 上做 C 三维开发最忌讳把不同编译器编出来的库混在一起。MSVC 的 C ABI 和 MinGW 的 ABI 完全不同前者基于 MSVC CRT后者依赖 libstdc。就算你把头文件都指对了链接阶段也可能没报错但运行时就会因为堆管理方式不同而崩溃。这个包把 MSVC2017 作为统一工具链OSG、osgEarth、Qt 三者的库都是同一套 CRT 编出来的这是“能用”的底线。另一个现实问题是 Qt 官方二进制发行版和工具链的对应关系。Qt 5.14 时代官方同时提供 msvc2017_64 和 msvc2019_64 两套预编译包但 osgEarth 2.10 编译时大多对齐的是 VS2017 工具链。如果你拿 msvc2019 的 Qt 去链 MSVC2017 编出来的 osgEarth大多数情况下能过可一旦遇到跨模块传递 STL 容器或 QString就开始出怪问题。所以这个包把版本钉在 MSVC2017 上不是保守是自洽。提示解压完先检查机器上有没有 VS2017 对应的 VC 运行库vcruntime140.dll、msvcp140.dll 那一套。缺了这个任何库的 DLL 起来第一步就会失败。2.2 OSG 3.6.3为什么是这个版本而不是 3.4 或 3.7OSG 的 3.6 系列相比 3.4 变化不小支持 OpenGL 核心模式、多重采样、更严格的状态管理。3.6.3 是这一代里修 bug 比较充分的版本社区里很多 osgEarth 2.x 项目默认就搭在 3.6.x 上。3.7 之后的设计方向更偏向新渲染管线很多老项目的插件接口开始不对齐。只是做三维 GIS 场景展示的话3.6.3 的稳定性比追新更划算。还有一个实际因素osgEarth 2.10 在编译时对 OSG 版本有隐式依赖。你去看 osgEarth 2.10 的源码会发现它的 CMake 默认查找的 OSG 库名和接口范围是以 3.6 为基准的。虽然源码也允许在更高版本 OSG 上编译但部分回调函数签名、StateSet 行为有差异新手排错时很难分清是 osgEarth 的坑还是 OSG 版本差异的坑。用预编译好的 3.6.3 组合至少能把变量先固定住。2.3 osgEarth 2.10 与 3.x 的分水岭osgEarth 2.10 是 2.x 时代的成熟版本它把地图数据抽象成 Map 和 MapNode图层用 ImageLayer、ElevationLayer 表示模型非常直白。2.10 在加载本地 TMS、WMS 或离线影像时驱动逻辑稳定、公开资料多。二次开发时主要跟 osgEarth::Util 里的类打交道学习曲线平缓。3.x 系列把底层地形引擎彻底重写了渲染方式、线程模型都不同。新功能确实多比如更精细的剔除、更现代的地形纹理管理但接口变动大编译和迁移成本高。如果你的项目目标是离线三维 GIS、智慧城市这类并没有用到 3.x 新增特性的必要留在 2.10 是合理的。反过来说如果就是奔着新架构去的那这个包不适合你别勉强。2.4 包里的物理内容头文件、库、插件和工具这类 7z 包解压后通常能看到三个根目录osg-3.6.3、osgearth-2.10、Qt/5.14.x。每个目录下是 include、lib、bin 三件套。bin 里除了库本身的 DLL还有 OSG 的插件目录 osgPlugins-3.6.3里面躺着 osgdb_osg、osgdb_tiff、osgdb_qt 等几十个格式插件。osgEarth 的 bin 目录下则是 osgdb_earth、osgdb_osgearth_tms、osgdb_osgearth_image 这类插件负责把 .earth 文件和影像、高程驱动接进 OSG 的节点读取体系。注意插件目录名必须和 OSG 版本严格一致。osgPlugins-3.6.3 里混入 3.4 的插件osgDB 会直接拒绝加载因为插件接口对不上。3. 解压与套件配置把 7z 包变成可用的构建环境3.1 目录规划与解压检查先规划一个纯 ASCII 且没有空格的路径比如 D:\dev\geo3d。不要解压到带中文或空格的路径Windows 下这一条能帮你避开一半的玄学问题。解压完成后核对三层目录结构osg-3.6.3、osgearth-2.10、Qt/5.14.x。重点检查 osg-3.6.3\bin 下有没有 osgPlugins-3.6.3 这个目录。我见过一些分包方式会把插件单独拆出来结果用户只拷贝了 DLL插件目录漏了运行时 osgDB 找不到任何格式读取器。最好解压完先用 dir /s /b 把目录树导出一份存着后面排错经常要对照。3.2 环境变量注入环境变量只加三个OSG_DIR、OSGEARTH_DIR 和 PATH。在管理员命令行里执行setx OSG_DIR D:\dev\geo3d\osg-3.6.3 setx OSGEARTH_DIR D:\dev\geo3d\osgearth-2.10 setx PATH D:\dev\geo3d\osg-3.6.3\bin;D:\dev\geo3d\osgearth-2.10\bin;D:\dev\geo3d\Qt\5.14.x\msvc2017_64\bin;%PATH%这段的作用是把三个 bin 目录塞进 PATH。OSG 和 osgEarth 的 DLL 相互依赖运行时是在 PATH 里找对方的Qt 的 DLL 同样在 PATH 里找。setx 写入的是用户级环境变量设置完必须重开终端才生效。我一般不建议把 Qt 的 bin 永久塞进系统 PATH因为机器上有多个 Qt 版本时会互相干扰。更稳妥的做法是只在 Qt Creator 的构建环境里给当前项目注入这些路径不影响全局。下面套件配置里就是这么干的。3.3 Qt Creator 套件绑定MSVC2017 工具链与 qmake 的选择打开 Qt Creator 的 Tools Options Kits按顺序做四步在 Qt Versions 选项卡点击 Add定位到 D:\dev\geo3d\Qt\5.14.x\msvc2017_64\bin\qmake.exe让 Creator 识别出这个 Qt 版本。切到 Compilers 选项卡确认有 Microsoft Visual C Compiler 15.xx86_64。没有的话手动添加编译器路径指向 VS2017 安装目录下的 cl.exe通常需要配合 vcvars64.bat 使用。在 Kits 选项卡新建套件编译器选 MSVC2017 64 位Qt version 选刚添加的 5.14.x 版本CMake generator 选 Ninja 或 Visual Studio 15 2017 Win64。调试器可以先选 none不影响编译和运行。关键点是套件的 ABI 标签要和 Qt 的目录名一致MSVC2017 套件配 msvc2017_64 的 Qt。如果 Creator 里残留了别的 Qt 版本比如之前装过 5.15.2/msvc2019_64一定要在 Kits 里手动指定当前项目使用哪个 Qt否则 qmake 会把错误的头文件路径写进构建系统。这一步做错就会出现后面避坑笔记里的 dependent error。3.4 第一个最小验证程序新建一个纯 C 项目关掉 Qt 模块依赖先验证 OSG 本身能起来。main.cpp 写#include osgViewer/Viewer #include osgDB/ReadFile #include osg/Geode int main(int argc, char** argv) { osgViewer::Viewer viewer; // 优先加载包内自带的模型没有就创建一个空场景兜底 osg::ref_ptrosg::Node root osgDB::readNodeFile(D:/dev/geo3d/osg-3.6.3/data/cow.osg); if (!root.valid()) { root new osg::Geode(); } viewer.setSceneData(root.get()); viewer.setUpViewInWindow(100, 100, 1280, 800); return viewer.run(); }readNodeFile 如果失败会返回空指针这里用空 Geode 兜底保证窗口一定能弹出来。setUpViewInWindow 会在 Windows 上自动创建原生窗口如果这步连窗口都不弹基本可以断定是 DLL 缺依赖或显卡上下文初始化失败先回到 3.2 检查 PATH。4. 工程对接CMake 模板、库清单与运行时 DLL 部署4.1 直接吃这个包的 CMakeLists 模板预编译包的价值在于工程能直接引用。我习惯用 CMake模板如下cmake_minimum_required(VERSION 3.16) project(Geo3dDemo) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_PREFIX_PATH D:/dev/geo3d/osg-3.6.3 D:/dev/geo3d/osgearth-2.10 D:/dev/geo3d/Qt/5.14.x/msvc2017_64 ) find_package(OpenSceneGraph REQUIRED COMPONENTS osgDB osgGA osgUtil osgViewer osgText) find_package(Qt5 5.14 REQUIRED COMPONENTS Widgets Gui Core) find_package(osgEarth REQUIRED) add_executable(Geo3dDemo main.cpp) target_link_libraries(Geo3dDemo PRIVATE ${OPENSCENEGRAPH_LIBRARIES} ${OSGEARTH_LIBRARIES} Qt5::Widgets )CMAKE_PREFIX_PATH 同时指向三套库的根目录find_package 就能在这三个前缀下搜索各自的配置文件。OSG 的 CMake 模块比较规范OpenSceneGraph 这个 find_package 一般没问题。Qt5 的模块也稳定。osgEarth 2.10 的 CMake 配置文件不如 OSG 规范find_package(osgEarth) 在某些发行版里根本找不到。常见做法是放弃 find_package直接手工指定include_directories(D:/dev/geo3d/osgearth-2.10/include) link_directories(D:/dev/geo3d/osgearth-2.10/lib) target_link_libraries(Geo3dDemo PRIVATE osgEarth osgEarthUtil osgEarthFeatures osgEarthSymbology )手工指定时要注意库的依赖顺序osgEarth 依赖 osgEarthUtilosgEarthUtil 又依赖 osgEarthFeatures链接器是从右往左解析符号的顺序反了会出现 unresolved external symbol。4.2 参数与库清单这套环境里常用的库名我列一张表对照着看模块库名用途OSG 核心osg、osgUtil场景图与算法基础OSG 渲染osgViewer、osgGA窗口管理和交互控制OSG 读写osgDB节点/纹理/模型加载OSG 文本osgText屏幕文字osgEarth 主库osgEarthMap、图层、驱动核心osgEarth 工具osgEarthUtil操纵器、测量、几何工具osgEarth 要素osgEarthFeatures、osgEarthSymbology矢量和符号渲染Qt 基础Qt5::Core、Qt5::Gui、Qt5::Widgets界面框架lib 目录下的导入库命名有规律。MSVC 环境下osg 系列是 osg.lib、osgViewer.lib 这种osgEarth 是 osgEarth.lib、osgEarthUtil.lib。Debug 版本会带 d 后缀比如 osgd.lib、osgEarthd.lib。但我强烈建议只用 Release 库原因在避坑笔记 5.3 里说。4.3 运行时 DLL 部署编译通过只是第一步。把程序拷到别的机器或者在本机换目录运行DLL 找不到是最常见的翻车点。我一般用这种批处理把依赖打到一个 deploy 目录set SRCD:\dev\geo3d set DSTD:\dev\demo\deploy if not exist %DST% mkdir %DST% REM OSG 主 DLL copy /Y %SRC%\osg-3.6.3\bin\*.dll %DST%\ REM osgEarth 及其依赖含 GDAL、GEOS、libcurl 等 copy /Y %SRC%\osgearth-2.10\bin\*.dll %DST%\ REM Qt5 核心 copy /Y %SRC%\Qt\5.14.x\msvc2017_64\bin\Qt5Core.dll %DST%\ copy /Y %SRC%\Qt\5.14.x\msvc2017_64\bin\Qt5Gui.dll %DST%\ copy /Y %SRC%\Qt\5.14.x\msvc2017_64\bin\Qt5Widgets.dll %DST%\ REM OSG 插件整目录必须和版本号对应 xcopy /E /I /Y %SRC%\osg-3.6.3\bin\osgPlugins-3.6.3 %DST%\osgPlugins-3.6.3OSG 的插件目录必须和 osgDB 期望的版本号一致。程序运行时 osgDB 会去 PATH 和程序目录下找 osgPlugins-3.6.3找不到就报 Warning: Cannot find plugin to read objects from file。osgEarth 的插件 osgdb_earth.dll、osgdb_osgearth_tms.dll 在 osgearth 的 bin 里要一并复制。提示如果程序仍然报 Qt 平台插件找不到错误通常是“could not find or load the Qt platform plugin windows”把 D:\dev\geo3d\Qt\5.14.x\msvc2017_64\plugins\platforms 整个目录拷到程序目录程序会在 platforms 子目录下找 qwindows.dll。4.4 Qt 窗口嵌入 OSG 场景的两种写法把 OSG 场景嵌进 Qt 窗口方案取决于包里有没有 osgQt 这个桥接库。有就用 GraphicsWindowQt#include QApplication #include QMainWindow #include osgViewer/Viewer #include osgQt/GraphicsWindowQt #include osgDB/ReadFile #include osgEarth/EarthManipulator int main(int argc, char** argv) { QApplication app(argc, argv); osgViewer::Viewer viewer; osg::ref_ptrosgQt::GraphicsWindowQt gw new osgQt::GraphicsWindowQt(0, 0, 1024, 768); viewer.getCamera()-setGraphicsContext(gw.get()); viewer.getCamera()-setViewport(0, 0, 1024, 768); osg::ref_ptrosg::Node node osgDB::readNodeFile(D:/data/local.earth); viewer.setSceneData(node.get()); viewer.setCameraManipulator(new osgEarth::Util::EarthManipulator()); QMainWindow win; win.setCentralWidget(gw-getGLWidget()); win.resize(1024, 768); win.show(); return app.exec(); }GraphicsWindowQt 把 GL 上下文封装成 Qt 窗口控件viewer 的相机直接挂在这个控件上事件循环由 Qt 接管。这种写法的好处是鼠标键盘事件天然融合缺点是 osgQt 本身要跟着这套工具链编译包里不一定带。如果包内没有 osgQt退一步用 QTimer 驱动 viewer.frame()QTimer timer; QObject::connect(timer, QTimer::timeout, [viewer]() { viewer.frame(); }); timer.start(16);这种轮询方式的坑在于16ms 固定刷新会拖累界面线程场景复杂时性能下降明显。我一般会把 timer 间隔改成 30ms并设置 viewer.setRunMaxFrameRate(30)避免 CPU 空转。两种方式选哪种取决于包内是否预制了 osgQt 的导入库没有就别硬上轮询法足够应付大多数展示型项目。5. 避坑笔记Qt 版本串扰、Debug/Release 混用与插件加载失败5.1 dependent error 指向不存在的 Qt 路径现象在 Qt Creator 里编译项目报错信息是:-1: error: dependent ..\..\..\..\..\..\qt\5.15.2\msvc2019_64\include\qtwidgets/qtwidgetsglobal.h does not exist.而且这个路径在你的工程里根本不存在。原因这是 Qt Creator 的构建系统残影。项目之前在某台机器上用 Qt 5.15.2/msvc2019_64 构建过.pro.user 文件或 CMakeCache.txt 里记住了旧的头文件路径。换到这台机器后qmake 虽然指向了 5.14但旧缓存文件还在构建系统把两套路径混在一起了。解决第一步删除构建目录在 Qt Creator 里清掉 shadow build 目录。第二步删除项目根目录下的 .pro.user 文件如果是 CMake 工程删掉 CMakeCache.txt 和 CMakeFiles 目录。第三步重新在 Kits 里确认 Qt version 指向 msvc2017_64 的 qmake然后重新构建。这套组合拳能把绝大多数 dependent error 清掉。5.2 osgEarth 加载 .earth 文件报找不到插件现象程序编译通过运行后窗口是黑的控制台打印Warning: Cannot find plugin to read objects from file D:/data/local.earth或者加载影像层时瓦片始终不出来日志里有 osgdb_qt 相关提示。原因osgDB 是插件式架构.earth 文件由 osgdb_earth.dll 读取TMS 影像由 osgdb_osgearth_tms.dll 读取这些插件 DLL 没在程序目录、也没在 PATH 里。另一种可能是 Qt 图像插件缺失osgEarth 2.10 通过 osgdb_qt.dll 读 Qt 支持的图片格式这个插件依赖 Qt 的 qwindows.dll。解决在系统环境变量里强制指定插件路径set OSG_LIBRARY_PATHD:\dev\geo3d\osg-3.6.3\bin set QT_QPA_PLATFORM_PLUGIN_PATHD:\dev\geo3d\Qt\5.14.x\msvc2017_64\plugins\platforms set PATHD:\dev\geo3d\osg-3.6.3\bin;D:\dev\geo3d\osgearth-2.10\bin;%PATH%设置后重开程序看 osgEarth 的加载日志确认 osgdb_earth.dll 被真正加载。别迷信程序目录里已经有部分 DLL插件目录和主 DLL 目录经常是分离的。5.3 Debug/Release 混用导致运行期崩溃现象用 Debug 配置构建 Qt 项目启动后几秒内崩溃调用栈随机出现在 QString 析构、osg::ref_ptr 释放或 std::vector 扩容的位置。有时报错是HEAP: Free Heap block ... modified at ... after it was freed。原因预编译包是 Release 版CRT 用的是 release 运行时。Debug 版项目链接了 Release 库两套 CRT 的堆管理机制不同跨模块分配和释放内存必然出问题。这是 MSVC 平台的老规矩不是 OSG 或 osgEarth 的 bug。解决项目构建配置强制用 Release。CMake 工程在配置时写明set(CMAKE_BUILD_TYPE Release)Qt Creator 里把构建步骤中的 Build configuration 选成 Release。如果确实需要 Debug 版调试唯一的正路是自己用同一套 MSVC2017 工具链把 OSG、osgEarth 全部重新编译成 Debug 库没有捷径。5.4 中文路径导致 TMS 瓦片黑屏现象TMS 影像数据放在D:\项目数据\某地\这种中文路径下osgEarth 启动不报错但场景里地形没有纹理或者影像层加载进度一直为零。原因Windows 文件系统底层是 UTF-16而 OSG 和 osgEarth 的窄字符串路径处理依赖本地代码页。MSVC2017 下程序把路径当作 GBK 处理但 TMS 瓦片文件名在驱动内部被解析成 UTF-8两者对不上文件打开失败而且这种失败往往只留一条极简的 debug 日志看起来无声无息。解决最省心的方案是数据路径和工程路径全部用 ASCII。公司内部如果强制中文目录临时方案是把瓦片打包成 zip用 osgEarth 的 zip driver 读取绕开文件系统路径解析。另一个备用方案是把路径统一转成 file:// URL 格式再传给驱动但实测仍有版本兼容问题不如直接改路径来得干净。5.5 本地 TMS 与地图 Profile 不匹配现象添加本地 TMS 影像和高程后场景出现大范围偏移或者瓦片加载后出现缝隙控制台报 profile 相关错误。原因osgEarth 2.10 的 Map 有一个全局 profile所有图层必须落在同一个 profile 里。如果本地 TMS 瓦片是 EPSG:3857 即球形墨卡托生成但 .earth 文件里默认用的是 global-geodetic两者坐标基准不一致瓦片自然对不上。解决在 .earth 文件的 options 里显式声明 profileoptions profilespherical-mercator/profile /options如果数据源是经纬度切片就用 global-geodetic。拿不准的时候打开 TMS 目录下的 tileset.xml 或 tilemapresource.xml里边的 proj 节点会写明坐标系统照着填就行。这事不解决后面所有排错都是白费功夫。6. 进阶验证用本地影像与高程数据跑通离线 osgEarth 场景6.1 用命令行工具快速验证环境环境配好之后别急着写业务代码先用 osgEarth 自带的 osgearth_viewer 命令行工具验证整条链路。这个工具是 osgEarth 打包出来的独立可执行程序直接读取 .earth 文件并渲染场景osgearth_viewer D:/data/local.earth --lat 30.5 --lon 104.04 --elev 500参数 --lat、--lon 是初始视角的经纬度--elev 是视点海拔。如果这条命令能弹出三维窗口并显示地形说明 OSG、osgEarth、Qt 插件、显卡上下文全部正常。如果失败按上一章的排查顺序逐项检查不要急着写代码。6.2 本地影像与高程的 .earth 配置离线项目最稳的做法是把瓦片数据下载到本地再用 .earth 文件组织起来。一套典型配置是这样的map namelocal typegeocentric version2 options profilespherical-mercator/profile cache typefilesystem pathD:/data/cache/path /cache /options image nameimg drivertms urlD:/data/landsat_tms/url /image elevation namedem drivertms urlD:/data/srtm_tms/url /elevation /mapcache 节点的作用是建立本地瓦片缓存第二次加载同一区域直接从缓存读能省掉大量磁盘 IO。经度 104.04、纬度 30.5 对应的是成都附近你要是做别的区域把经纬度换成自己的测试点就行。type 用 geocentric 表示全球场景单区域项目改成 local 也可以但 local 模式下 profile 必须更精确否则区域外会黑屏。6.3 验证技巧与调试习惯场景跑起来以后验证是否真的加载成功有三个办法。第一看瓦片加载日志osgEarth 里控制日志级别的方式是设置 notify level信息输出能看到每个瓦片请求的文件路径和耗时。第二在 osgearth_viewer 里按键盘上的统计键屏幕会显示帧率和场景节点数量帧率稳定在 30 以上基本可接受。第三手动缩放视角快速从全球级别拉到建筑物级别重点观察瓦片切换时是否出现白块或长时间空白白块说明对应层级的瓦片缺失。我在这套环境上栽过最大的一个跟头就是 Qt 5.15 的旧目录残留在 CMakeCache 里折腾了半天全是那个 dependent error。从那以后每次换机器我都强制走一遍同样的流程删干净构建目录、让 Qt Creator 重新自动扫描 qmake 路径、用 osgearth_viewer 验证一次 .earth 文件、再开正式工程。这套流程走完五分钟收益是省掉后面一晚上的玄学排错。这个习惯也建议你保留希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑