☰
VS2017 64位下OSG与Qt环境搭建与OSGQT嵌入实战
2026/10/19 12:19:05 网站建设 项目流程

简介:这是一份面向Qt与OpenSceneGraph(OSG)三维渲染开发者的整合示例工程,基于Github上广受好评的OSGtree项目整理而来,作者在VS2017搭配64位OSG环境下完成调试,可直接运行,几乎覆盖了OSG与Qt结合所需的各类功能,适合有一定C++与图形编程基础、希望快速上手三维可视化界面的开发者参考。资源包共72个文件,约2.88MB,以cpp与h源码为主体,配合ui界面文件、qrc资源文件、vcxproj工程文件及pro工程文件,另含obj、tlog、pdb等编译中间产物,结构完整,只需调整环境变量即可复用。目前已有2354人学习下载。工程内包含相机控制、三维视图、树形结构、模型项管理等多个模块,读者可借此理解OSG渲染窗口嵌入Qt界面的组织方式、信号槽与场景交互的实现思路,并对照现有目录结构快速搭建自己的三维应用框架。

1. 从一次编译翻车说起:OSG 与 Qt 在 VS2017 下的 64 位环境搭建

去年帮一个做三维可视化的朋友处理一个老项目,需求很明确:把 OSG 的渲染窗口嵌进 Qt 界面里,用 VS2017 编译,目标平台必须是 64 位。他之前拿到的工程在 32 位下跑得好好的,一换到 64 位就各种链接错误,折腾了两天没搞定。我接手后发现问题根本不在代码,而是 OSG 和 Qt 的库版本、编译选项、运行时库三者没对齐。OSGQT 这个组合本身不复杂,但它的坑集中在“位数”和“运行时库”这两个点上,一旦错一个,报错信息往往指向别处,查起来很费时间。这套环境适合需要在 Qt 里做三维场景嵌入的开发者,尤其是维护老项目、必须用 VS2017 和 64 位编译的场景。下面把我实际走通的流程拆开讲,从环境清单到编译验证,每一步都给出可复现的操作。

2. 环境清单与版本选择:VS2017 下 OSG 和 Qt 的位数对齐

2.1 为什么 64 位下最容易翻车的是运行时库

VS2017 默认新建项目用的是动态运行时库(/MD 或 /MDd),而很多预编译的 OSG 库是用静态运行时库(/MT 或 /MTd)编出来的。32 位下这个问题有时被掩盖,因为链接器对符号的容忍度稍高,但 64 位下符号修饰规则更严格,一旦运行时库不匹配,就会出现大量LNK2005重复定义或LNK2019无法解析的外部符号。我一般会先确认三件事:OSG 的 lib 文件是用哪个运行时库编的、Qt 的 lib 是动态还是静态、自己的工程设置是否一致。这三者必须统一,否则后面怎么改代码都没用。

2.2 版本组合与下载来源

我这次用的组合是 OSG 3.6.5、Qt 5.12.12、VS2017 15.9。OSG 3.6.5 对 VS2017 的支持比较成熟,Qt 5.12 是 LTS 版本,和 VS2017 的兼容性经过大量项目验证。下载时注意:OSG 要选已经编译好的 64 位二进制包,或者自己用 CMake 编;Qt 要选msvc2017_64那个套件。如果你拿到的 OSG 包只有 32 位,那后面所有工作都是白费,所以第一步就是确认位数。

组件版本位数获取方式
Visual Studio2017 15.964 位官方安装器
Qt5.12.12msvc2017_64在线安装器选对应套件
OSG3.6.564 位预编译包或 CMake 自编
CMake3.20+64 位官方安装器

2.3 环境变量与目录规划

我习惯把第三方库集中放在一个盘符下,比如D:\DevLibs,下面分osg、qt、cmake三个目录。这样做的目的是让 CMake 和 VS 的路径引用清晰,迁移时也方便。设置两个系统环境变量:OSG_DIR指向D:\DevLibs\osg,QTDIR指向D:\DevLibs\qt\5.12.12\msvc2017_64。然后把%OSG_DIR%\bin和%QTDIR%\bin加到PATH里。这一步不做的话,后面运行程序会提示找不到osg165-osg.dll或Qt5Core.dll。

# 在系统环境变量里添加,不是临时 set OSG_DIR = D:\DevLibs\osg QTDIR = D:\DevLibs\qt\5.12.12\msvc2017_64 PATH = %OSG_DIR%\bin;%QTDIR%\bin;%PATH%

提示:环境变量改完要重启 VS,否则 VS 读到的还是旧值。我见过有人改完直接点编译,结果 CMake 找不到 Qt,白白浪费半小时。

3. 编译 OSGQT 桥接库:从 CMake 配置到 VS2017 生成

3.1 OSGQT 的角色与源码获取

OSGQT 是 OSG 官方提供的一个桥接模块,作用是把 OSG 的osgViewer::Viewer嵌入到 Qt 的QWidget里。它本身不是独立库,而是 OSG 源码树里的一个子目录src/osgQt。如果你拿到的 OSG 预编译包没有带 osgQt 的 lib,那就需要自己从源码编译这个模块。我这次用的是 OSG 3.6.5 的完整源码,里面已经包含 osgQt。

3.2 CMake 配置的关键参数

打开 CMake GUI,源码路径指向D:\DevLibs\osg\src\osgQt的上一级,也就是 OSG 源码根目录。构建路径新建一个build-osgqt文件夹。点 Configure,选择Visual Studio 15 2017 Win64。第一次配置会报错,因为找不到 Qt,需要手动指定:

# 在 CMake GUI 里添加以下条目 CMAKE_PREFIX_PATH = D:/DevLibs/qt/5.12.12/msvc2017_64 QT_QMAKE_EXECUTABLE = D:/DevLibs/qt/5.12.12/msvc2017_64/bin/qmake.exe BUILD_OSG_APPLICATIONS = OFF BUILD_OSG_EXAMPLES = OFF OSG_BUILD_APPLICATION_BUNDLES = OFF

这几个参数的含义:CMAKE_PREFIX_PATH告诉 CMake 去哪里找 Qt 的配置文件;QT_QMAKE_EXECUTABLE直接指定 qmake 路径,避免找到系统里其他版本的 Qt;后面三个关掉是为了只编译 osgQt 相关目标,节省时间。配置成功后,在 CMake 里搜索osgQt,确认BUILD_OSGQT是勾选状态。

3.3 用 VS2017 生成解决方案并编译

点 Generate,成功后用 VS2017 打开build-osgqt下的OSG.sln。在解决方案里找到osgQt项目,右键设为启动项目,然后选择Release x64配置。编译前检查一下项目的运行时库设置:右键项目 → 属性 → C/C++ → 代码生成 → 运行库,确认是多线程 DLL (/MD)。如果 OSG 主库用的是/MD,这里也必须一致。然后生成解决方案,等待编译完成。编译成功后,在build-osgqt\lib\Release下会得到osgQt.lib和对应的osgQt.dll。

# 编译完成后,把 osgQt 的头文件和 lib 复制到 OSG 安装目录 copy D:\DevLibs\osg\build-osgqt\lib\Release\osgQt.lib D:\DevLibs\osg\lib\ copy D:\DevLibs\osg\build-osgqt\bin\Release\osgQt.dll D:\DevLibs\osg\bin\ # 头文件在源码的 include\osgQt 下,整个目录复制过去 xcopy /E /I D:\DevLibs\osg\src\osgQt\include\osgQt D:\DevLibs\osg\include\osgQt

注意:复制 dll 时不要漏掉osgQt.dll依赖的其他 OSG dll,比如osg165-osg.dll、osg165-osgDB.dll。用 Dependency Walker 或dumpbin /dependents可以查依赖。

4. 在 VS2017 里配置 Qt 项目并嵌入 OSG 窗口

4.1 创建 Qt 项目并设置 64 位平台

在 VS2017 里新建项目,选择Qt GUI Application,项目名随意。创建向导里会问用哪个 Qt 版本,选msvc2017_64。项目建好后,第一件事是把平台从x86改成x64:在工具栏的解决方案平台下拉框里选x64,如果没有就点“配置管理器”新建一个。这一步不做,后面链接 64 位 lib 会直接报错。

4.2 项目属性里的包含目录和库目录

右键项目 → 属性,在VC++ 目录里设置:

# 包含目录 D:\DevLibs\osg\include D:\DevLibs\qt\5.12.12\msvc2017_64\include # 库目录 D:\DevLibs\osg\lib D:\DevLibs\qt\5.12.12\msvc2017_64\lib

然后在链接器 → 输入 → 附加依赖项里加入:

osgQt.lib osg.lib osgDB.lib osgViewer.lib osgGA.lib Qt5Core.lib Qt5Gui.lib Qt5Widgets.lib Qt5OpenGL.lib

这些库的顺序有时会影响链接结果,如果出现无法解析的外部符号,可以尝试调整顺序,把osgQt.lib放在最前面。

4.3 嵌入 OSG 窗口的代码实现

在 Qt 的主窗口类里,添加一个osgQt::GLWidget成员,并把它设为中心部件。下面是一个最小可运行的示例:

// mainwindow.h #include <QMainWindow> #include <osgQt/GraphicsWindowQt> #include <osgViewer/Viewer> #include <osgDB/ReadFile> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget* parent = nullptr); private: osg::ref_ptr<osgViewer::Viewer> m_viewer; }; // mainwindow.cpp #include "mainwindow.h" #include <osgQt/GLWidget> MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { // 创建 OSG 图形窗口特性 osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; traits->x = 0; traits->y = 0; traits->width = 800; traits->height = 600; traits->windowDecoration = false; traits->doubleBuffer = true; traits->sharedContext = 0; // 创建 Qt 的 GLWidget,内部会创建 GraphicsWindowQt osgQt::GLWidget* glWidget = new osgQt::GLWidget(traits.get()); setCentralWidget(glWidget); // 初始化 Viewer 并关联相机 m_viewer = new osgViewer::Viewer; m_viewer->setCameraManipulator(new osgGA::TrackballManipulator); m_viewer->setSceneData(osgDB::readNodeFile("cow.osg")); m_viewer->getCamera()->setGraphicsContext(glWidget->getGraphicsWindow()); m_viewer->getCamera()->setViewport(0, 0, 800, 600); m_viewer->realize(); }

这段代码的逻辑:先构造GraphicsContext::Traits描述窗口属性,然后用osgQt::GLWidget创建 Qt 部件,它内部会生成GraphicsWindowQt。接着把 Viewer 的相机绑定到这个图形上下文上,最后realize()完成初始化。cow.osg是 OSG 自带的示例模型,用来验证渲染是否正常。

4.4 运行时 dll 的部署

编译成功后,把osgQt.dll、osg165-osg.dll、osg165-osgDB.dll、osg165-osgViewer.dll、osg165-osgGA.dll以及 Qt 的Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll、Qt5OpenGL.dll复制到 exe 同目录。Qt 的插件目录platforms也要复制过去,里面至少要有qwindows.dll。这一步漏了,程序启动时会报“This application failed to start because no Qt platform plugin could be initialized”。

5. 避坑排查:OSGQT 在 VS2017 64 位下的五个高频问题

5.1 链接错误 LNK2019:无法解析的外部符号

现象:编译时大量LNK2019,指向osgQt::GLWidget的构造函数或osgViewer::Viewer的成员函数。
原因:最常见的是库位数不匹配,比如项目是 x64 但链接的osgQt.lib是 32 位的;其次是运行时库不一致,项目用/MD但 lib 是/MT编的。
解决:用dumpbin /headers osgQt.lib | findstr machine确认 lib 是 x64;在项目属性里把运行库改成和 OSG 库一致,通常都是/MD。

5.2 程序启动崩溃:0xc000007b

现象:双击 exe 直接报0xc000007b,或者提示“应用程序无法正常启动”。
原因:这个错误码基本就是位数混用,比如 exe 是 64 位但某个 dll 是 32 位。
解决:用 Dependency Walker 打开 exe,看哪个 dll 标红。重点检查osgQt.dll、osg165-osg.dll和 Qt 的 dll 是否都是 64 位。Qt 的platforms\qwindows.dll也必须是 64 位版本。

5.3 黑屏或白屏:窗口创建了但没渲染

现象:Qt 窗口正常弹出,但中间区域是黑的或白的,没有三维模型。
原因:GraphicsWindowQt的上下文没有正确绑定到 Viewer 的相机,或者realize()调用时机不对。
解决:确认m_viewer->getCamera()->setGraphicsContext()传入的是glWidget->getGraphicsWindow(),并且setViewport的尺寸和实际窗口一致。如果用了多线程渲染,把m_viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded)先改成单线程排查。

5.4 找不到 osgQt 头文件

现象:编译时报Cannot open include file: 'osgQt/GLWidget'。
原因:osgQt 的头文件没有复制到 OSG 的 include 目录,或者项目包含目录里没加D:\DevLibs\osg\include。
解决:确认D:\DevLibs\osg\include\osgQt\GLWidget这个文件存在。如果不存在,从源码的src\osgQt\include\osgQt复制过去。

5.5 Qt 插件加载失败

现象:程序启动时报“no Qt platform plugin could be initialized”。
原因:exe 同目录下缺少platforms\qwindows.dll,或者该 dll 的位数不对。
解决:从QTDIR\plugins\platforms复制整个platforms文件夹到 exe 目录。如果还不行,用QT_DEBUG_PLUGINS=1环境变量启动程序,看它到底去哪个路径找插件。

6. 装完怎么验证:三步确认 OSGQT 真的跑通了

6.1 用最小场景验证渲染管线

不要一上来就跑复杂模型。我一般先用 OSG 自带的cow.osg做验证,因为它小、加载快、不需要额外纹理。把cow.osg放在 exe 同目录,运行程序后如果能看到一头牛在窗口里,并且可以用鼠标拖动旋转,说明 OSG 的渲染管线、Qt 的窗口嵌入、相机交互这三条链路都通了。如果牛出不来,先检查osgDB::readNodeFile的返回值是不是空,可以在代码里加一行if (!node) return;来确认文件是否加载成功。

6.2 检查 dll 依赖是否干净

在 exe 目录下打开命令行,运行:

dumpbin /dependents YourApp.exe

看输出的 dll 列表里有没有带路径的、或者指向System32下 32 位版本的。重点确认osgQt.dll、osg165-osg.dll、Qt5Core.dll都在同目录,并且用dumpbin /headers查一下它们的 machine 字段都是x64。这一步能提前发现大部分“在我机器上能跑”的问题。

6.3 用 Release 和 Debug 分别跑一遍

很多人只测 Release,结果 Debug 下崩溃。原因是 Debug 版链接的库不同,运行时库是/MDd,而 OSG 预编译包通常只有 Release 版。如果必须在 Debug 下调试,要么自己用/MDd重新编译 OSG,要么在 Debug 项目里把运行库改成/MD并链接 Release 版的 OSG lib。我一般会在项目里建两个配置:Release|x64用/MD链接 Release 库,Debug|x64也链接 Release 库但开启调试信息,这样能避开/MDd缺失的问题。

# 验证时用到的检查命令汇总 dumpbin /headers D:\DevLibs\osg\bin\osgQt.dll | findstr machine dumpbin /dependents YourApp.exe # 设置 Qt 插件调试 set QT_DEBUG_PLUGINS=1 YourApp.exe

从那以后我每次配 OSGQT 环境,都强制先跑一遍dumpbin /headers确认所有 lib 和 dll 的 machine 字段是 x64,再开始写代码。这个习惯帮我省掉了至少三次重装系统的冲动。希望帮到你。

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

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

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

立即咨询