OpenGL ES入门之顶点缓冲区对象相关

天大地大妈咪最大 提交于 2019-12-15 04:03:20

顶点属性的概念、如何指定它们和它们支持的数据格式、如何绑定顶点属性以用于顶点着色器、在OpenGL ES 3.0中用顶点属性绘制图元的方法

顶点属性

  • 顶点属性:顶点数据,指定每个顶点的数据,这种逐顶点可以为每个顶点指定,也可用于所有顶点的常量,例如一个纯色三角形具有固定颜色的顶点和逐顶点位置属性。

  • 所有OpenGL ES 3.0实现必须支持最少16个顶点属性,可以通过glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, maxVertexAttribs, 0);查询支持的顶点属性数量

  • 常量顶点属性:对于一个图元的所有顶点只需指定一个值,glVertexAttrib*命令用于加载index(通用顶点属性索引)指定的顶点属性

  • 顶底数组指定每个顶点的属性,是保存在应用程序地址空间的缓冲区,是顶点缓冲对象的基础,提供指定顶点属性数据的一种高效灵活的手段,顶点数组常使用glVertexAttribPointer函数指定,对于该函数最后一个参数,如果使用的顶点数组保存顶点数据,则存放顶点数组名,如果使用顶点缓冲区对象,则存放该缓冲区内的偏移量;对于使用glVertexAttribPointer函数指定顶点数组的程序,需在调用glDraw方法前,对属性调用glEnableVertexAttribArray()方法进行使能启用,同时调用glDraw方法后最好使用glDisableVertexArrayAttribArray()禁用

  • 分配和存储顶点属性数据的两种常用方式:

    • 结构数组:在一个缓冲区中存储顶点属性,结构表示顶点的所有属性,每个顶点有一个属性的数组
    • 数组结构:在单独的缓冲区中保存每个顶点属性

    在下图中,表示结构数组的存储方式下,多属性顶点数据的存储方式;其中,下图中,缓冲区的跨距为10*4。在数组结构的存储方式下,每个属性都保存在单独的缓冲区中
    在对于OpenGL ES 3.0硬件实现上,大部分情况下,结构数组的分配方式更加高效,每个顶点属性数据顺序读取,这可能造成高效的内存访问模式;但使用结构数组的缺点在需要修改特定属性时变得明显,修改特定特定属性会导致顶点缓冲区跨距更新,需要重新加载整个顶点属性缓冲区
    顶点属性存储方式

  • 顶点缓冲区性能优化:

    • 选择结构数组还是顶点数组:以上
    • 顶点属性选用的数据格式:glVertexAttribPointer中参数type指定的顶点属性数据格式不仅影响顶点属性数据的图形内存存储格式,而且影响整体性能,这是渲染帧所需内存带宽的一个函数,数据空间占用越小,需要的内存带宽越小。OpenGL ES 3.0支持名为GL_HALF_FLOAT的16位浮点顶点格式(半浮点数),在指定纹理坐标、法线、副法线、切向量等属性时,推荐使用该数据格式;颜色可以存储为GL_UNSIGENED_BYTE;顶点位置常采用GL_FLOAT;
    • glVertexAttribPointer的规范化标志参数的工作原理:在用于顶点着色器前,顶点属性在内部保存为单精度浮点数,如果数据类型表示顶点属性不为浮点数,顶点属性将于用在顶点着色器之前转化为单精度浮点数。规范化标志控制非浮点顶点数据到单精度浮点值的转换:如果置为false,则顶点数据被直接转换为浮点数,类似强制类型转换;如果置为true,且数据类型为GL_BYTE、GL_SHORT、GL_FIXED,则数据[-1.0, 1.0]范围内,如果数据类型为GL_UNSIGNED_BYTE或GL_UNSIGNED_SHORT,则被映射到[0.0, 1.0]范围内,映射函数如下图所示:
      映射函数表
  • 在顶点着色器中声明顶点属性变量

    • 在glsl文件中,用关键字in修饰,且属性变量不能声明为数组或结构
    • 支持的顶点属性个数是有限资源(取决于硬件设备,如果使用的属性个数超过GL_MAX_VERTEX_ATTRIBS,顶点着色器将无法连接),且与编译器自动打包的顶点着色器输出变量/片段着色器输入变量不同,属性不进行自动打包,在使用小于四分量向量的尺寸声明顶点属性时要小心考虑;同时对于声明但未使用的属性,不会被认为是活动属性,不会被计入个数限制
    • 顶点着色器中声明的顶点属性变量是只读变量,不可修改
  • 将顶点属性绑定到顶点着色器中的属性变量有三种方法:

    • 使用location限定符给每个变量赋上索引值,使用时直接使用索引值,推荐使用,简单高效
    • OpenGL ES 3.0将通用顶点属性索引绑定到属性名称:使用glBindAttribLocation(int program, int index, String name);这种绑定在下一次程序链接时生效,不会改变当前链接程序中使用的绑定,如果之前绑定了name属性,则它所指定的绑定被索引代替,该函数甚至可以在顶点着色器链接到程序对象之前调用,这个调用可以用于绑定除不存在的和顶点着色器中不活动的属性以外(绑定后会被忽略)的任何属性名称;
    • 使用应用程序将属性索引绑定到属性名称:在程序链接时进行,使用glGetAttribLocation(int programId, String name);实现,该函数返回值是program定义的程序对象最后一次链接时绑定到属性变量name的通用属性索引,如果name不是一个活动属性变量,或者program不是一个有效的程序对象或者程序链接失败,则返回-1;

顶点缓冲区对象

  • 顶点数组保存在程序地址空间中,在进行图元绘制时,需将这些数据复制到图形内存中,在图形内存中缓存这些数据可以显著的改进渲染性能,降低内存带宽和电力消耗需求,这时就可以使用顶点缓冲对象
  • 顶点缓冲区对象使OpenGL ES 3.0应用程序可以在高性能的图形内存中分配和缓存顶点数据,并从这个内存进行渲染,从而避免在每次图元绘制时重新发送数据,不仅是顶点数据,描述图元顶点索引、作为GLDrawElements参数传递元素索引时也可以使用缓存
  • OpenGL ES 3.0支持两类缓冲区对象用于指定顶点和图元数据:数组缓冲区对象(GL_ARRAY_BUFFER标志用于指定数组缓冲区对象用于创建保存顶点数组数据的缓冲区对象)和元素数组缓冲区对象(GL_ELEMENT_ARRARY_BUFFER标志用于指定元素数组缓冲区对象用于创建图元索引的缓冲区对象),为了得到最佳的性能,推荐在OpenGL ES 3.0应用程序中对顶点属性数据和元素索引使用顶点缓冲区对象
  • 创建和绑定缓冲区对象:
    • glGenBuffer(int n, int[] buffers, int offset); 分配n个缓冲区对象名称,并在buffers数组中返回它们
    • glBindBuffer(int target, int buffer);指定当前缓冲区对象,分配成功,则分配的对象绑定为目标的当前缓冲区对象
    • glBufferData(int target, int size, java.nio.Buffer data, int usage);用于顶点数组数据或元素数组数据创建和初始化,将根据size的值保留相应的数据存储;参数size引用由GLBufferData指定的缓冲区对象数据的大小;usage是对应用程序如何使用存储在缓冲区对象中的数据的提示,常见的使用方式如下,默认常用GL_STATIC_DRAW;
      缓冲区使用方法
    • GLBufferSubData(int target, int offset, int size, java.nio.Buffer data);用于对缓冲区对象数据存储的内容的初始化或更新
    • 使用GLBindData和GLBufferSubData函数初始化或者更新缓冲区对象数据存储后,对于静态的几何形状,应用程序可以释放客户数据存储,减少应用程序消耗的系统内存,对于动态几何形状,这可能无法做到

映射缓冲区对象

  • 应用程序除了可以将通过glBindData将数据加载到缓冲区对象中,还可以将缓冲区对象数据存储映射到应用程序地址空间,映射较加载主要有以下优势:
    • 映射缓冲区可以减少应用程序的内存占用,因为可能只需要存储数据的地址空间的直接指针
    • 在使用共享内存的架构上,映射缓冲区返回GPU存储缓冲区的地址空间的直接指针。通过映射缓冲区,应用程序可以避免复制步骤,从而实现更新性能
  • java.nio.Buffer glMapBufferRange(int target, int offset, int length, int access, int access);命令返回所有或者一部分缓冲区对象,这个可以供应用程序读取或更新缓冲区对象的内容。glUnmapBuffer(int target);可用于指示更新已完成和释放缓冲区
  • glFlushMappedBufferRange(int target, int offset, int length);刷新映射的缓冲区

复制缓冲区对象

glCopyBufferSubData(int readTarget, int writeTarget, int readOffset, int writeOffset, int size);可用于复制缓冲区对象,任何类型的缓冲区对象都可以绑定到(将writeTarget置为)GL_COPY_READ_BUFFER或GL_COPY_WRITE_BUFFER目标,可以使得应用程序在执行缓冲区复制时不必改变真正的缓冲区绑定。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!