VAO 全称 Vertex Array Object,翻译过来叫顶点数组对象,但和Vertex Array(顶点数组)毫无联系!
VAO可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。
glBindVertexArray可以设置当前绑定的VAO,然后glGenBuffers生成VBO后,通过glBindBuffer,就将VBO关联到当前的VAO了。VAO里的VBOs都设置好了以后,在绘制的地方只需要设置当前绑定的VAO是哪个,就能按照初始化的VAO来绘制,即调用glDrawArrays。也就是说在初始化的时候,事先确定VAO的格式,在需要的调用的时候,只需要绑定这个特定的VAO就好。
glBindVertexArray(0)表示当前不用VAO了,并不是VAO释放了,所以不是你说的那样要一直“bind,unbind,重新Gen”,而是在显存里专门开辟好了这种VAO对应的格式,只需要填数据就好了,而VAO对象是作为一个整体在合适的时候bind。但是看https://stackoverflow.com/questions/25412767/opengl-glbindbuffer0-outside-vao的解释,不需要对vao,vbo unbind,需要用的时候bind就够了。
方式 | VAO | VBO | EBO | 纹理 |
获取 | getGenVertexArrays | getGenBuffers | getGenBuffers | glGenTextures |
绑定 | glBindBuffer | glBindBuffer | glBindBuffer | glBindTexture |
属性定义 | glVertexAttribPointer | glVertexAttribPointer | 不需要 | glTexImage2D glGenerateMipmap |
解绑 | glBindVertexArray | glBindBuffer | glBindBuffer | 不解绑 |
使用 | glBindVertexArray glDrawArrays | 默认作用于绑定时的顶点 | glBindBuffer glDrawElements | 默认使用 |
删除 | glDeleteVertexArrays | glDeleteBuffers | glDeleteBuffers |
|
当VAO处于活动状态时,不要解除绑定EBO,因为绑定元素缓冲对象存储在VAO中,但是可以解绑VBO,因为调用glvertexattributpointer,已将VBO注册为当前绑定的顶点缓冲区对象,所以可以安全的解除绑定。
OpenGL的核心模式要求我们使用VAO,所以它知道该如何处理我们的顶点输入。如果我们绑定VAO失败,OpenGL会拒绝绘制任何东西。
一个顶点数组对象会储存以下这些内容:
VAO VBO IBO的全称:
EBO(比起EBO,我更喜欢用IBO)也是一个缓冲,它专门储存索引,OpenGL调用这些顶点的索引来决定该绘制哪个顶点。绑定EBO, 后用glBufferData把索引复制到缓冲里, 把缓冲的类型定义为GL_ELEMENT_ARRAY_BUFFER, 最后一件要做的事是用glDrawElements来替换glDrawArrays函数,来指明我们从索引缓冲渲染。glDrawElements函数从当前绑定到GL_ELEMENT_ARRAY_BUFFER目标的EBO中获取索引。
顶点数组对象同样可以保存索引缓冲对象的绑定状态。VAO绑定时正在绑定的索引缓冲对象会被保存为VAO的元素缓冲对象。绑定VAO的同时也会自动绑定EBO。
当目标是GL_ELEMENT_ARRAY_BUFFER的时候,VAO会储存glBindBuffer的函数调用。这也意味着它也会储存解绑调用,所以确保你没有在解绑VAO之前解绑索引数组缓冲,否则它就没有这个EBO配置了。
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
参考: