Qt+OpenGL 的一些问题记录

1. 开发背景

大多数人学习OpenGL估计都是从LearnOpenGL这个网站开始的吧,这个教程使用的是VS Studio + glfw + glad的开发方式。

好吧,我之前也是这么做的,但是OpenGL本身是C风格的API, 如果用面向对象的思想其写,稍微有一些麻烦,而且在调试参数的时候不太方便。

后来我在Review LearnOpenGL的时候就想换一种开发方式学习:

方案一: ImGui + OpenGL

  • 优点:ImGui集成了glfw环境,可以实现一些简单的控件来修改参数,方便查看实时效果,就如下图。
  • 缺点:我懒得去学习一门新的ui库=。=

参考教程:ImGui+OpenGL教程

方案二: Qt+OpenGL

Qt-OpenGL的几个优势:

  • Qt内嵌了opengl的相关环境,不需要我们自己来搭建,这对小白来说是很友好的。
  • Qt和opengl都具有优良的跨平台特性,使用Qt做opengl开发可谓是强强联合。
  • Qt可以轻松的控制窗口的各种处理事件以及窗口属性。
  • Qt提供了opengl函数的C++封装,使得opengl原来的C风格API可以通过C++的面向对象技术来实现。
  • Qt提供了十分完善的官方文档,有助于我们掌握QtOpenGL的各种细节。

缺点:

  • Qt对OpenGL的封装导致代码和Qt框架耦合,很难抽离出来作为独立SDK,这在一些团队中可能不会考虑

由于个人对Qt熟悉一些,所以后来都是用Qt作为OpenGL学习的开发测试环境。

2. Qt+OpenGL开发

Qt中使用OpenGL一般有两种方式:

  • 封装成Widget嵌套在其他页面中
  • 作为独立Window使用

2.1 第一种:作为Widget嵌套使用

在Qt5.4之前,可以自定义一个类,继承自QGLWidget来实现,后来推出了### QOpenGLWidget这个类简化了流程。

QOpenGLWidegt

创建opengl窗口只需新建类继承于QOpenGLWidegt,再实现QOpenGL提供的三个虚函数,就可以完成opengl窗口的创建。

  • initializeGL()---建立OpenGL的资源和状态。在第一次调用resizeGL()或paintGL()之前调用一次
  • resizeGL()---设置OpenGL视口,投影等。每当调整Widget的大小时(第一次显示窗口Widget时会调用它,因为所有新创建Widget都会自动获得调整大小的事件)。
  • paintGL()---渲染OpenGL场景,需要更新Widget时就会调用。

QOpenGLExtraFunctions

QOpenGLExtraFunctions类继承于QOpenGLFunctions,相较于QOpenGLFunctions,额外提供了对OpenGL ES 3.0、3.1和3.2 API的跨平台访问,如果我们需要在类中使用opengl函数,只需要使类继承于QOpenGLExtraFunctions。

以如下代码为例:

arduino 复制代码
#ifndef OPENGL_TOOLS_QGL_WIDGET_HPP  
#define OPENGL_TOOLS_QGL_WIDGET_HPP  
  
#include <QOpenGLWidget>  
#include <QOpenGLFunctions>  
#include <QOpenGLBuffer>  
  
class QGL_Widget : public QOpenGLWidget, QOpenGLFunctions{  
    Q_OBJECT  

public:  
    using QOpenGLWidget::QOpenGLWidget;  

    explicit QGL_Widget(QWidget* parent = nullptr) : QOpenGLWidget(parent)  
    {  
    }  

    void initializeGL() override  
    {  
        initializeOpenGLFunctions();  
        glClearColor(0.2f, 0.1f, 0.8f, 0);  
    };  

    void resizeGL(int w, int h) override  
    {  
    };  

    void paintGL() override  
    {  
    };  

};  
#endif //OPENGL_TOOLS_QGL_WIDGET_HPP

调用如上窗口:

scss 复制代码
QGL_Widget *gl_widget = new QGL_Widget(this);  
gl_widget->setGeometry(QRect(0,00,400,300));  
gl_widget->show();

2.2 作为Window独立显示

Qt6中提供了QOpenGLWindow作为一个gl独立窗口,注意在Qt5和Qt6中稍有不同,建议使用最新的Qt6开发。

我们创建一个类集成自QOpenGLWindow和QOpenGLFunctions,来实现一个绘制三角形的窗口

SimpleTriangleWindow.h

c++ 复制代码
#ifndef OPENGL_TOOLS_SIMPLETRIANGLEWINDOW_H  
#define OPENGL_TOOLS_SIMPLETRIANGLEWINDOW_H  
  
#include <QOpenGLWindow>  
#include <QOpenGLFunctions>  
#include <QOpenGLVertexArrayObject>  
#include <QOpenGLBuffer>  
#include <QOpenGLShaderProgram>  
  
class SimpleTriangleWindow : public QOpenGLWindow, protected QOpenGLFunctions  
{  
    Q_OBJECT  
public:  
    SimpleTriangleWindow()  
    {  
    }  
  
    ~SimpleTriangleWindow()  
    {  
    }  
  
protected:  
  
    void initializeGL()  
    {  
    }  
  
    void initShader()  
    {  

    }  
  
    void resizeGL(int w, int h)  
    {  

    };  
  
    void paintGL()  
    {  
    };  
};  
  
#endif //OPENGL_TOOLS_SIMPLETRIANGLEWINDOW_H

创建VAO、VBO、着色器程序:

ini 复制代码
private:  
    //着色器程序  
    QOpenGLShaderProgram program;  
    QOpenGLVertexArrayObject m_vao;  
    QOpenGLBuffer m_vbo;  

在初始化方法:

scss 复制代码
void initializeGL()  
{  
    initializeOpenGLFunctions();  
    glEnable(GL_DEPTH_TEST); //开启深度测试  
    glClearColor(0.0,0.0,0.0,0.0);  

    //创建vao.vbo  
    m_vao.create();  
    m_vbo.create();  

    //shader加载  
    initShader();  

    float _vertex[] = {  
        0.0, 0.5, 0.0,  
        -0.5, -0.5, 0.0,  
        0.5, -0.5, 0.0,  
    };  
    m_vao.bind();  
    m_vbo.bind();  
    m_vbo.allocate(_vertex,sizeof(_vertex));  

    program.bind();  
    program.setAttributeBuffer("vPos",GL_FLOAT,0,3,0);  
    program.enableAttributeArray("vPos");  

    program.release();  
    m_vao.release();  
}

initShader()是着色器加载程序,这里没有对着色器代码做修改

scss 复制代码
void initShader()  
{  
    // Compile vertex shader  
    if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/resource/shader/vshader.glsl"))  
        close();  

    // Compile fragment shader  
    if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/resource/shader/fshader.glsl"))  
        close();  

    // Link shader pipeline  
    if (!program.link())  
        close();  

    // Bind shader pipeline for use  
    if (!program.bind())  
        close();  
}

着色器代码:

fshader.glsl 复制代码
#version 330 core  
  
void main()  
{  
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);  
}
vshader.glsl 复制代码
#version 330 core  
in vec3 vPos;  
  
void main()  
{  
    gl_Position = vec4(vPos, 1.0);  
}

到此初始化工作已经完成,在paintGL中绘制:

scss 复制代码
void paintGL()  
{  
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
    m_vao.bind(); //启用VAO  
    program.bind(); //启用Shader  
    glDrawArrays(GL_TRIANGLES,0,3); //用三个点画一个三角形  
    program.release(); //释放Shader  
    m_vao.release(); //释放VAO  
};

最终效果:

我们从上述代码可以看到在Qt的环境中开发OpenGL有很多和传统API不一样:

  • Qt提供了QOpenGLVertexArrayObject类来创建管理VAO对象
  • QOpenGLBuffer用来创建管理VBO/IBO/EBO对象
  • QOpenGLShaderProgram类来加载、管理着色器程序

对着色器参数修改也非常方便:

scss 复制代码
m_program->bind(); 
m_program->setAttributeBuffer("vPos", GL_FLOAT, 0, 3, 0); 
m_program->enableAttributeArray("vPos");

整体代码非常简洁,流程比原生API要清晰很多,但是问题也在这里,对于不熟悉Qt GL Api的人来说,刚开始很难熟悉,即时熟悉了之后的开发也会局限在Qt框架中, 非常不利于扩展。

关于具体使用Qt-OpenGL教程,可以参考如下系列:


后续我不太想在Qt中进行OpenGL开发,局限性太大,据我所知,很多公司在实际开发中,即时选择了Qt框架,也不会选择Qt的OpenGL API开发,而是自己封装一套接口或者使用的其他反方库。优点就是可扩展性好,和Qt框架解耦,在移植的时候代价更小。

相关推荐
余额瞒着我当琳5 小时前
C++多态深入解析:多态概念,多态实现条件,虚函数表与内存分布,常见问题及注意事项
c++·算法
ocean21035 小时前
2025-2026年C++专题大厂面试高频问题
开发语言·c++·面试
程序猿编码5 小时前
扔掉 Python!纯 C++ 本地跑扩散 TTS,GGML 加持,支持 RK3588 NPU 加速
c++·计算机视觉·大模型·transformer·rk3588·推理·ggml
郝学胜-神的一滴5 小时前
C++20模板元编程 02:吃透模板基础核心四大核心知识点
开发语言·数据结构·c++·程序人生·软件工程·visual studio
代码王同学6 小时前
2026.9.15日C++类和对象学习收获笔记
c语言·c++
刚入门的大一新生6 小时前
Linux-简单设计libc库
linux·c++
6Hzlia7 小时前
【Classic 150 刷题计划】 LeetCode 14. 最长公共前缀 | C++ 纵向扫描法与防越界细节
c++·算法·leetcode
讳疾忌医丶15 小时前
深度拆解 RocksDB 内核:基于 C++17 的 FIFO 调度状态机与温度阶梯自愈设计
java·c++·算法·架构
2401_8685347816 小时前
OpenStack 架构全景:八个核心组件深度拆解
c++·需求分析
软件课代表CiCi16 小时前
运行库是什么?电脑缺运行库怎么办?c++运行库缺失修复全攻略
开发语言·c++·windows·电脑·dll修复·dll丢失·运行库