[TOC]
摘要
文章主要介绍了在OpenGL ES 3.0中,顶点属性(数据)的声明和运用,通过阅读本章可以掌握顶点属性是什么以及如何利用顶点属性来画图形。
顶点数据,也称为顶点属性,指明了每个顶点的数据。这种逐顶点数据可以每个顶点单独声明,也可以用一个常量为所有顶点声明。例如要画一个纯色的三角形,那么就可以用一个常量值声明为所有顶点的颜色值,但是每个顶点的位置属性则需要单独指定。
1. Specifying Vertex Attribute Data
顶点属性可以用一个顶点数组来为每个顶点声明数据,也可以用一个常量为所有顶点声明数据。
所有OpenGL ES 3.0的实现必须至少支持16个顶点属性。可以通过下面方法查询各个实现所支持的具体数量:
GLint maxVertexAttribs;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
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);
上面这些命令用来给由index指定的顶点属性加载数据。其中glVertexAttrib1f和glVertexAttrib1fv会加载(x, 0.0, 0.0, 1.0),glVertexAttrib2f和glVertexAttrib2fv会加载(x, y, 0.0, 1.0),glVertexAttrib3f和glVertexAttrib3fv会加载(x, y, z, 1.0),glVertexAttrib4f和glVertexAttrib4fv会加载(x, y, z, w)。在实际中,常量顶点属性提供了和uniform等价的功能,两者都可以选用。
Vertex Array
顶点数组逐顶点指定数据,是保存在应用程序地址空间(OpenGL ES称为client space)的缓存区。用如下函数声明:
void glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * ptr)
void glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void * ptr)
- index : 顶点属性的索引,从0开始
- size : 在顶点数组中为该顶点属性所指定的分量数量,有效值为1-4
- type : 数据格式。两个函数都包括的有效值是:
GL_BYTE
GL_UNSIGNED_BYTE
GL_SHORT
GL_UNSIGNED_SHORT
GL_INT
GL_UNSIGNED_INT
其中 glVertexAttribPointer 还包括:
GL_HALF_FLOAT
GL_FLOAT
GL_FIXED
GL_INT_2_10_10_10_REV
GL_UNSIGNED_INT_2_10_10_10_REV- normalized : 用来表示当非浮点值转为浮点值时是否进行规范化处理。
- stride : 每个顶点的由size指定的顶点属性的分量是顺序储存的。stride指定了两个相邻索引之间数据的位移,如果stride是0,所有顶点的顶点数据按顺序储存,如果stride大于0,则使用该值作为获取下一个顶点数据的跨距。
- ptr : 如果使用的是顶点数组,则表示指向保存顶点数据的缓冲区的指针。如果使用的是顶点缓冲对象,则表示缓冲区内的偏移量。
接下来,我们通过一些例子来看一下怎样声明顶点属性。通常情况下,有两种方法来分配和储存顶点属性数据:
- array of structure : 将顶点属性储存在一个缓冲区中,结构体表示一个顶点的所有属性,我们拥有由所有顶点的结构体的数组
- structure of array : 将每一个顶点属性分别储存在一个单独的缓冲区中
假设每个顶点有4个属性:位置、法线、两个纹理坐标,所有顶点属性储存在一个缓冲区中,其中位置是一个包含3个float的向量,法线是一个包含3个float的向量,纹理坐标是一个包含2个float的向量,如图所示:

下面的例子展示了如果通过glVertexAttribPointer来声明顶点数据。注意,我们推荐使用顶点缓冲对象(后面会介绍)来声明顶点数据,这比使用顶点数据要更有效率。
Example 6-1 Array of Structures
#define VERTEX_POS_SIZE 3
#define VERTEX_NORMAL_SIZE 3
#define VERTEX_TEXCOORD0_SIZE 2
#define VERTEX_TEXCOORD1_SIZE 2
#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 = (float*) malloc(numVertices * VERTEX_ATTRIB_SIZE * sizeof(float));
// position is vertex attribute 0
glVertexAttribPointer (VERTEX_POS_INDX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float), p);
// normal is vertex attribute 1
glVertexAttribPointer (VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE, GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float), (p + VERTEX_NORMAL_OFFSET));
// texture coordinate 0 is vertex attribute 2
glVertexAttribPointer (VERTEX_TEXCOORD0_INDX, VERTEX_TEXCOORD0_SIZE, GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float), (p + VERTEX_TEXCOORD0_OFFSET));
// texture coordinate 1 is vertex attribute3
glVertexAttribPointer (VERTEX_TEXCOORD1_INDX, VERTEX_TEXCOORD1_SIZE, GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float), (p + VERTEX_TEXCOORD1_OFFSET));
在下面的例子中,顶点属性储存在单独的缓冲区中:
Example 6-2 Structure of Arrays
float * position = (float * ) malloc(numVertices * VERTEX_POS_SIZE * sizeof(float));
float * normal = (float * ) malloc(numVertices * VERTEX_NORMAL_SIZE * sizeof(float));
float * texcoord0 = (float * ) malloc(numVertices * VERTEX_TEXCOORD0_SIZE * sizeof(float));
float * texcoord1 = (float * ) malloc(numVertices * VERTEX_TEXCOORD1_SIZE * sizeof(float));
// position is vertex attribute 0
glVertexAttribPointer (VERTEX_POS_INDX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE,
VERTEX_POS_SIZE * sizeof(float), position);
// normal is vertex attribute 1
glVertexAttribPointer (VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE, GL_FLOAT, GL_FALSE,
VERTEX_NORMAL_SIZE * sizeof(float), normal);
// texture coordinate 0 is vertex attribute 2
glVertexAttribPointer (VERTEX_TEXCOORD0_INDX, VERTEX_TEXCOORD0_SIZE, GL_FLOAT, GL_FALSE,
VERTEX_TEXCOORD0_SIZE * sizeof(float), texcoord0);
// texture coordinate 1 is vertex attribute3
glVertexAttribPointer (VERTEX_TEXCOORD1_INDX, VERTEX_TEXCOORD1_SIZE, GL_FLOAT, GL_FALSE,
VERTEX_TEXCOORD1_SIZE * sizeof(float), texcoord1);
Performance Hints
How to Store Different Attributes of a Vertex
多数情况下,用 array of structures 比较好,访问效率比较高,但是当要修改某一顶点属性时效率就会比较低,这时可以将要修改的顶点属性保存在一个单独的缓冲区来避免这种情况。
Which Data Format to Use for Vertex Attributes
尽量使用小的格式,纹理坐标、法线、切线等建议使用 GL_HALF_FLOAT(16位浮点数),颜色建议使用 GL_UNSIGNED_BYTE。
How the Normalized Flag in gIVertexAttribPointer Works
顶点属性在使用前是以一个单精度浮点数储存的,所以非浮点数的顶点属性将会被转换为单精度浮点数。Normalized Flag控制非浮点数到浮点数的转换,如果为false,就是直接转换,如果是true,GL_BYTE, GL_SHORT, or GL_FIXED 被映射到【-1,1】的范围内,GL_UNSIGNED_BYTE or GL_UNSIGNED_SHORT被映射到【0,1】范围。
Selecting Between a Constant Vertex Attribute or a Vertex Array
可以用 glEnableVertexAttribArray 和 glDisableVertexAttribArray 来控制某个顶点属性索引是否启用顶点数组,如果没有启用顶点数组,那么就会使用常量数据。
void glEnableVertexAttribArray (GLuint index)
void glDisableVertexAttribArray (GLuint index)
- index : 顶点属性索引
网友评论