很久没有更新博客,打算用最近的一段时间来完成Opengl的学习,如果大家也想学习的话,可以跟我一起学习。本学习内容为 LearnOpenGL 中的知识点,在qt中编程,因为QT已经封装好了对应的东西,我们不需要glfw 与 glad 。
然后我们在qt中可以不用考虑这样一件事。
中间黑色的部分对应控件为:
这里我界面为了好看些吧,添加了样式,这个你自己看着办吧。
然后我把这个控件提升为我自己写的类中。
不会提升类的小伙伴们,还是把qt基础学习好了,在说吧。或者可以看我博客qt对应的基础学习。
https://blog.csdn.net/weixin_42126427/category_9666776.html?spm=1001.2014.3001.5482
然后在类中我们要这样写
#ifndef BKQOPENGLW_H
#define BKQOPENGLW_H
#include
#include
class BKQOpenglW : public QOpenGLWidget, QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
explicit BKQOpenglW(QWidget *parent = nullptr);
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
signals:
public slots:
private:
unsigned int VBO, VAO;
unsigned int shaderProgram;
};
#endif // BKQOPENGLW_H
QOpenGLFunctions_3_3_Core 这里是使用对应的版本号,这里使用的是3.3.因为书中使用的是这个,为了统一这里使用了3.3版本的api函数。
这里从写了三个继承的函数
virtual void initializeGL(); //初始化数据
virtual void resizeGL(int w, int h); //改变窗口大小
virtual void paintGL();//主要绘制数据
写完后,我们也要从显卡gpu中获取对应指针函数,我们上面说使用的是 glad 的 gladLoadGLLoader来获取的,但是在qt中我们使用的是
initializeOpenGLFunctions();
这个函数功能就是glad 的gladLoadGLLoader 的作用。
而我们继承的QOpenGLWidget 可以看作是glfw 的作用,这样你也很快的了解了吧。
opengl是一个状态机的概念,可以把VAO当成工头(这里管理顶点数组对象的),他手下可以管理很多工人,他是知道对应的工人的消息,当我们想用了解某个工人的时候,我们不用直接找对应工人,我们直接找这个工头,让他来找到你要的工人信息。
VBO是管理顶点数据的缓冲对象。你可以理解就是工人(对应技术的),他是可以拿到顶点数据,然后后期发送个gpu,让gpu知道你数据顶点
我们以一个图:
比如VAO (工头)数据管理 VBO (工人),VBO 中记录了大量的顶点数据。
比如代码段:
const float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
void BKQOpenglW::initializeGL()
{
initializeOpenGLFunctions();
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
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), nullptr);
glEnableVertexAttribArray(0);
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
glBindVertexArray(0);
}
通过glGenVertexArrays 创建vao , glGenBuffers(1, &VBO); 创建vbo。
然后是绑定部分,glBufferData来写入我们的顶点数据GL_STATIC_DRAW表示为静态。
glVertexAttribPointer 通过这个来告诉gpu按着这个规则来执行。偏移量为0 然后3个数据一组,数据是float类型,不用标准,然后告诉整个顶点的大小。
glBindVertexArray(VAO);
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), nullptr);
glEnableVertexAttribArray(0);
然后我们绑定完后可以使用:
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
来告诉工头与工人,你们去休息吧,有事我就开始在来找你,找人的时候我们在绑定就可以了。
glBindVertexArray(VAO); //呼叫工头
下面就是 图形渲染管线 流程图了,蓝色部分就是我们可以是自定义的着色器。没有定义的话,就使用默认。
我们把顶点相关东西处理好了,然后我们要开始给图形对应的着色器了。
现代OpenGL需要我们至少设置一个顶点和一个片段着色器, 如果我们打算做渲染的话。这里就是设置基本是xyz坐标相关
#version 330 core
layout (location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position.x, position.y, position.z, 1.0);
}
//上面是对应的代码段,但是我们需要的是下面的字符串模式。
//顶点着色器
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";
这里主要是对颜色处理。计算机图形中颜色被表示为有4个元素的数组: 红色、 绿色、 蓝色和alpha(透明度)元素, 通常缩写为RGBA
#version 330 core
out vec4 color;
void main()
{
color = vec4(1.0f, 0.5f, 0.2f, 1.0f);//RGBA
}
//上面是对应的代码段,但是我们需要的是下面的字符串模式。
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 vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader,1,&vertexShaderSource,(nullptr));
glCompileShader(vertexShader);
int success;
char infoLog[512];
glGetShaderiv(vertexShader,GL_COMPILE_STATUS,&success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//创建片段着色器
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//链接着色器
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram,vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
然后通过链接着色器把,顶点与片段的着色器链接起来
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram,vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
然后我们使用的使用直接调用glUseProgram(shaderProgram); 就告诉了gpu当前应该使用那些着色器了。
绘制代码:
void BKQOpenglW::paintGL()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES,0,3);
}
这里我们
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
就告诉gpu我们用的着色器,与对应顶点数据(工头)
让后使用
glDrawArrays(GL_TRIANGLES,0,3);
绘制图形。这里简单说下,opengl中能后绘制的多边形只能是三角形,也就是所所有图形都是三角形变化过来的。
整体代码:
#ifndef BKQOPENGLW_H
#define BKQOPENGLW_H
#include
#include
class BKQOpenglW : public QOpenGLWidget, QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
explicit BKQOpenglW(QWidget *parent = nullptr);
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
signals:
public slots:
private:
unsigned int VBO, VAO;
unsigned int shaderProgram;
};
#endif // BKQOPENGLW_H
对应cpp
#include "bkqopenglw.h"
#include
const float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
//顶点着色器
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";
BKQOpenglW::BKQOpenglW(QWidget *parent) : QOpenGLWidget(parent)
{
}
void BKQOpenglW::initializeGL()
{
initializeOpenGLFunctions();
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
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), nullptr);
glEnableVertexAttribArray(0);
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
glBindVertexArray(0);
//创建顶点着色器
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader,1,&vertexShaderSource,(nullptr));
glCompileShader(vertexShader);
int success;
char infoLog[512];
glGetShaderiv(vertexShader,GL_COMPILE_STATUS,&success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//创建片段着色器
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//链接着色器
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram,vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
void BKQOpenglW::resizeGL(int w, int h)
{
glViewport(0,0,w,h);
}
void BKQOpenglW::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上学习opengl的伙伴,可以关注与点赞博客,让我们共同进步吧。