图形学学习(一):VAO、VBO、EBO的关系到底是啥??

这学期开始了对图形学的学习,打算向这方面努力,加油!!,也是第一次写博客,希望记录自己学习中遇到的问题,对大家也能有所收获,有所错误也请见谅。

VAO与VBO

VAO、VBO的关系,VBO是顶点数据的载体,VAO是顶点数据的解释器。

注:VAO可对应多个VBO,因为顶点属性不只一种,如下图,VAO存储了位置和颜色两种顶点属性解释结果。

 

VBO:VBO是显存中的一片缓冲区域,用于存放从内存中提交过来的顶点数据。这些数据包括顶点的位置、颜色、纹理坐标等。当GPU进行绘制时,它需要从VBO中读取这些数据,并根据这些数据来渲染图形。VBO的主要作用是减少CPU与GPU之间的数据传输次数,提高渲染效率。

VAO:VAO则是一个用于存储和管理顶点数据状态的对象。它记录了如何解析VBO中的数据,即如何将这些数据映射到着色器中的相应属性上。VAO通过记录glVertexAttribPointer等方法的调用结果,使得在绘制时可以直接通过VAO来访问VBO中的数据,而无需每次绘制时都重新解析VBO。

通过VAO,我们可以避免在每次绘制时都重新解析VBO,从而提高了渲染效率。这是因为VAO存储了顶点数据的解析状态,使得在多次绘制中可以重复使用这些状态。

顶点着色器:

const char *vertexShaderSource = "#version 330 core\n"

"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";

Opengl学习代码(VAO、VBO、EBO):

    //生成VAO,VBO,EBO

    unsigned int VBO,VAO,EBO;

    //生成顶点数组对象

    glGenVertexArrays(1, &VAO);



    //生成顶点缓存和元素缓存

    glGenBuffers(1, &VBO);

    glGenBuffers(1, &EBO);



    //先绑定VAO,在VBO之前

    glBindVertexArray(VAO);

    

    //把顶点数组复制到缓冲中供OpenGL使用

    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    

    //传入索引数据

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    

    // 设置0号顶点属性指针

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    

    //启用0号顶点属性指针

    glEnableVertexAttribArray(0);



    //在顶点数据传输和解释完毕后解绑

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindVertexArray(0);

在OpenGL的学习中,对VBO的处理主要有3个函数(生成->绑定->传入顶点数据):

glGenBuffers(1, &VBO);

1是一个缓冲ID,用Gen生成一个缓冲区。

glBindBuffer(GL_ARRAY_BUFFER, VBO);  

此时对VBO进行了绑定,所有输入GL_ARRAY_BUFFER的数据,都会直接被送入现在这个VBO

注:GL_ARRAY_BUFFER是一种顶点缓冲类型

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glBufferData将数据从cpu的流中提取出来,放入显存中的VBO管理的区域里。

VAO中的几个函数:

glBindVertexArray(VAO);

绑定VAO,并且要在VBO、EBO之前

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

函数绑定的VBO和顶点属性配置(也就是数据放在哪里,和如何读取数据),0就是0号顶点属性。

glEnableVertexAttribArray和glDisableVertexAttribArray这两个函数用来启用顶点属性,和取消顶点属性链接。

VBO与EBO

EBO(元素缓冲对象)主要作用是与Vertex Buffer Object(顶点缓冲区对象,VBO)配合使用,以缓存图元索引数据,进而提升渲染效率。

  float vertices[] = {

       0.5f,  0.5f, 0.0f,  // top right

       0.5f, -0.5f, 0.0f,  // bottom right

      -0.5f, -0.5f, 0.0f,  // bottom left

      -0.5f,  0.5f, 0.0f   // top left

  };

  unsigned int indices[] = {  // note that we start from 0!

      0, 1, 3,  // first Triangle

      1, 2, 3   // second Triangle

  };

如上:避免顶点数据的冗余,包括位置和颜色数据的冗余,6个顶点我们需要使用 6 个 float3 来表示位置和颜色,当需要渲染的顶点数时会造成更大的冗余,我们看看如何针对这个问题来进行优化,事实上我们复用了两个顶点,也就是说其实 4 个顶点数据就够用了。

更详细的内容、实例参考以下内容,讲的很好!!!

[OpenGL]VBO,VAO和EBO详解 - 简书 (jianshu.com)

  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这里提供一个简单的OpenGL代码示例,演示如何进行着色、纹理映射等基本图形渲染操作。 ```c++ #include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> // 窗口大小 const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600; // 顶点着色器代码 const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "layout (location = 1) in vec3 aColor;\n" "layout (location = 2) in vec2 aTexCoord;\n" "out vec3 ourColor;\n" "out vec2 TexCoord;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos, 1.0);\n" " ourColor = aColor;\n" " TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n" "}\0"; // 片段着色器代码 const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "in vec3 ourColor;\n" "in vec2 TexCoord;\n" "uniform sampler2D ourTexture;\n" "void main()\n" "{\n" " FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0f);\n" "}\n\0"; int main() { // 初始化GLFW glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 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); // 初始化GLAD if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } // 编译顶点着色器 int success; char infoLog[512]; unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); 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, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上角 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下角 -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下角 -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上角 }; unsigned int indices[] = { 0, 1, 3, // 第一个三角形 1, 2, 3 // 第二个三角形 }; unsigned int VBO, VAO, EBO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2); // 创建纹理 unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); // 设置纹理参数 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 加载并生成纹理 int width, height, nrChannels; stbi_set_flip_vertically_on_load(true); unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0); if (data) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); } else { std::cout << "Failed to load texture" << std::endl; } stbi_image_free(data); // 渲染循环 while (!glfwWindowShouldClose(window)) { // 输入处理 processInput(window); // 渲染指令 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // 绑定纹理 glBindTexture(GL_TEXTURE_2D, texture); // 设置uniform glUseProgram(shaderProgram); int textureLocation = glGetUniformLocation(shaderProgram, "ourTexture"); glUniform1i(textureLocation, 0); // 绘制矩形 glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // 交换缓冲区和轮询IO事件 glfwSwapBuffers(window); glfwPollEvents(); } // 释放资源 glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteBuffers(1, &EBO); glfwTerminate(); return 0; } void processInput(GLFWwindow *window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); } ``` 这段代码实现了一个简单的矩形渲染,并将颜色和纹理信息传递到顶点和片段着色器中进行处理。在渲染循环中,先绑定纹理,设置uniform,然后绘制矩形。注意,这里使用了stb_image库来加载纹理图片。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值