简介这是一套面向三维地理信息与虚拟仿真开发者的编译成品环境包整合 MSVC2017 编译器下的 OpenSceneGraph 3.63、osgEarth 2.10 与 Qt 5.14主要解决自行编译工具链耗时长、依赖库版本易冲突的问题。包内除已编译好的 osg 与 osgEarth 头文件、库文件外还附带一个可直接用 Qt Creator 打开的简易 Qt 工程方便快速验证三维地球渲染与窗口嵌入流程适合具备 C 基础、正在做 GIS 可视化或仿真项目的初中级开发者。压缩包约 530MB含 2000 个文件以 hpp 与 h 头文件、dll 动态库、lib 导入库、pdb 调试符号为主同时包含 exe 示例程序、earth 与 osgb、osgt 场景数据、shaders 着色器及 shp 矢量样例覆盖编译、调试与运行三类需求。已有 536 人学习下载可据此快速搭建起可运行的三维地球环境。1. 版本对齐比安装速度更重要一次「DLL 全在、程序依然崩溃」的开工msvc2017、osg3.63、osgearth2.10、qt5.14 这四个名字凑在一起基本就是一个三维桌面端项目的全部依赖。我第一次拿到这种打包好的环境是在一个地球渲染的演示项目上压缩包解压完目录整整齐齐bin、include、lib 一个不缺我以为半小时能收工结果编译出来的 exe 双击之后窗口闪一下就没影了任务管理器里连进程都没留下。这类预编译包最容易让人误以为“装完了”。真正容易卡住的地方从来不是解压而是位数、运行库、插件目录、Qt 的 msvc 版本这几条线有没有对齐。对齐了它是省下两天编译时间的加速工具对不齐它就是那只让人反复重启 IDE 的黑匣子。这篇写给我自己也写给正在 QtCreator 里配 MSVC2017 OSG 3.6.3 osgEarth 2.10 的人。2. 版本对齐MSVC2017、OSG 3.6.3、osgEarth 2.10、Qt 5.14 必须是一条编译链2.1 谁决定谁四个组件的依赖顺序很多人装这类环境是「哪个报错修哪个」最后越修越乱。正确的心智模型是一条单向链编译器 → Qt 二进制包 → OSG 预编译库 → osgEarth。链条上任何一环换了版本后面的全部作废。MSVC2017 对应的是 v141 工具集_MSC_VER是 1916 这一档它决定了 C 标准库的 ABI、异常处理模型和运行库/MD 还是 /MT。Qt 5.14 官方提供的 Windows 二进制包是按编译器分的msvc2017_64和msvc2015_64不是一回事虽然它们只差一个工具集版本号但链接阶段会因为 ABI 不一致直接对不上。OSG 3.6.3 本身是纯 C 库二进制兼容性完全取决于它是用哪个 MSVC、哪种运行库编出来的。osgEarth 2.10 又依赖 OSG 3.6 的 API它链接的是 osg、osgDB、osgViewer 这几个模块的具体符号。所以判断一个包能不能用不用看它有多大只要确认三件事Qt 是 msvc2017 版、OSG 和 osgEarth 是同一个编译器同一运行库出的、位数三者一致。缺一条后面所有配置工作都是白费。2.2 解压前的一张对照表我在解压任何预编译包之前会先填这张表填不满就先别往下走。表格里的「装错的表现」这一列最有用它把后面几章要排查的症状提前对上了号。组件版本位数关键约束装错的表现MSVC2017v141 工具集64 位工程运行库与库一致都用 /MD 或都用 /MTLNK2038、运行库冲突Qt5.14 系列msvc2017_64必须是 msvc2017 编译的那份大量 unresolved external symbolOSG3.6.364 位插件目录名固定为 osgPlugins-3.6.3读文件时报找不到 pluginosgEarth2.1064 位依赖 OSG 3.6不能配 3.4 的库启动即崩、符号找不到表里 Qt 那一行的小版本我故意写成「5.14 系列」因为打包的 7z 文件名通常被截断真实小版本要以解压后qmake -v的输出为准。这个细节看着小实际上决定了你后面能不能直接用 Qt 官方安装器补组件——小版本对不上有时候能编过但运行期在 QWidget 初始化阶段莫名其妙的挂。2.3 目录布局铺成别人也能复现的形状预编译包解压出来往往是一堆平行目录直接扔在下载文件夹里用短期没问题等项目多起来就是灾难。我一般会固定一个D:\dev\作为根把所有东西按用途摆好D:\dev\ ├─ osg_stack\ # 本包解压后的根 │ ├─ bin\ # dll 与 exeosgVersion.exe 在这 │ │ └─ osgPlugins-3.6.3\ # 插件目录名字别改 │ ├─ include\ # osg / osgEarth 头文件 │ ├─ lib\ # 导入库 │ └─ data\ # 示例数据与 earth 文件 └─ qt514\msvc2017_64\ # Qt 5.14 的 msvc2017_64 那一份有两条硬性约束路径里不要有中文和空格深度尽量控制在三层以内。这不是强迫症osgDB 在读文件时对非 ASCII 路径的处理一直不算稳而 CMake 生成的 Makefile 里如果-I后面跟着带空格的路径还会被拆成多个参数报出来的错跟真实原因完全不沾边。踩过一次之后我所有开发目录都用纯英文短路径。3. 解压与目录布局让 7z 包解压后可以直接运行 osgVersion3.1 解压方式优先使用命令行右键解压不是不行但解压这种几十个 G 的包中途断了你不知道断在哪而且解压出来的顶层目录名往往又长又带加号。我一般用 7z 命令行可控并且能看到进度:: 解压到指定目录-o 后面不能有空格-y 表示全部覆盖不询问 7z x msvc2017osg3.63osgearth2.10qt5.14.7z -oD:\dev\osg_stack -y :: 解压完先看目录结构确认 bin/include/lib 是平级的 dir /b D:\dev\osg_stack-o是输出目录参数7z 的语法里它和路径之间不能加空格写成-o D:\...会被当成两个参数结果解到当前目录。-y是覆盖确认重装时省一次交互。解压完先确认三件事bin下有没有osgVersion.exe、include下有没有osg和osgEarth两个子目录、lib下是不是.lib文件。这三个都在说明包本身是完整的缺任何一个别急着配环境先去确认压缩包是不是没下完。3.2 三个必设变量PATH、OSG_FILE_PATH、OSG_LIBRARY_PATH这一步是整个安装过程里最容易漏、也最容易在半年后重新踩一遍的地方。我不建议手写进系统环境变量而是写一个setenv.bat每次开命令行或者从 QtCreator 启动前先执行它。好处是换机器只要拷一个文件坏处是你得记得每次都要执行——我后来干脆把它挂到了快捷方式上。echo off :: 根目录按自己实际解压位置改 set OSG_ROOTD:\dev\osg_stack :: 1. 让系统能找到 dll 和 exe set PATH%OSG_ROOT%\bin;%PATH% :: 2. 让 osgDB 能找到数据文件.earth / .tif / .osgb 的搜索路径 set OSG_FILE_PATH%OSG_ROOT%\data;%OSG_ROOT%\data\osgearth :: 3. 让 osgDB 能找到插件osgdb_*.dll set OSG_LIBRARY_PATH%OSG_ROOT%\bin\osgPlugins-3.6.3 :: 4. 调试期打开日志正式跑可以去掉 set OSG_NOTIFY_LEVELINFO echo OSG_ROOT%OSG_ROOT% osgVersion三个变量的分工要讲清楚不然很容易把它们混成一个PATH解决的是「exe 和 dll 在运行时能不能被操作系统加载」是进程级的问题OSG_LIBRARY_PATH解决的是「osgDB 这个库自己去哪里找格式插件」是库级的问题它对应的是osgPlugins-3.6.3这个目录OSG_FILE_PATH解决的是「你写的相对路径world.tif去哪找」是数据级的问题。三者缺一症状完全不同。缺 PATH 是程序双击就退出缺 OSG_LIBRARY_PATH 是控制台打印Error no plugin could be found缺 OSG_FILE_PATH 是打开 earth 文件时报Failed to find file。记住这个对应关系排查能省一半时间。提示OSG_NOTIFY_LEVEL支持的值从低到高大致是 FATAL、WARN、NOTICE、INFO、DEBUG_INFO、DEBUG_FP。日常用 INFO追插件加载问题用 DEBUG_INFO输出会很多建议重定向到文件再看。3.3 第一次体检用 osgVersion 和 osgconv 各验一遍环境变量设完不要急着开 QtCreator先在同一个命令行窗口里跑两条命令。这两条命令分别验证了「动态库能不能加载」和「插件能不能用」是最便宜的自检。:: 第一条验证 dll 加载和版本 osgVersion :: 第二条验证插件与读写链路把 osg 格式转成 osgb :: 找不到 cow.osg 就换成 data 目录下任意一个 .osg / .osgt 文件 osgconv %OSG_FILE_PATH%\cow.osg D:\temp\cow.osgbosgVersion正常会输出三行左右第一行是版本号后面是编译器和构建信息。如果它报「不是内部或外部命令」说明 PATH 没生效或者窗口没重开如果它报「无法定位程序输入点」说明 PATH 里存在另一个版本的 OSG加载到了旧的 dll这时候把%OSG_ROOT%\bin提到 PATH 最前面。osgconv这一条更有价值它同时触发了读插件和写插件成功的话会在D:\temp下生成一个 osgb 文件大小不为零。如果它提示找不到 plugin问题一定在OSG_LIBRARY_PATH或者osgPlugins-3.6.3目录被改名了——有些压缩工具会把带点的目录名处理掉我遇到过目录变成osgPlugins-3.6.3 (1)的情况找原因找了很久。4. QtCreator 配置MSVC2017 Kit 带起 OSG 3.6.3 与 osgEarth 2.10 工程4.1 Kit 三件套编译器、Qt Versions、调试器QtCreator 里报的「没有可用的 Kit」绝大多数不是 Qt 的问题而是编译器没被探测到。MSVC2017 的编译器是cl.exe它不能孤立使用必须先有 vcvars 环境。QtCreator 通常会通过注册信息自动探测探测不到就手动加编译器路径...\VC\Tools\MSVC\工具集版本\bin\Hostx64\x64\cl.exe选C类型ABI 会自动识别成 x86-windows-msvc2017-pe-64bit。Qt Versions指向 Qt 5.14 的msvc2017_64\bin\qmake.exe加完点 Apply下面那行版本信息必须显示 5.14 且是 64 位。调试器Windows 下用 CDB 或者 VS 的调试引擎都行。OSG 程序崩在渲染线程里的时候没调试器基本只能靠猜。Kit 配好之后有个隐藏检查项点开 Kit 的 CMake 或 qmake 配置看CMAKE_PREFIX_PATH有没有被自动填上 Qt 路径。没填的话CMake 工程会去找系统里的其他 Qt编出来一半是新的一半是旧的报错极难定位。4.2 .pro 写法INCLUDEPATH 与 LIBS 的顺序小工程我用 qmake改起来快不需要动 CMakeLists。关键是路径写成正斜杠、库的链接顺序不能乱——osgEarth 依赖 OSG被依赖的必须写在后面。QT core gui widgets opengl CONFIG c11 TARGET EarthDemo # 用正斜杠qmake 在 Windows 下会自己转 OSG_ROOT D:/dev/osg_stack INCLUDEPATH $$OSG_ROOT/include DEPENDPATH $$OSG_ROOT/include LIBS -L$$OSG_ROOT/lib # 顺序先 osgEarth再 OSG被依赖的放后面 LIBS -losgEarth -losgEarthUtil -losgEarthAnnotation LIBS -losgViewer -losgGA -losgUtil -losgDB -losg # Debug 构建时库名要带 d 后缀否则会撞 _ITERATOR_DEBUG_LEVEL CONFIG(debug, debug|release) { LIBS -losgEarthd -losgViewerd -losgDBd -losgd }这里有两个坑值得单独说。一是Debug 后缀OSG 系列的 Debug 版库名统一在末尾加dosgd.lib、osgDBd.lib、osgEarthd.lib如果你的包里没提供 Debug 版就别用 Debug 配置构建直接用 Release 调试否则链接阶段会报一堆LNK2038。二是-losgEarth展开后是什么qmake 会拼成osgEarth.lib如果包里的库文件叫osgEarth2.lib或者带版本后缀就得写成完整文件名别想当然。4.3 CMake 写法find_package 的四个组件工程大一点就得用 CMake因为 osgEarth 2.10 自带的FindosgEarth.cmake能帮我们把一堆依赖理清楚。下面是能跑通的最小结构cmake_minimum_required(VERSION 3.10) project(EarthDemo LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 指向 Qt 5.14 的 msvc2017_64 目录避免找到别的 Qt set(CMAKE_PREFIX_PATH D:/dev/qt514/msvc2017_64 ${CMAKE_PREFIX_PATH}) set(OSG_ROOT D:/dev/osg_stack) # osgEarth 的 Find 模块不一定在默认搜索路径里手动加进去 list(APPEND CMAKE_MODULE_PATH ${OSG_ROOT}/lib/cmake/osgEarth) find_package(OpenSceneGraph 3.6.3 REQUIRED COMPONENTS osg osgDB osgGA osgUtil osgViewer) find_package(osgEarth 2.10 REQUIRED) add_executable(EarthDemo main.cpp) target_include_directories(EarthDemo PRIVATE ${OSG_ROOT}/include ${OSGEARTH_INCLUDE_DIR} ) target_link_libraries(EarthDemo PRIVATE ${OSGEARTH_LIBRARIES} ${OPENSCENEGRAPH_LIBRARIES} )find_package的版本号我建议写死成3.6.3和2.10不要留空。留空的话 CMake 会接受系统里任意一个版本的 OSG而这台机器上往往同时装着好几个版本的 OSG找到哪个全看运气。CMAKE_MODULE_PATH那一行是给找不到 osgEarth 的情况准备的如果find_package(osgEarth)报Could not find a package configuration file九成是缺这一句。4.4 运行时把 DLL 和插件目录送到 exe 旁边编译通过不等于能跑。QtCreator 默认在build-xxx\debug或release子目录里启动 exe这个目录里既没有 OSG 的 dll也没有osgPlugins-3.6.3所以运行必然失败。三个办法我按推荐顺序排在 QtCreator 的 Projects → Run → Environment 里把PATH、OSG_LIBRARY_PATH、OSG_FILE_PATH三条加进去可以直接引用setenv.bat里的值。这个办法最干净不影响系统环境。在 Run → Working directory 里把工作目录设成数据目录这样 earth 文件里的相对路径能直接命中。用 CMake 的add_custom_command(TARGET EarthDemo POST_BUILD ...)把 dll 拷到输出目录。工程要发给别人的时候用这个。其中第 1 条有一个容易忽略的点QtCreator 的运行时环境变量是在启动时读取的改完必须重启 QtCreator光点 Apply 不生效。这一点让我浪费过一整个下午。5. 避坑与排查五条装完才炸的记录5.1 双击 exe 窗口一闪就退控制台没有任何输出现象编译链接全绿QtCreator 里点运行程序窗口闪一下就没了输出面板空空如也。原因Windows 的 GUI 子系统程序没有控制台加载 dll 失败时系统只弹一个对话框QtCreator 里看不到。最常见的是bin不在 PATH 里其次是osgPlugins-3.6.3里某个插件依赖的第三方 dll比如图像格式库缺失。解决先把 exe 拖到一个已经执行过setenv.bat的 cmd 窗口里运行这样错误会直接打印出来。还不行就用依赖查看工具扫一遍 exe 的导入表红色项就是缺的 dll。我现在养成的习惯是新环境第一次运行永远从命令行起不从 IDE 起。5.2 LNK2038 / 运行库不匹配现象链接阶段报LNK2038: 检测到RuntimeLibrary的不匹配: 值MD_DynamicRelease与值MT_StaticRelease不匹配或者_ITERATOR_DEBUG_LEVEL不匹配。原因工程用的是 Debug 配置链的却是 Release 版库或者反过来也可能是工程设了静态运行库/MT而预编译的 OSG 是/MD出的。这两种情况的本质都是 ABI 不一致。解决先统一 Debug/Release。包里只有 Release 库时工程就只用 Release 构建调试信息从 pdb 里取。确认不了的用dumpbin /directives osg.lib | findstr DEFAULTLIB看一眼库自己声明的运行库比猜快得多。5.3 读文件时报 no plugin could be found现象osgDB::readNodeFile(world.earth)返回空日志里是Error no plugin could be found for format。原因分两种。一是OSG_LIBRARY_PATH没设osgDB 找不到osgdb_earth.dll、osgdb_gdal.dll这些插件二是osgPlugins-3.6.3目录位置不对——它必须和 exe 在同一层或者被OSG_LIBRARY_PATH显式指到。解决先把OSG_NOTIFY_LEVEL设成DEBUG_INFO日志里会打印 osgDB 尝试搜索的所有插件目录一眼就能看出它去哪找了。然后确认目录名没被压缩工具改名确认插件 dll 的文件名和 OSG 版本号匹配。版本号不匹配的插件不会被加载而且是静默跳过。5.4 Qt 版本对上了符号却对不上现象编译能过链接时几十条unresolved external symbol全都指向 Qt 的类。原因CMAKE_PREFIX_PATH或者 qmake 的 Qt Versions 指到了msvc2015_64而工程 Kit 是 msvc2017。两个包的类布局在绝大多数情况下一致但在某些容器和模板实例化上会不一致表现出来就是符号名对不上。解决在 QtCreator 的 Qt Versions 里确认路径结尾是msvc2017_64然后在构建目录里删掉CMakeCache.txt重新 configure。缓存不清改了路径也没用这也是我每次动 Qt 路径之后必做的一步。5.5 换了台机器同一个工程跑不起来现象工程在自己机器上好好的拷到同事机器上就崩报「无法定位程序输入点」或者版本号对不上。原因同事机器上装了另一个版本的 OSG它的bin在 PATH 里排得更前运行时加载到了旧 dll。OSG 的 dll 之间靠导出符号互相调用版本一混某些新符号在旧库里不存在直接报输入点找不到。解决把本项目的bin强制提到 PATH 最前面或者干脆用setenv.bat起一个干净的会话。长期方案是不依赖系统 PATH把 dll 拷到 exe 同目录——Windows 加载 dll 时优先找 exe 所在目录这个优先级高于 PATH。6. 装完怎么验证三级验证让地球真正运行起来装完的验证不能只停在「编译通过」得按从底层到应用的顺序分三级走每一级失败指向的原因完全不同。我一般按这张表逐条过级别操作通过标准失败先查一级库osgVersion输出 3.6.3 与编译器信息PATH 顺序、dll 缺失二级插件osgconv a.osg b.osgb生成非空 osgb 文件OSG_LIBRARY_PATH、插件目录名三级场景加载一个最小 earth 文件打印出 mapNode 有效、图层数 0数据路径、gdal 插件前两级在 3.3 已经写过了第三级才是真正验证 osgEarth 2.10 有没有配通。我一般写一个十几行的空壳程序不挂 Qt跑通了再往 Qt 的窗口里嵌#include osgDB/ReadFile #include osgViewer/Viewer #include osgEarth/MapNode #include osgEarth/EarthManipulator #include iostream int main(int argc, char** argv) { // 换成你手上的 earth 文件绝对路径 osg::ref_ptrosg::Node node osgDB::readNodeFile(D:/dev/osg_stack/data/test.earth); if (!node.valid()) { std::cerr read earth failed, check OSG_FILE_PATH std::endl; return 1; } // 关键一步确认读进来的是 MapNode而不是普通节点 osgEarth::MapNode* mapNode osgEarth::MapNode::findMapNode(node.get()); if (!mapNode) { std::cerr not a map node, plugin may fallback std::endl; return 2; } std::cout layers: mapNode-getMap()-getNumImageLayers() std::endl; osgViewer::Viewer viewer; viewer.setCameraManipulator(new osgEarth::EarthManipulator()); viewer.setSceneData(node.get()); return viewer.run(); }MapNode::findMapNode这个调用是验证的核心它比node.valid()更能说明问题文件读进来了但插件降级成了普通节点valid()依然返回 true而findMapNode返回空。getNumImageLayers()打印出来如果是 0说明 earth 文件里的影像源一个都没加载上这时候多半是 gdal 插件缺了或者数据路径写错。最小 earth 文件我也留了一份在没有现成数据的时候用它验证链路map nameselftest typegeocentric version2 !-- 用一个本地 tif避免验证阶段受网络影响 -- image namebase drivergdal urlworld.tif/url /image /map注意version2这个属性osgEarth 2.x 里不写也能跑但某些驱动在没有版本声明时行为会有差异写上有益无害。验证阶段尽量用本地栅格不要用在线图层——网络问题会伪装成配置问题我在这上面至少有两次判断错误记录。三级都过了再往 Qt 的窗口里嵌。Qt 5.14 配 OSG 3.6.3 有两条路用osgQOpenGLWidgetOSG 3.6 自带的 osgQt 子工程需要额外链接osgQOpenGL并保留QT opengl或者老办法自己用QOpenGLWidget包一层osgViewer::GraphicsWindowEmbedded。前者省事但要求包里带了对应的库后者麻烦但可控。先确认lib目录下有没有osgQOpenGL.lib有就直接用前者没有就别硬找走后者。从那以后我每次拿到这种预编译包都强制走一遍 osgVersion、osgconv、最小 earth、空壳 viewer 这四步一步都不省。因为这类环境的坑几乎全在「编译过了但跑不起来」这一段而这一段没有任何编译器会替你报错。希望你少走一遍我踩过的那些弯路。本文还有配套的精品资源点击获取