本章参考官方教程:learnopengl-cn

本章的GLSL不需要改变参考第三章中的VertexShader.glsl和FragmentShader.glsl即可

顶点数组对象:Vertex Array Object, 		VAO
顶点缓冲对象:Vertex Buffer Object,		VBO
元素缓冲对象:Element Buffer Object,		EBO

其中EBO又可以叫做 : IBO(Index Buffer Object) 索引缓冲对象 ,EBO是比较常见的叫法。

当我们使用OpenGL绘制图形时,需要将顶点的位置、颜色等信息传递给显卡,让显卡进行绘制。

1、VBO(Vertex Buffer Object)是用来存储这些顶点数据的特殊缓冲区,可以将数据保存在显卡中,避免频繁地从CPU传输数 据到显卡,提高绘制效率。

2、VAO(Vertex Array Object)则是用来管理这些顶点数据的设置的。
绘制一个图形通常需要多个顶点属性(比如位置、颜色等),VAO帮助我们预先指定这些属性在VBO中的排列方式,
避免在每次绘制时重新设置属性,简化绘制代码。

3、EBO(Element Buffer Object)是用来存储图元的索引数据的缓冲区,比如三角形的三个顶点索引。
它可以帮助我们避免重复存储顶点数据,只需存储少量的索引数据就可以描述复杂的图形,提高绘制效率。

综合来说,VBO、VAO和EBO是帮助我们优化OpenGL绘制图形的工具。

VBO将顶点数据存储在显卡中,
VAO管理这些数据的属性设置,
EBO帮助我们高效描述复杂图形的顶点索引。

它们的组合可以让我们更高效地绘制图形,让图形的渲染速度更快,性能更好。

OpengGL教程(四)---使用EBO方式绘制矩形_OpenGL

简单来说EBO可以根据顶点数据中的下标,绘制对应样式的结构。

GLfloat vertices_1[] = 
{
    0.0f,   700.0f,   0.0f,
	0.0f,   0.0f,     0.0f,
	700.0f, 0.0f,     0.0f,
	700.0f, 700.0f,   0.0f,
	
};

GLfloat vertices_2[] = 
{
	0.0f, 0.0f, 0.0f,
	0.0f, 1.0f, 0.0f,
	0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
};

GLint vertices_3[] = 
{
	0,1,3,
    1,2,3,
};

vertices_1是顶点数据,vertices_2是每个顶点的颜色定义,vertices_3即是EBO,用来控制最终绘制的结构。可以参考上图矩形理解。

main.cpp

#include <iostream>

#include "glew.h"
#include "glfw3.h"

#include "glm/glm.hpp"                  // GLM 的基本数学类型,例如 glm::mat4 和 glm::vec3
#include "glm/gtc/matrix_transform.hpp" // GLM 的矩阵变换函数,例如 glm::ortho
#include "glm/gtc/type_ptr.hpp"         // 用于 glm::value_ptr 函数


#include "log.h"
#include "GlslDealConfig.h"

GLfloat vertices_1[] = 
{
    0.0f,   700.0f,   0.0f,
	0.0f,   0.0f,     0.0f,
	700.0f, 0.0f,     0.0f,
	700.0f, 700.0f,   0.0f,
	
};

GLfloat vertices_2[] = 
{
	0.0f, 0.0f, 0.0f,
	0.0f, 1.0f, 0.0f,
	0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
};

GLint vertices_3[] = 
{
	0,1,3,
    1,2,3,
};

int main()
{
    GlslDealConfig mdeal;
    int major = 0, minor = 0, rev = 0;
    glfwGetVersion(&major, &minor, &rev);
    LOGI("glfw version %d - %d - %d", major,minor,rev);

    if (glfwInit() == GLFW_FALSE)
    {
        LOGE("glfwInit failed");
        return 0;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(700, 700, "Ebo", NULL, NULL);
    if (window == NULL)
    {
        LOGE("glfwCreateWindow failed");
        return 0;
    }

    glfwMakeContextCurrent(window);

    GLenum err = glewInit();
    if (err != GLEW_OK)
    {
        LOGE("glew init failed : %s", reinterpret_cast<const char*>(glewGetErrorString(err)));
        return 0;
    }

    glfwSwapInterval(1);

    std::string vertexShader = mdeal.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/VaoAndVbo/VertexShader.glsl");
    std::string fragmentShader = mdeal.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/VaoAndVbo/FragmentShader.glsl");

    const GLchar *vData = vertexShader.c_str();
    const GLchar *fData = fragmentShader.c_str();

    GLuint vShader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);

    glShaderSource(vShader,1,&vData,NULL);
    glShaderSource(fShader,1,&fData,NULL);

    glCompileShader(vShader);
    glCompileShader(fShader);

    GLuint Progma = glCreateProgram();
    glAttachShader(Progma,vShader);
    glAttachShader(Progma,fShader);
    glLinkProgram(Progma);

    glDeleteShader(vShader);
    glDeleteShader(fShader);

    GLuint VAO,VBO[2],EBO;

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

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices_1),vertices_1, GL_STATIC_DRAW);
    glVertexAttribPointer(0,3, GL_FLOAT,GL_FALSE,3*sizeof(GLfloat),(void*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices_2),vertices_2, GL_STATIC_DRAW);
    glVertexAttribPointer(1,3, GL_FLOAT,GL_FALSE,3*sizeof(GLfloat),(void*)0);
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(vertices_3),vertices_3, GL_STATIC_DRAW);

    glBindVertexArray(0);

    glm::mat4 projection = glm::ortho(0.0f, 600.0f, 0.0f, 600.0f, -1.0f, 1.0f);
    glUseProgram(Progma);
    GLuint projLoc = glGetUniformLocation(Progma, "projection");
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

    // 绘制循环
    while (!glfwWindowShouldClose(window))
    {
        glViewport(0, 0, 700, 700);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(Progma);
        glBindVertexArray(VAO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(2, VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteProgram(Progma);

    glfwTerminate();
    return 0;
}

注意

glDrawArrays

用途: 用于绘制一组连续的图形原语(例如,三角形、线段等)。
数据来源: 从当前绑定的顶点缓冲对象(VBO)中按顺序读取顶点数据。
不使用 EBO: glDrawArrays 不使用 EBO,因为它不需要索引来决定顶点的绘制顺序。

glDrawElements

用途: 用于绘制图形原语,但允许使用索引来指定哪些顶点需要被绘制。
数据来源: 顶点数据从绑定的 VBO 中读取,索引数据则来自于绑定的 EBO。
使用 EBO: glDrawElements 利用 EBO 中的索引来决定哪些顶点需要被绘制,从而可以重用顶点数据,避免冗余。

运行截图

OpengGL教程(四)---使用EBO方式绘制矩形_数据_02


感谢阅读^ _ ^

如有错误感谢指正。