Vertex Array
不同于其他任何图形API,OpenGL中存在Vertex Array的概念,在默认情况下(OpenGL Profile被设定为 GLFW_OPENGL_COMPAT_PROFILE
)OpenGL会为我们自动创建1个vertex array。
因此一般情况下如果我们只绑定一个vertex buffer,是感受不到vertex array的存在的。
float positions[] = {
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f,
-0.5f, 0.5f
};
unsigned int indices[] = {
0, 1, 2,
2, 3, 0
};
unsigned int bufferId;
glGenBuffers(1, &bufferId);
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(float), positions, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 2 * sizeof(float), (const void*)0);
// 此时vertex array已被自动创建,并使VertexAttribPointer指向index=0的buffer
unsigned int ibo;
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);
上述流程其实是隐式地将vertex array与vertex buffer进行了绑定。我们知道vertex buffer本质是一堆数据,它并不知道自己的类型,因此仅通过vertex buffer无法获得整个buffer的布局。
而vertex array的作用就是将数据的布局与vertex buffer进行绑定,使得index buffer可以根据索引获取相应的数据。 因此,从严谨性的角度看,我们应该显示地指定vertex array(按照以下方式调整OpenGL Profile后,不指定vertex array会出现错误)。
// 设定版本为3.3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
定义vertex array:
unsigned int vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
unsigned int bufferId;
glGenBuffers(1, &bufferId);
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(float), positions, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 2 * sizeof(float), (const void*)0);
// 此时vertex buffer通过VertexAttribPointer与位于vao处的vertex array绑定
那么,除了严谨的角度以外,我们为什么仍然有指定vertex array的必要呢? 考虑一种情况,在已经有vertex buffer被绑定并且屏幕已经绘制出图形的情况下,我们需要在某一时间用不同的buffer去绘制其他的图形,这个时候我们需要重新绑定不同的vertex buffer:
while (!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader);
// 仅有一个全局vertex array的情况,以下部分需要重新执行
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 2 * sizeof(float), (const void*)0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glDrawElements(...);
...
}
而在我们定义vertex array后,上述过程可以简化为
while (!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader);
glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glDrawElements(...);
...
}
在把OpenGL的相应流程封装至类中时,vertex array可以更多地增强代码的可读性。