顶点属性,顶点数组,缓冲区对象
指定顶点属性数据
- 顶点属性数据可以用一个顶点数组对每个顶点指定,也可以将一个常量值用于一个图元的所有顶点,所有OpenGL ES 3.0实现必选支持最少16顶点属性。
常量顶点属性
- 常量顶底属性对于一个图元的所有顶点都相同,所以对一个图元的所有顶点只需要指定一个值
顶点数组
- 顶点数组指定每个顶点的属性,是保存在应用程序地址空间(OpenGL ES称为客户端)的缓冲区。它们作为你顶点缓冲对象的基础,提供指定属性数据的一种高效,灵活的手段。顶点数组用 glVertexAttribPointer 或 glVertexAttriblPointter函数指定
- 分配和存储顶点属性数据的常用的两种方法
- 在一个缓冲区中存储顶点属性--- 这种方法称为结构数组。结构表示顶点的所有属性,每个顶点有一个属性的数组
- 在单独的缓冲区中保存每个顶点属性--这种方法称为数组结构
- 假定每个顶点有4个顶点属性---位置,法线和两个纹理坐标哦---这些属性一起保存在为所有顶点分配的一个缓冲区中。顶点位置属性一个3个浮点数的向量(x,y,z)的形式指定,顶点法线也以3个浮点数组组成的向量形式指定,每个纹理坐标以两个浮点数组组成的向量的形式指定
- 在常量顶点属性和顶点数组之间选择
-
glEnableVertexAttribArray
和 glDisableVertexAttribArray
分别用于启用和禁用通用顶点属性数组。如果某个通用属性索引的顶点属性数组被禁用,将使用为该索引指定的常量顶点属性数据
在顶点着色器中声明顶点属性变量
- 在顶点着色器中,变量通过使用
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;
}
映射缓冲区
复制缓冲区