OpenGL ES 3.0-顶点属性,顶点数组,缓冲区对象(VAO, VBO)

顶点属性,顶点数组,缓冲区对象

指定顶点属性数据

  • 顶点属性数据可以用一个顶点数组对每个顶点指定,也可以将一个常量值用于一个图元的所有顶点,所有OpenGL ES 3.0实现必选支持最少16顶点属性。

常量顶点属性

  • 常量顶底属性对于一个图元的所有顶点都相同,所以对一个图元的所有顶点只需要指定一个值

顶点数组

  • 顶点数组指定每个顶点的属性,是保存在应用程序地址空间(OpenGL ES称为客户端)的缓冲区。它们作为你顶点缓冲对象的基础,提供指定属性数据的一种高效,灵活的手段。顶点数组用 glVertexAttribPointer 或 glVertexAttriblPointter函数指定
  • 分配和存储顶点属性数据的常用的两种方法
    • 在一个缓冲区中存储顶点属性--- 这种方法称为结构数组。结构表示顶点的所有属性,每个顶点有一个属性的数组
    • 在单独的缓冲区中保存每个顶点属性--这种方法称为数组结构
  • 假定每个顶点有4个顶点属性---位置,法线和两个纹理坐标哦---这些属性一起保存在为所有顶点分配的一个缓冲区中。顶点位置属性一个3个浮点数的向量(x,y,z)的形式指定,顶点法线也以3个浮点数组组成的向量形式指定,每个纹理坐标以两个浮点数组组成的向量的形式指定
image.png
  • 在常量顶点属性和顶点数组之间选择
    • glEnableVertexAttribArrayglDisableVertexAttribArray分别用于启用和禁用通用顶点属性数组。如果某个通用属性索引的顶点属性数组被禁用,将使用为该索引指定的常量顶点属性数据
image.png

在顶点着色器中声明顶点属性变量

  • 在顶点着色器中,变量通过使用in限定符声明顶点属性。属性变量也可以选择包含一个布局限定符号,提供属性索引。
    layout(location = 0) in vec4 a_position;
    layout(location = 1) in vec2 a_texcoord;
    layout(location = 2) in vec3 a_noraml;
  • in 限定符只能用于数据类型为 float, vec2, vec3, vec4, int, ivec2, ivec3, ivec4, uint. uvec2, uvec3, uvec4, mat2, mat2x2, mat2x3, mat2x4, mat3, mat3x3, mat3x4, mat4, mat4x2, mat4x3
  • 属性变量不能声明为数组或者结构
  • 在顶点着色器中声明为顶点属性的变量是只读变量,不能修改

将顶点属性绑定到顶点着色器中的属性变量

  • 在OpenGL ES 3.0中,可以使用3种方法将通用顶点属性索引映射到顶点着色器中的一个属性变量名称
    • 索引可以在顶点着色器源代码中用 layout(location = N)限定符指定
    • OpenGL ES 3.0将通用属性索引绑定到属性名称 glBindAttribLocation, 这种绑定在下一次程序链接时生效---不会改变当前链接的程序中使用的绑定
    • 【没懂这句话】应用程序可以将顶点属性索引绑定到属性名称, 这种绑定在程序链接时进行 可使用glGetAttribLocation命令查询分配的绑定

顶点缓冲区对象

  • 顶点缓冲区对象使OpenGL ES 3.0应用程序可以在高性能的图形内存中分配和缓存顶点数据,并从这个内存进行渲染,从而避免在每次绘制图元的时候重新发送数据。
  • 不仅是顶点数据,描述图元顶点索引,作为glDrawElements参数传递的元素也可以缓存
  • OpenGL ES 3.0支持两类缓冲区对象,用于指定顶点和图元数据:数组缓冲区对象元素数组缓冲区对象
  • GL_ARRAY_BUFFER标志指定的数组缓冲区对象用于创建保存顶点数据的缓冲区对象
  • GL_ELEMENT_ARRAY_BUFFER标志指定元素缓冲区对象用于创建保存图元索引的缓冲区对象
  • 在使用缓冲对象渲染之前,需要分配缓冲区对象并将顶点数据和元素索引上传到相应的缓冲区对象

void initVertexBufferObjects(const GLvoid * vertextBuffer,
                             GLushort *indices,
                             GLuint numVertices,
                             GLuint numIndices,
                             GLuint *vboIds) {
    // 创建两个缓冲区对象
    glGenBuffers(2, vboIds);
    // 一个用于保存实际的顶点属性数据
    glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
    glBufferData(GL_ARRAY_BUFFER,
                 numVertices * sizeof(const void*),
                 vertextBuffer,
                 GL_STATIC_DRAW);
    // 用于保存组成图元的元素索引
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIds[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                 numVertices * sizeof(GLushort),
                 indices,
                 GL_STATIC_DRAW);
    
}
  • void glGenBuffers (GLsizei n, GLuint* buffers)分配n个缓冲区对象名称,并在buffers中返回它们
  • glBindBuffer命令用于指定当前缓冲区对象
  • void glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) 将根据size的值保留相应的数据存储。data参数可以为NULL,表示保留的数据存储不进行初始化。如果data是一个有效的指针,则其内容被复制到分配到的数据内存中
void drawPrimituveWithoutVBOs(GLfloat *vertices, GLint vtxStride, GLint numIndices, GLushort *indices) {
    GLfloat *vtxBuf = vertices;
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    
    glEnableVertexAttribArray(VERTEX_POS_INDX);
    glEnableVertexAttribArray(VERTEX_COLOR_INDX);
    
    glVertexAttribPointer(VERTEX_POS_INDX,
                          VERTEX_POS_SIZE,
                          GL_FLOAT,
                          GL_FALSE,
                          vtxStride,
                          vtxBuf);
    vtxBuf += VERTEX_POS_SIZE;
    glVertexAttribPointer(VERTEX_COLOR_INDX,
                          VERTEX_COLOR_SIZE,
                          GL_FLOAT,
                          GL_FALSE,
                          vtxStride,
                          vtxBuf);
    glDrawElements(GL_TRIANGLES,
                   numIndices,
                   GL_UNSIGNED_SHORT,
                   indices);
    glDisableVertexAttribArray(VERTEX_POS_INDX);
    glDisableVertexAttribArray(VERTEX_COLOR_INDX);
}

void drawPrimituveWithVBOs(ESContext *esContext,
                           GLint numVertices,
                           GLfloat *vtxBuf,
                           GLint vtxStide,
                           GLint numIndices,
                           GLushort *indices) {
    UserData *userData = (UserData*)esContext->userData;
    GLuint offset = 0;
    if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {
        glGenBuffers(2, userData->vboIds);
        glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
        glBufferData(GL_ARRAY_BUFFER, vtxStide * numVertices, vtxBuf, GL_STATIC_DRAW);
        
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * numIndices, indices, GL_STATIC_DRAW);
    }
    glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
    glEnableVertexAttribArray(VERTEX_POS_INDX);
    glEnableVertexAttribArray(VERTEX_COLOR_INDX);
    glVertexAttribPointer(VERTEX_POS_INDX,
                          VERTEX_POS_SIZE,
                          GL_FLOAT,
                          GL_FALSE,
                          vtxStide,
                          (const void*)offset);
    offset += VERTEX_POS_SIZE * sizeof(GLfloat);
    glVertexAttribPointer(VERTEX_COLOR_INDX,
                          VERTEX_COLOR_SIZE,
                          GL_FLOAT,
                          GL_FALSE,
                          vtxStide,
                          (const void*)offset);
    glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0);
    glDisableVertexAttribArray(VERTEX_POS_INDX);
    glDisableVertexAttribArray(VERTEX_COLOR_INDX);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
}

顶点数组对象

  • 加载顶点属性的两种方式: 顶点数组和顶点缓冲区对象
  • 顶点缓冲区对象由于顶点数组,因为能够减少CPU和GPU之间复制的数据量,从而获得更好的性能
  • 顶点数组对象(VAO)能够使顶点数组使用更加高效
  • 在使用顶点缓冲区对象设置绘图操作可能多次需要调用 glbindBuffer, glVertexAttribPointer 和 glEnableVertexAttribArray。为了更快地再顶点数组配置之间切换, OPenGL ES 3.0推出了顶点数组对象
  • void glGenVertexArrays (GLsizei n, GLuint* arrays)创建新的顶点数组对象
  • 每个VAO都包含一个完整的状态向量,描述所有顶点缓冲区绑定和启用的顶点客户状态。绑定VAO时,它 的状态向量提供顶点缓冲区状态的当前设置。
  • glBindVertexArray绑定顶点数组对象后,更改顶点数组状态的后续调用将影响新的VAO
  • 这样,应用程序可以通过绑定一个已经设置状态的顶点数组对象快速地再顶点数组配置之前切换。所有变化可以在一个函数中调用完成,没有必要多次调用以更改顶点数组状态

int Init ( ESContext *esContext )
{
   UserData *userData = esContext->userData;
   const char vShaderStr[] =
      "#version 300 es                            \n"
      "layout(location = 0) in vec4 a_position;   \n"
      "layout(location = 1) in vec4 a_color;      \n"
      "out vec4 v_color;                          \n"
      "void main()                                \n"
      "{                                          \n"
      "    v_color = a_color;                     \n"
      "    gl_Position = a_position;              \n"
      "}";


   const char fShaderStr[] =
      "#version 300 es            \n"
      "precision mediump float;   \n"
      "in vec4 v_color;           \n"
      "out vec4 o_fragColor;      \n"
      "void main()                \n"
      "{                          \n"
      "    o_fragColor = v_color; \n"
      "}" ;

   GLuint programObject;

   // 3 vertices, with (x,y,z) ,(r, g, b, a) per-vertex
   GLfloat vertices[3 * ( VERTEX_POS_SIZE + VERTEX_COLOR_SIZE )] =
   {
      0.0f,  0.5f, 0.0f,        // v0
      1.0f,  0.0f, 0.0f, 1.0f,  // c0
      -0.5f, -0.5f, 0.0f,        // v1
      0.0f,  1.0f, 0.0f, 1.0f,  // c1
      0.5f, -0.5f, 0.0f,        // v2
      0.0f,  0.0f, 1.0f, 1.0f,  // c2
   };
   // Index buffer data
   GLushort indices[3] = { 0, 1, 2 };

   // Create the program object
   programObject = esLoadProgram ( vShaderStr, fShaderStr );

   if ( programObject == 0 )
   {
      return GL_FALSE;
   }

   // Store the program object
   userData->programObject = programObject;

   // Generate VBO Ids and load the VBOs with data
   glGenBuffers ( 2, userData->vboIds );

   glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] );
   glBufferData ( GL_ARRAY_BUFFER, sizeof ( vertices ),
                  vertices, GL_STATIC_DRAW );
   glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1] );
   glBufferData ( GL_ELEMENT_ARRAY_BUFFER, sizeof ( indices ),
                  indices, GL_STATIC_DRAW );

   // Generate VAO Id
   glGenVertexArrays ( 1, &userData->vaoId );

   // Bind the VAO and then setup the vertex
   // attributes
   glBindVertexArray ( userData->vaoId );

   glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] );
   glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1] );

   glEnableVertexAttribArray ( VERTEX_POS_INDX );
   glEnableVertexAttribArray ( VERTEX_COLOR_INDX );

   glVertexAttribPointer ( VERTEX_POS_INDX, VERTEX_POS_SIZE,
                           GL_FLOAT, GL_FALSE, VERTEX_STRIDE, ( const void * ) 0 );

   glVertexAttribPointer ( VERTEX_COLOR_INDX, VERTEX_COLOR_SIZE,
                           GL_FLOAT, GL_FALSE, VERTEX_STRIDE,
                           ( const void * ) ( VERTEX_POS_SIZE * sizeof ( GLfloat ) ) );

   // Reset to the default VAO
   glBindVertexArray ( 0 );

   glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );
   return GL_TRUE;
}

void Draw ( ESContext *esContext )
{
   UserData *userData = esContext->userData;

   glViewport ( 0, 0, esContext->width, esContext->height );
   glClear ( GL_COLOR_BUFFER_BIT );
   glUseProgram ( userData->programObject );

   // Bind the VAO
   glBindVertexArray ( userData->vaoId );

   // Draw with the VAO settings
   glDrawElements ( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, ( const void * ) 0 );

   // Return to the default VAO
   glBindVertexArray ( 0 );
}

void Shutdown ( ESContext *esContext )
{
   UserData *userData = esContext->userData;

   glDeleteProgram ( userData->programObject );
   glDeleteBuffers ( 2, userData->vboIds );
   glDeleteVertexArrays ( 1, &userData->vaoId );
}

int esMain ( ESContext *esContext )
{
   esContext->userData = malloc ( sizeof ( UserData ) );

   esCreateWindow ( esContext, "VertexArrayObjects", 320, 240, ES_WINDOW_RGB );

   if ( !Init ( esContext ) )
   {
      return GL_FALSE;
   }

   esRegisterShutdownFunc ( esContext, Shutdown );
   esRegisterDrawFunc ( esContext, Draw );

   return GL_TRUE;
}

映射缓冲区

  • 待补充

复制缓冲区

  • 待补充
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容