美文网首页
Modern OpenGL - 顶点数组、属性与绑定点(Open

Modern OpenGL - 顶点数组、属性与绑定点(Open

作者: IcyllisMilica | 来源:发表于2021-06-09 12:21 被阅读0次

    本文介绍如何在OpenGL 4.5+环境下用最现代的方式渲染顶点。本文持续更新中。

    前言

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

    1. 直接状态访问 DSA (Direct State Access)

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

    • glGenTextures(1, &texture)生成一个纹理
    • 第一次调用glBindTexture(GL_TEXTURE_2D, texture)时,定义了该纹理是2D纹理。

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

    • glCreateTextures(GL_TEXTURE_2D, 1, &texture),创建一个2D纹理。

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

    2. 顶点数组对象 Vertex Array Object

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

    创建一个VAO

    GLuint vao_id;
    glCreateVertexArrays(/* number */ 1, &vao_id);
    

    3. 通用顶点属性 Generic Vertex Attribute

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

    layout(location = 0) in vec2 a_Pos;
    layout(location = 1) in vec4 a_Color;
    
    smooth out ...
    void main() {
    ...
    

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

    4. 顶点缓冲绑定点 Vertex Buffer Binding Point

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

    20210609183833.png
    设置绑定点与VBO关系的函数为glVertexArrayVertexBuffer
    glVertexArrayVertexBuffer(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride)
    
    • vaobj - 要操作的VAO
    • bindingindex - 绑定点的索引,0起始
    • buffer - 存储顶点数据的那个VBO
    • offset - 第一个顶点属性的起始偏移量
    • stride - 到下个顶点数据的跨度

    比如:我们定义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用一个还是多个,数据怎么存放,可自由设置。

    5. 顶点属性格式 Vertex Attribute Format

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

    glVertexArrayAttribFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, 
                    GLboolean normalized, GLuint relativeoffset);
    

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

    • size - 指定了该顶点属性的大小,不是字节数,是分量数!该值只能是1,2,3,4。例如 vec2,分量数是 2。
    • type - 指定了VBO数据中对应此属性的类型。可选的值有 GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, GL_FIXED, GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REVGL_UNSIGNED_INT_10F_11F_11F_REV
    • normalized - 指定了数据是否要被标准化。如果数据类型的符号型数值,则转换为[-1, 1]的浮点值,如果是无符号型数值,则转换为[0, 1]的浮点值。如果不标准化,则直接转换为浮点值。坐标不需要标准化,所以这里输入 GL_FALSE

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

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

    glVertexArrayAttribFormat(vao_id, 0, 2, GL_FLOAT, false, 0);
    // location = 1, vec4 (4个分量), 相对偏移量是8
    glVertexArrayAttribFormat(vao_id, 1, 4, GL_UNSIGNED_BYTE, true, 8);
    

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

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

    glVertexArrayAttribBinding(GLuint vaobj, GLuint attribindex, GLuint bindingindex);
    

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

    glVertexArrayAttribBinding(vao_id, 0, 0);
    glVertexArrayAttribBinding(vao_id, 1, 0);
    

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

    6. 元素缓冲对象 Element Buffer Object

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

    glVertexArrayElementBuffer(vao_id, ebo_id);
    

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

    glBindVertexArray(vao_id);
    glUseProgram(program);
    glDrawElementsInstanced(...);
    

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

    相关文章

      网友评论

          本文标题:Modern OpenGL - 顶点数组、属性与绑定点(Open

          本文链接:https://www.haomeiwen.com/subject/wfzseltx.html