几张图看明白VAO、VBO、EBO的关系和代码顺序
0.详细教程可看https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/
1.可以简单地认为VAO的作用是这样的:
由于每渲染一次物体就要用一个VBO,而每次绑定一次VBO就要设置各个的顶点的属性,启动各个属性,代码十分复杂,复用性很差,因为每个物体的属性个数什么的都不一样(也就是说不是同构的),循环根本解决不了。所以就抽象出一层VAO来解决这个问题,相当于复用代码,使之简介快速。只在一开始将所有的VBO绑定对应的VAO就OK了,之后渲染的时候完全就可以绑定VAO,然后你就循环处理同构的VAO就好了。
2.其实EBO的作用也可以简单地理解为复用跟减少冗余,貌似还可以节约内存跟缓存:
否则的话VBO中储存大量的数据
3.VAO中存储着VBO的信息和EBO的信息
所以正确的绑定顺序是VAO、VBO、EBO,将后两者的信息也绑定进VAO中去啊
如果不适用VAO的话,那么最后一部分每次渲染都要设置第四部分的顶点属性,要好多行好多行,而用了VAO就一行绑定VAO就OK了啊,嘻嘻
4.创建相同的两个三角形,但对它们的数据使用不同的VAO和VBO:
1 #include <glad/glad.h> 2 #include <GLFW/glfw3.h> 3 4 #include <iostream> 5 6 void framebuffer_size_callback(GLFWwindow* window, int width, int height); 7 void processInput(GLFWwindow *window); 8 9 // settings 10 const unsigned int SCR_WIDTH = 800; 11 const unsigned int SCR_HEIGHT = 600; 12 13 const char *vertexShaderSource = "#version 330 core\n" 14 "layout (location = 0) in vec3 aPos;\n" 15 "void main()\n" 16 "{\n" 17 " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" 18 "}\0"; 19 const char *fragmentShaderSource = "#version 330 core\n" 20 "out vec4 FragColor;\n" 21 "void main()\n" 22 "{\n" 23 " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" 24 "}\n\0"; 25 26 int main() 27 { 28 // glfw: initialize and configure 29 // ------------------------------ 30 glfwInit(); 31 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 32 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 33 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 34 35 #ifdef __APPLE__ 36 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X 37 #endif 38 39 // glfw window creation 40 // -------------------- 41 GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); 42 if (window == NULL) 43 { 44 std::cout << "Failed to create GLFW window" << std::endl; 45 glfwTerminate(); 46 return -1; 47 } 48 glfwMakeContextCurrent(window); 49 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 50 51 // glad: load all OpenGL function pointers 52 // --------------------------------------- 53 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) 54 { 55 std::cout << "Failed to initialize GLAD" << std::endl; 56 return -1; 57 } 58 59 60 // build and compile our shader program 61 // ------------------------------------ 62 // vertex shader 63 int vertexShader = glCreateShader(GL_VERTEX_SHADER); 64 glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); 65 glCompileShader(vertexShader); 66 // check for shader compile errors 67 int success; 68 char infoLog[512]; 69 glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); 70 if (!success) 71 { 72 glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); 73 std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; 74 } 75 // fragment shader 76 int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 77 glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); 78 glCompileShader(fragmentShader); 79 // check for shader compile errors 80 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); 81 if (!success) 82 { 83 glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); 84 std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; 85 } 86 // link shaders 87 int shaderProgram = glCreateProgram(); 88 glAttachShader(shaderProgram, vertexShader); 89 glAttachShader(shaderProgram, fragmentShader); 90 glLinkProgram(shaderProgram); 91 // check for linking errors 92 glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); 93 if (!success) { 94 glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); 95 std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; 96 } 97 glDeleteShader(vertexShader); 98 glDeleteShader(fragmentShader); 99 100 // set up vertex data (and buffer(s)) and configure vertex attributes 101 // ------------------------------------------------------------------ 102 float firstTriangle[] = { 103 -0.9f, -0.5f, 0.0f, // left 104 -0.0f, -0.5f, 0.0f, // right 105 -0.45f, 0.5f, 0.0f, // top 106 }; 107 float secondTriangle[] = { 108 0.0f, -0.5f, 0.0f, // left 109 0.9f, -0.5f, 0.0f, // right 110 0.45f, 0.5f, 0.0f // top 111 }; 112 unsigned int VBOs[2], VAOs[2]; 113 glGenVertexArrays(2, VAOs); // we can also generate multiple VAOs or buffers at the same time 114 glGenBuffers(2, VBOs); 115 // first triangle setup 116 // -------------------- 117 glBindVertexArray(VAOs[0]); 118 glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]); 119 glBufferData(GL_ARRAY_BUFFER, sizeof(firstTriangle), firstTriangle, GL_STATIC_DRAW); 120 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // Vertex attributes stay the same 121 glEnableVertexAttribArray(0); 122 // glBindVertexArray(0); // no need to unbind at all as we directly bind a different VAO the next few lines 123 // second triangle setup 124 // --------------------- 125 glBindVertexArray(VAOs[1]); // note that we bind to a different VAO now 126 glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]); // and a different VBO 127 glBufferData(GL_ARRAY_BUFFER, sizeof(secondTriangle), secondTriangle, GL_STATIC_DRAW); 128 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); // because the vertex data is tightly packed we can also specify 0 as the vertex attribute's stride to let OpenGL figure it out 129 glEnableVertexAttribArray(0); 130 // glBindVertexArray(0); // not really necessary as well, but beware of calls that could affect VAOs while this one is bound (like binding element buffer objects, or enabling/disabling vertex attributes) 131 132 133 // uncomment this call to draw in wireframe polygons. 134 //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 135 136 // render loop 137 // ----------- 138 while (!glfwWindowShouldClose(window)) 139 { 140 // input 141 // ----- 142 processInput(window); 143 144 // render 145 // ------ 146 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 147 glClear(GL_COLOR_BUFFER_BIT); 148 149 glUseProgram(shaderProgram); 150 // draw first triangle using the data from the first VAO 151 glBindVertexArray(VAOs[0]); 152 glDrawArrays(GL_TRIANGLES, 0, 3); 153 // then we draw the second triangle using the data from the second VAO 154 glBindVertexArray(VAOs[1]); 155 glDrawArrays(GL_TRIANGLES, 0, 3); 156 157 // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) 158 // ------------------------------------------------------------------------------- 159 glfwSwapBuffers(window); 160 glfwPollEvents(); 161 } 162 163 // optional: de-allocate all resources once they've outlived their purpose: 164 // ------------------------------------------------------------------------ 165 glDeleteVertexArrays(2, VAOs); 166 glDeleteBuffers(2, VBOs); 167 168 // glfw: terminate, clearing all previously allocated GLFW resources. 169 // ------------------------------------------------------------------ 170 glfwTerminate(); 171 return 0; 172 } 173 174 // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly 175 // --------------------------------------------------------------------------------------------------------- 176 void processInput(GLFWwindow *window) 177 { 178 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) 179 glfwSetWindowShouldClose(window, true); 180 } 181 182 // glfw: whenever the window size changed (by OS or user resize) this callback function executes 183 // --------------------------------------------------------------------------------------------- 184 void framebuffer_size_callback(GLFWwindow* window, int width, int height) 185 { 186 // make sure the viewport matches the new window dimensions; note that width and 187 // height will be significantly larger than specified on retina displays. 188 glViewport(0, 0, width, height); 189 }