Qt与OpenGL编程 - IBO与线框模式

1. 索引缓冲对象(IBO)

索引缓冲对象(Element Buffer Object, EBO,也叫Index Buffer Object, IBO)。假设我们要绘制得不是三角形,而是四边形,那么我们要写生成得节点组合可能是这样的:

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

可以看到我们要生成得四边形只有4个顶点,而我们却生成6个顶点。如果一个模型有很对顶点,为了避免不必要得开销,索引我们引入了索引缓冲对象。IBO是一个索引顶点得缓冲对象,OpenGL调用这些顶点的索引来决定该绘制哪些顶点。我们只需要定义顶点和绘制矩形所需的索引:

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

GLuint indices[] = { // 注意索引从0开始! 
    0, 1, 3, // 第一个三角形
    1, 2, 3  // 第二个三角形
};
(1) 创建IBO

同VBO使用函数 glGenBuffers 创建一个IBO对象

// 创建IBO
GLuint m_nIBOId;
glGenBuffers(1, &m_nIBOId);
(2)设置IBO的数据

使用函数 glBindBuffer 绑定IBO, 函数 glBufferData 设置IBO的数据

// 初始化IBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIBOId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
(3)绘制

使用函数 glDrawElements 绘制

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIBOId);
// 绘制
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

函数 glDrawElements 的原型如下:

glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)

  • 参数 mode : 绘制模式, GL_TRIANGLES 为三角形。
  • 参数 count : 绘制的顶点数目。
  • 参数 type : 数据的类型,这里索引数据存储的为 GL_UNSIGNED_INT
  • 参数 indices : 表示偏移。

下图为绘制的四变型效果:


2. 线框模式

函数 glPolygonMode 表示OpenGL使用何种方式绘制图元。

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

表示使用线框模式,效果如下图说是

如果要恢复成填充模式,可以使用如下代码:

glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

完整代码如下:

头文件:

#ifndef OPENGLRENDERWIDGET_H
#define OPENGLRENDERWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions_2_0>
#include <QOpenGLFunctions_3_3_Core>

class OpenglRenderWidget : public QOpenGLWidget, public QOpenGLFunctions_2_0
{
    Q_OBJECT
public:
    struct VertexAttributeData
    {
        // Postion
        float pos[3];
        float color[3];
    };

public:
    OpenglRenderWidget(QWidget* parent = nullptr);
    ~OpenglRenderWidget();

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

private:
    bool initShaderProgram(void);
    void createVertexAttributeData(VertexAttributeData* pVetAttr);

    GLuint m_shaderProgramId;
    QOpenGLShaderProgram* m_pShaderProgram = nullptr;
    QOpenGLShader* m_pVertexShader = nullptr;
    QOpenGLShader* m_pFragmentShader = nullptr;

    GLuint m_nVBOId;
    GLuint m_nIBOId;

    // Attribute Location
    GLint m_nPosAttrLocationId;
    GLint m_nColorAttrLocationId;
};

#endif

cpp文件

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

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

}

OpenglRenderWidget::~OpenglRenderWidget()
{

}

void OpenglRenderWidget::initializeGL()
{
    this->initializeOpenGLFunctions();

    // 初始化GPU程序
    bool result = initShaderProgram();
    if (!result)
        return;

    m_shaderProgramId = m_pShaderProgram->programId();
    // 获取位置和颜色的locationID
    m_nPosAttrLocationId = glGetAttribLocation(m_shaderProgramId, "pos");
    m_nColorAttrLocationId = glGetAttribLocation(m_shaderProgramId, "color");

    // 创建顶点属性数据
    VertexAttributeData vAttrData[4];
    createVertexAttributeData(vAttrData);
    // 创建IBO数据
    GLuint indices[] = {0, 1, 3, 1, 2, 3};

    // 创建VBO
    glGenBuffers(1, &m_nVBOId);
    // 创建IBO
    glGenBuffers(1, &m_nIBOId);

    // 初始化VBO
    glBindBuffer(GL_ARRAY_BUFFER, m_nVBOId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vAttrData), vAttrData, GL_STATIC_DRAW);

    // 初始化IBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIBOId);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // 设置顶点信息属性指针
    glVertexAttribPointer(m_nPosAttrLocationId, 4, GL_FLOAT, GL_FALSE, sizeof(VertexAttributeData), (void*)0);
    glEnableVertexAttribArray(m_nPosAttrLocationId);
    // 设置原色信息属性指针
    glVertexAttribPointer(m_nColorAttrLocationId, 4, GL_FLOAT, GL_FALSE, sizeof(VertexAttributeData), (void*)(sizeof (float) * 3));
    glEnableVertexAttribArray(m_nColorAttrLocationId);

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

void OpenglRenderWidget::resizeGL(int w, int h)
{
    this->glViewport(0, 0, w, h);

    return QOpenGLWidget::resizeGL(w, h);
}

void OpenglRenderWidget::paintGL()
{
    glClearColor(51.0f / 255.0f, 76.0f / 255.0f, 76.0f / 255.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    // 使用shader
    m_pShaderProgram->bind();

    //glBindBuffer(GL_ARRAY_BUFFER, m_nVBOId);
//    // 绘制
//    glDrawArrays(GL_POLYGON, 0, 4);
    //glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIBOId);
    // 绘制
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


    m_pShaderProgram->release();
}

bool OpenglRenderWidget::initShaderProgram(void)
{
    m_pShaderProgram = new QOpenGLShaderProgram(this);

    // 加载顶点着色器
    QString vertexShaderStr(":/vertexshader.vsh");
    m_pVertexShader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    bool result = m_pVertexShader->compileSourceFile(vertexShaderStr);
    if (!result)
    {
        qDebug() << m_pVertexShader->log();
        return false;
    }

    // 加载片段着色器
    QString fragmentShaderStr(":/fragmentshader.fsh");
    m_pFragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    result = m_pFragmentShader->compileSourceFile(fragmentShaderStr);
    if (!result)
    {
        qDebug() << m_pFragmentShader->log();
        return false;
    }

    // 创建ShaderProgram
    m_pShaderProgram = new QOpenGLShaderProgram(this);
    m_pShaderProgram->addShader(m_pVertexShader);
    m_pShaderProgram->addShader(m_pFragmentShader);
    return m_pShaderProgram->link();
}

void OpenglRenderWidget::createVertexAttributeData(VertexAttributeData* pVetAttr)
{
    // 第一个点位置信息
    pVetAttr[0].pos[0] = -0.5f;
    pVetAttr[0].pos[1] = 0.5f;
    pVetAttr[0].pos[2] = 0.0f;
    // 第一个点颜色信息
    pVetAttr[0].color[0] = 1.0f;
    pVetAttr[0].color[1] = 0.0f;
    pVetAttr[0].color[2] = 0.0f;

    // 第二个点位置信息
    pVetAttr[1].pos[0] = -0.5f;
    pVetAttr[1].pos[1] = -0.5f;
    pVetAttr[1].pos[2] = 0.0f;
    // 第二个点颜色信息
    pVetAttr[1].color[0] = 0.0f;
    pVetAttr[1].color[1] = 1.0f;
    pVetAttr[1].color[2] = 0.0f;

    // 第三个点位置信息
    pVetAttr[2].pos[0] = 0.5f;
    pVetAttr[2].pos[1] = -0.5f;
    pVetAttr[2].pos[2] = 0.0f;
    // 第三个点颜色信息
    pVetAttr[2].color[0] = 0.0f;
    pVetAttr[2].color[1] = 0.0f;
    pVetAttr[2].color[2] = 1.0f;

    // 第四个点位置信息
    pVetAttr[3].pos[0] = 0.5f;
    pVetAttr[3].pos[1] = 0.5f;
    pVetAttr[3].pos[2] = 0.0f;
    // 第四个点颜色信息
    pVetAttr[3].color[0] = 0.0f;
    pVetAttr[3].color[1] = 1.0f;
    pVetAttr[3].color[2] = 1.0f;
}
不会飞的纸飞机
扫一扫二维码,了解我的更多动态。

下一篇文章:Qt与OpenGL趣味开发 - 绘制炫酷倒影