这个教程是从一个国外的博主搬过来的,我做了一些简单翻译和简化工作,因为我发现目前存在的qtopengl的代码版本太低了,很难上手使用,因此我将在Qt5的基础上,说明下qtopengl的使用,并将文章作为自己的一些笔记,笔者能力有限,可能存在一些疏忽的地方,欢迎大家指证。
在接下来的介绍中,已经默认读者掌握了Qt的 信号和槽的使用,并能简单的构建一些应用程序。注意这里不是opengl或qt的教程,而是这两者技术的结合。
注意如果你对Opengl的知识还是不够了解,请移步这里至了解下基础知识,方便后面教程的理解。
QOpenGLWindow
QOpenGLWindow是一个新类,仅继承自基于Qt5Gui的类。 之所以重要,是因为它允许我们提供OpenGL抽象,而无需Qt5Widgets模块。
我们真正关心的只有QOpenGLWindow的是以下功能:
initializeGL()
resizeGL(int width,int height)
paintGL()
可以想象,这些功能使我们能够执行OpenGL逻辑。 实际上,这就是创建一个简单的OpenGL应用程序所需要的全部。
QOpenGLFunctions
为方便起见,我们可以选择将QOpenGLFunctions与QOpenGLWindow一起子类化。在现代版本的Qt中,不需要GLEW,并且QOpenGLFunctions是可以使用OpenGL ES 2.0 API的结构。 (我们将简短地进行此操作。)
注:此处的Opengl ES2.0是早期的版本,开放性不及3.3版本,此外3.3以后的版本更新都是在此基础上增加额外功能,因此我们这里使用的都是3.3以上的API接口,如果你是只老鸟就忽略吧
你不需要子类来访问QOpenGLFunctions的功能,如果愿意,可以通过QOpenGLContext获取可用的功能。可以通过以下两种方式之一将其实例化为局部变量:
QOpenGL函数示例C ++
QOpenGLFunctions functions(QOpenGLContext::currentContext());
functions.glFunctionHere();
// or...
QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
functions->glFunctionHere();
之所以默认为OpenGL ES 2.0,是出于兼容性方面的考虑,因此我们可以轻松地将产品移植到嵌入式平台(例如Android和iOS)。我们的示例将使用此函数上下文,但是我们也可以要求特定的函数上下文。 (例如QOpenGLFunctions_4_3_Core)
这是很不错的,因为我们可以将自己限制为仅要支持的上下文。除了OpenGL ES 2.0 API,我们没有其他需要,因此我们将坚持使用QOpenGLFunctions类。
创建一个窗口
有了这些知识,我们将从创建OpenGL窗口开始。 多亏了Qt5,使该应用程序变得微不足道,因为跨平台OpenGL最难的部分已设置好。 (由于需要GLEW,因此特别涉及Windows。)这将为我们其他项目提供一个不错的起点。
1,准备一个新项目(Qt5.4 GuiModule)
首先,我们将创建一个新的Qt项目,没什么特别的-我通常选择Console Application并随需添加所需的内容。
有一点我们需要注意的是,我们需要在我们的项目文件中添加一些东西,我们要把Qt5Gui的模块添加进去。
原来的项目文件如:
# ...
QT += core
QT -= gui
# ...
添加完Qt5Gui 模块后,我们需要编辑为如下:
# ...
QT += core gui
# ...
现在我们可以使用QOpengl相关的类了,因为这是Qt5Gui模块的一部分。
2.创建一个窗口类
接下来,我们需要用之前了解到的opengl类创建一个窗口类,首先新建一个类文件。
然后我们编辑我们的windows.h文件如下:
#ifndef WINDOW_H
#define WINDOW_H
#include <QOpenGLWindow>
#include <QOpenGLFunctions>
class Window : public QOpenGLWindow,
protected QOpenGLFunctions
{
Q_OBJECT
// OpenGL Events
public:
~Window();
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
void teardownGL();
private:
// Private Helpers
void printContextInformation();
};
#endif // WINDOW_H
这里我们继承了两个类,从而以便于我们可以使用一些opengl的功能,但是有两个新内容我们有必要解释一下,
- teardownGL()
- 我们需要一个函数来执行清理的工作。如果 OpenGLWindow::makeCurrent()第一次被调用,清理工作会在析构中完成,但是我想要一个特殊的函数使得我们的接口清晰明显。
- printContextInformation()
- 此辅助函数将打印有关我们已获取的OpenGL上下文的信息,以用于调试目的。
接下来我们实现我们的函数(window.cpp)
#include "window.h"
#include <QDebug>
#include <QString>
Window::~Window()
{
makeCurrent();//当基础平台窗口已被破坏时,这样可以确保析构函数中的OpenGL资源清理操作始终有效。
teardownGL();
}
/*******************************************************************************
* OpenGL Events
******************************************************************************/
void Window::initializeGL()
{
// Initialize OpenGL Backend
initializeOpenGLFunctions();
printContextInformation();
// Set global information
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
void Window::resizeGL(int width, int height)
{
(void)width;
(void)height;
}
void Window::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
}
void Window::teardownGL()
{
// Currently we have no data to teardown
}
/*******************************************************************************
* Private Helpers
******************************************************************************/
void Window::printContextInformation()
{
QString glType;
QString glVersion;
QString glProfile;
// Get Version Information
glType = (context()->isOpenGLES()) ? "OpenGL ES" : "OpenGL";
glVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
// Get Profile Information
#define CASE(c) case QSurfaceFormat::c: glProfile = #c; break
switch (format().profile())
{
CASE(NoProfile);
CASE(CoreProfile);
CASE(CompatibilityProfile);
}
#undef CASE
// qPrintable() will print our QString w/o quotes around it.
qDebug() << qPrintable(glType) << qPrintable(glVersion) << "(" << qPrintable(glProfile) << ")";
}
这是十分基本的步骤,这里仅有一个棘手的部分。
- initializeGL()
- QOpenGLFunctions::initializeOpenGLFunctions(), 在当前opengl上下文环境下执行初始化。
- Window::printContextInformation() 这是我们自己编写的函数, 这会在输出台打印一些版本的信息。
- glClearColor() 实际上继承于QOpenGLFunctions,这可以简单的设置清除的颜色
- resizeGL()
- 由于我们没有做任何复杂的图形,因此在此功能中我们什么也不做。
- paintGL()
- 同样,非常基本,我们只是使用在initializeGL()中设置的颜色清除背景色。
- teardownGL()
- 什么也不做, 我们还没有在GPU上分配任何资源。
- printContextInformation()
- 基于我们如何创建OpenGL上下文,我们可以访问OpenGL的不同版本和功能。 据我们所知,在控制台中打印版本非常有用(作为健全性检查)。
快要完了,但还有一些工作。
3.更新main.cpp来创建函数
实际上,我前面没有提到另一个Qt5类。 QGuiApplication。 它不是特定于OpenGL的,因此我不愿意为它添加详细的说明。 现在,main.cpp中的默认功能包括创建一个QCoreApplication并运行它。
我们将使用QGuiApplication进行更改(因为这就是我们所做的)。 在执行exec()之前,我们将创建并显示一个Window。
#include <QGuiApplication>
#include "window.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
// Set OpenGL Version information
// Note: This format must be set before show() is called.
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(3,3);
// Set the window up
Window window;
window.setFormat(format);
window.resize(QSize(800, 600));
window.show();
return app.exec();
}
这段代码不言自明。 QSurfaceFormat是我们设置请求的OpenGL版本的方式。 无需明确提供QSurfaceFormat,但这是检查我们的printContextInformation()函数功能的好方法。
此时,您应该能够保存,构建和运行。 使用当前输入,您将看到获得的响应(如果您的计算机支持OpenGL 3.3)为:
OpenGL 3.3(CoreProfile)
对于我们所做的所有工作,您都应该拥有一个平淡无奇的窗户。
尝试将CoreProfile更改为CompatibilityProfile,并在不同的版本号之间进行操作; 检查输出是否有意义。 NoProfile保留用于不包含核心/兼容性上下文请求的版本。