本章参考官方教程: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帮助我们高效描述复杂图形的顶点索引。
它们的组合可以让我们更高效地绘制图形,让图形的渲染速度更快,性能更好。
简单来说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 中的索引来决定哪些顶点需要被绘制,从而可以重用顶点数据,避免冗余。
运行截图
感谢阅读^ _ ^
如有错误感谢指正。