OpenGL渲染时涉及到的数据传输
- 准备好需要绘制的顶点数据。(自己定义的或者是从某些模型文件中读取出来的)
- 在GPU中开辟一块内存。
- 将顶点数据传到上一步开辟的GPU的内存中。
- 将着色器代码转化为着色器程序,并链接到当前的执行程序中。
- GPU根据着色器的逻辑将这块内存的数据进行计算。(指定该如何将数据发送给显卡)
- 将这块已经计算完的数据一并发送给显卡进行渲染绘制。
- 上述流程中涉及到的内存
根据上面的流程,可知整个数据渲染的流程中,涉及到两个数据传输的流程,一个是将数据传输到GPU中的内存、另一个是将GPU中的数据传输给显卡。
GPU的内存通过顶点缓冲对象(Vertex Buffer Objects),也就是VBO来管理这个内存,它会在GPU内存(通常被称为显存)中储存大量顶点。
使用这VBO的好处是我们可以一次性的发送一大批数据到显卡上,而不是每个顶点发送一次。
一、VBO的相关API调用
- 生成一个VBO对象:
int vboId = glGenBuffers();
- 设置顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER,将创建的vbo对象绑定到当前的执行程序上,也可以理解为激活。
glBindBuffer(GL_ARRAY_BUFFER, vboId);
- 将准备好的顶点数据复制到缓冲的内存中,posBuffer为顶点数据,
GL_STATIC_DRAW表示数据不会改变和几乎不会改变。
第三个参数一共有三个选择:
GL_STATIC_DRAW
表示数据不会或几乎不会改变、
GL_DYNAMIC_DRAW
表示数据会被改变很多、
GL_STREAM_DRAW
表示数据每次绘制时都会改变。
glBufferData(GL_ARRAY_BUFFER, posBuffer, GL_STATIC_DRAW);
比如说一个缓冲中的数据将频繁被改变,那么使用的类型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW,这样就能确保显卡把数据放在能够高速写入的内存部分。
- 指定输入数据的哪一个部分对应顶点着色器的哪一个顶点属性,也就是在渲染前指定OpenGL该如何解释VBO中的顶点数据。glVertexAttribPointer中的参数的意义分别是:
第一个参数为顶点着色器中layout (location=0) in vec3 position;中的location的值。
第二个参数为第二个参数指定顶点属性的维数,如果是vec3,它由3个值组成,所以大小是3。
第三个参数为数据的类型。
第四个参数为是否希望数据被标准化,如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。
第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。设置为0的意思是让OpenGL自己去识别步长。
最后一个参数表示位置数据在缓冲中起始位置的偏移量(Offset)。由于位置数据在数组的开头,所以这里是0。
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
二、渲染vbo内存中的数据需要调用的API
进行完上面的步骤,准备工作已经做完,接下来就是要准备渲染了。
glEnableVertexAttribArray(0);//使vbo的内存变为可用状态。
glUseProgram(shaderProgram);//调用着色器程序。
glDrawElements(GL_TRIANGLES, getVertexCount(), GL_UNSIGNED_INT, 0);//绘制方法
- 当一个vbo中的数据准备完成以后,绘制的过程是需要执行以上三步,
- 如果程序中只有一种顶点类型的vbo,那倒还好;
- 如果有很多种vbo(在激活vbo时,调用glBindBuffer时是可以选择很多种类型的),这样一来,在绘制这些vbo时,就需要重复调用很多次上面的三个步骤。
- 为了简化这个流程,也减少GPU和显卡的交互次数。
- 思考:有没有一些方法可以使我们把所有这些状态配置储存在一个对象中,并且可以通过绑定这个对象来恢复状态,此时就提出了VAO的概念。
三、VAO的相关调用的API
顶点数组对象(Vertex Array Object )VAO可以像VBO顶点缓冲对象那样被创建和绑定。当一个VAO被创建绑定之后,任何随后的顶点属性调用都会储存在这个VAO中。 这样一来如果有多个vbo对象,在渲染绘制时,就不用执行很多次前面提到的渲染程序,只需要执行一次绑定的VAO的渲染API即可。
- 创建VAO的流程与VBO类似。
int vaoId = glGenVertexArrays();//创建
glBindVertexArray(vaoId);//绑定,激活
VAO渲染
glBindVertexArray(getVaoId());
glEnableVertexAttribArray(0);
glUseProgram(shaderProgram);
glDrawElements(GL_TRIANGLES, getVertexCount(), GL_UNSIGNED_INT, 0);
四、VBO和VAO的解绑
- VBO解绑
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
- VAO解绑
glDisableVertexAttribArray(0);
glBindVertexArray(0);
参考链接:https://www.pianshen.com/article/647381560/