最近在学习OpenGL的时候,看到了一篇关于讲解VAO和VBO比较好的文章,这里做一下搬运和改良。
原文地址:AB是一家?VAO与VBO
VBO
与其他buffer object一样,VBO归根到底是显卡存储空间里的一块缓存区(Buffer)而已,这个Buffer有它的名字(VBO的ID),OpenGL在GPU的某处记录着这个ID和对应的显存地址(或者地址偏移,类似内存)。用代码看看吧:
1 | //生成一个Buffer的ID,不管是什么类型的 |
这里是VBO的初始化阶段。在这里我们看到了这是对位置,还是颜色,还是纹理坐标,还是法线,还是其他顶点属性进行设置的吗?是的,这个信息是:起码在初始化阶段,一个VBO对于交给它存储的数据到底是什么,完全不知道。这些信息都是在渲染的时候确定的。
以下是渲染代码:
1 | glBindBuffer(GL_ARRAY_BUFFER, PositionVBO); |
在这段渲染代码中,glVertexAttribPointer(VAT_POSITION, 2, GL_INT, GL_FALSE, 0, NULL)
函数指定了VBO里的是什么数据——顶点位置,int类型,2个int指定一个顶点位置,在区域里无偏移的采集数据等等。
之后的glDrawElements
只不过根据组织模式(GL_TRIANGLES)和索引数据去采集VBO里的这些数据罢了,具体的表现是,它从GL-context
获取了glBindBuffer
指定的位置等信息,进行绘制。
总的来说,VBO在渲染阶段才指定数据位置和“顶点信息”(Vertex Specification),然后根据此信息去解析缓存区里的数据,联系这两者中间的桥梁是GL-Contenxt。
GL-context整个程序一般只有一个,所以如果一个渲染流程里有两份不同的绘制代码,GL-context就负责在它们之间进行状态切换。这也是为什么要在渲染过程中,在每份绘制代码之中有glBindBuffer/glEnableVertexAttribArray/glVertexAttribPointer。
那么优化方法就来了——把这些都放到初始化时候完成吧!——这样做的限制条件是“负责记录状态的GL-context整个程序一般只有一个”,那么就不直接用GL-context记录,用别的东西做状态记录吧——这个东西针对"每份绘制代码“有一个,记录该次绘制所需要的所有VBO所需信息,把它保存到GPU特定位置,绘制的时候直接在这个位置取信息绘制。
VAO
VAO的全名是Vertex Array Object,首先,它不是Buffer-Object,所以不用作存储数据;其次,它针对”顶点“而言,也就是说它跟”顶点的绘制“息息相关。
按上所述,它的定位是state-object(状态对象,记录存储状态信息)。这明显区别于buffer-object。VAO记录的是一次绘制中做需要的信息,这包括
-
”数据在哪里-glBindBuffer(GL_ARRAY_BUFFER)“、
-
”数据的格式是怎样的-glVertexAttribPointer“(顶点位置的数据在哪里,顶点位置的数据的格式是怎样的/纹理坐标的数据在哪里,纹理坐标的数据的格式是怎样的…视乎你让它关联多少个VBO、VBO里有多少种数据)
顺带一提的是,这里的状态还包括这些属性关联的shader-attribute的location的启用(glEnableVertexAttribArray)、这些顶点属性对应的顶点索引数据的位置(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER),如果你指定了的话)。
使用VAO可以使渲染的部分变得简单。