openGL API

发布时间:2024年01月18日

glbindvertexArray

这个是 mesa的OGL API.? openGL规范里面对应的是BindVertexArray

glspec33.core

2.10 Vertex Array Objects

void BindVertexArray( uint array );
array is the vertex array object name. The resulting vertex array object is a new
state vector, comprising all the state values listed in tables 6.4 and 6.5.
BindVertexArray may also be used to bind an existing vertex array object.
If the bind is successful no change is made to the state of the bound vertex array
object, and any previous binding is broken.
The currently bound vertex array object is used for all commands which modify
vertex array state, such as VertexAttribPointer and EnableVertexAttribArray;
all commands which draw from vertex arrays, such as DrawArrays and DrawElements; and all queries of vertex array state (see chapter 6).
BindVertexArray fails and an INVALID_OPERATION error is generated if array is not zero or a name returned from a previous call to GenVertexArrays, or if
such a name has since been deleted with DeleteVertexArrays.

https://blog.csdn.net/qq_32974399/article/details/103956589

详解Opengl的VBO和VAO
前言
什么是VBO
如何创建VBO
什么VAO
如何执行VAO
总结
前言
对于Opengl中的VBO和VAO相信很多人都熟悉这2个名字,但是有时候缺容易混淆2个概念或者说不理解这2个概念的作用是什么。本人对Opengl的理解也很浅显,所以专门对这2个概念做了学习,这里记录下,便于以后查看。这里使用的Opengl版本为3.3版本,部分api可能在老版本的Opengl会报错。

什么是VBO
VBO全名顶点缓冲对象(Vertex Buffer Object),他主要的作用就是可以一次性的发送一大批顶点数据到显卡上,而不是每个顶点发送一次。我们知道CPU传送数据给GPU其实是比较耗费时间的,所以尽可能的一次性把需要的顶点数据全部传给GPU,这样顶点着色器几乎能立即访问到顶点,有助于加快顶点着色器效率。

如何创建VBO
就目前所有游戏引擎来说VBO的机制已经是基础了,我们看下VBO在Opengl中是如何创建的。
首先,我们需要在Opengl生成一个缓冲类型的ID。

unsigned int VBO;
glGenBuffers(1, &VBO);
1
2
这里我们创建了一个缓冲的ID,当然了你也可以通过数组来批量创建一系列的VBO的ID,

unsigned int VBO[3];
glGenBuffers(3,VBO);
1
2
接下来,我们要将这个ID绑定给指定类型来告诉Opengl这个缓冲是什么类型的;

glBindBuffer(GL_ARRAY_BUFFER, VBO);
1
这里我们将VBO赋予了GL_ARRAY_BUFFER的类型,告诉Opengl这个VBO变量是一个顶点缓冲对象。
注意从这一刻起,我们使用的任何(在GL_ARRAY_BUFFER目标上的)缓冲调用都会用来配置当前绑定的缓冲(VBO)

至此对于VBO的声明就结束了,其实也是蛮简单的,我们回顾下:
1)生成一个缓冲类型的ID;
2)指定ID的缓冲类型为GL_ARRAY_BUFFER;
接下来我们看下,如何给VBO赋值,我们假定有如下三角形的顶点数据:

float vertices[] = {
?? ??? ?// 位置 ? ? ? ? ? ? ?// 颜色
?? ??? ?0.5f, -0.5f, 0.0f, ?1.0f, 0.0f, 0.0f, ? // 右下
?? ??? ?-0.5f, -0.5f, 0.0f, ?0.0f, 1.0f, 0.0f, ? // 左下
?? ??? ?0.0f, ?0.5f, 0.0f, ?0.0f, 0.0f, 1.0f ? ?// 顶部
?? ?};
1
2
3
4
5
6
Opengl通过glBufferData接口来复制顶点数据到缓冲中供Opengl使用,对于上面的三角形顶点数据,代码就可能是这样的:

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
1
第一个参数表示目标的缓冲类型,这里指当前绑定到GL_ARRAY_BUFFER上的顶点缓冲对象,第二个参数表示数据大小(字节为单位),第三个参数表示我们实际发出的数据。第四个参数GL_STATIC_DRAW表示Opengl如何处理上传的数据,Opengl中一共有三种类型:

GL_STATIC_DRAW :数据不会或几乎不会改变。
GL_DYNAMIC_DRAW:数据会被改变很多。
GL_STREAM_DRAW :数据每次绘制时都会改变。
到这一步,我们的数据就已经上传到GPU上去了。一个VBO过程也就结束了。
那这里引申出一个问题,我们看到vertices中有注释,表明这个数组里面有两种数据,一种是顶点坐标,一种是顶点颜色,那都放在一个float的数组中,Opengl如何来区分他们呢?这里就要用到glVertexAttribPointer这个接口了,该接口就是帮助Opengl解释如何处理数据的。我们来看下对于vertices的数据处理的代码:
?? ?// 位置属性
?? ?glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
?? ?glEnableVertexAttribArray(0);
?? ?// 颜色属性
?? ?glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
?? ?glEnableVertexAttribArray(1);
1
2
3
4
5
6
我们分析下这段代码,首先我们来解释下glVertexAttribPointer的各个参数的意义。

第一个参数表示我们希望数据中哪部分数据放在对应的Location位置上,这个可能主要体现在shader部分,我们看下顶点着色器的代码:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
1
2
3
我们根据变量名大致能猜出来location为0的是顶点坐标,location为1的是颜色值,这里的location就是对应了上面代码种glVertexAttribPointer第一个参数,location没有准确的位置定义,并没有说location为0的一定要是顶点坐标属性,也可以是颜色或者uv坐标属性,只是常规来说,我们习惯将坐标放在第一个位置。

glVertexAttribPointer第二个参数表示属性大小,坐标和颜色的大小都是3,所以这里填3;
第三个参数表示数据类型;
第四个参数表示我们是否希望数据标准化。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE;
第五个参数表示步长,数据之间的间隔,我们这里不管坐标还是颜色都是3个分量,所以坐标和坐标数据之间隔了6个float字节,颜色和颜色之间隔了6个float字节;
第六个参数表示偏移量,即在一段数据中,指定的数据偏移多少位置开始。在这里,坐标数据都是每段数据的起始位置,所以偏移量是0,而颜色数据在坐标数据之后,坐标数据有3个分量,所以每个颜色数据偏移三个float字节开始算;
每次设定好一个location的值之后,记得要开启对应的位置数据glEnableVertexAttribArray,因为Opengl默认是全关闭的。
经过glVertexAttribPointer执行之后,shader中对应layout的位置数据就有对应的值了。
什么VAO
VAO全名顶点数组对象(Vertex Array Object),每当我们绘制一个物体的时候都必须重复这一过程。这看起来可能不多,但是如果有超过5个顶点属性,上百个不同物体呢(这其实并不罕见)。绑定正确的缓冲对象,为每个物体配置所有顶点属性很快就变成一件麻烦事。VAO的作用就是顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。简单来说有点类似复用的概念。

如何执行VAO
VAO的生成和绑定都和VBO很类似;

unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
1
2
3
其过程也是先从生成一个ID开始,然后将ID绑定到顶点数组对象上。任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。概括来说就是从绑定之后起,我们应该绑定和配置对应的VBO和属性指针,之后解绑VAO供之后使用。
一个顶点数组对象会储存以下这些内容:

glEnableVertexAttribArray和glDisableVertexAttribArray的调用。
通过glVertexAttribPointer设置的顶点属性配置。
通过glVertexAttribPointer调用与顶点属性关联的顶点缓冲对象。
我们来举个例子,我们还是使用上面三角形数据:

float vertices[] = {
?? ??? ?// 位置 ? ? ? ? ? ? ?// 颜色
?? ??? ?0.5f, -0.5f, 0.0f, ?1.0f, 0.0f, 0.0f, ? // 右下
?? ??? ?-0.5f, -0.5f, 0.0f, ?0.0f, 1.0f, 0.0f, ? // 左下
?? ??? ?0.0f, ?0.5f, 0.0f, ?0.0f, 0.0f, 1.0f ? ?// 顶部
?? ?};
1
2
3
4
5
6
借用GLM的数学库,我们生成2个位置向量:

glm::vec3 traPositions[] = {
?? ??? ??? ??? ?glm::vec3(0.0f, ?0.0f, ?0.0f),
?? ??? ??? ??? ?glm::vec3(0.5f, ?0.5f, 0.5f),
?? ??? ??? ?};
1
2
3
4
生成VBO和VAO的ID:

unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
1
2
3
4
5
复制顶点数据到缓冲:

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
1
分别处理位置和颜色的数据:

// 位置属性
?? ?glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
?? ?glEnableVertexAttribArray(0);
?? ?// 颜色属性
?? ?glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
?? ?glEnableVertexAttribArray(1);
?? ?glBindBuffer(GL_ARRAY_BUFFER, 0);
?? ?//解除VAO绑定
?? ?glBindVertexArray(0);
1
2
3
4
5
6
7
8
9
生成一个model的矩阵变量(这个model矩阵做用就是model->world的转变矩阵)

glm::mat4 model = glm::mat4(1.0f);
1
创建model是方便做测试,在世界坐标中绘制不同位置的三角形,其在顶点着色器的作用如下代码:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;

uniform mat4 model;

void main()
{
? ? gl_Position = model*vec4(aPos, 1.0);
? ? ourColor = aColor;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
我们的片段着色器很简单,仅仅是把传入的颜色直接输出:

#version 330 core
out vec4 FragColor;

in vec3 ourColor;

void main()
{
? ? FragColor = vec4(ourColor, 1.0f);
}
1
2
3
4
5
6
7
8
9
好,至此准备工作都完成了,我们开始做渲染:

while (!glfwWindowShouldClose(window))
?? ?{
?? ??? ?...
?? ??? ?glBindVertexArray(VAO);
?? ??? ?for (int i = 0; i < 2; i++)
?? ??? ?{
?? ??? ??? ?glm::mat4 model = glm::mat4(1.0f);
?? ??? ??? ?model = glm::translate(model, traPositions[i]);
?? ??? ??? ?outShader.setMat4("model", model);
?? ??? ??? ?glDrawArrays(GL_TRIANGLES, 0, 3);
?? ??? ?}
?? ??? ?...
?? ?}
1
2
3
4
5
6
7
8
9
10
11
12
13
运行结果就是:

我放出了关键代码,其他代码不是我们关注的重点。我们看到glBindVertexArray(VAO)这个方法之后,代码逻辑就是直接循环绘制了2个三角形,我们注意到这其中我们不需要重复创建VBO,重复设定顶点数据,只需要绑定我们需要绘制的VAO对象,我们就能绘制任意数量的图形,这就是VAO带来的重复绘制的高效性。

总结
对于VBO/VAO的讲解基本差不多了,基于我本人对opengl的认识程度也不深,如果有理解不到位或者有误的地方,还望各位看官不吝赐教!
[参考]:
https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/
————————————————
版权声明:本文为CSDN博主「代码乐」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_32974399/article/details/103956589

文章来源:https://blog.csdn.net/pcj2007/article/details/135674160
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。