在现代图形应用开发中,Qt与OpenGL的结合已成为构建高性能、跨平台图形界面应用的首选方案。Qt作为一个功能强大的C++ GUI框架,提供了丰富的UI组件和跨平台支持;而OpenGL作为行业标准的图形API,能够充分发挥GPU的渲染能力。两者的整合使得开发者能够在保持图形渲染性能的同时,实现复杂的用户交互和界面设计。Qt 通过 QOpenGLWidget 和 QOpenGLWindow 等类封装了 OpenGL 的集成过程,简化了跨平台开发流程,同时保留了对现代 OpenGL 特性的支持。本报告将深入分析Qt中实现OpenGL应用的编程框架,包括集成方式、开发环境配置、核心类使用方法、信号槽交互策略以及性能优化和跨平台兼容性解决方案。
一、Qt与OpenGL的集成方式
Qt提供了多种方式将OpenGL集成到应用程序中,主要包括QOpenGLWidget和QOpenGLWindow两种主要类,它们各自适用于不同的场景。
QOpenGLWidget是Qt 5.4版本引入的类,继承自QWidget,适用于需要将 OpenGL 渲染内容嵌入到Qt 窗口或布局中的场景 。它提供了三个核心虚拟函数:initializeGL()用于初始化OpenGL资源和状态,resizeGL()用于设置视口和投影,paintGL()用于执行实际的渲染操作。这种设计使得开发者可以像使用其他QWidget继承类一样方便地集成OpenGL内容 [2] 。QOpenGLWidget内部使用帧缓冲区对象(FBO)进行离屏渲染,这与QOpenGLWindow的行为相似,更新行为设置为PartialUpdateBlit或PartialUpdateBlend,意味着在paintGL()调用之间保留内容,便于增量渲染 [15] 。
QOpenGLWindow则继承自QWindow,适合需要独立窗口或直接管理渲染上下文的场景。它提供了更底层的控制,允许开发者直接处理窗口系统和OpenGL上下文的关系。QOpenGLWindow通常用于全屏应用或需要直接与底层窗口系统交互的情况,而QOpenGLWidget更适合与Qt的其他GUI组件一起使用。
在Qt6中,QOpenGLWidget仍然是集成OpenGL的主要方式,但需要注意一些API变化。例如,Qt6推荐使用CMake作为构建系统而非qmake,因此配置OpenGL的方式有所调整。同时,Qt6中QOpenGLWidget的更新行为默认为PartialUpdate,这意味着颜色缓冲区和辅助缓冲区不会在帧之间失效,从而提高了渲染效率 。
二、开发环境配置与项目设置
在Qt中使用OpenGL需要进行适当的环境配置和项目设置,确保OpenGL库能够正确加载和使用。
对于使用qmake的项目,需要在.pro文件中添加QT += opengl openglwidgets语句,以声明使用QtOpenGL模块 。这会确保qmake在生成编译器所需的Makefile时添加所需的OpenGL库依赖。同时,在代码中需要包含相应的头文件,如#include 。
对于使用CMake的Qt6项目,配置过程略有不同。需要在CMakeLists.txt中使用find_package(Qt6OpenGL)来查找OpenGL模块,并在目标链接中包含Qt6::OpenGLWidgets [12] 。完整的CMake配置示例如下:
cmake_minimum_required(VERSION 3.16)
project(MyOpenGLApp LANGUAGES CXX)
find_package(Qt6 REQUIRED COMPONENTS Widgets OpenGLWidgets)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 添加源文件
add_executable(myapp main.cpp glwidget.cpp glwidget.h)
# 链接Qt模块和OpenGL库
target_link_libraries(myapp PRIVATE
Qt6::Widgets
Qt6::OpenGLWidgets
)
在跨平台配置方面,需要考虑不同操作系统的差异。在macOS上,由于Apple已弃用OpenGL,推荐使用MoltenVK将Vulkan转换为Metal API [40] 。在Linux上,需要确保系统安装了适当的OpenGL库(如mesa),并且可能需要处理GLVND(OpenGL Version Negotiation Done)与传统库路径的兼容性问题。在Windows上,则需要确保安装了适当的OpenGL驱动,并且在项目中链接了opengl32库。
值得注意的是,在构造 QApplication 实例之前,应该调用 QSurfaceFormat::setDefaultFormat() 来设置 OpenGL 上下文格式 ,特别是在macOS上请求核心配置文件上下文时。这可以确保所有后续创建的OpenGL上下文使用相同的版本和配置文件,避免兼容性问题 [15] 。
三、QOpenGLWidget类的使用方法与渲染流程
QOpenGLWidget是Qt中实现OpenGL渲染的核心类,其使用方法包括继承、重写核心函数和资源管理几个方面。
首先,创建一个继承自QOpenGLWidget的类,并包含QOpenGLFunctions作为受保护的基类,以方便调用OpenGL函数:
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT
public:
MyOpenGLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {}
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
};
在initializeGL()函数中,需要初始化OpenGL环境、创建和编译着色器、分配和填充顶点缓冲区等操作:
void MyOpenGLWidget::initializeGL() {
// 初始化OpenGL函数指针
initializeOpenGLFunctions();
// 设置清除颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// 创建顶点缓冲对象
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(m_points.data(), m_points.size() * sizeof(QVector3D));
// 创建着色器程序
m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
m_program.link();
}
在paintGL()函数中,执行实际的渲染操作。每次调用 paintGL() 时,应首先调用 glClear() 清除缓冲区,然后绑定着色器程序和顶点缓冲区,设置顶点属性指针,最后执行绘制命令 [15] :
void MyOpenGLWidget::paintGL() {
// 清除颜色和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 绑定着色器程序
m_program.bind();
// 绑定顶点缓冲区
m_vbo.bind();
// 设置顶点属性指针
int positionLocation = m_program attributeLocation("position");
m_program enableAttributeArray(positionLocation);
m_program.setAttributeBuffer(positionLocation, GL_FLOAT, 0, 3, sizeof(QVector3D));
// 绘制图形
glDrawArrays(GL_TRIANGLES,0, m_points.size());
// 释放资源
m_vbo.release();
m_program.release();
}
在resizeGL()函数中,设置视口和投影矩阵,以适应窗口大小的变化:
void MyOpenGLWidget::resizeGL(int width, int height) {
// 设置视口
glViewport(0, 0, width, height);
// 设置投影矩阵
mprojection = QMatrix4x4();
mprojection.perspective(45.0f, float(width)/float(height), 0.1f, 100.0f);
}
渲染流程的优化是提高应用程序性能的关键。在Qt6中,QOpenGLWidget提供了多种优化选项。例如,可以使用QSurfaceFormat来设置深度缓冲区大小、模板缓冲区大小和OpenGL版本:
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
此外,还可以使用QOpenGLWidget的updateBehavior()函数来设置更新行为,如使用NoPartialUpdate或PartialUpdate,以适应不同的渲染需求 。
对于大规模图形渲染,可以考虑使用QOpenGLWidget的离屏渲染能力,将渲染结果保存到纹理中,然后在需要时进行显示。这种方法可以避免在每次渲染时都重新计算和绘制所有内容,提高渲染效率。
四、Qt信号与槽机制与OpenGL渲染的交互策略
Qt的信号与槽机制是实现用户交互与OpenGL渲染之间通信的核心机制。通过信号与槽,可以实现从用户界面事件到 OpenGL 渲染更新的无缝连接,同时确保跨线程通信的安全性。
在单线程场景中,可以通过简单的信号槽连接实现用户交互与渲染的更新。例如,当用户点击按钮时,发出一个信号,触发OpenGL渲染的更新:
connectui->btnRotate, &QPushButton::clicked, this, [this]{
emit updateRotation(10.0f); // 发送旋转角度更新信号
});
在OpenGL渲染类中,可以定义一个槽函数来处理这些更新:
class GLWidget : public QOpenGLWidget {
Q_OBJECT
public:
GLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {}
// 定义信号
signals:
void frameRenderStart();
void frameRenderFinished();
// 定义槽函数
slots:
void handleRotationUpdate(float degrees) {
m_rotation += degrees;
update(); // 触发重新渲染
}
// 其他函数...
};
对于动画渲染,通常使用 QTimer 来定期触发 update() 函数,从而实现 paintGL() 的循环调用 :
// 在构造函数中设置定时器
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &GLWidget::update);
timer->start(16); // 约60帧/秒
在多线程渲染场景中,信号与槽机制尤为重要。由于OpenGL渲染通常需要在特定的上下文中执行,跨线程通信需要特别注意上下文的安全性。
一种常见的多线程渲染方案是使用一个专门的渲染线程,该线程与QOpenGLWidget的上下文共享资源。通过信号槽机制,可以控制渲染线程的执行:
class GLWidgetRenderer : public QObject, public QOpenGLFunctions {
Q_OBJECT
public:
GLWidgetRenderer/GLWidget *parent) : QObject(parent), m_parent(parent) {}
// 设置OpenGL上下文
void setGLContext(QOpenGLContext *context, QThread *thread) {
m_context = context;
m_thread = thread;
move to thread(thread); // 将渲染对象移动到渲染线程
}
// 停止渲染
void stopRenderer() {
emit frameRenderFinished(); // 发送渲染完成信号
m exiting = true;
}
// 获取渲染结果纹理
RefTexture *grabTexture() {
return m_outputTexture;
}
public slots:
void render() {
// 在渲染线程中使用共享上下文
m_context->makeCurrent(m_thread);
// 渲染到纹理
glClear(GL_COLOR_BUFFER_BIT);
// ...渲染代码...
emit frameRenderFinished(); // 发送渲染完成信号
// 交换缓冲区
m_parent->swapBUFFers();
}
private:
GLWidget *m_parent;
QOpenGLContext *m_context;
QThread *m_thread;
RefTexture *m_outputTexture;
bool m_exiting;
};
在主窗口中,可以使用信号槽机制控制渲染线程的执行:
// 创建渲染线程
QThread *renderThread = new QThread;
// 创建渲染器
GLWidgetRenderer *renderer = new GLWidgetRendererui->glWidget);
// 将渲染器移动到渲染线程
renderer->moveToThread(renderThread);
// 设置上下文
renderer->setGLContext ui->glWidget->context(), renderThread);
// 连接信号槽
connect this, &MainWindow::startRenderer, renderer, &GLWidgetRenderer::render, Qt::QueuedConnection);
connect renderer, &GLWidgetRenderer::frameRenderFinished, this, &MainWindow::handleFrameFinished, Qt::QueuedConnection);
connect renderThread, &QThread::finished, renderer, &QObject::deleteLater);
connect renderer, &GLWidgetRenderer::frameRenderFinished, renderThread, &QThread::exit);
// 启动渲染线程
renderThread->start();
在跨平台场景中,信号与槽机制的延迟和性能表现可能有所不同 。例如,在macOS上使用Metal API时,渲染线程与UI线程的通信可能需要额外的优化。可以通过测量不同平台下信号槽的延迟,选择最适合的通信方式。
五、性能优化策略
在Qt中使用OpenGL进行图形渲染时,性能优化是确保应用程序流畅运行的关键。以下是几种常见的性能优化策略。
首先,使用现代 OpenGL 特性如 VAO 、 VBO 和 EBO 来管理顶点数据,避免在每次渲染时都重新发送顶点数据到GPU 。例如,可以使用以下代码创建和管理顶点数组对象:
// 创建VAO和VBO对象
glGenVertexArrays(1, &VAO_id);
glGenBuffers(1, &VBO_id);
// 绑定VAO,开始记录属性相关
glBindVertexArray(VAO_id);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER,VBO_id);
// 把数据放进VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_data), vertices_data, GL_STATIC_DRAW);
// 解析数据
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 解绑VBO和VAO
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);
其次,使用定时器驱动的渲染循环,而不是依赖于窗口系统的自动重绘机制。通过控制渲染频率,可以更好地管理性能和用户体验 :
// 在构造函数中设置定时器
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &GLWidget::update);
timer->start(16); // 约60帧/秒
在Qt6.9版本中,QThread 现在支持指定CPU 能效核心调度策略 ,有助于提升多线程应用的性能表现。同时,OpenGL平台恢复了质groundbufferObject加速渲染,进一步提升了渲染效率 [36] 。
对于大规模图形数据,可以考虑使用分层渲染策略,将高频更新和低频更新的元素放在不同的图层中处理,减少不必要的渲染开销 。例如,可以将背景和静态元素放在一个图层中,而将动态元素放在另一个图层中,只在需要时更新特定图层。
此外,避免在渲染过程中频繁打印调试信息,特别是在性能敏感的应用场景中。研究表明,在单线程和多线程情况下,频繁打印调试信息会导致处理时间大幅增加,甚至达到无打印计算的400倍 。
最后,使用适当的渲染模式 ,如双缓冲或帧缓冲区对象(FBO)渲染,可以有效避免画面撕裂和闪烁现象 [15] 。在QOpenGLWidget中,可以通过设置updateBehavior()函数来选择合适的更新行为。
六、跨平台兼容性解决方案
Qt与OpenGL的结合提供了强大的跨平台能力,但不同平台上的实现细节和性能表现可能存在差异。以下是针对不同平台的兼容性解决方案。
在macOS上,由于Apple已弃用OpenGL,推荐使用MoltenVK将Vulkan转换为Metal API [40] 。可以通过以下方式配置:
// 在macOS上设置使用Vulkan通过MoltenVK转译到Metal
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::Vulkan);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
对于需要在macOS上使用OpenGL的应用,必须在构造 QApplication 实例之前调用QSurfaceFormat::setDefaultFormat() 来设置OpenGL 上下文格式 [15] ,特别是在请求核心配置文件上下文时。这可以确保所有后续创建的OpenGL上下文使用相同的版本和配置文件,避免兼容性问题。
在Linux上,需要确保系统安装了适当的OpenGL库(如mesa),并且可能需要处理GLVND与传统库路径的兼容性问题 [12] 。可以通过以下方式设置:
// 在Linux上设置使用GLVND
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(3, 3);
QSurfaceFormat::setDefaultFormat(format);
同时,可能需要在系统环境变量中设置OpenGL库路径:
export LD_LIBRARY_PATH=/usr/lib/aarch64-linux-gnu:$LD_LIBRARY_PATH
在Windows上,需要确保安装了适当的OpenGL驱动,并且在项目中链接了opengl32库。可以通过以下方式配置:
// 在Windows上设置使用OpenGL 3.3
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(3, 3);
QSurfaceFormat::setDefaultFormat(format);
此外,Qt6.9 版本引入了新的渲染循环机制 ,在Windows平台上使用Direct3D垂直同步监视线程,降低CPU负载与延迟,显著改善拖拽等UI交互操作的响应速度 [36] 。
对于跨平台的纹理处理,需要注意不同平台对纹理格式的支持差异。例如,在macOS上,某些纹理格式可能不被支持,需要转换为兼容的格式。可以通过以下方式处理:
// 创建纹理
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D,mTexture);
// 设置纹理参数
glTexParameteri(GL_TEXTURE_2D,GL_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_FILTER,GL_ LINEAR);
// 加载纹理数据
glTexImage2D(GL_TEXTURE_2D,0, GL RGB,width, height, 0, GL RGB, GL UNSIGNEDByte, data);
// 生成Mipmap(可选)
glGenerateMipmap(GL_TEXTURE_2D);
对于跨平台的着色器编译,需要注意不同平台对GLSL版本的支持差异。可以通过以下方式处理:
// 编译着色器
QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, this);
vertexShader->setSourceCode(vertexShaderSource);
if (!vertexShader->compile()) {
qDebug() << "Vertex Shader编译失败:" << vertexShader->log();
return;
}
QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, this);
fragmentShader->setSourceCode(fragmentShaderSource);
if (!fragmentShader->compile()) {
qDebug() << "Fragment Shader编译失败:" << fragmentShader->log();
return;
}
// 链接着色器
m_program.addShader(vertexShader);
m_program.addShader(fragmentShader);
if (!m_program.link()) {
qDebug() << "着色器程序链接失败:" << m_program.log();
return;
}
// 释放资源
delete vertexShader;
delete fragmentShader;
七、实践案例与应用场景
Qt与OpenGL的结合在多种应用场景中表现出色,包括科学可视化、游戏开发、工业设计和医疗成像等。
在科学可视化领域,Qt与OpenGL的结合可以实现复杂的3D数据展示和交互 。例如,可以使用QOpenGLWidget来渲染地质数据或气象数据,同时使用Qt的界面组件来控制渲染参数和交互方式。这种组合能够提供直观的用户界面和高效的图形渲染能力,满足科学可视化应用的需求。
在工业设计领域,Qt与OpenGL的结合可以实现CAD软件中的3D模型展示和编辑 。例如,可以使用QOpenGLWidget来渲染机械部件或建筑模型,同时使用Qt的界面组件来提供工具栏、属性面板和实时预览等功能。这种组合能够提供专业的图形渲染能力和友好的用户界面,满足工业设计应用的需求。
在医疗成像领域,Qt与OpenGL的结合可以实现医学影像的三维重建和可视化 。例如,可以使用QOpenGLWidget来渲染CT或MRI扫描结果,同时使用Qt的界面组件来提供切片控制、颜色映射和测量工具等功能。这种组合能够提供高质量的图形渲染能力和专业的用户界面,满足医疗成像应用的需求。
在游戏开发领域,Qt与OpenGL的结合可以实现游戏引擎的开发,提供图形渲染和用户界面管理的能力 [6] 。例如,可以使用QOpenGLWidget来渲染游戏场景,同时使用Qt的界面组件来提供游戏菜单、设置面板和实时状态显示等功能。这种组合能够提供强大的图形渲染能力和灵活的界面设计,满足游戏开发应用的需求。
八、未来发展趋势与建议
随着图形技术的不断发展,Qt与OpenGL的结合也在不断演进。未来的发展趋势包括:
首先,Vulkan 和 DirectX 12 等现代图形 API 的普及将影响Qt与OpenGL的结合方式。这些API提供了更低的开销和更高的性能,但学习曲线也更陡峭。Qt可能会提供对这些API的更好支持,或者通过抽象层简化它们的使用。
其次,跨平台渲染引擎的发展将使得Qt与OpenGL的结合更加灵活。例如,Qt Quick 3D已经提供了对多种图形API的支持,未来可能会进一步扩展,使得开发者可以在不同平台上选择最适合的渲染方式。
最后,AI 驱动的图形渲染技术将为Qt与OpenGL的结合带来新的可能性。例如,可以使用AI算法来优化图形渲染流程,提高渲染效率和质量。
基于以上分析,对开发者提出以下建议:
首先,熟悉 Qt6 和 OpenGL 4.x 的最新特性 ,特别是Qt6.9版本中引入的性能优化和跨平台改进 [36] 。这可以帮助开发者充分利用框架的优势,构建更高效的应用程序。
其次,采用模块化的设计方法,将UI组件和图形渲染逻辑分离,便于跨平台适配和维护。例如,可以将OpenGL渲染逻辑封装在单独的类中,而将UI交互逻辑封装在另一个类中,通过信号槽机制进行通信。
最后,关注跨平台性能优化,特别是在不同操作系统上针对特定图形API进行优化。例如,在macOS上可以考虑使用Metal API替代OpenGL,以获得更好的性能和兼容性。
九、结论
Qt与OpenGL的结合为开发者提供了一个强大的图形应用开发框架,能够兼顾图形渲染性能和用户界面设计的灵活性。通过QOpenGLWidget和QOpenGLWindow等类,可以方便地将OpenGL集成到Qt应用程序中,实现跨平台的图形渲染。
在实际应用中,开发者需要根据具体需求选择合适的集成方式和渲染策略。对于需要将图形渲染嵌入到复杂用户界面的应用,QOpenGLWidget是更合适的选择;而对于需要直接控制渲染上下文和窗口系统的应用,QOpenGLWindow可能更适合。
性能优化和跨平台兼容性是Qt与OpenGL应用开发中的重要挑战。通过采用现代OpenGL特性、优化渲染循环、管理多线程通信和适配不同平台的图形API,可以构建高效、流畅的跨平台图形应用程序。
随着图形技术的不断发展,Qt与OpenGL的结合方式也在不断演进。开发者应该关注框架的最新特性和发展趋势,以便构建更高效、更现代的应用程序。
参考来源:
1. Qt结合OpenGL4.x的高效跨平台框架指南-CSDN文库
3. 探索Qt与OpenGL的完美结合:高效图形程序开发-CSDN文库
5. Qt环境下的OpenGL模块使用实践教程-CSDN博客
6. qt通过OpenGL实现3d游戏开发框架-腾讯云开发者社区-腾讯云
8. 探索QtOpenGL:一个强大的图形编程框架-CSDN博客
11. CMake查找OpenGL的详细过程及配置解析-CSDN博客
12. qt的cmake工程,查找opengl的流程-CSDN博客
13. 使用QOpenGLWidget(调用GPU)渲染QImage加载的图片
14. Qt结合OpenGL 4.x的高效跨平台框架指南-CSDN文库
15. Qt QOpenGLWidget类讲解-CSDN博客
16. Qt音视频开发24-视频显示QOpenGLWidget方式(占用GPU)知...
17. QGLWidget、QOpenGLWidget详解及区别
18. Qt6.3.1中使用QOpenGLWidget-飘杨.-博客园
19. qt6下配置qopengl_qt6 qopenglwidget-CSDN博客
21. qt+opengl着色器VAO、VBO、EBO(四)qtopengl vaovbo-CSDN博客
23. QOpenGLWidget的多线程渲染_opengl多线程渲染-CSDN博客
24. QT编程入门教程:从HelloQt到OpenGL绘图-CSDN文库
26. 关于QOpenGLWidget的多线程渲染_contextqopenglwidget-CSDN博客
29. Qt 6.9新版本来袭:3D可视化与AI工具革新,如何应对职场挑战?Unicode_应用_支持
35. OpenGL、Vulkan、DirectX、Metal 在移动端和 PC 端的对比分析
36. Qt框架6.9发布:增强 3D 数据可视化、升级 emoji 支持-IT之家
37. 事实胜于雄辩,苹果MacOs能不能玩儿机器/深度(ml/dl)学习...
39. 使用QOpenGLWidget(调用GPU)渲染QImage加载的图片
40. OpenGL、Vulkan、DirectX、Metal 在移动端和 PC 端的对比分析
42. QT6窗口系统之QT底层窗口QWindow:QT框架中哪些常见窗口是基于...
43. Linux环境下编译配置Qt开源版全攻略-CSDN文库
44. 使用QOpenGLWidget(调用GPU)渲染QImage加载的图片