一、Vertex Attributes简介
Vertex属性即顶点数据,它指定了每个顶点的各种属性数据。在OpenGL ES1.1中,顶点属性有四个预定义的名字:position(位置), normal(法线), color(颜色), 和 texture coordinates(纹理坐标)。在OpenGL ES2.0中,用户必须定义“顶点属性的名字”。
二、常量顶点属性(Constant Vertex Attribute)
常量顶点属性对所有顶点都是一样的。因此只需要指定一个值就可以应用于所有顶点。一般很少使用。其设置函数有:
- void glVertexAttrib1f(GLuint index, GLfloat x);
- void glVertexAttrib2f(GLuint index, GLfloat x, GLfloat y);
- void glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z);
- void glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z,GLfloat w);
- void glVertexAttrib1fv(GLuint index, const GLfloat *values);
- void glVertexAttrib2fv(GLuint index, const GLfloat *values);
- void glVertexAttrib3fv(GLuint index, const GLfloat *values);
- void glVertexAttrib4fv(GLuint index, const GLfloat *values);
三、 如何装载顶点数据?(Vertex Arrays)
Vertex Array(顶点数组):是一个存储在应用程序空间(Client)中的内存buffer,它存储了每个顶点的属性数据。
如何把顶点数据组的数据传递给GPU呢?
void glVertexAttribPointer(GLuint index,
GLint size, //每个属性元素个数有效值1-4(x,y,z,w)
GLenum type,
GLboolean normalized,
GLsizei stride, //如果数据连续存放,则为0
const void *ptr) //顶点数组指针
举例如下:
- GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f,
- -0.5f, -0.5f, 0.0f,
- 0.5f, -0.5f, 0.0f };
-
-
- glViewport ( 0, 0, esContext->width, esContext->height );
-
-
- glClear ( GL_COLOR_BUFFER_BIT );
-
-
- glUseProgram (programObject );
-
-
- glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
四、顶点属性数据存储方式
1.一个顶点的所有属性存储在一起(Array of Structures)
如下图所示,顶点的位置(x,y,z)、法线(x,y,z)和两个纹理坐标(s,t)存储在一起,如下图所示:
例子代码如下(当然,此代码在CPU上运动):
- #define VERTEX_POS_SIZE 3 // x, y and z
- #define VERTEX_NORMAL_SIZE 3 // x, y and z
- #define VERTEX_TEXCOORD0_SIZE 2 // s and t
- #define VERTEX_TEXCOORD1_SIZE 2 // s and t
-
- #define VERTEX_POS_INDX 0
- #define VERTEX_NORMAL_INDX 1
- #define VERTEX_TEXCOORD0_INDX 2
- #define VERTEX_TEXCOORD1_INDX 3
-
-
-
- #define VERTEX_POS_OFFSET 0
- #define VERTEX_NORMAL_OFFSET 3
- #define VERTEX_TEXCOORD0_OFFSET 6
- #define VERTEX_TEXCOORD1_OFFSET 8
-
- #define VERTEX_ATTRIB_SIZE VERTEX_POS_SIZE + \
- VERTEX_NORMAL_SIZE + \
- VERTEX_TEXCOORD0_SIZE + \
- VERTEX_TEXCOORD1_SIZE
-
- float *p = malloc(numVertices * VERTEX_ATTRIB_SIZE
- * sizeof(float));
-
-
- glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE,
- GL_FLOAT, GL_FALSE,
- VERTEX_ATTRIB_SIZE * sizeof(float),
- p+VERTEX_POS_OFFSET);
-
-
- glVertexAttribPointer(VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE,
- GL_FLOAT, GL_FALSE,
- VERTEX_ATTRIB_SIZE * sizeof(float),
- (p + VERTEX_NORMAL_OFFSET));
-
-
- glVertexAttribPointer(VERTEX_TEXCOORD0_INDX, VERTEX_TEXCOORD0_SIZE,
- GL_FLOAT, GL_FALSE,
- VERTEX_ATTRIB_SIZE * sizeof(float),
- (p + VERTEX_TEXCOORD0_OFFSET));
-
-
- glVertexAttribPointer(VERTEX_TEXCOORD1_INDX, VERTEX_TEXCOORD1_SIZE,
- GL_FLOAT, GL_FALSE,
- VERTEX_ATTRIB_SIZE * sizeof(float),
- (p + VERTEX_TEXCOORD1_OFFSET));
2. 顶点的每个属性单独存储(Structure of Arrays)
例子代码如下(当然,此代码在CPU上运动):
- #define VERTEX_POS_SIZE 3 // x, y and z
- #define VERTEX_NORMAL_SIZE 3 // x, y and z
- #define VERTEX_TEXCOORD0_SIZE 2 // s and t
- #define VERTEX_TEXCOORD1_SIZE 2 // s and t
-
- #define VERTEX_POS_INDX 0
- #define VERTEX_NORMAL_INDX 1
- #define VERTEX_TEXCOORD0_INDX 2
- #define VERTEX_TEXCOORD1_INDX 3
-
-
- #define VERTEX_ATTRIB_SIZE VERTEX_POS_SIZE + \
- VERTEX_NORMAL_SIZE + \
- VERTEX_TEXCOORD0_SIZE + \
- VERTEX_TEXCOORD1_SIZE
-
- float *position = malloc(numVertices * VERTEX_POS_SIZE *
- sizeof(float));
- float *normal = malloc(numVertices * VERTEX_NORMAL_SIZE *
- sizeof(float));
- float *texcoord0 = malloc(numVertices * VERTEX_TEXCOORD0_SIZE *
- sizeof(float));
- float *texcoord1 = malloc(numVertices * VERTEX_TEXCOORD1_SIZE *
- sizeof(float));
-
-
- glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE,
- GL_FLOAT, GL_FALSE,
- VERTEX_POS_SIZE * sizeof(float), position);
-
-
- glVertexAttribPointer(VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE,
- GL_FLOAT, GL_FALSE,
- VERTEX_NORMAL_SIZE * sizeof(float), normal);
-
-
- glVertexAttribPointer(VERTEX_TEXCOORD0_INDX, VERTEX_TEXCOORD0_SIZE,
- GL_FLOAT, GL_FALSE, VERTEX_TEXCOORD0_SIZE *
- sizeof(float), texcoord0);
-
-
- glVertexAttribPointer(VERTEX_TEXCOORD1_INDX, VERTEX_TEXCOORD1_SIZE,
- GL_FLOAT, GL_FALSE,
- VERTEX_TEXCOORD1_SIZE * sizeof(float),
- texcoord1);
3. 哪种顶点属性数据存储方式在GPU上性能更好?
答案是:把一个顶点的所有属性放在一起(array of structures)。其原因是每个顶点的属性数据以连续的方式读取,使内存访问效率更高。其缺点是,如果要修改其中部分属性数据,将导致整个属性buffer全部重新装载,解决此问题的方法是把这些需要动态修改的属性数据放在单独的buffer中。
五、 顶点属性数据类型优化
顶点属性数据类型不会影响在GPU上每个顶点占用的内存,但在Render a Frame时,它影响CPU与GPU之间的内存带宽。推荐尽量使用GL_HALF_FLOAT_OES。
六、glVertexAttribPointer中的归一化参数
如果normalized为GL_FALSE:则直接把数据转换为GLfloat,因为Vertex Shader内部把顶点属性当作GLfloat(32位)来进行存储。
GL_BYTE, GL_SHORT or GL_FIXED被归一化为[-1,1];GL_UNSIGNED_BYTE or GL_UNSIGNED_SHORT被归一化为[0.0,1.0]。具体转换公式为:
七、选择常量或顶点数组Vertex Attribute
可通过以下函数来Enable或Disable顶点数组(Vertex Array)。
void glEnableVertexAttribArray(GLuint index);
void glDisableVertexAttribArray(GLuint index);
其关系如下图所示:
八、申明attribute变量(在Vertex Shader中)
Attribute变量的数据类型只能为:float, vec2,vec3, vec4, mat2, mat3, and mat4;Attribute变量不能为数组或结构。如下面的申明是错误的:
- attribute foo_t a_A;
- attribute vec4 a_B[10];
每个GPU支持GL_MAX_VERTEX_ATTRIBS vec4。float、vec2和vec3也被当作一个vec4来进行存储;mat2、mat3、mat4被分别当作2、3、和4个vec4来进行存储。
九、把“顶点属性索引”绑定到“顶点属性名”
把“顶点属性索引”绑定到“顶点属性名”有以下两个方法:
1)OpenGL ES 2.0把“顶点属性索引”绑定到“顶点属性名”,当link program时,OpengGL ES 2.0执行此绑定。然后应用程序通过glGetAttribLocation(失败时返回-1)获取“顶点属性索引”。
- GLint glGetAttribLocation(GLuint program, const GLchar *name)
2)应用程序通过glBindAttribLocation把“顶点属性索引”绑定到“顶点属性名”,glBindAttribLocation在program被link之前执行。
- void glBindAttribLocation(GLuint program, GLuint index,const GLchar *name)
在link program时,OpenGL ES 2.0对每个顶点属性执行如下操作:
(1)首先检查属性变量是否被通过glBindAttribLocation绑定了属性索引,如果是,则使用此绑定的属性索引;否则,为之分配一个属性索引。
在应用程序中,一般使用函数glBindAttribLocation来绑定每个attribute变量的位置,然后用函数glVertexAttribPointer为每个attribute变量赋值。
十、顶点buffer对象(Vertex Buffer Objects)
顶点数组(Vertex Array)被保存在客户端内存,当执行glDrawArrays 或 glDrawElements时,才把它们从客户端内存copy到图形内存。这样占用了大量的内存带宽,Vertex Buffer Objects允许OpengGL ES2.0应用在高性能的图形内存中分配并cache顶点数据,然后从此图形内存中执行render,这样避免了每次画一个原语都要重发送数据。
Vertex Buffer Objects有以下两种类型:
(1)array buffer objects:通过GL_ARRAY_BUFFER标记创建,并存储vertex data。
(2)element array buffer objects:通过GL_ELEMENT_ARRAY_BUFFER标记创建,并存储indices of a primitive。
创建和绑定Vertex Buffer Objects例子代码如下:
- void initVertexBufferObjects(vertex_t *vertexBuffer,
- GLushort *indices,
- GLuint numVertices, GLuint numIndices
- GLuint *vboIds)
- {
- glGenBuffers(2, vboIds);
- glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
- glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(vertex_t),
- vertexBuffer, GL_STATIC_DRAW); //save vertex attribute data
- // bind buffer object for element indices
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIds[1]);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,
- numIndices * sizeof(GLushort),indices,
- GL_STATIC_DRAW); // save element indices that make up the primitive
- }
有无Vertex Buffer Object画图例子:
- #define VERTEX_POS_SIZE 3 // x, y and z
- #define VERTEX_NORMAL_SIZE 3 // x, y and z
- #define VERTEX_TEXCOORD0_SIZE 2 // s and t
- #define VERTEX_POS_INDX 0
- #define VERTEX_NORMAL_INDX 1
- #define VERTEX_TEXCOORD0_INDX 2
- //
- // vertices – pointer to a buffer that contains vertex attribute
- data
- // vtxStride – stride of attribute data / vertex in bytes
- // numIndices – number of indices that make up primitive
- // drawn as triangles
- // indices - pointer to element index buffer.
- //
- void drawPrimitiveWithoutVBOs(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_NORMAL_INDX);
- glEnableVertexAttribArray{VERTEX_TEXCOORD0_INDX);
- glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE,
- GL_FLOAT, GL_FALSE, vtxStride, vtxBuf);
- vtxBuf += VERTEX_POS_SIZE;
- glVertexAttribPointer(VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE,
- GL_FLOAT, GL_FALSE, vtxStride, vtxBuf);
- vtxBuf += VERTEX_NORMAL_SIZE;
- glVertexAttribPointer(VERTEX_TEXCOORD0_INDX,
- VERTEX_TEXCOORD0_SIZE, GL_FLOAT,
- GL_FALSE, vtxStride, vtxBuf);
- glBindAttribLocation(program, VERTEX_POS_INDX, "v_position");
- glBindAttribLocation(program, VERTEX_NORMAL_INDX, "v_normal");
- glBindAttribLocation(program, VERTEX_TEXCOORD0_INDX,"v_texcoord");
- glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT,indices);
- }
- void drawPrimitiveWithVBOs(GLint numVertices, GLfloat *vtxBuf,
- GLint vtxStride, GLint numIndices,
- GLushort *indices)
- {
- GLuint offset = 0;
- GLuint vboIds[2];
- // vboIds[0] – used to store vertex attribute data
- // vboIds[1] – used to store element indices
- glGenBuffers(2, vboIds);
- glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
- glBufferData(GL_ARRAY_BUFFER, vtxStride * numVertices,
- vtxBuf, GL_STATIC_DRAW);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIds[1]);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,
- sizeof(GLushort) * numIndices,
- indices, GL_STATIC_DRAW);
- glEnableVertexAttribArray(VERTEX_POS_INDX);
- glEnableVertexAttribArray(VERTEX_NORMAL_INDX);
- glEnableVertexAttribArray{VERTEX_TEXCOORD0_INDX);
- glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE,
- GL_FLOAT, GL_FALSE, vtxStride,
- (const void*)offset);
- offset += VERTEX_POS_SIZE * sizeof(GLfloat);
- glVertexAttribPointer(VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE,
- GL_FLOAT, GL_FALSE, vtxStride,
- (const void*)offset);
- offset += VERTEX_NORMAL_SIZE * sizeof(GLfloat);
- glVertexAttribPointer(VERTEX_TEXCOORD0_INDX,
- VERTEX_TEXCOORD0_SIZE,
- GL_FLOAT, GL_FALSE, vtxStride,
- (const void*)offset);
- glBindAttribLocation(program, VERTEX_POS_INDX, "v_position");
- glBindAttribLocation(program, VERTEX_NORMAL_INDX, "v_normal");
- glBindAttribLocation(program, VERTEX_TEXCOORD0_INDX,"v_texcoord");
- glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0);
- glDeleteBuffers(2, vboIds);
- }