索引缓冲对象(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 // 第二个三角形
};
同VBO使用函数 glGenBuffers 创建一个IBO对象
// 创建IBO
GLuint m_nIBOId;
glGenBuffers(1, &m_nIBOId);
使用函数 glBindBuffer 绑定IBO, 函数 glBufferData 设置IBO的数据
// 初始化IBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIBOId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
使用函数 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)
下图为绘制的四变型效果:
函数 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;
}