QT中使用OPENGL的方法
前言
传统上学习OpenGL的代码需要先配置好GLFW和GLAD。
GLFW允许用户创建OpenGL上下文,定义窗口参数以及处理用户输入。
由于OpenGL驱动版本众多,它大多数函数的位置都无法在编译时确定下来,需要用到GLAD在运行时辅助我们查询。
而Qt作为一个GUI框架,在提供窗口的同时又对OpenGL进行了很好的封装,免去了自己配置GLFW和GLAD环境的麻烦。
本文的第三节以经典的绘制三角形为例,描述了如何将原生OpenGL绘制三角形代码迁移到Qt中运行起来。
第一节和第二节是对第三节中环境迁移的背景补充。
1 QOpenGLWidget类的使用
QOpenGLWidget用于显示OpenGL代码绘制的图形。使用时需要:
- 创建一个新类(本例名为QtOpenGLWidget )并继承自QOpenGLWidget和QOpenGLFunctions_3_3_Core。QOpenGLFunctions_x_x_x中可选择OpenGL的版本等信息,本例选择的是OpenGL3.3核心模式。
- 重载三个重要的虚函数initializeGL、paintGL、resizeGL。
protected:
void initializeGL() override; //负责初始化
void paintGL() override;//负责执行绘画,该函数会被自动的反复调用
void resizeGL(int width, int height) override;//窗口尺寸改变时调用
2 在创建好的Qt空窗口中调用上一步自定义的类
本例首先通过Qt的向导默认创建了一个继承自QMainWindow的名为Tranangle的空窗口类。然后在其构造函数中调用和设置用于显示OpenGL图形的自定义类QtOpenGLWidget。
Tranangle::Tranangle(QWidget *parent)
: QMainWindow(parent)
{
this->resize(619, 418);//设置程序窗口的大小
//在头文件中声明QtOpenGLWidget *glWidget;
glWidget = new QtOpenGLWidget(this);//实例化用于显示图形的自定义类
setCentralWidget(glWidget);//将图形显示界面的大小铺满程序窗口
}
配置好以上的代码,构造空窗口Tranangle的时候,会进而自动调用QtOpenGLWidget来完成图案绘制与显示。
3 将原生OpenGL代码迁移至Qt
本节以图片的方式详细的描述了如何将原生OpenGL绘制三角形的代码移植到Qt中完成三角形的绘制。
4 源码
main.cpp
#include "tranangle.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Tranangle w;
w.show();
return a.exec();
}
tranangle.h
#pragma once
#include <QtWidgets/QMainWindow>
#include "qtOpenGLWidget.h"
class Tranangle : public QMainWindow
{
Q_OBJECT
public:
Tranangle(QWidget *parent = Q_NULLPTR);
private:
QtOpenGLWidget *glWidget;
};
tranangle.cpp
#include "tranangle.h"
Tranangle::Tranangle(QWidget *parent)
: QMainWindow(parent)
{
this->resize(619, 418);
glWidget = new QtOpenGLWidget(this);
setCentralWidget(glWidget);
}
qtOpenGLWidget.h
#pragma once
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QDebug>
class QtOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
QtOpenGLWidget(QWidget *parent = 0);
~QtOpenGLWidget();
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
};
qtOpenGLWidget.cpp
#include "qtOpenGLWidget.h"
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n" "void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\n\0";
unsigned int shaderProgram, VAO, VBO;
QtOpenGLWidget::QtOpenGLWidget(QWidget *parent)
: QOpenGLWidget(parent)
{
}
QtOpenGLWidget::~QtOpenGLWidget()
{
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
}
void QtOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();//初始化
//顶点着色器
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
//判断顶点着色器是否编译成功
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
qDebug() << "ERROR::SHADER" << infoLog;
}
//片段着色器
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
//判断片段着色器是否编译成功
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
qDebug() << "ERROR::FRAGEMENT" << infoLog;
}
//链接阶段
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
//判断是否链接成功
glGetShaderiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
qDebug() << "ERROR::SHADERPROGRAM" << infoLog;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
//建立顶点数据
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
//配置顶点属性
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
//绑定VAO
glBindVertexArray(VAO);
//绑定并设置VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//配置顶点属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void QtOpenGLWidget::resizeGL(int width, int height)
{
}
void QtOpenGLWidget::paintGL()
{
//着色
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//绘制第一个三角形
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
结尾
Qt以面向对象的方式另外提供了更友好的编码风格和方式,大大提高了代码的可读性。QT中使用OpenGL的方法(二)会介绍如何以这种方式完成Qt中OpenGL的编码。
如果觉得本文对您有所帮助的话,请打赏1毛钱让我实时的感受到技术分享的乐趣。您的支持与鼓励是我前进的动力!