關於opengl中VAO、VBO和EBO的概念整理


最近在學習下opengl相關知識,主要通過這個地方學習:https://www.jianshu.com/p/6bda18e953f6

第二章和第三章中提到這么幾個概念:頂點緩沖對象(VBO Vertex Buffer Object)、頂點數組對象(VAO Vertex Array Object)和元素緩存對象(EBO Element Buffer Object)

由於對於OpenGL圖形渲染管線相關不是很熟,所以查閱相關資料,整理如下

  上圖是一張經典的OpenGL圖形渲染管線概念圖,

  首先,我們以數組的形式傳遞3個3D坐標作為圖形渲染管線的輸入,用來表示一個三角形,這個數組叫做頂點數據(Vertex Data);頂點數據是一系列頂點的集合。一個頂點(Vertex)是一個3D坐標的數據的集合。而頂點數據是用頂點屬性(Vertex Attribute)表示的,它可以包含任何我們想用的數據。

  為了讓OpenGL知道我們的坐標和顏色值構成的到底是什么,OpenGL需要你去指定這些數據所表示的渲染類型。我們是希望把這些數據渲染成一系列的點?一系列的三角形?還是僅僅是一個長長的線?做出的這些提示叫做圖元(Primitive),任何一個繪制指令的調用都將把圖元傳遞給OpenGL。這是其中的幾個:GL_POINTSGL_TRIANGLESGL_LINE_STRIP

  圖形渲染管線的第一個部分是頂點着色器(Vertex Shader),它把一個單獨的頂點作為輸入。頂點着色器主要的目的是把3D坐標轉為另一種3D坐標,同時頂點着色器允許我們對頂點屬性進行一些基本處理。

  圖元裝配(Primitive Assembly)階段將頂點着色器輸出的所有頂點作為輸入(如果是GL_POINTS,那么就是一個頂點),並所有的點裝配成指定圖元的形狀;。

  圖元裝配階段的輸出會傳遞給幾何着色器(Geometry Shader)。幾何着色器把圖元形式的一系列頂點的集合作為輸入,它可以通過產生新頂點構造出新的(或是其它的)圖元來生成其他形狀。例子中,它生成了另一個三角形。

  幾何着色器的輸出會被傳入光柵化階段(Rasterization Stage),這里它會把圖元映射為最終屏幕上相應的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器運行之前會執行裁切(Clipping)。裁切會丟棄超出你的視圖以外的所有像素,用來提升執行效率。

  片段着色器的主要目的是計算一個像素的最終顏色,這也是所有OpenGL高級效果產生的地方。通常,片段着色器包含3D場景的數據(比如光照、陰影、光的顏色等等),這些數據可以被用來計算最終像素的顏色。

  在所有對應顏色值確定以后,最終的對象將會被傳到最后一個階段,我們叫做Alpha測試和混合(Blending)階段。這個階段檢測片段的對應的深度(和模板(Stencil))值(后面會講),用它們來判斷這個像素是其它物體的前面還是后面,決定是否應該丟棄。這個階段也會檢查alpha值(alpha值定義了一個物體的透明度)並對物體進行混合(Blend)。所以,即使在片段着色器中計算出來了一個像素輸出的顏色,在渲染多個三角形的時候最后的像素顏色也可能完全不同。

  可以看到,圖形渲染管線非常復雜,它包含很多可配置的部分。然而,對於大多數場合,我們只需要配置頂點和片段着色器就行了。幾何着色器是可選的,通常使用它默認的着色器就行了。

  頂點緩沖對象(Vertex Buffer Objects,VBO) 

  頂點緩沖對象VBO是在顯卡存儲空間中開辟出的一塊內存緩存區,用於存儲頂點的各類屬性信息,如頂點坐標,頂點法向量,頂點顏色數據等。在渲染時,可以直接從VBO中取出頂點的各類屬性數據,由於VBO在顯存而不是在內存中,不需要從CPU傳輸數據,處理效率更高。 

  所以可以理解為VBO就是顯存中的一個存儲區域,可以保持大量的頂點屬性信息。並且可以開辟很多個VBO,每個VBO在OpenGL中有它的唯一標識ID,這個ID對應着具體的VBO的顯存地址,通過這個ID可以對特定的VBO內的數據進行存取操作。

  頂點數組對象(Vertex Arrary Object,VAO) 

  VBO保存了一個模型的頂點屬性信息,每次繪制模型之前需要綁定頂點的所有信息,當數據量很大時,重復這樣的動作變得非常麻煩。VAO可以把這些所有的配置都存儲在一個對象中,每次繪制模型時,只需要綁定這個VAO對象就可以了。 

  VAO是一個保存了所有頂點數據屬性的狀態結合,它存儲了頂點數據的格式以及頂點數據所需的VBO對象的引用。 

  VAO本身並沒有存儲頂點的相關屬性數據,這些信息是存儲在VBO中的,VAO相當於是對很多個VBO的引用,把一些VBO組合在一起作為一個對象統一管理。

  執行VAO綁定之后其后的所有VBO配置都是這個VAO對象的一部分,可以說VBO是對頂點屬性信息的綁定,VAO是對很多個VBO的綁定。

  索引緩沖對象(Element Buffer Object,EBO) 

  索引緩沖對象EBO相當於OpenGL中的頂點數組的概念,是為了解決同一個頂點多洗重復調用的問題,可以減少內存空間浪費,提高執行效率。當需要使用重復的頂點時,通過頂點的位置索引來調用頂點,而不是對重復的頂點信息重復記錄,重復調用。 

  EBO中存儲的內容就是頂點位置的索引indices,EBO跟VBO類似,也是在顯存中的一塊內存緩沖器,只不過EBO保存的是頂點的索引。

 

  1 #include <iostream>
  2 
  3 // GLEW
  4 #define GLEW_STATIC
  5 #include <GL/glew.h>
  6 
  7 // GLFW
  8 #include <GLFW/glfw3.h>
  9 
 10 
 11 // Function prototypes
 12 void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
 13 
 14 // Window dimensions
 15 const GLuint WIDTH = 800, HEIGHT = 600;
 16 
 17 // Shaders
 18 const GLchar* vertexShaderSource = "#version 330 core\n"
 19     "layout (location = 0) in vec3 position;\n"
 20     "void main()\n"
 21     "{\n"
 22     "gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
 23     "}\0";
 24 const GLchar* fragmentShaderSource = "#version 330 core\n"
 25     "out vec4 color;\n"
 26     "void main()\n"
 27     "{\n"
 28     "color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
 29     "}\n\0";
 30 
 31 // The MAIN function, from here we start the application and run the game loop
 32 int main()
 33 {
 34     // Init GLFW
 35     glfwInit();
 36     // Set all the required options for GLFW
 37     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
 38     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
 39     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 40     glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
 41 
 42     // Create a GLFWwindow object that we can use for GLFW's functions
 43     GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
 44     glfwMakeContextCurrent(window);
 45 
 46     // Set the required callback functions
 47     glfwSetKeyCallback(window, key_callback);
 48 
 49     // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
 50     glewExperimental = GL_TRUE;
 51     // Initialize GLEW to setup the OpenGL Function pointers
 52     glewInit();
 53 
 54     // Define the viewport dimensions
 55     int width, height;
 56     glfwGetFramebufferSize(window, &width, &height);  
 57     glViewport(0, 0, width, height);
 58 
 59 
 60     // Build and compile our shader program
 61     // Vertex shader
 62     GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
 63     glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
 64     glCompileShader(vertexShader);
 65     // Check for compile time errors
 66     GLint success;
 67     GLchar infoLog[512];
 68     glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
 69     if (!success)
 70     {
 71         glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
 72         std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
 73     }
 74     // Fragment shader
 75     GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
 76     glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
 77     glCompileShader(fragmentShader);
 78     // Check for compile time errors
 79     glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
 80     if (!success)
 81     {
 82         glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
 83         std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
 84     }
 85     // Link shaders
 86     GLuint shaderProgram = glCreateProgram();
 87     glAttachShader(shaderProgram, vertexShader);
 88     glAttachShader(shaderProgram, fragmentShader);
 89     glLinkProgram(shaderProgram);
 90     // Check for linking errors
 91     glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
 92     if (!success) {
 93         glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
 94         std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
 95     }
 96     glDeleteShader(vertexShader);
 97     glDeleteShader(fragmentShader);
 98 
 99 
100     // Set up vertex data (and buffer(s)) and attribute pointers
101     // We add a new set of vertices to form a second triangle (a total of 6 vertices); the vertex attribute configuration remains the same (still one 3-float position vector per vertex)
102     GLfloat 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     GLfloat 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     GLuint 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     // ================================
116     // First Triangle setup
117     // ===============================
118     glBindVertexArray(VAOs[0]);
119     glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
120     glBufferData(GL_ARRAY_BUFFER, sizeof(firstTriangle), firstTriangle, GL_STATIC_DRAW);
121     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);    // Vertex attributes stay the same
122     glEnableVertexAttribArray(0);
123     glBindVertexArray(0);
124     // ================================
125     // Second Triangle setup
126     // ===============================
127     glBindVertexArray(VAOs[1]);    // Note that we bind to a different VAO now
128     glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]);    // And a different VBO
129     glBufferData(GL_ARRAY_BUFFER, sizeof(secondTriangle), secondTriangle, GL_STATIC_DRAW);
130     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)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.
131     glEnableVertexAttribArray(0);
132     glBindVertexArray(0);
133 
134     // Game loop
135     while (!glfwWindowShouldClose(window))
136     {
137         // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
138         glfwPollEvents();
139 
140         // Render
141         // Clear the colorbuffer
142         glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
143         glClear(GL_COLOR_BUFFER_BIT);
144 
145         // Activate shader (same shader for both triangles)
146         glUseProgram(shaderProgram);
147         // Draw first triangle using the data from the first VAO
148         glBindVertexArray(VAOs[0]);
149         glDrawArrays(GL_TRIANGLES, 0, 3);
150         // Then we draw the second triangle using the data from the second VAO
151         glBindVertexArray(VAOs[1]);
152         glDrawArrays(GL_TRIANGLES, 0, 3);
153         glBindVertexArray(0);
154 
155         // Swap the screen buffers
156         glfwSwapBuffers(window);
157     }
158     // Properly de-allocate all resources once they've outlived their purpose
159     glDeleteVertexArrays(2, VAOs);
160     glDeleteBuffers(2, VBOs);
161     // Terminate GLFW, clearing any resources allocated by GLFW.
162     glfwTerminate();
163     return 0;
164 }
165 
166 // Is called whenever a key is pressed/released via GLFW
167 void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
168 {
169     if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
170         glfwSetWindowShouldClose(window, GL_TRUE);
171 }

  如上所示,VAO主要是保存了VBO的初始化過程

  EBO則也類似。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM