頂點數據,也稱為頂點屬性,指每一個頂點數據。指能被用來描述每個頂點的數據,或能被所有頂點使用的常量值。例如你想繪制一個具有顏色的立方體三角形。你指定一個恒定的值用于三角形的所有三個頂點顏色。但三角形的三個頂點位置是不同的,你需要指定一個頂點矩陣存儲三個位置值。
指定頂點屬性數據
頂點屬性數據可以使用頂點數組或常量值指定每個頂點數據,OpenGL ES 3.0 必須至少支持16 個頂點屬性。應用應該能夠查詢編譯器支持的確切屬性數。下面的程序指出如何查詢。
GLint maxVertexAttribs; // n will be >= 8glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
常量頂點屬性
常量頂點屬性是指基元的所有頂點屬性是相同的,因此僅僅對基元的所有頂點僅僅需要指定一個值。頂點屬性常量使用下面的函數指定:
void glVertexAttriblf(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 glVertexAttriblfv(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);
glVertexAttrib*通過索引來裝在一般頂點屬性,glVertexAttriblf和glVertexAttriblfv加載(x, 0.0, 0.0, 1.0)到頂點屬性,glVertexAttrib2f and glVertexAttrib2fv 裝載(x, y, 0.0, 1.0),glVertexAttrib3f 和glVertexAttrib3fv 裝載(x, y, z, 1.0),glVertexAttrib4f and
glVertexAttrib4fv 裝載(x, y, z, w)
頂點數組
頂點數組指定每個頂點的屬性數據即存儲在應用程序地址空間(OpenGL ES 叫clientspace)緩沖區的數據。它們提供有效的和靈活的方法指定頂點屬性數據。頂點數組使用glVertexAttribPointer 或glVertexAttribipointer函數指定:
存儲所有的頂點屬性在一個緩沖區中,這種存儲頂點屬性的方法叫結構數組,這種方法描述每個頂點的所有屬性。存儲每個頂點屬性到分開的緩沖區,這種存儲頂點屬性的方法叫數組結構每個頂點有四個屬性—位置、法線、兩個貼圖坐標,這些屬性被存儲在一個緩沖區中,被所有頂點分享。頂點位置屬性是三個浮點數(x, y, z)的矢量。法線也是三個浮點數的矢量,每個貼圖坐標是兩個浮點數的矢量。
結構數組代碼示例
#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_TEXCOORDl_SIZE 2 // s and t#define VERTEX_POS_INDX 0#define VERTEX_NORMAL_INDX 1#define VERTEX_TEXCOORD0_INDX 2#define VERTEX_TEXCOORDl_INDX 3// the following 4 defines are used to determine the locations// of various attributes if vertex data are stored as an array// of structures#define VERTEX_POS_OFFSET 0#define VERTEX_NORMAL_OFFSET 3#define VERTEX_TEXCOORD0_OFFSET 6#define VERTEX_TEXC00RD1_0FFSET 8#define VERTEX_ATTRIB_SIZE (VERTEX_POS_SIZE + / VERTEX_NORMAL_SIZE + / VERTEX_TEXCOORD0_SIZE + / VERTEX_TEXC00RD1_SIZE)
float *p = (float*) malloc(numVertices * VERTEX_ATTRIB_SIZE* sizeof(float));// position is vertex attribute 0glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, VERTEX_ATTRIB_SIZE * sizeof(float), p);// normal is vertex attribute 1glVertexAttribPointer(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 2glVertexAttribPointer(VERTEX_TEXCOORDO_INDX, VERTEX_TEXCOORD0_SIZE, GL_FLOAT, GL_FALSE, VERTEX_ATTRIB_SIZE * sizeof(float), (p + VERTEX_TEXCOORD0_OFFSET));// texture coordinate 1 is vertex attribute 3glVertexAttribPointer(VERTEX_TEXCOORDl_INDX, VERTEX_TEXC00RD1_SIZE, GL_FLOAT, GL_FALSE, VERTEX_ATTRIB_SIZE * sizeof(float), (p + VERTEX_TEXC00RD1_0FFSET));
數組結構示例代碼
float *position = (float*) malloc(numVertices *VERTEX_POS_SIZE * sizeof(float));float *normal = (float*) malloc(numVertices *VERTEX_NORMAL_SIZE * sizeof(float));float *texcoordO = (float*) malloc(numVertices *VERTEX_TEXCOORD0_SIZE * sizeof(float));float *texcoordl = (float*) malloc(numVertices *VERTEX_TEXC00RD1_SIZE * sizeof(float));// position is vertex attribute 0glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, VERTEX_POS_SIZE * sizeof(float), position);// normal is vertex attribute 1glVertexAttribPointer(VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE, GL_FLOAT, GL_FALSE, VERTEX_NORMAL_SIZE * sizeof(float), normal);// texture coordinate 0 is vertex attribute 2glVertexAttribPointer(VERTEX_TEXCOORDO_INDX, VERTEX_TEXCOORD0_SIZE, GL_FLOAT, GL_FALSE, VERTEX_TEXCOORD0_SIZE *sizeof(float), texcoordO);// texture coordinate 1 is vertex attribute 3glVertexAttribPointer(VERTEX_TEXCOORDl_INDX, VERTEX_TEXC00RD1_SIZE, GL_FLOAT, GL_FALSE, VERTEX_TEXC00RD1_SIZE * sizeof(float), texcoordl);
性能提示
1.如何存儲不同的頂點屬性。
使用結構數組要比使用數組結構性能更有優,原因是每個頂點的屬性數據能夠被連續的讀出,這種內存結構更有效。但使用array of structures 不好的是當我們想去修改指定的屬性時。如果一個頂點屬性需要被修改(像貼圖坐標),這將必須更新頂點緩沖區。當頂點緩沖區作為緩沖區對象時,整個
頂點屬性緩沖區將需要更新加載,
2.頂點屬性使用哪種數據格式
頂點屬性數據格式 通過調用glVertexAttribPointer 函數的參數type指定,這樣做不但影響頂點屬性的繪圖數據的存儲要求,也影響全部的執行工作,它是渲染幀時的內存帶寬的要求。數據量越小,對帶寬要求越低。OpenGL ES 3支持16位浮點頂點格式命名gl_half_float,建議盡量使用gl_half_float,Texture coordinates, normals, binormals, tangent vectors都適合使用gl_half_float來存儲,顏色使用四個GL_UNSIGNED_BYTE來存儲每個頂點顏色,頂點位置應該存儲為GL_FLOAT。
3.如何標準化glVertexAttribPointer 工作
頂點屬性在被頂點著色器使用前,作為單一精度的浮點值被存儲在內存中。如果頂點屬性的數據類型不是浮點數,那么它們的值將在著色器使用前轉變為浮點值。normalized標志指示非浮點頂點屬性數據轉化為單一精度的浮點值。如果normalized符為false,頂點數值被直接轉化為浮點值,轉化非浮點變量為浮點類型是相似的
GLfloat f;GLbyte b;f = (GLfloat)b; // f rePResents values in the range [-128.0,// 127.0]
如果normalized為true,頂點數據類型如果是GL_BYTE, GL_SHORT 或 GL_FIXED 被匹配到[-1.0,1.0],數據類型如果是GL_UNSIGNED_BYTE or GL_UNSIGNED_SHORT 被匹配到[0.0,1.0]。下面描述非浮點值數據類型normalized轉換過程
也有可能訪問整數頂點屬性數據為整數的頂點著色器,而不將它們轉換為浮點數。在這種情況下,glvertexattribipointer功能應使用頂點屬性應該被聲明為一個整數類型的頂點著色。
4.在常量頂點屬性和頂點數組之間選擇
int Init ( ESContext *esContext ){ UserData *userData = (UserData*) esContext->userData; const char vShaderStr[] = "#version 300 es /n" "layout(location = 0) in vec4 a_color; /n" "layout(location = 1) in vec4 a_position; /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;// Create the program object programObject = esLoadProgram ( vShaderStr, fShaderStr ); if ( programObject == 0 ) return GL_FALSE;// Store the program object userData->programObject = programObject; glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f ); return GL_TRUE;}
void Draw ( ESContext *esContext ){ UserData *userData = (UserData*) esContext->userData; GLfloat color[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; // 3 vertices, with (x, y, z) per-vertex GLfloat vertexPos[3 * 3] = { 0.0f, 0.5f, 0.0f, // v0 -0.5f, -0.5f, 0.0f, // v1 0.5f, -0.5f, 0.0f // v2 }; glViewport ( 0, 0, esContext->width, esContext->height ); glClear ( GL_COLOR_BUFFER_BIT ); glUseProgram ( userData->programObject ); glVertexAttrib4fv ( 0, color ); glVertexAttribPointer ( 1, 3, GL_FLOAT, GL_FALSE, 0, vertexPos ); glEnableVertexAttribArray ( 1 ); glDrawArrays ( GL_TRIANGLES, 0, 3 ); glDisableVertexAttribArray ( 1 );}
在頂點著色器中聲明頂點屬性變量
在頂點著色器中,通過限定符【in】來聲明一個變量作為頂點屬性。屬性變量也可以加【layout】限定符,用于提供屬性索引。如下
layout(location = 0) in vec4 a_position;layout(location = 1) in vec2 a_texcoord;layout(location = 2) in vec3 a_normal;
【in】限度符支持的數據類型包括float, vec2,vec3, vec4, int, ivec2, ivec3, ivec4, uint, uvec2, uvec3,uvec4, mat2, mat2x2, mat2x3, mat2x4, mat3, mat3x3, mat3x4,=mat4, mat4x2, and mat4x3.它并不支持數組和結構體。下面聲明會發生編譯錯誤
in foo_t a_A; // foo_t is a structurein vec4 a_B[10];
聲明的頂點屬性變量是只讀的,不可被修改
頂點緩存對象
頂點數據通過頂點數組存儲在客戶端內存中,當使用glDrawArrays or glDrawElements繪制時,此數據必須從客戶端內存復制到圖形內存。頂點緩存區對象允許應用程序頂點數據分配和緩存在具有高性能的圖形內存并渲染。而避免重發數據的每一次圖元的繪制。緩存對象支持2種類型,數據緩存對象和元素數組緩存對象,數組緩存對象使用GL_ARRAY_BUFFER令牌來指定,元素數組緩存對象使用GL_ELEMENT_ARRAY_BUFFER來指定,在使用緩沖區對象之前,我們需要分配緩沖對象并將頂點數據和元素索引上傳到緩沖對象。
void initVertexBufferObjects(vertex_t *vertexBuffer, GLushort *indices, GLuint numVertices, GLuint numlndices, GLuint *vboIds){ glGenBuffers(2, vboIds); glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(vertex_t), vertexBuffer, GL_STATIC_DRAW);// bind buffer object for element indicesglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIds[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(GLushort), indices, GL_STATIC_DRAW);}
glGenBuffers用于創建緩存區對象。N為0的值被系統保留不會分配,所以當使用0時會發生錯誤。
glBindBuffer函數是一個緩存區對象設置為當前在使用的緩存區對象。glGenBuffers 在被使用glBindBuffer 綁定前,不要求被指定緩沖區對象名,一個應用能指定一個未使用的緩沖區對象名字去綁定。但我們推薦使用glGenBuffers 返回的名字而不是自己指定。
與緩存對象關聯的狀態有如下
頂點數組數據和元素頂點數組數據被創建和初始化使用glBufferData 函數
#define VERTEX_POS_SIZE 3 // x, y, and z#define VERTEX_COLOR_SIZE 4 // r, g, b, and a#define VERTEX_POS_INDX 0#define VERTEX_COLOR_INDX 1//// 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 primitives// 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_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 DrawPrimitiveWithVBOs(ESContext *esContext,GLint numVertices, GLfloat *vtxBuf,GLint vtxStride, GLint numIndices,GLushort *indices){ UserData *userData = (UserData*) esContext->userData; GLuint offset = 0; // vboIds[0] - used to store vertex attribute data // vboIds[l] - used to store element indices if ( userData->vboIds[0] == 0 && userData->vboIds[1] == 0 ) { // Only allocate on the first draw glGenBuffers(2, userData->vboIds); glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]); glBufferData(GL_ARRAY_BUFFER, vtxStride * 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, vtxStride, (const void*)offset); offset += VERTEX_POS_SIZE * sizeof(GLfloat); glVertexAttribPointer(VERTEX_COLOR_INDX, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, vtxStride, (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);}void Draw ( ESContext *esContext ){ UserData *userData = (UserData*) esContext->userData;// 3 vertices, with (x, y, z),(r, g, b, a) per-vertex GLfloat vertices[3 * (VERTEX_POS_SIZE + VERTEX_COLOR_SIZE)] = { -0.5f, 0.5f, 0.0f, // v0 1.0f, 0.0f, 0.0f, 1.0f, // c0 -1.0f, -0.5f, 0.0f, // v1 0.0f, 1.0f, 0.0f, 1.0f, // c1 0.0f, -0.5f, 0.0f, // v2 0.0f, 0.0f, 1.0f, 1.0f, // c2 }; // index buffer data GLushort indices[3] = { 0, 1, 2 }; glViewport ( 0, 0, esContext->width, esContext->height ); glClear ( GL_COLOR_BUFFER_BIT ); glUseProgram ( userData->programObject ); glUniform1f ( userData->offsetLoc, 0.0f ); DrawPrimitiveWithoutVBOs ( vertices, sizeof(GLfloat) * (VERTEX_POS_SIZE + VERTEX_COLOR_SIZE), 3, indices ); // offset the vertex positions so both can be seen glUniform1f ( userData->offsetLoc, 1.0f ); DrawPrimitiveWithVBOs ( esContext, 3, vertices, sizeof(GLfloat) * (VERTEX_POS_SIZE + VERTEX_COLOR_SIZE), 3, indices );}
頂點數組對象
OpenGL ES 3.0推出的頂點數組對象。負責單一對象的頂點數組/頂點緩沖區對象轉換配置。
#define VERTEX_POS_SIZE 3 // x, y, and z#define VERTEX_COLOR_SIZE 4 // r, g, b, and a#define VERTEX_POS_INDX 0#define VERTEX_COLOR_INDX 1#define VERTEX_STRIDE ( sizeof(GLfloat) * / ( VERTEX_POS_SIZE + / VERTEX_COLOR_SIZE ) )int Init ( ESContext *esContext ){ UserData *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 }; 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 set up 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 ( 0.0f, 0.0f, 0.0f, 0.0f ); return GL_TRUE; }void Draw ( ESContext *esContext ){ UserData *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 );}
新聞熱點
疑難解答