👇实现效果一览
0 准备工作
- 新建用于openGL的C++类,命名为yourOpenGLWidget,继承选择QWidget
- 创建好后手动修改.h和.cpp中的继承父类,如下图红框所示
- .h文件中包OpenGL相关头文件,如下图所示
- .h文件中重写QOpenGLWidget库下的三个protected三个虚函数,如下图下方红框所示
- 在ui界面中导入QOpenglWidget对象,并右键将其提升为yourOpenGLWidget类
- mainWindow.cpp的构造函数中将opengl界面设置成铺满窗口:
setCentralWidget(ui->openGLWidget);
1 使用VBO VAO显示三角形
#include "myopenglwidget.h"
unsigned int VBO, VAO;
unsigned int vertexShader;
unsigned int fragmentShader;
unsigned int shaderProgram;
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
// 1顶点 着色器
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";
// 5片段 着色器
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";
myOpenGLWidget::myOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent) // 疑问1:为什么.cpp中只有一个父类,而.h中有两个
{
}
void myOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions(); // 初始化
/* VAO */
glGenVertexArrays(1, &VAO); // 1个 顶点数组对象(vertex Array Object, VAO) -> 告知显卡如何解析数据 or 操作VBO的框架
glBindVertexArray(VAO); // 绑定VAO
/* VBO */
glGenBuffers(1, &VBO); // 1个 顶点缓冲对象(vertex Buffer Object, VBO) -> 数据
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 内存的图像数据迁移至显存(缓冲区?)
// 告知显卡如何解析缓冲区的值
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0); // 起始,每个属性长度(xyz=3),类型,是否需要归一化,周期, 偏移量
// 开启VAO管理的第一个属性值 为什么开第一个? -> 如果vertices如果每一行有6个属性(3坐标+3颜色),且周期为3,那么这里可以为1,代表调用出颜色
glEnableVertexAttribArray(0);
// VBO VAO 用完解绑?
glBindBuffer(GL_ARRAY_BUFFER, 0); // VBO下班
// 顶点着色器vertexShader 创建&编译
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() << "vertex shader compilation failed\n" << infoLog;;
// }
// 片段着色器fragmentShader 创建&编译
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
// if(!success) {
// glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
// qDebug() << "fragment shader compilation failed\n" << infoLog;;
// }
// 将两个着色器进行链接 shaderProgram
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
// if(!success) {
// glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
// qDebug() << "shader program linking failed\n" << infoLog;
// }
// 链接后删除两个着色器
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 填充方式-线条填充
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBindVertexArray(0); // VAO下班(VAO是ob者,他要观察所有事件都完成之后才下班,很辛苦)
}
void myOpenGLWidget::resizeGL(int w, int h)
{
}
void myOpenGLWidget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.5f, 0.5f); // 空指针?只set不clear,什么颜色?
glClear(GL_COLOR_BUFFER_BIT);
// 应用!
glUseProgram(shaderProgram);
glBindVertexArray(VAO); // VAO 上班!
glDrawArrays(GL_TRIANGLES, 0, 3); // 画vertives中前3个顶点
}
2 使用EBO显示矩形
EBO通过索引方式减少重复顶点的绘制(6个点->4个点)
#include "myopenglwidget.h"
unsigned int VBO, VAO, EBO;
unsigned int vertexShader;
unsigned int fragmentShader;
unsigned int shaderProgram;
// opengl基础图形是三角形,任何复杂图形都是由三角形拼接出来的
//float vertices[] = {
// 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 // 重复
//};
float vertices[] = {
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f
};
unsigned int indices[] = {
0, 1, 2,
1, 2, 3
};
// 1顶点 着色器
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";
// 5片段 着色器
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";
myOpenGLWidget::myOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent) // 疑问1:为什么.cpp中只有一个父类,而.h中有两个
{
}
void myOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions(); // 初始化
/* VAO */
glGenVertexArrays(1, &VAO); // 1个 顶点数组对象(vertex Array Object, VAO) -> 告知显卡如何解析数据 or 操作VBO的框架
glBindVertexArray(VAO); // 绑定VAO
/* VBO */
glGenBuffers(1, &VBO); // 1个 顶点缓冲对象(vertex Buffer Object, VBO) -> 数据
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 内存的图像数据迁移至显存(缓冲区?)
// 告知显卡如何解析缓冲区的值
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0); // 起始,每个属性长度(xyz=3),类型,是否需要归一化,周期, 偏移量
// 开启VAO管理的第一个属性值 为什么开第一个? -> 如果vertices如果每一行有6个属性(3坐标+3颜色),且周期为3,那么这里可以为1,代表调用出颜色
glEnableVertexAttribArray(0);
// VBO VAO 用完解绑?
glBindBuffer(GL_ARRAY_BUFFER, 0); // VBO下班
// 顶点着色器vertexShader 创建&编译
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() << "vertex shader compilation failed\n" << infoLog;;
// }
// 片段着色器fragmentShader 创建&编译
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
// if(!success) {
// glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
// qDebug() << "fragment shader compilation failed\n" << infoLog;;
// }
// 将两个着色器进行链接 shaderProgram
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
// if(!success) {
// glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
// qDebug() << "shader program linking failed\n" << infoLog;
// }
// 链接后删除两个着色器
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 填充方式-线条填充
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
/* EBO */
glGenBuffers(1, &EBO); // 1个 索引缓冲对象(Element Buffer Object, EBO)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 传送数据至显存
// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // 若EBO未下班的话,draw时候 无需索引; 反之需要索引
glBindVertexArray(0); // VAO下班(VAO是ob者,他要观察所有事件都完成之后才下班,很辛苦)
}
void myOpenGLWidget::resizeGL(int w, int h)
{
}
void myOpenGLWidget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.5f, 0.5f); // 空指针?只set不clear,什么颜色?
glClear(GL_COLOR_BUFFER_BIT);
// 应用!
glUseProgram(shaderProgram);
glBindVertexArray(VAO); // VAO 上班!
// glDrawArrays(GL_TRIANGLES, 0, 6); // 画vertives中前6个顶点
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0/*&indices*/); // 若EBO在init中没班了
// glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, &indices); // 若EBO在init中下班, 6代表6个索引
}
3 绘制两个图形,并赋予两种填充和边界颜色
#include "youropenglwidget.h"
//unsigned int VBO, VAO, EBO;
unsigned int VBOs[2], VAOs[2], EBO; // 同时绘制两个图形
unsigned int vertexShader;
//unsigned int fragmentShader;
unsigned int fragmentShader1, fragmentShader2;
//unsigned int shaderProgram;
unsigned int shaderProgram1, shaderProgram2;
float vertices1[] = {
0.0f, 0.5f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f
};
float vertices2[] = {
0.5f, -0.5f, 0.0f,
0.0f, -0.5f, 0.0f,
0.0f, -1.0f, 0.0f
};
// 1顶点 着色器
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";
// 5片段 着色器
const char* fragmentShaderSource1 = "#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";
const char* fragmentShaderSource2 = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"
"}\n\0";
yourOpenGLWidget::yourOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
}
void yourOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions(); // 初始化
/* VAO & VBO 初始化 */
glGenVertexArrays(2, VAOs); // 1个 顶点数组对象(vertex Array Object, VAO) -> 告知显卡如何解析数据 or 操作VBO的框架
glGenBuffers(2, VBOs); // 1个 顶点缓冲对象(vertex Buffer Object, VBO) -> 数据
/* 图像1: VAO & VBO 绑定 */
glBindVertexArray(VAOs[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1), vertices1, GL_STATIC_DRAW); // 内存的图像数据迁移至显存(缓冲区?)
// 告知显卡如何解析缓冲区的值
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0); // 起始,每个属性长度(xyz=3),类型,是否需要归一化,周期, 偏移量
// 开启VAO管理的第一个属性值 为什么开第一个? -> 如果vertices如果每一行有6个属性(3坐标+3颜色),且周期为3,那么这里可以为1,代表调用出颜色
glEnableVertexAttribArray(0);
// VBO VAO 用完解绑?
glBindBuffer(GL_ARRAY_BUFFER, 0); // VBO下班
/* 图像2: VAO & VBO 绑定 */
glBindVertexArray(VAOs[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 1); // ???
// 顶点着色器vertexShader 创建&编译
vertexShader = glCreateShader(GL_VERTEX_SHADER); // 创建顶点着色器
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); // 绑定字符串中的源码
glCompileShader(vertexShader); // 编译
// 片段着色器1 fragmentShader 创建&编译
fragmentShader1 = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader1, 1, &fragmentShaderSource1, NULL);
glCompileShader(fragmentShader1);
// 片段着色器2 fragmentShader 创建&编译
fragmentShader2 = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader2, 1, &fragmentShaderSource2, NULL);
glCompileShader(fragmentShader2);
// 将两个着色器进行链接 shaderProgram
shaderProgram1 = glCreateProgram();
glAttachShader(shaderProgram1, vertexShader);
glAttachShader(shaderProgram1, fragmentShader1);
glLinkProgram(shaderProgram1);
shaderProgram2 = glCreateProgram();
glAttachShader(shaderProgram2, vertexShader);
glAttachShader(shaderProgram2, fragmentShader2);
glLinkProgram(shaderProgram2);
// 链接后删除两个着色器
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader1);
glDeleteShader(fragmentShader2);
// 填充方式-线条填充
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBindVertexArray(0);
}
void yourOpenGLWidget::resizeGL(int w, int h)
{
}
void yourOpenGLWidget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.5f, 0.5f); // 空指针?只set不clear,什么颜色?
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram1);
glBindVertexArray(VAOs[0]); // VAO 上班!
glDrawArrays(GL_TRIANGLES, 0, 3);
glUseProgram(shaderProgram2);
glBindVertexArray(VAOs[1]);
glDrawArrays(GL_TRIANGLES, 0, 3);
}