【OpenGL学习】(三)元素缓冲对象(EBO)的使用
【OpenGL学习】(三)元素缓冲对象的使用
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 // 左上角(重复)
};
现在我们需要渲染这两个三角形,但它们包含重复的顶点,导致内存的浪费。通过使用索引来组合这些顶点,我们可以复用重复的顶点,从而节省内存。
// 使用vertices中的第0,1,3个顶点绘制第一个三角形 // 使用vertices中的第1,2,3个顶点绘制第二个三角形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 // 左上角};
这可以通过元素缓冲对象(EBO)实现:
以下代码对【OpenGL学习】(二)OpenGL渲染简单图形中的代码进行进行修改(修改内容用!标记)
// https://github.com/JoeyDeVries/LearnOpenGL/tree/master/src/1.getting_started/2.2.hello_triangle_indexed
#include <glad/glad.h> // 加载 OpenGL 函数指针
#include <GLFW/glfw3.h> // GLFW 用于创建窗口和处理输入#include <iostream> // 回调函数声明:当窗口大小发生改变时调用
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
// 处理输入的函数声明
void processInput(GLFWwindow* window);// 设置窗口宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;// 顶点着色器GLSL源码
const char* vertexShaderSource = "#version 330 core\n" // 使用 OpenGL 3.3 对应的 GLSL 版本(即 GLSL 3.30)
"layout (location = 0) in vec3 aPos;\n" // 顶点位置属性,位置值为0
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" // 设置顶点位置
"}\0";// 片段着色器GLSL源码
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" // 设置输出颜色为橙色
"}\n\0";int main()
{// 初始化并配置 GLFWglfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL 主版本号glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // OpenGL 次版本号glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // MacOS 需要加这句
#endif// 创建 GLFW 窗口GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate(); // 初始化失败,退出程序return -1;}glfwMakeContextCurrent(window); // 将窗口上下文设为当前线程上下文glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 设置窗口大小改变时的回调// 初始化 GLAD,用于加载 OpenGL 函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}// ------------------构建并编译着色器程序------------------// 顶点着色器unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); // 创建着色器对象glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); // 附加源码glCompileShader(vertexShader); // 编译着色器// 检查编译错误int success;char infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}// 片段着色器unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);// 创建一个片段着色器对象glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); // 将片段着色器源码附加到着色器对象上glCompileShader(fragmentShader); // 编译片段着色器源码// 检查编译错误glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}// 着色器程序unsigned int shaderProgram = glCreateProgram(); // 创建程序对象glAttachShader(shaderProgram, vertexShader); // 附加着色器glAttachShader(shaderProgram, fragmentShader);glLinkProgram(shaderProgram); // 链接程序// 检查链接错误glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if (!success) {glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;}// 删除已编译的着色器对象,已链接到程序中不再需要glDeleteShader(vertexShader);glDeleteShader(fragmentShader);// --------------------------------------------------------// !设置顶点数据和缓冲,并配置顶点属性 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 // 左上角};// 索引!unsigned int indices[] = {0, 1, 3, // 使用vertices中的第0,1,3个顶点绘制第一个三角形 1, 2, 3 // 使用vertices中的第1,2,3个顶点绘制第二个三角形};unsigned int VBO, VAO, EBO; // !glGenVertexArrays(1, &VAO); // 创建顶点数组对象glGenBuffers(1, &VBO); // 创建顶点缓冲对象glGenBuffers(1, &EBO); // ! 创建元素缓冲对象glBindVertexArray(VAO); // 绑定 VAOglBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定 VBO glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); // !绑定 EBO glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 传入数据glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); //! 传入索引// 设置顶点属性指针glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glEnableVertexAttribArray(0); // 启用顶点属性// !当 VAO 处于活动状态时,不要解绑 EBO”,是因为解绑会改变 VAO 记录的 EBO 绑定,导致后续绘制失效//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑 VBO,为了安全,非必须glBindVertexArray(0); // 解绑 VAO// 可以取消注释以使用线框模式绘制:也就是不填充图形,只画出边框线glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);// 渲染循环while (!glfwWindowShouldClose(window)){// 处理输入processInput(window);// 清屏并设置背景颜色glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// 绘制三角形glUseProgram(shaderProgram); // 使用着色器程序glBindVertexArray(VAO); // 绑定 VAO//glDrawArrays(GL_TRIANGLES, 0, 6);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // !// 交换缓冲区并查询IO事件glfwSwapBuffers(window);glfwPollEvents();}// 可选:释放所有资源glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteProgram(shaderProgram);// 释放 GLFW 资源glfwTerminate();return 0;
}// 处理输入:如果按下 ESC 键,则关闭窗口
void processInput(GLFWwindow* window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}// 当窗口大小发生改变时,自动调整视口大小
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{glViewport(0, 0, width, height); // 设置 OpenGL 视口大小
}