OpenGL -- 你好三角形
一、概念
VAO:Vertex Array Object
VBO:Vertex Buffer Object
EBO/IBO:Element Buffer Object,Index Buffer Object
二、解释为何如此编程
A vertex buffer object (VBO) is nothing fancy - it's just an array of data (usually floats).
A VBO is not an "object" in the object-oriented programming sense, it is a simple array of data. OpenGL has several other types of data that it refers to as singular "buffer objects".
(一)为什么要用VBO?:The key idea of VBOs is this: in the old, "immediate-mode" days of OpenGL, before VBOs, we would define the vertex data in main memory (RAM), and copy them one-by-one each time that we draw. With VBOs, we copy the whole lot into a buffer before drawing starts, and this sits on the graphics hardware memory instead. This is much more efficient for drawing because, although the bus between the CPU and the GPU is very wide, a bottleneck for drawing performance is created when drawing operations stall to send OpenGL commands from the CPU to the GPU. To avoid this we try to keep as much data and processing on the graphics hardware as we can.
解释一下,其实就是IO瓶颈,其实读取一个字节和1千个字节花费时间是差不多的,这也是为什么提出B-树,红黑树的原因,时间的花费主要在不同速度存储介质上寻找(可能这段不对,请具体查询)。
而3.3之前的OpenGL是立即渲染模式"immediate-mode",这大大浪费IO,这也是为什么大多数教程从3.3开始教OpenGL,使用VBO可以使得大量点的数据一次读入,节约IO,但这提升肯定不够,于是就有了VAO,可以理解为是VBO的数组?
但还是不够,为什么?因为大多数物体表面肯定不是我们所看到那样,极有可能是各种三角形、线段组成的,这会使得如果使用VAO,那么会有大量的点重复(因为物体是封闭的,两个三角形之间有一条边是重合的等其他情况)造成内存浪费,于是就提出IBO,索引,一个点就存一次,如果要使用,我们就是用像访问数组的方式,通过下标直接访问,这样大大节约空间。
以上仅仅是我的理解,不一定对,请多多参考他人。
代码:
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 const char* vertexShaderSource = "#version 330 core\n" 9 "layout (location = 0) in vec3 aPos;\n" 10 "void main()\n" 11 "{\n" 12 " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" 13 "}\0"; 14 const char* fragmentShaderSource = "#version 330 core\n" 15 "out vec4 FragColor;\n" 16 "void main()\n" 17 "{\n" 18 " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" 19 "}\n\0"; 20 int main() 21 { 22 glfwInit(); 23 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 24 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 25 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 26 27 GLFWwindow* window = glfwCreateWindow(2000, 2000, "LearnOpenGL", NULL, NULL); 28 if (window == NULL) 29 { 30 std::cout << "Failed to create GLFW window" << std::endl; 31 glfwTerminate(); 32 return -1; 33 } 34 glfwMakeContextCurrent(window); 35 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 36 37 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) 38 { 39 std::cout << "Failed to initialize GLAD" << std::endl; 40 return -1; 41 } 42 43 44 45 unsigned int vertexShader; 46 vertexShader = glCreateShader(GL_VERTEX_SHADER); 47 glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); 48 glCompileShader(vertexShader); 49 int success; 50 char infoLog[512]; 51 glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); 52 if (!success) 53 { 54 glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); 55 std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; 56 } 57 58 59 unsigned int fragmentShader; 60 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 61 glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); 62 glCompileShader(fragmentShader); 63 64 65 unsigned int shaderProgram; 66 shaderProgram = glCreateProgram(); 67 glAttachShader(shaderProgram, vertexShader); 68 glAttachShader(shaderProgram, fragmentShader); 69 glLinkProgram(shaderProgram); 70 71 glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); 72 if (!success) { 73 glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); 74 std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; 75 } 76 77 glUseProgram(shaderProgram); 78 glDeleteShader(vertexShader); 79 glDeleteShader(fragmentShader); 80 81 float vertices[] = { 82 -0.5f, -0.5f, 0.0f, 83 0.0f, -0.5f, 0.0f, 84 -0.25f, 0.5f, 0.0f, 85 0.0f, -0.5f, 0.0f, 86 0.5f, -0.5f, 0.0f, 87 0.25f, 0.5f, 0.0f 88 }; 89 90 unsigned int indices[] = { // 注意索引从0开始! 91 0, 1, 2, // 第一个三角形 92 3, 4, 5 // 第二个三角形 93 }; 94 unsigned int VBO, VAO, EBO; 95 glGenBuffers(1, &EBO); 96 glGenVertexArrays(1, &VAO); 97 glGenBuffers(1, &VBO);//生成 98 99 glBindVertexArray(VAO); 100 101 glBindBuffer(GL_ARRAY_BUFFER, VBO); 102 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 103 104 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);//把新创建的缓冲绑定到目标上 105 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 106 //一个专门用来把用户定义的数据复制到当前绑定缓冲的函数 107 108 109 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//告诉OpenGL该如何解析顶点数据 110 //每个顶点属性从一个VBO管理的内存中获得它的数据,而具体是从哪个VBO(程序中可以有多个VBO)获取 111 //则是通过在调用glVertexAttribPointer时绑定到GL_ARRAY_BUFFER的VBO决定的。 112 glEnableVertexAttribArray(0);//vertex attribute location as its argument. 113 //vertex attributes are disabled by default. 114 115 glBindBuffer(GL_ARRAY_BUFFER, 0);//参数0,好像是解绑的意思 116 glBindVertexArray(0); 117 //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 118 while (!glfwWindowShouldClose(window)) 119 { 120 processInput(window); 121 122 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 123 glClear(GL_COLOR_BUFFER_BIT); 124 125 glUseProgram(shaderProgram); 126 glBindVertexArray(VAO); 127 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 128 glBindVertexArray(0); 129 130 glfwSwapBuffers(window); 131 glfwPollEvents(); 132 } 133 134 glDeleteVertexArrays(1, &VAO); 135 glDeleteBuffers(1, &VBO); 136 glDeleteBuffers(1, &EBO); 137 138 glfwTerminate(); 139 140 return 0; 141 } 142 143 144 void framebuffer_size_callback(GLFWwindow* window, int width, int height) 145 { 146 glViewport(0, 0, width, height); 147 } 148 149 void processInput(GLFWwindow* window) 150 { 151 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) 152 glfwSetWindowShouldClose(window, true); 153 }