学习OpenGL(三):QOpenGLShaderProgram的使用

目录

  • 引言
  • 代码示例
  • 代码解析
  • 补充

本系列文章主要是记录学习OpenGL的过程,旨在驱动学习理解OpenGL,最终达到能够使用相关接口解决实际项目问题,学习流程参考《LearnOpenGL》。主要展现形式是"代码示例+接口分析"的形式,编码主要是基于Qt封装的OpenGL模块展开,这样对于我来说更加熟悉。

引言

前一章已经完成了矩形的绘制,但并没有与界面的交互,这章增加相关交互以及QOpenGLShaderProgram的使用,效果如下:

学习OpenGL(三):QOpenGLShaderProgram的使用_第1张图片

代码示例

#include 
#include 
#include 

class CustomGLWidget : public QOpenGLWidget, QOpenGLFunctions_3_3_Core
{
    Q_OBJECT
public:
    explicit CustomGLWidget(QWidget *parent = nullptr);
    ~CustomGLWidget() override;

    enum ShapeType{

        ST_NULL,
        ST_RECT,
        ST_TRIANGLE,
    };
    void drawShape(ShapeType shapeType);

    void setLineMode(bool isLine);

signals:

public slots:

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

private:
    ShapeType m_shapeType;
    QOpenGLShaderProgram m_shaderProgram;
};
static unsigned int VBO, VAO, EBO;

//static 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   // 左上角
//};

static 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   // 左上角
};

static unsigned int indices[] = {
    // 注意索引从0开始!
    // 此例的索引(0,1,2,3)就是顶点数组vertices的下标,
    // 这样可以由下标代表顶点组合成矩形

    0, 1, 3, // 第一个三角形
    1, 2, 3  // 第二个三角形
};

static 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";

static 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"
                                          "}\0";

CustomGLWidget::CustomGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
    , m_shapeType(ST_NULL)
{

}

CustomGLWidget::~CustomGLWidget()
{
    makeCurrent();
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    doneCurrent();
}

void CustomGLWidget::drawShape(CustomGLWidget::ShapeType shapeType)
{
    if(m_shapeType != shapeType){
        m_shapeType = shapeType;
        update();
    }
}

void CustomGLWidget::setLineMode(bool isLine)
{
    makeCurrent();
    if(isLine){
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }
    else{
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
    doneCurrent();

    update();
}

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

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    // 绑定顶点数组对象
    glBindVertexArray(VAO);

    // 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 复制我们的索引数组到一个索引缓冲中,供OpenGL使用
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // 设定顶点属性指针
    glVertexAttribPointer(0 ,3, GL_FLOAT, GL_FALSE, 3* sizeof(float), nullptr);
    glEnableVertexAttribArray(0);

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    m_shaderProgram.link();
}

void CustomGLWidget::resizeGL(int w, int h)
{
    QOpenGLWidget::resizeGL(w, h);
}

void CustomGLWidget::paintGL()
{
    // 背景颜色
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    switch (m_shapeType) {
    case ST_RECT:
        m_shaderProgram.bind();
        glBindVertexArray(VAO);
        //glDrawArrays(GL_TRIANGLES, 0, 6);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
        break;
    default:
        break;
    }
}

代码解析

当调用 ‎‎paintGL‎‎()、‎‎resizeGL‎‎() 或 initializeGL() 时,部件的 ‎‎OpenGL‎‎ 渲染上下文将变为最新。‎‎如果您需要从其他地方调用标准的 OpenGL API 函数(例如,在部件的构造函数或您自己的绘制函数中),则必须首先调用 ‎‎makeCurrent‎‎()。‎

如代码中设置线框模式setLineMode,需要在函数中调用OpenGL接口glPolygonMode,因此在调用前需要先调用makeCurrent‎‎,结束后调用doneCurrent。

QOpenGLShaderProgram 封装程度较高,用于简化代码,对比如下:

    // 编译顶点着色器
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    // 编译片段着色器
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    // 链接
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
	m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    m_shaderProgram.link();

补充

前文中ShaderProgram的加载方式除了通过字符串的方式,这个方式不利于书写和理解,更常用的方式是通过资源文件的方式加入程序中,如下图所示:

学习OpenGL(三):QOpenGLShaderProgram的使用_第2张图片
增加了两个文件shapes.vert、shapes.frag,一个是顶点着色器,一个是片段着色器,将上述字符串粘贴至文件中,去除连接符和换行符即可。c++代码中加载Shader源文件的地方需要进行修改,如下 所示:

    m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/shapes.vert");
    m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/shapes.frag");
    m_shaderProgram.link();

你可能感兴趣的:(学习OpenGL,学习,qt)