首页 > 代码库 > 基于Qt的OpenGL可编程管线学习(1)- 绘制一个三角形

基于Qt的OpenGL可编程管线学习(1)- 绘制一个三角形

0、写在前面的话

        这里只是学习的时候做的笔记记录方便日后的查看,如果有大神看到觉得有问题的地方希望能给予指出,方便日后的学习,谢谢!

我是用的Qt版本为Qt5.6,开发环境为Qt Creator


1、QOpenGLWidget

在Qt开发环境下,使用OpenGL的可编程管线绘制一个三角形

效果如下图所示:

技术分享


这里使用QOpenGLWidget进行绘制的,在QOpenGLWidget中需要重写

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

这三个函数,initializeGL()函数为初始化窗口调用的函数,resizeGL()函数改变窗口大小时调用,paintGL()是每一帧绘制的函数。


2、创建Program,加载Shader

使用OpenGL的shader绘制三角形,就要创建一个shader的程序,Qt中实现了QOpenGLShader、QOpenGLShaderProgram类,方便程序的创建和添加Shader。使用代码如下,本例中程序加载两个shader(vertex shader 和fragment shader):

// 创建一个GPU程序
    GLuint createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile);
    QOpenGLShader *m_VertexShader;
    QOpenGLShader *m_FragmentShader;
    QOpenGLShaderProgram *m_Program;


createGPUProgram()函数实现如下:


GLuint OpenGLWidget::createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile)
{
    m_VertexShader = new QOpenGLShader(QOpenGLShader::Vertex);
    bool isOK = m_VertexShader->compileSourceFile(nVertexShaderFile);
    if (!isOK)
    {
        delete m_VertexShader;
        m_VertexShader = nullptr;
        return 0;
    }
    m_FragmentShader = new QOpenGLShader(QOpenGLShader::Fragment);
    if (!m_FragmentShader->compileSourceFile(nFragmentShaderFile))
    {
        delete m_VertexShader;
        delete m_FragmentShader;
        m_FragmentShader = nullptr;
        return 0;
    }
    m_Program = new QOpenGLShaderProgram(this);
    m_Program->addShader(m_VertexShader);
    m_Program->addShader(m_FragmentShader);
    m_Program->link();
    return m_Program->programId();
}

其中Vertex shader和fragment shader的内容如下:


3、shader内容

在OpenGL的Shader使用,GLSL语言,与C语言很类似。下面是关于GLSL的一些简单的介绍:

attribute CPU与Shader传递数据,只能在CPU与Vertex Shader之间使用。

uniform 可用于CPU与任意Shader传递数据,一般用于传递矩阵等较大的数据。

varying 可用于Shader间传递数据。

本例子中,数据是CPU->Vertex Shader->Fragmen Shader顺序传递,Vertex shader是处理数据,Fragment shader会终的转换为像素的处理。最后通过OpenGL渲染到屏幕输出。


Vertex Shader:

attribute vec3 pos;
attribute vec4 color;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;

varying vec4 M_Color;

void main()
{
        M_Color = color;
        gl_Position = P * V * M * vec4(pos, 1.0);
}

由于OpenGL中为列向量和矩阵,这里需要使用矩阵左乘的形式。Vertex shader对应一个点的调用。

其中,M为传递的模型矩阵、V为视图矩阵、P为投影矩阵。


Fragment Shader:

varying vec4 M_Color;

void main()
{
        gl_FragColor = M_Color;
}


4、为Shader中传入数据

为attribute中和uniform中的数据赋值,需要获取Shader中的Location


// 获取Uniform ID & Attribute ID
    m_MLocationMat = OpenGLCore->glGetUniformLocation(programId, "M");
    m_VLocationMat = OpenGLCore->glGetUniformLocation(programId, "V");
    m_PLocationMat = OpenGLCore->glGetUniformLocation(programId, "P");
    m_PosVector = OpenGLCore->glGetAttribLocation(programId, "pos");
    m_ColorVector = OpenGLCore->glGetAttribLocation(programId, "color");

其中OpenGLCore为

OpenGLCore = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();

使用OpenGL3.2的核心,programId为QOpenGLShaderProgram的programId。


顶点数据如下:

struct VertexInfo
{
    float pos[3];
    float color[4];
};
    VertexInfo nVertexInfo[3];
    nVertexInfo[0].pos[0] = 0.0f;
    nVertexInfo[0].pos[1] = 0.0f;
    nVertexInfo[0].pos[2] = -30.0f;
    nVertexInfo[0].color[0] = 1.0f;
    nVertexInfo[0].color[1] = 0.0f;
    nVertexInfo[0].color[2] = 0.0f;
    nVertexInfo[0].color[3] = 1.0f;

    nVertexInfo[1].pos[0] = 10.0f;
    nVertexInfo[1].pos[1] = 0.0f;
    nVertexInfo[1].pos[2] = -30.0f;
    nVertexInfo[1].color[0] = 0.0f;
    nVertexInfo[1].color[1] = 1.0f;
    nVertexInfo[1].color[2] = 0.0f;
    nVertexInfo[1].color[3] = 1.0f;

    nVertexInfo[2].pos[0] = 0.0f;
    nVertexInfo[2].pos[1] = 10.0f;
    nVertexInfo[2].pos[2] = -30.0f;
    nVertexInfo[2].color[0] = 0.0f;
    nVertexInfo[2].color[1] = 0.0f;
    nVertexInfo[2].color[2] = 1.0f;
    nVertexInfo[2].color[3] = 1.0f;


创建VBO(Vertex Buffer Object)

// 创建VBO
OpenGLCore->glGenBuffers(1, &m_VBO);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
OpenGLCore->glBufferData(GL_ARRAY_BUFFER, sizeof(VertexInfo) * 3, nVertexInfo, GL_STATIC_DRAW);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);


创建IBO(Element Buffer Object)

// 创建IBO
unsigned int index[3] = {0, 1, 2};
OpenGLCore->glGenBuffers(1, &m_IBO);
OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
OpenGLCore->glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), index, GL_STATIC_DRAW);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);


为uniform数据赋值

Qt提供了矩阵类,QMatrix4x4(默认为列向量)

OpenGLCore->glUniformMatrix4fv(m_MLocationMat, 1, GL_FALSE, nMormalMat.data());
OpenGLCore->glUniformMatrix4fv(m_VLocationMat, 1, GL_FALSE, nMormalMat.data());
OpenGLCore->glUniformMatrix4fv(m_PLocationMat, 1, GL_FALSE, mProjectionMatrix.data());


为attribute数据赋值

OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
OpenGLCore->glEnableVertexAttribArray(m_PosVector);
OpenGLCore->glVertexAttribPointer(m_PosVector, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)0);
OpenGLCore->glEnableVertexAttribArray(m_ColorVector);
OpenGLCore->glVertexAttribPointer(m_ColorVector, 4, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)(sizeof(float) * 3));
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);



5、矩阵值的设置及绘制指令

投影矩阵设置,在resizeGL()函数中

mProjectionMatrix.perspective(45.0f, w * 1.0f / h, 0.1f, 500.0f);

这里模型矩阵和视图矩阵都使用单位矩阵


在paintGL()函数中

使用VBO绘制(使用VBO中的数值为attribute中变量赋值,一共赋值三次)

OpenGLCore->glDrawArrays(GL_TRIANGLES, 0, 3);


使用IBO绘制(利用IBO中的Index绘制,即VBO中的索引)

// 使用DeawElement绘制
OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
OpenGLCore->glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


6、完整代码

triangle.h

#ifndef TRIANGLE_H
#define TRIANGLE_H
//#include <glew.h>
#include <QObject>
#include <QOpenGLWidget>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_3_2_Core>
#include <QFile>
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>
#include <QMatrix>
#include <QTimer>
#include <GL/gl.h>

struct VertexInfo
{
    float pos[3];
    float color[4];
};

class OpenGLWidget : public QOpenGLWidget
{
    Q_OBJECT
public:
    OpenGLWidget();
    ~OpenGLWidget();

private:
    // 创建一个GPU程序
    GLuint createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile);
    QOpenGLShader *m_VertexShader;
    QOpenGLShader *m_FragmentShader;
    QOpenGLShaderProgram *m_Program;

    // MVP Location
    GLint m_MLocationMat;
    GLint m_VLocationMat;
    GLint m_PLocationMat;
    GLint m_PosVector;
    GLint m_ColorVector;

    // VBO
    GLuint m_VBO;
    // IBO
    GLuint m_IBO;

    QMatrix4x4 mProjectionMatrix;
    QTimer *m_Timer;

    QOpenGLFunctions_3_2_Core *OpenGLCore = nullptr;

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

protected slots:
    void onTimerOut(void);
};

#endif // TRIANGLE_H


triangle.cpp

#include "triangle.h"
#include <QDebug>

OpenGLWidget::OpenGLWidget()
{
    m_Timer = new QTimer;
}

OpenGLWidget::~OpenGLWidget()
{

}

GLuint OpenGLWidget::createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile)
{
    m_VertexShader = new QOpenGLShader(QOpenGLShader::Vertex);
    bool isOK = m_VertexShader->compileSourceFile(nVertexShaderFile);
    if (!isOK)
    {
        delete m_VertexShader;
        m_VertexShader = nullptr;
        return 0;
    }
    m_FragmentShader = new QOpenGLShader(QOpenGLShader::Fragment);
    if (!m_FragmentShader->compileSourceFile(nFragmentShaderFile))
    {
        delete m_VertexShader;
        delete m_FragmentShader;
        m_FragmentShader = nullptr;
        return 0;
    }
    m_Program = new QOpenGLShaderProgram(this);
    m_Program->addShader(m_VertexShader);
    m_Program->addShader(m_FragmentShader);
    m_Program->link();
    return m_Program->programId();
}

void OpenGLWidget::initializeGL(void)
{
    createGPUProgram("trianglevertexshader.vert", "trianglefragmentshader.frag");

    OpenGLCore = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
    GLuint programId = m_Program->programId();
    // 获取Uniform ID & Attribute ID
    m_MLocationMat = OpenGLCore->glGetUniformLocation(programId, "M");
    m_VLocationMat = OpenGLCore->glGetUniformLocation(programId, "V");
    m_PLocationMat = OpenGLCore->glGetUniformLocation(programId, "P");
    m_PosVector = OpenGLCore->glGetAttribLocation(programId, "pos");
    m_ColorVector = OpenGLCore->glGetAttribLocation(programId, "color");

    // 创建VBO
    VertexInfo nVertexInfo[3];
    nVertexInfo[0].pos[0] = 0.0f;
    nVertexInfo[0].pos[1] = 0.0f;
    nVertexInfo[0].pos[2] = -30.0f;
    nVertexInfo[0].color[0] = 1.0f;
    nVertexInfo[0].color[1] = 0.0f;
    nVertexInfo[0].color[2] = 0.0f;
    nVertexInfo[0].color[3] = 1.0f;

    nVertexInfo[1].pos[0] = 10.0f;
    nVertexInfo[1].pos[1] = 0.0f;
    nVertexInfo[1].pos[2] = -30.0f;
    nVertexInfo[1].color[0] = 0.0f;
    nVertexInfo[1].color[1] = 1.0f;
    nVertexInfo[1].color[2] = 0.0f;
    nVertexInfo[1].color[3] = 1.0f;

    nVertexInfo[2].pos[0] = 0.0f;
    nVertexInfo[2].pos[1] = 10.0f;
    nVertexInfo[2].pos[2] = -30.0f;
    nVertexInfo[2].color[0] = 0.0f;
    nVertexInfo[2].color[1] = 0.0f;
    nVertexInfo[2].color[2] = 1.0f;
    nVertexInfo[2].color[3] = 1.0f;

    // 创建VBO
    OpenGLCore->glGenBuffers(1, &m_VBO);
    OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
    OpenGLCore->glBufferData(GL_ARRAY_BUFFER, sizeof(VertexInfo) * 3, nVertexInfo, GL_STATIC_DRAW);
    OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);
    // 创建IBO
    unsigned int index[3] = {0, 1, 2};
    OpenGLCore->glGenBuffers(1, &m_IBO);
    OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
    OpenGLCore->glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), index, GL_STATIC_DRAW);
    OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);

    glClearColor(41.0f / 255.0f, 71.0f / 255.0f, 121.0f / 255.0f, 1.0f);

    QObject::connect(m_Timer, SIGNAL(timeout()), this, SLOT(onTimerOut()));
    m_Timer->start(10);
}

void OpenGLWidget::resizeGL(int w, int h)
{
        glViewport(0, 0, w, h);

        mProjectionMatrix.perspective(45.0f, w * 1.0f / h, 0.1f, 500.0f);
        repaint();
}

void OpenGLWidget::paintGL(void)
{
    bool result = m_Program->bind();
    if (!result)
        return;
    QMatrix4x4 nMormalMat;

    OpenGLCore->glUniformMatrix4fv(m_MLocationMat, 1, GL_FALSE, nMormalMat.data());
    OpenGLCore->glUniformMatrix4fv(m_VLocationMat, 1, GL_FALSE, nMormalMat.data());
    OpenGLCore->glUniformMatrix4fv(m_PLocationMat, 1, GL_FALSE, mProjectionMatrix.data());

    OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, m_VBO);

    // 使用DrawArrays绘制
    OpenGLCore->glEnableVertexAttribArray(m_PosVector);
    OpenGLCore->glVertexAttribPointer(m_PosVector, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)0);
    OpenGLCore->glEnableVertexAttribArray(m_ColorVector);
    OpenGLCore->glVertexAttribPointer(m_ColorVector, 4, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)(sizeof(float) * 3));
    //OpenGLCore->glDrawArrays(GL_TRIANGLES, 0, 3);
    OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);

    // 使用DeawElement绘制
    OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
    OpenGLCore->glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
    OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    m_Program->release();
}

void OpenGLWidget::onTimerOut(void)
{
    repaint();
}


trianglevertexshader.vert

attribute vec3 pos;
attribute vec4 color;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;

varying vec4 M_Color;

void main()
{
        M_Color = color;
        gl_Position = P * V * M * vec4(pos, 1.0);
}


trianglefragmentshader.frag

varying vec4 M_Color;

void main()
{
        gl_FragColor = M_Color;
}


main.cpp

#include <QApplication>
#include "triangle.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    OpenGLWidget wdt;
    wdt.show();

    return a.exec();
}


本文出自 “不会飞的纸飞机” 博客,请务必保留此出处http://douzhq.blog.51cto.com/12552184/1930925

基于Qt的OpenGL可编程管线学习(1)- 绘制一个三角形