本帖将参考LearnOpenGL这一经典教程,使用Qt的原生环境完成教程中所提的所有流程,并尽量和原教程保持一致,如有错误,欢迎评论!
为了方便大家参考,我将项目分享至了gitee,并实时进行更行:Qt实现OpenGL的经典教程: (gitee.com)
Qt版本:6.7
操作系统:Windows10
Qt如何创建OpenGL渲染窗口
类比GLFW库,Qt也提供了供OpenGL渲染的窗口,参考Qt原文档,可找到以下内容:
-
子类QOpenGLWidget以如下方式渲染纯3D内容:
-
重新实现initializeGL()和resizeGL()函数来设置OpenGL状态并提供透视图转换。
-
重新实现paintGL()来绘制3D场景,只调用OpenGL函数。
-
还可以使用QPainter将2D图形绘制到QOpenGLWidget子类上。在paintGL()中,不是发出OpenGL命令,而是构建一个用于部件的QPainter对象。
综合一下,就是说重写子类QOpenGLWidget的initializeGL、resizeGL和paintGL三个函数,可以实现渲染的全部功能,并且可以结合QPainter将2D图像渲染到OpenGL窗口上(注意QPainter没调用OpenGL渲染)
如何实现OpenGL函数绑定
上一节我们找到了创建窗口的方法,实现了类似GLFW库的功能,那么如何使用GLAD库的函数绑定功能呢?Qt也提供了途径,再次参考帮助文档,可以找到如下内容:
-
在调用OpenGL函数时,强烈建议避免直接调用函数。相反,更喜欢使用QOpenGLFunctions(当制作可移植的应用程序时)或版本化的变体(例如,QOpenGLFunctions_3_2_Core或类似的,当目标是现代的、只针对桌面的OpenGL时)。这样,应用程序将在所有Qt构建配置中正确工作,包括那些执行动态OpenGL实现加载的应用程序,这意味着应用程序没有直接链接到GL实现,因此直接函数调用是不可行。
-
在paintGL()中,通过调用QOpenGLContext::currentContext()始终可以访问当前上下文。在这个上下文中,通过调用QOpenGLContext::functions()可以取得一个已经初始化的、随时可用的QOpenGLFunctions实例。除了给每个GL调用都加上前缀之外,还有一种方法是继承QOpenGLFunctions,然后在initializeGL()中调用QOpenGLFunctions::initializeOpenGLFunctions()。
-
至于OpenGL头文件,请注意,在大多数情况下,不需要直接包含任何像GL.h这样的头文件。与opengl相关的Qt头文件将包括qopengl.h,而qopengl.h又将包括一个适合系统的头文件。这可能是OpenGL ES 3。X或2.0头文件(可用的最高版本),或者系统提供的gl.h。此外,对于OpenGL和OpenGL ES, Qt都提供了扩展头的副本(在某些系统上称为glext.h)。这些将在可行的平台上自动包含。这意味着来自ARB、EXT、OES扩展的常量和函数指针typedefs是自动可用的
总结一下就是,Qt提供了绑定OpenGL函数指针的方法,不需要包含GLAD库中类似gl.h这样的头文件,不建议直接调用OpenGL函数,需要通过QOpenGLFunctions这一实例来进行函数调用,那么这种调用方法主要有以下俩种:
1、直接从OpenGLContext::functions()中获取
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
2、继承QOpenGLFunctions
class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
...
void initializeGL() override
{
initializeOpenGLFunctions();
glClearColor(...);
...
}
...
};
显然第二种更加方便,我们将使用第二种方法实现。
开始实现
创建子类MyOpenGLWidget.h
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QtOpenGLWidgets/QtOpenGLWidgets>
class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
MyOpenGLWidget();
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
};
#endif // MYOPENGLWIDGET_H
创建子类MyOpenGLWidget.cpp
#include "myopenglwidget.h"
MyOpenGLWidget::MyOpenGLWidget() {
//设置上下文属性
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 3); //OpenGL 版本号3.3
format.setProfile(QSurfaceFormat::CoreProfile);// 核心模式
this->setFormat(format);
}
void MyOpenGLWidget::initializeGL(){
//绑定OpenGL函数指针?类似GLAD库的作用?
initializeOpenGLFunctions();
}
void MyOpenGLWidget::paintGL(){
//由于继承了QOpenGLFunctions,可以直接使用OpenGL中的函数
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
void MyOpenGLWidget::resizeGL(int w, int h){
}
其中每次窗口刷新都会调用我们重写的paintGL()函数,那么我们调用glClearColor()为其设置一个颜色,并清除掉颜色缓冲,接着我们在MainWindow.cpp中创建我们的窗口
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
_myOpenGLWidget = new MyOpenGLWidget;
this->setCentralWidget(_myOpenGLWidget);
//设置窗口大小
this->setGeometry(0,0,800,600);
}
MainWindow::~MainWindow() {
if(_myOpenGLWidget)delete _myOpenGLWidget;
}
设置完窗口大小后,那么我们运行就能看到以下一个窗口:
下图是原教程的运行结果: