qt中对OpenGL函数的引入(shader,vao,vbo)

QT代替原有的部分gl默认库

在qt框架中使用OpenGL,即用qt窗口框架代替glfw窗口设计,用qt中的QOpenGLFunctions_4_5_Core(也可以为其他版本)代替glad来做OpenGL函数的管理。
故在vs2019中不再需要对glfw,glad进行配置操作(相关配置操作详看这里)。


本篇文章的重点

在qt中有对OpenGL库的封装库(QOpenGLShaderProgram,QOpenGLShader等等),本片文章既使用qt自带的封装库,也使用OpenGL的接口,便于以后写出自己的封装库。
有关qt自带的封装库学习自 “懂deeee珍惜”的现代OpenGL+Qt学习笔记之三:显示一个彩色三角形
个人认为以qt为窗口按钮框架,OpenGL做图形处理是最优选择。至于最终哪种形式更好,希望读者和本博主在今后的学习中再做判断。


使用qt框架

openglwidget.h

#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>

class QOpenGLShaderProgram;
class OpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    OpenGLWidget(QWidget *parent = 0);

protected:
    void initializeGL();
    void resizeGL(int w, int h);
    void paintGL();

private:
    QOpenGLShaderProgram *program;
    QOpenGLVertexArrayObject VAO;
};

#endif // OPENGLWIDGET_H

在头文件中引入两个私有类:
一个是QOpenGLShaderProgram类指针program,用作渲染管线的绑定。
另一个是QOpenGLBuffer VAO,用于储存顶点属性。

关于OpenGL的用法本文不做介绍,有关内容参照本人OpenGL专栏

openglwidget.cpp

#include "openglwidget.h"
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>

OpenGLWidget::OpenGLWidget(QWidget *parent)
    :QOpenGLWidget(parent)
{

}

void OpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    // vertex shader
    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    vshader->compileSourceFile("://shader/basic.vert");
    // fragment shader
    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    fshader->compileSourceFile("://shader/basic.frag");

    // shader program
    program = new QOpenGLShaderProgram;
    program->addShader(vshader);
    program->addShader(fshader);

    program->link();
    program->bind();


    // vertices array
    GLfloat positionData[] = {
        -0.8f, -0.8f, 0.0f,
        0.8f, -0.8f, 0.0f,
        0.0f, 0.8f, 0.0f };
    GLfloat colorData[] = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 1.0f };

	QOpenGLBuffer VBO(QOpenGLBuffer::VertexBuffer);
    VBO.create();
    if(!VBO.isCreated()){
        qDebug()<<"vbo is not created.";
    }
    VBO.bind();
    VAO.create();
    if(!VAO.isCreated()){
        qDebug()<<"vao is not created.";
    }
    VAO.bind();

    // vertex positions
	VBO.allocate(vertices1,sizeof (vertices1));
    program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 8*sizeof(GL_FLOAT));
    program->enableAttributeArray(0);
    program->setAttributeBuffer(1, GL_FLOAT, 3*sizeof (GL_FLOAT), 3, 8*sizeof(GL_FLOAT));
    program->enableAttributeArray(1);
    //program->setAttributeBuffer(2, GL_FLOAT, 6*sizeof (GL_FLOAT), 2, 8*sizeof(GL_FLOAT));
    //program->enableAttributeArray(2);
    program->release();
    VAO.release();


    // set color used to clear background
    glClearColor(0.5f, 0.0f, 1.0f, 1.0f);
    glEnable(GL_DEPTH_TEST);
}

void OpenGLWidget::resizeGL(int w, int h)
{

}

void OpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	program->bind();
    VAO.bind();
    // draw
    glDrawArrays(GL_TRIANGLES, 0, 3 );
}

initializeOpenGLFunctions();

相当于对glad初始化。任何对OpenGL函数的调用都需要初始化,(在这儿吃了大亏),无论是OpenGL接口还是qt的封装库。

shader

QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
vshader->compileSourceFile("://shader/basic.vert");

相当于对

GLuint vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);

另加文件读取 的封装

program

program = new QOpenGLShaderProgram;
program->addShader(vshader);
program->addShader(fshader);
program->link();
program->bind();

相当于对

Program = glCreateProgram();
glAttachShader(Program, vertexShader);
glAttachShader(Program, fragmentShader);
glLinkProgram(Program);
glUseProgram(Program);

的封装

顶点属性

QOpenGLBuffer VBO(QOpenGLBuffer::VertexBuffer);
// vertex buffer object
VAO.create();
VBO.create();
VBO.bind();

VAO.bind();
// vertex positions
VBO.allocate(vertices1,sizeof (vertices1));
program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 8*sizeof(GL_FLOAT));
program->enableAttributeArray(0);
program->setAttributeBuffer(1, GL_FLOAT, 3*sizeof (GL_FLOAT), 3, 8*sizeof(GL_FLOAT));
program->enableAttributeArray(1);
    //program->setAttributeBuffer(2, GL_FLOAT, 6*sizeof (GL_FLOAT), 2, 8*sizeof(GL_FLOAT));
    //program->enableAttributeArray(2);
program->release();
VAO.release();

VAO.bind()一定要在vbo.bind()之后才能绑定成功。

void QOpenGLBuffer::allocate(const void *data, int count)

Allocates count bytes of space to the buffer, initialized to the
contents of data. Any previous contents will be removed. It is assumed
that create() has been called on this buffer and that it has been
bound to the current context.

在vbo.bind之后在显卡开辟出count字节内存,并将data数据存入。
主要封装函数为

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
启用顶点属性(另一种方法)
GLuint vPosition = program->attributeLocation("VertexPosition");
program->setAttributeBuffer(vPosition, GL_FLOAT, 0, 3, 0);
glEnableVertexAttribArray(vPosition);

program->attributeLocation封装了

GLint glGetAttribLocation(	GLuint program, const GLchar *name);//获取顶点属性地址

program->setAttributeBuffer(vPosition(顶点属性位置), GL_FLOAT, 0(偏移量), 3(顶点个数), 0(步长));封装的函数暂且不知。
类似于如下函数。

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(0));

添加修改数据(补充)

vbo.write(9sizeof(GLfloat), colorData, 9sizeof(GLfloat));
vbo.write(修改起始位置,修改数据,修改字节数)

Replaces the count bytes of this buffer starting at offset with the contents of data. Any other bytes in the buffer will be left unmodified.

修改该vbo中从起始位置开始特定字节数的数据。
封装函数暂且不知。有可能是内部自设函数。

在绘制函数中绘制

program->bind();
VAO.bind();
glDrawArrays(GL_TRIANGLES, 0, 3 );

绑定绘制管线和绘制数据,然后绘制。

不使用qt封装库运行代码

代码来自我的OpenGL三角形的绘制博客
详细解释参阅博客内容。

#include "openglwidget.h"
#include <iostream>

const GLchar* vertexShaderSource =
    "#version 330 core\n"
    "layout (location = 0) in vec3 position;\n"//这儿有分号
    "layout (location = 1) in vec3 inColor;\n"
    "out vec3 Color;\n"
    "void main()\n"
    "{\n"
    "gl_Position = vec4(position, 1.0);\n"
    "Color = inColor;\n"
    "}\0";

const GLchar* fragmentShaderSource =
    "#version 330 core\n"
    "in vec3 Color;"
    "out vec4 FragColor;\n"   //输出一个四分量向量,表示最终颜色。
    "void main(){\n"
    "FragColor = vec4(1.0f,0.0f,0.0f,0.5f);\n"
    "}\n";


OpenGLWidget::OpenGLWidget(QWidget* parent)
    :QOpenGLWidget(parent)
{

}

void OpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    /*使用顶点着色器*/
    GLuint vertexShader;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    //判断编译是否成功
    GLint success;
    GLchar infoLog[512];//存储错误信息
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    //检查是否编译成功
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        //获取错误消息
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    /*使用片段着色器*/
    GLuint 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);
        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, NULL, infoLog);
        std::cout << "ERROR::PROGRAM::SHADER::LINK_FAILED\n" << infoLog << std::endl;
    }
    //把着色器对象链接到程序对象以后,我们就不再需要它们了
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    /*使用索引数据存储点的顺序,用IBO索引缓冲对象管理*/
    GLfloat vertices[] = {
        1.0f,  0.0f, 0.0f,  1.0f, 0.0f, 0.0f,
        0.0f, -1.0f, 0.0f,  0.0f, 0.0f, 0.0f,
        -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
    };
    GLuint VBO1;
    glGenBuffers(1, &VBO1);
    glGenVertexArrays(1, &VAO1);
    glBindVertexArray(VAO1);
    glBindBuffer(GL_ARRAY_BUFFER, VBO1);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);//启用顶点属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);//启用顶点属性
    glBindVertexArray(0);

    glClearColor(0.5f, 0.5f, 1.0f, 1.0f);
    glEnable(GL_DEPTH_TEST);


}

void OpenGLWidget::resizeGL(int w, int h)
{

}

void OpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_BLEND);

    glUseProgram(shaderProgram);//要用在循环里
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);//设置为填充模式(默认)
    glBindVertexArray(VAO1);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    //  glDrawArrays渲染顺序为先进后出的栈顺序。

}

结语

下一节将进行对自定义库的封装,自定义库的学习即可更清楚的了解qt封装库的内部操作,也可不受限于qt而定制个性化的库函数。

补充(2021.8.7):在qt框架下自定义库较为艰难,需要熟知qt框架。故今后将先使用qt封装库,待学习完OpenGL全部基本知识,再来学习qt框架。(为了自定义库,挠头苦干4天,头发掉了不少,最后学会了放下执念!!!)

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Qt OpenGL提供了一种实现高效渲染的机制,它使用了VAO(Vertex Array Object)和VBO(Vertex Buffer Object)技术。 VAO是一种对象,用于存储顶点数据、顶点属性以及它们之间的关联关系。它可以理解为一个顶点属性的容器。通过使用VAO,我们可以将顶点数据存储在显存,而不是每次渲染时都从CPU传输顶点数据到显存,从而提高渲染的效率。 VBO是一个存储顶点数据的缓冲区对象。通过将顶点数据存储在VBO,我们可以将数据一次性地传输到显存,并且可以高效地管理和使用这些数据。同时,VBO还可以提供顶点缓存和索引缓存功能,用于顶点的重用和图元的索引。 Qt OpenGL提供了许多方便的API来操作VAOVBO。我们可以使用QOpenGLVertexArrayObject类来创建和管理VAO,通过调用QOpenGLBuffer类的相关方法来创建和管理VBO。例如,我们可以使用QOpenGLVertexArrayObject::bind()和QOpenGLVertexArrayObject::release()方法来绑定和释放VAO,使用QOpenGLBuffer::bind()和QOpenGLBuffer::release()方法来绑定和释放VBO。 使用VAOVBO可以大大简化OpenGL代码的编写,并且能够有效提高渲染效率。我们可以将需要渲染的数据一次性地传输到显存,并且设置好相应的渲染状态,然后每次渲染时只需要绑定VAO进行渲染即可,不需要重复的数据传输和状态设置操作。 综上所述,Qt OpenGLVAOVBO技术可以帮助我们实现高效渲染,提高应用程序的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Elsa的迷弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值