初始Opengl之QT
1. OpenGL 概述
OpenGL是一个规范,此规范有各个厂家最终实现。OpenGL的对外体现就是一个库的概念。
OpenGL现在分为核心模式和立即渲染模式(即固定的渲染管线),核心模式是OpenGL 3.3开始支持的,核心模式指可编程模式,核心模式的渲染管线(其实就是从一系列数据到最终配置图像的流程)如下:

其中顶点着色器和片段着色器需要程序员自己实现。
若要写一个OpenGL的可视化程序则需要另外两个库的辅助GLFW和GLAD。
GLFW的作用是用来解决图形界面显示、消息处理及用户输入处理。GLAD则使得代码可以用于不同的OpenGL驱动,即对OpenGL做的封装,使得不同系统的上层使用可以更简便的使用OpenGL。
可以借助QT对OpenGL的封装,实现无需配置第三方库的简便开发。
CPU数据如何与GPU数据交互,我们在程序中书写的顶点数据是在CPU中的,如果GPU要使用这些数据,我们应该明确的告诉GPU应该怎么使用。这其中涉及到VBO(Vertex Buffer Objects)、VAO(Vertex Array Objects)及VEO(Vertex Element Objects),其中VEO也被称为VIO(Vertex Index Objects)
VBO: 用来在GPU中创建内存并存储顶点数据,顶点缓冲对象类型为GL_ARRAY_BUFFERVAO: 用来配置OpenGL如果解析这些内存,VAO中存储的是VBO数据类型的定义,而不存储数据本身VEO: 它用来存储顶点数据的索引,绘图时根据索引进行绘制
VBO、VAO及VEO的关系:

VAO 存储EBO的glBindBuffer的调用,但不会存储VBO的glBindBuffer调用
2. QT 项目
UI界面:

黑色窗口为OpenGL Widget,这里对其进行了提升即修改了其继承的父类为自定的MyOpenGLWidget,在控件中右键选择提升即可操作界面如下:

myopenglwidget.h内容如下:
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
class MyOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
explicit MyOpenGLWidget(QWidget *parent = nullptr);
~MyOpenGLWidget();
protected:
virtual void initializeGL() override;
virtual void paintGL() override;
signals:
private:
GLuint m_VBO;
GLuint m_VAO;
// 这里使用非静态变量glCreateProgram会报错,目前还不清楚具体的是为什么(也可能是我环境本身的问题)
static GLuint m_shaderProgram;
// 这里使用非静态变量报错,目前还不清楚具体的是为什么(也可能是我环境本身的问题)
static GLuint m_EBO;
};
#endif // MYOPENGLWIDGET_H
myopenglwidget.cpp内容如下:
#include "myopenglwidget.h"
#include <QDebug>
GLuint MyOpenGLWidget::m_shaderProgram = 0;
GLuint MyOpenGLWidget::m_EBO = 0;
MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
}
MyOpenGLWidget::~MyOpenGLWidget()
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
}
GLfloat vertex[]={
-0.5f,-0.5f,0.0f,
0.5f,-0.5f,0.0f,
0.0f,0.5f,0.0f
};
// 存在重复的坐标点
//GLfloat vertexRectangle[]={
// 0.5f,0.5f,0.0f,
// 0.5f,-0.5f,0.0f,
// -0.5f,0.5f,0.0f,
// 0.5f,-0.5f,0.0f,
// -0.5f,-0.5f,0.0f,
// -0.5f,0.5f,0.0f
//};
GLfloat vertexRectangle[]={
0.5f,0.5f,0.0f,
0.5f,-0.5f,0.0f,
-0.5f,-0.5f,0.0f,
-0.5f,0.5f,0.0f
};
GLuint index[]={
0,1,3,
1,2,3
};
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos,1.0f);\n"
"}\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";
void MyOpenGLWidget::initializeGL()
{
/*
* 初始化OpenGL的函数接口
* 这个接口不能写在构造函数中,否则会程序运行出错,因为initializeOpenGLFunctions()中会需要窗口句柄,而窗口句柄在构造函数完成之后才会被创建
*/
initializeOpenGLFunctions();
// 创建VAO
glGenVertexArrays(1,&m_VAO);
// 创建VBO
glGenBuffers(1,&m_VBO);
// 绑定VAO和VBO对象
glBindVertexArray(m_VAO);
glBindBuffer(GL_ARRAY_BUFFER,m_VBO);
// 为缓冲对象创建一个新的数据存储
glBufferData(GL_ARRAY_BUFFER,sizeof(vertexRectangle),vertexRectangle,GL_STATIC_DRAW);
// 告诉先看如何解析缓冲里的属性值
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3 * sizeof(GLfloat),(void*)0);
// 开启VAO管理的第一个属性值
glEnableVertexAttribArray(0);
// 创建EBO,必须在VAO解绑之前创建,因为EBO也会被VAO所记录,但是解绑VBO却不会对VAO中的记录产生影响
glGenBuffers(1,&m_EBO);
// 绑定EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_EBO);
// 给EBO分配空间,并存储数据
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(index),index,GL_STATIC_DRAW);
// 解除EBO,若此处解绑EBO则VAO中就会不在记录EBO内容,需要在使用的时候重新绑定EBO,这里放在析构函数中解绑
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
// 解除VBO和VAO的绑定
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);
// 创建顶点着色器
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
// 绑定着色器
glShaderSource(vertexShader,1,&vertexShaderSource,NULL);
// 编译顶点着色器
glCompileShader(vertexShader);
// 创建片段着色器
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader,1,&fragmentShaderSource,NULL);
// 编译片段着色器
glCompileShader(fragmentShader);
// 链接 shaders
m_shaderProgram = glCreateProgram();
glAttachShader(m_shaderProgram,vertexShader);
glAttachShader(m_shaderProgram,fragmentShader);
glLinkProgram(m_shaderProgram);
//释放
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 指定绘图的模式,这里设定为GL_LINE,此模式只绘制线框而不进行填充
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
}
void MyOpenGLWidget::paintGL()
{
// 设置Clear的属性
glClearColor(0.2f,0.3f,0.3f,1.0f);
// 使用
glClear(GL_COLOR_BUFFER_BIT);
// 使用shader
glUseProgram(m_shaderProgram);
// 绑定VAO
glBindVertexArray(m_VAO);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_EBO);
// 绘制图形
//glDrawArrays(GL_TRIANGLES,0,3);
/*
* 根据索引绘制
* 若没有创建EBO,最后一个参数可以直接传递索引的数组
*/
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0/*&index*/);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
// 解除VAO
glBindVertexArray(0);
}
mainwindow.cpp内容如下:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setCentralWidget(ui->openGLWidget);
}
MainWindow::~MainWindow()
{
delete ui;
}
其他代码均为存在修改,运行效果如下:

本文介绍了如何在QT环境下使用OpenGL进行图形渲染。首先,概述了OpenGL的核心模式和固定渲染管线,并提到了辅助库GLFW和GLAD的作用。接着,详细解释了在QT中如何利用QOpenGLWidget进行OpenGL编程,包括顶点着色器、片段着色器的创建,以及VBO、VAO和EBO在GPU数据交互中的作用。示例代码展示了在QT项目中创建OpenGL窗口,初始化OpenGL函数,设置顶点数据和索引数据,以及绘制3D图形的过程。
4681

被折叠的 条评论
为什么被折叠?



