Modern OpenGL - 顶点数组、属性与绑定点(OpenGL 4.5+)

我请分析下,Modern OpenGL - 顶点数组、属性与绑定点(OpenGL 4.5+)
最新回答
再美都是荒涼

2024-04-17 12:02:36

上一节 我们讲了如何在Modern OpenGL下渲染矩形体,但其中用到的主要是OpenGL 3.x中内容。OpenGL 4.x增添了很多新内容,并且一部分3.x的内容得到改进。本节中会详细讲解新版和与旧版的区别与联系,并给出应用最新技术的例子。

OpenGL 4.5中给了我们DSA,可以在调用方法时直接传入要操作的OpenGL对象,而不再需要绑定操作,更加高效。与之对应的,所有 glGen* 方法均不再使用,因为这些方法只生成了一个对象(实质上是分配了一个ID),并没有定义这个对象的类型。在4.5以前,一个对象的定义发生在首次绑定时。例如:

从OpenGL 4.5起,由于不再需要绑定操作,所有 创建 OpenGL对象的操作都被 glCreate* 所替代,例如:

如果依然使用 glGen* 而不进行首次绑定,则该OpenGL对象是无效对象,操作时会报出GL_INVALID_OPERATION错误。

Modern OpenGL渲染中必须使用VAO,它无处不在。VAO本身不存储任何顶点数据,它会保存我们要渲染时所需要的顶点的 定义、规格与配置 。一旦我们配置好了一个VAO,只要绑定它,就可以直接调用渲染函数,而不需要调用任何定义/配置类的函数。

顶点属性是顶点着色器(Vertex Shader)的输入。在Modern OpenGL中,我们必须自己定义我们需要哪些顶点属性。每定义的一个属性,都可以叫做通用顶点属性。与之对应的,在传统固定管线中,顶点着色器存在内置顶点属性,但它们都已经废弃。所以我们说的顶点属性都是指通用顶点属性,它们的名字、类型都是完全自定义的。例如,我们的顶点着色器:

顶点属性用 in 表示, location = 0 显式指定了属性索引(attribute index)是0,location不一定连续。属性名在这种情况下仅在shader内使用。

顶点缓冲绑定点是在一个VAO内共享的,我们可以将一种配置绑定到一个绑定点上,再将一个或多个顶点属性与之绑定,这样就可以随时切换顶点属性而不需要重新配置缓冲。也就是说,我们定义一个绑定点,和它如何从VBO中读取数据,这个定义信息会被存入VAO,关系图如下:

比如:我们定义0号绑定点对应12个字节大小,然后我们的VBO创建为vbo_first,它的数据是每12个字节一组顶点属性,那么offset是0,stride为12。如果它第8到20字节,28到40字节以此类推,是我们需要的数据的话,那么offset是8(第一组数据是8开始),stride是20(8到28是20, 20到40是20)。剩下的一个个8字节(0到8,20到28...)也许是其他地方需要的数据。因为一个VBO可以存任何东西,VBO用一个还是多个,数据怎么存放,可自由设置。

重点来了,虽然我们在shader中定义了顶点属性,但OpenGL不知道我们是如何定义的,也就不知道怎么把数据传给shader的attribute,所以我们必须指定它们。

第一个参数传入我们的VAO。attribindex传入属性索引,也就是顶点着色器中(location = 几)的几。

注意:上面的函数最终会把数据转成浮点型。如果使用 glVertexArrayAttribIFormat (多了一个I, Integer),则会转成整数类型,并且 type 只能 GL_BYTE , GL_UNSIGNED_BYTE , GL_SHORT , GL_UNSIGNED_SHORT , GL_INT GL_UNSIGNED_INT 。这个方法不含 normalized 参数。

此外还有一个relativeoffset参数,相对偏移量,也就是在绑定点中的偏移量,而不是VBO中的偏移量。我们刚刚定义了绑定点0占据12个字节,这样的话我们可以把之前的 vec2 a_Pos vec4 a_Color 都绑定到这个绑定点上,如果我们的VBO是前8个字节是坐标,后4个字节是颜色,那么就分别调用:

在VBO中存储后4个字节为RGBA颜色值,标准化到浮点型,因为unsigned int是0~255,所以转换时会除255得到shader中使用的颜色值。8是因为坐标2两个浮点值8字节,一个绑定点是12字节。

别忘了, glVertexArrayAttribFormat 只定义了属性格式,要绑定到绑定点上,还需要调用 glVertexArrayAttribBinding

第一个参数是VAO,第二个是属性索引,第三个是绑定点的索引。这里我们就需要把0和1号属性全都绑定到0号绑定点:

假设我们顶点着色器中还有2号和3号属性,也是vec2和vec4。那么只需最开始调用glVertexArrayAttribFormat,然后在需要切换的时候调用glVertexArrayAttribBinding切换绑定即可。你会发现,我们并没有重新配置绑定点,也就是怎么从VBO中读取。而且,我们从来没绑定过VBO,它只是作为参数传入。

如果需要索引绘制(indexed drawing),我们还要将所使用的EBO配置进VAO:

这样一来,所有的配置都存进了VAO,由多个VBO存放顶点数据,只要绑定VAO即可渲染,渲染循环如下:

要更新顶点数据,只需在下一帧渲染开始前使用 glNamedBufferSubData 更新VBO即可。