开始opengl第八版-1.VAO与VBO

本文深入探讨了OpenGL中顶点数组对象(VAO)和顶点缓冲对象(VBO)的概念及应用,解释了如何利用这两种对象高效地管理和传递顶点数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.cnblogs.com/MyGameAndYOU/p/4678657.html 

http://blog.youkuaiyun.com/candycat1992/article/details/39676669    理解VAO与VBO

传递顶点数据

那么,现在的问题是,如果是你,你会怎么把顶点和它相关的信息,例如纹理坐标、法线等,传递给GLSL呢?一般人都会想到多维数组。我们下面把它称为顶点流(Vertex Stream)。(什么?!你不是这么想的?!没关系,OpenGL是这么想的就好。。。)

我们负责创建这个顶点流,然后只需要告诉OpenGL怎样解读它就可以了。
为了渲染一个对象,我们必须使用一个shader program。而这个program会定义一系列顶点属性,例如上述Vertex Shader中的vPosition一行。这些属性决定了我们需要传递哪些顶点数据。每一个属性对应了一个数组,并且这些数据的维度都必须相等,即是一一对应的关系。
比如我们想要渲染3个顶点,我们会定义下面的数据:
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. { {1, 1, 1}, {0, 0, 0}, {0, 0, 1} }  
这些顶点的顺序是非常重要的,OpenGL将会根据这些顺序渲染网格。我们可以直接使用上述这种数据来直接渲染,也可以使用索引(indices)来指定顺序,这样可以重复使用同一个顶点。
例如,我们使用下面的索引列表:
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. {2, 1, 0, 2, 1, 2}  
那么OpenGL将会渲染6个顶点:
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. { {0, 0, 1}, {0, 0, 0}, {1, 1, 1}, {0, 0, 1}, {0, 0, 0}, {0, 0, 1} }  
现在,我们还想传递一个新的顶点属性,即每个顶点的纹理坐标,那么新的纹理数组可能长这样:
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. { {0, 0}, {0.5, 0}, {0, 1} }  
注意,纹理数据的维度大小一定要和上面的坐标数组大小一致,而其他顶点属性数组的维度也要满足这个条件。这是非常容易理解的。
那么,合并后的顶点属性列表就是:
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. [{0, 0, 1}, {0, 1}], [{0, 0, 0}, {0.5, 0}], [{1, 1, 1}, {0, 0}], [{0, 0, 1}, {0, 1}], [{0, 0, 0}, {0.5, 0}], [{0, 0, 1}, {0, 1}] }  

OpenGL的做法:VAO和VBO


OpenGL使用了VAO来实现上述管理顶点数据的数据作用,以及VBO来存放真正的顶点属性数据。

VAO(Vertex Array Object)


我们这里遇到了第一种OpenGL对象——VAO(Vertex Array Object)。前面说到OpenGL对象是状态的集合,那么VAO就是所有顶点数据的状态集合。它存储了顶点数据的格式以及顶点数据数据所需的缓存对象的引用。前面提过,OpenGL对象都有三个非常重要的函数,而VAO对应的就是glGenVertexArraysglDeleteVertexArraysglBindVertexArray​。

VAO负责管理顶点属性,而这些顶点属性从0到GL_MAX_VERTEX_ATTRIBS​ - 1 被编号。这些属性在Vertex Shader里的表现就是类似下面的语句:
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. layout(location = 0) in vec4 vPosition;  

上述顶点属性vPosition被编号为0。
每个属性可以被enable或者disable,被disable的属性是不会传递给shader的,即便在shader里定义了这些属性,它们读出的值也会是一个常量,而非真正的数据。一个新建的VAO的所有属性访问都是disable的。而开启一个属性是通过下面的函数:
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. void glEnableVertexAttribArray​(GLuint index​);  
与其对应的是 glDisableVertexAttribArray  函数。
而为了使用上述函数来改变VAO的状态,我们首先需要把VAO绑定到当前的context上。

VBO(Vertex Buffer Object)

VBO是一种Buffer Object,即它也是一个OpenGl对象。VBO是顶点数组数据真正所在的地方。
为了指定一个属性数据的格式和来源,我们需要告诉OpenGL,编号为0的属性使用哪个VBO,编号为1的属性使用哪个VBO等等。为了实现它,我们可以这么做。
首先,我们要知道,任何VBO都需要先绑定到GL_ARRAY_BUFFER​才可以对它进行操作。绑定后,我们可以调用下面的函数之一:
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. void glVertexAttribPointer​( GLuint index​, GLint size​, GLenum type​,  
  2.    GLboolean normalized​, GLsizei stride​, const void *offset​);  
  3.  void glVertexAttribIPointer​( GLuint index​, GLint size​, GLenum type​,  
  4.    GLsizei stride​, const void *offset​ );  
  5.  void glVertexAttribLPointer​( GLuint index​, GLint size​, GLenum type​,  
  6.    GLsizei stride​, const void *offset​ );  
它们的作用大同小异,就是告诉OpenGl,编号为index的属性使用当前绑定在 GL_ARRAY_BUFFER​的VBO。为了更好理解,我们举例:
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. glBindBuffer(GL_ARRAY_BUFFER, buf1);  
  2. glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);  
  3. glBindBuffer(GL_ARRAY_BUFFER, 0);  
上面第一行代码将buf1绑定到了 GL_ARRAY_BUFFER​上。第二行意味着,编号为0的属性将使用buf1的数据,因为当前绑定到 GL_ARRAY_BUFFER​上的是buf1。第三行将缓存对象0绑定到了 GL_ARRAY_BUFFER​上,这不会对顶点属性有任何影响,只有 glVertexAttribPointer函数可以影响它们!
这个过程就像一个中介人的作用,而中介人就是GL_ARRAY_BUFFER​。我们可以这么想,glBindBuffer 设置了一个全局变量,然后glVertexAttribPointer读取了这个全局变量并把它存储在VAO中,这个全局变量就是GL_ARRAY_BUFFER。当调用完glVertexAttribPointer后,顶点属性已经知道了数据来源就是buf1,它们之间就会直接联系,而不需要在通过GL_ARRAY_BUFFER


使用VAO与VBO,将其关联,并将数据传递到shader,用法
  /**
         函数原型:void glVertexAttribPointer(GLuint index, GLint size, GLenum type,
         GLboolean normalized, GLsizei stride, const GLvoid* pointer);
         之前我们调用了glBindData所传递给缓存的只是数据,之后我们要使用它,还必须指定数据类型。
         所以该函数完成的主要任务是:
         1、告诉OpenGL,该存储数据的格式
         2、因为我们使用着色器来编程,因此在顶点着色器阶段,我们会使用该函数来给着色器中的in类型的属性变量传递数据。
            那么它们是怎么联系起来的呢?
            便是通过第一个参数index,指明了着色器程序中变量的下标的作用。
            例如:layout( location=index ) in vec4 position;
            如果这个index和glVertexAttribPointer的第一个参数一样,那么相关缓存区的数据就会传递到这个position变量中去。
         3、该函数执行之后,会影响改变VAO的状态,VBO会被复制保存到VAO中。之后如果改变了当前所绑定的缓存对象,也不会改变到VAO里的对象。
     */
     glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
     /**
         因为默认情况下,顶点属性数组是不可访问的,所以我们需要调用以下函数激活它。
         范围为0到GL_MAX_VERTEX_ATTRIBS-1
     */
     glEnableVertexAttribArray(vPosition);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值