OGLES 程序和着色器间的数据传递

    xiaoxiao2021-03-25  146

    一、通过顶点属性下标传递的顶点和索引数据

    1.Shader端设置:

    (1) 着色器输入输出等限定符 in(默认类型),out,inout, const, invariant。 GLSL 1.4版本中attribute和varying字段都删除了,都统一使用in out或inout关键字。 顶点着色器的输入数据有:位置,颜色,法向量,纹理坐标,雾坐标,实例id等,例如: gl_Vertex, gl_Color, gl_Normal, gl_TexCoord[n],, gl_SecondaryColor, gl_FogCoord, gl_MultiTexCoord[n], gl_VertexID, gl_InstanceID . (2)顶点属性和布局限定符: 本质上shader program将为每个顶点创建一个顶点数组,该数组包含了一个完整顶点数据(例如下面是pos,color), 每个顶点属性下标其实就是顶点数组的下标 ,GPU会根据VAO指定从离散的VBO位置索引到一个完整的顶点,然后每个完整的顶点是并行处理的。 所以每个顶点的顶点数组属性数量是有限制的,OES3.0上是16个,可以用: GLint maxVertexAttr = 0; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttr);查询。 布局限定符是用于建立映射的,例如顶点数组属性和shader中变量之间的映射,片段输出和MRT之间的映射,使用布局限定符比glBindAttribLocation (GLuint program, GLuint index, const GLchar *name)简洁。 顶点着色器输出和片段着色器输入不能使用布局限定符(顶点输入和片段输出中才有,输出的index是用于MRT指定的,默认是0),且他们输入输出的变量数量也是有限制的,OES3.0上顶点着色器输出变量最大为16,片段着色器输入为15。 shader中写法实例: uniform mat4 u_matViewProjection; //in声明输入的顶点属性变量,不能声明为结构或数组,且是只读变量不能修改。 // 不会进行打包,小于vec4的属性将会作为vec4输入,因此如果几个vec2,vec1可以合并为一个vec4 // 合并后有更好的效率。没有指定布局限定符的话要程序链接成功查询当前活动的属性变量。 layout(location = 0)in vec4 a_position layout(location = 1)in vec3 a_color; out vec3 v_color; void main(void) { gl_Position = u_matViewProjection * a_position; v_color = a_color; }

    2.app端设置:

    顶点着色器的输入用:

    1)常量顶点属性

    常量顶点属性,对图元中的所有顶点传入值都一样,指定一次即可,所用函数是: glVertexAttriblf(attrIdx, xValue); glVertexAttrib4f(attrIdx, xValue, yValue, zValue, wValue); 常量顶点属性较少使用,直接用顶点数组即可。

    2)顶点数组(VA)

    非缓冲区对象中的VAO,而是vertexShader输入的单个顶点对应的顶点数组,OES2.0是使用顶点数组传递数据,OES3.0总是建议用顶点缓冲区对象传递数据,已获得更好的性能。 对于一个顶点的顶点数组数据的单个属性下标进行数据指定: void  glVertexAttribPointer ( GLuint  index , GLint  size , GLenum  type , GLboolean  normalized , GLsizei  stride , const GLvoid *  pointer ); // 函数说明:说明数组类型下标, 顶点属性成员数量,成员类型,成员类型很影响带宽,建议位置为GL_FLOAT,法向量,uv等为GL_HALF_FLOAT,颜色为GL_UNSIGNED_BYTE。 normalized 非浮点数转换浮点格式时是否规范化[-1,1]一般是否。 stride为0则该顶点属性是顺序存储的,不为0则用该跨距作为当前到下一个该属性数据之间的跨距,跨距与自己存放的顶点数据是结构数组,数组结构紧密关联 pointer 数据块中该属性字节首地址,如果在顶点缓冲区VBO对象中,那么首地址用NULL,因为绑定VBO时已经有指针,偏移是NULL上的偏移。 顶点数据存储结构的指定,有两种:一种是结构数组(顶点pos,color, normal,uv等先组成一个结构,有元素个数为numVertexs的数组),另一种是数组结构(顶点每个元素组成一个数组如pos,这些数组并列着组成一个结构),具体用那种自己定义,因为glVertexAttribPointer也是自己定义的。用结构数组更加高效,因为渲染时候传入shader的信息是逐个顶点处理的,方便顺序访问,但是如果有更改需要重新加载整个顶点属性缓冲区,通过将动态改变的属性保存在单独的缓冲区可以改善性能。 例如: /顶点位置和颜色数据 const GLfloat vertexData[] = { -0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f }; glGenVertexArrays(1, &vaoId); glBindVertexArray(vaoId); //创建vertex buffer object对象 glGenBuffers(1, &vboId); glBindBuffer(GL_ARRAY_BUFFER, vboId); // GL_STATIC_DRAW,GL_DYNAMIC_DRAW是GL_BUFFER_USAGE是一个使用标志类似DX的使用标志但是更加 // 宽松。例如指定为GL_STATIC_DRAW也可以频繁修改。 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); // 指定VAO如何去解析VBO数据块中的数据。 // 在Shader创建之前或创建之后指定都是可以的,因为glDrawXXX时候才真正去处理Opengl状态机中的设置。 //启用顶点位置属性索引,要在display中指定的(vao包装了切换vao即可),因为需要开启顶点属性索引才能绘制,特别是绘制物体多的时候,需要切换才能正确绘制。 // 也可以封装在VAO中,只负责启用glEnableVertexAttribArray不关闭即可。 glEnableVertexAttribArray(0); // 激活顶点属性数组,否则是用常量顶点属性 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); //指定Position顶点属性数据格式,大小会根据glDrawArrays截断。 //启用顶点颜色属性索引 glEnableVertexAttribArray(1); //48是4*3*4 = 48, 指定Color顶点属性数据格式,大小会根据glDrawArrays截断。 // 输入到Shader中的时候会并行的从顶点取得一个数据,从颜色取得一个数据作为整个顶点的输入.再进行下一个顶点的输入。 glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)48);

    3)开始顶点数组属性下标(不开启是用常量顶点属性的输入)

    // 输入参数对应顶点输入的类型下标,默认下标从0开始 glEnableVertexAttribArray (0);

    4)顶点缓冲区对象(VBO)

    顶点数组是将数据保存在CPU中,每次drawcall都要传输到GPU中,使用顶点缓冲区VBO在初始化或改变时候才传递。这种方式能显著改善渲染性能,CPU内存开销,IO传输压力和电力开销。 VBO有两种,一种是GL_ARRAY_BUFFER用于保存顶点数据,一种是GL_ELEMENT_ARRAY_BUFFER用于创建保存图元索引的缓冲区对象(类似DX中的索引缓存)。 顶点缓冲区对象的创建: glGenBuffers(1, &vboId);// 只是一个可用标志间接地址 glBindBuffer(GL_ARRAY_BUFFER, vboId);// 将全局活动GL_ARRAY_BUFFER指针指向当前缓冲区对象 // GL_STATIC_DRAW,GL_DYNAMIC_DRAW是GL_BUFFER_USAGE是一个使用标志类似DX的使用标志但是更加 // 宽松。例如指定为GL_STATIC_DRAW也可以频繁修改。 // 缓冲区数据用glBufferData创建和初始化 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); // 缓冲区数据用glBufferSubData更新 glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); 删除缓冲区对象用: glDeleteBuffers(GLsizei n, const GLunit *buffers); 同一种类型缓冲区可以有多个,当前该类型全局指针指向一个进行操作即可,所以顶点数据可以用多个独立的顶点缓冲区处理,区分经常更新的缓冲区,避免全部数据的更新,提高性能。 例如:位置一个缓冲区,颜色一个缓冲区,顶点具体的数据需要 glVertexAttribPointer来解析,但是索引缓冲区仅绑定即可。 实例: #include "esUtil.h" typedef struct { // Handle to a program object GLuint programObject; // VertexBufferObject Ids GLuint vboIds[3]; } UserData; #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 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; // Create the program object programObject = esLoadProgram ( vShaderStr, fShaderStr ); if ( programObject == 0 ) { return GL_FALSE; } // Store the program object userData->programObject = programObject; userData->vboIds[0] = 0; userData->vboIds[1] = 0; userData->vboIds[2] = 0; glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f ); return GL_TRUE; } void DrawPrimitiveWithVBOs ( ESContext *esContext, GLint numVertices, GLfloat **vtxBuf, GLint *vtxStrides, GLint numIndices, GLushort *indices ) { UserData *userData = esContext->userData; // vboIds[0] - used to store vertex position // vboIds[1] - used to store vertex color // vboIds[2] - used to store element indices if ( userData->vboIds[0] == 0 && userData->vboIds[1] == 0 && userData->vboIds[2] == 0 ) { // Only allocate on the first draw glGenBuffers ( 3, userData->vboIds ); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] ); glBufferData ( GL_ARRAY_BUFFER, vtxStrides[0] * numVertices, vtxBuf[0], GL_STATIC_DRAW ); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[1] ); glBufferData ( GL_ARRAY_BUFFER, vtxStrides[1] * numVertices, vtxBuf[1], GL_STATIC_DRAW ); glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2] ); glBufferData ( GL_ELEMENT_ARRAY_BUFFER, sizeof ( GLushort ) * numIndices, indices, GL_STATIC_DRAW ); } glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] ); glEnableVertexAttribArray ( VERTEX_POS_INDX ); glVertexAttribPointer ( VERTEX_POS_INDX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, vtxStrides[0], 0 ); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[1] ); glEnableVertexAttribArray ( VERTEX_COLOR_INDX ); glVertexAttribPointer ( VERTEX_COLOR_INDX, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, vtxStrides[1], 0 ); glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2] ); 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 = esContext->userData; // 3 vertices, with (x,y,z) ,(r, g, b, a) per-vertex GLfloat vertexPos[3 * VERTEX_POS_SIZE] = { 0.0f, 0.5f, 0.0f, // v0 -0.5f, -0.5f, 0.0f, // v1 0.5f, -0.5f, 0.0f // v2 }; GLfloat color[4 * VERTEX_COLOR_SIZE] = { 1.0f, 0.0f, 0.0f, 1.0f, // c0 0.0f, 1.0f, 0.0f, 1.0f, // c1 0.0f, 0.0f, 1.0f, 1.0f // c2 }; GLint vtxStrides[2] = { VERTEX_POS_SIZE * sizeof ( GLfloat ), VERTEX_COLOR_SIZE * sizeof ( GLfloat ) }; // Index buffer data GLushort indices[3] = { 0, 1, 2 }; GLfloat *vtxBuf[2] = { vertexPos, color }; glViewport ( 0, 0, esContext->width, esContext->height ); glClear ( GL_COLOR_BUFFER_BIT ); glUseProgram ( userData->programObject ); DrawPrimitiveWithVBOs ( esContext, 3, vtxBuf, vtxStrides, 3, indices ); } void Shutdown ( ESContext *esContext ) { UserData *userData = esContext->userData; glDeleteProgram ( userData->programObject ); glDeleteBuffers ( 3, userData->vboIds ); } int esMain ( ESContext *esContext ) { esContext->userData = malloc ( sizeof ( UserData ) ); esCreateWindow ( esContext, "Example 6-6", 320, 240, ES_WINDOW_RGB ); if ( !Init ( esContext ) ) { return GL_FALSE; } esRegisterShutdownFunc ( esContext, Shutdown ); esRegisterDrawFunc ( esContext, Draw ); return GL_TRUE; }

    5)顶点数组对象(VAO)

    VAO是OES2.0不存在,3.0引入的。可以将VBO的当前缓冲区绑定,索引缓冲区绑定,和缓冲区数据块解析的glVertexAttribPointer 格式,启动顶点数组属性索引等信息绑定,绘制时候只需要一个glBindVertexArray调用就可以了减少API调用提高性能,但是初始化VBO和填充VBO的glBufferData却不能封装。删除用glDeleteVertexArrays()。例如: #include "esUtil.h" typedef struct { // Handle to a program object GLuint programObject; // VertexBufferObject Ids GLuint vboIds[2]; // VertexArrayObject Id GLuint vaoId; } UserData; #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 = 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; }

    6)映射缓冲区对象

    CPU中数据 可以用glBufferData,更新用glBufferSubData将数据传递到GPU内存中。也可以用glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)通过内存共享的方式获取GPU内存中数据地址(获取或写入全部或部分),用共享内存相比glBufferData, glBufferSubData更加高效,例如: GLfloat * vtxMappedBuf = ( GLfloat * ) glMapBufferRange ( GL_ARRAY_BUFFER, 0, vtxStride * numVertices, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT ); 使用标志:#define GL_MAP_READ_BIT 0x0001 读取返回指针数据 #define GL_MAP_WRITE_BIT 0x0002 写入数据到返回指针内存 glUnmapBuffer用于完成缓冲区数据的更新(会刷新整个缓冲区)和释放映射指针: glUnmapBuffer ( GL_ARRAY_BUFFER ) == GL_FALSE 可以用glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length);来刷新一个子区域,提高性能,前提是创建缓冲区映射时候用#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 标志创建缓冲区映射对象指针。顶点和索引缓冲区都可以使用,例如如下: #include "esUtil.h" #include <string.h> typedef struct { // Handle to a program object GLuint programObject; // VertexBufferObject Ids GLuint vboIds[2]; } UserData; #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 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; // Create the program object programObject = esLoadProgram ( vShaderStr, fShaderStr ); if ( programObject == 0 ) { return GL_FALSE; } // Store the program object userData->programObject = programObject; userData->vboIds[0] = 0; userData->vboIds[1] = 0; glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f ); return GL_TRUE; } void DrawPrimitiveWithVBOsMapBuffers ( ESContext *esContext, GLint numVertices, GLfloat *vtxBuf, GLint vtxStride, GLint numIndices, GLushort *indices ) { 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 ) { GLfloat *vtxMappedBuf; GLushort *idxMappedBuf; // Only allocate on the first draw glGenBuffers ( 2, userData->vboIds ); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] ); glBufferData ( GL_ARRAY_BUFFER, vtxStride * numVertices, NULL, GL_STATIC_DRAW ); vtxMappedBuf = ( GLfloat * ) glMapBufferRange ( GL_ARRAY_BUFFER, 0, vtxStride * numVertices, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT ); if ( vtxMappedBuf == NULL ) { esLogMessage ( "Error mapping vertex buffer object." ); return; } // Copy the data into the mapped buffer memcpy ( vtxMappedBuf, vtxBuf, vtxStride * numVertices ); // Unmap the buffer if ( glUnmapBuffer ( GL_ARRAY_BUFFER ) == GL_FALSE ) { esLogMessage ( "Error unmapping array buffer object." ); return; } // Map the index buffer glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1] ); glBufferData ( GL_ELEMENT_ARRAY_BUFFER, sizeof ( GLushort ) * numIndices, NULL, GL_STATIC_DRAW ); idxMappedBuf = ( GLushort * ) glMapBufferRange ( GL_ELEMENT_ARRAY_BUFFER, 0, sizeof ( GLushort ) * numIndices, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT ); if ( idxMappedBuf == NULL ) { esLogMessage ( "Error mapping element array buffer object." ); return; } // Copy the data into the mapped buffer memcpy ( idxMappedBuf, indices, sizeof ( GLushort ) * numIndices ); // Unmap the buffer if ( glUnmapBuffer ( GL_ELEMENT_ARRAY_BUFFER ) == GL_FALSE ) { esLogMessage ( "Error unmapping element array buffer object." ); return; } } 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 = esContext->userData; // 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 }; glViewport ( 0, 0, esContext->width, esContext->height ); glClear ( GL_COLOR_BUFFER_BIT ); glUseProgram ( userData->programObject ); DrawPrimitiveWithVBOsMapBuffers ( esContext, 3, vertices, sizeof ( GLfloat ) * ( VERTEX_POS_SIZE + VERTEX_COLOR_SIZE ), 3, indices ); } void Shutdown ( ESContext *esContext ) { UserData *userData = esContext->userData; glDeleteProgram ( userData->programObject ); glDeleteBuffers ( 2, userData->vboIds ); } int esMain ( ESContext *esContext ) { esContext->userData = malloc ( sizeof ( UserData ) ); esCreateWindow ( esContext, "MapBuffers", 320, 240, ES_WINDOW_RGB ); if ( !Init ( esContext ) ) { return GL_FALSE; } esRegisterShutdownFunc ( esContext, Shutdown ); esRegisterDrawFunc ( esContext, Draw ); return GL_TRUE; }

    7)缓冲区间数据的复制

    除了CPU传递更新数据到GPU渲染缓冲区,也可以复制GPU中的数据重复利用,可以使用: glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size)实现,readTarget和writeTarget是以下标志,具体是哪个缓冲区实例是最近绑定的一个: GL_ARRAY_BUFFER GL_ELEMENT_ARRAY_BUFFER GL_COPY_READ_BUFFER GL_COPY_WRITE_BUFFER GL_PIXEL_PACK_BUFFER GL_PIXEL_UNPACK_BUFFER GL_TRANSFORM_FEEDBACK_BUFFER GL_UNIFORM_BUFFER 上面的任何缓冲区数据都可以不改变格式复制到GL_COPY_READ_BUFFER和GL_COPY_WRITE_BUFFER中,这两个类型的缓冲区也可以有很多个实例,具体哪个就是最近绑定的。

    二、通过uniform,程序传递到Shader中,Shader中只读的数据(顶点和片段均可访问)

    Shader一些频繁变换的变量,或者纹理光照材质信息的传入,一般都是通过uniform变量或块。

    1)默认的统一变量类型,不可以程序对象间共享

    统一变量被存储在GPU硬件中,这个区域被称作“常量存储区”,所以数量有限制,OES3.0中顶点是256个向量,片段是224个向量。 Shader全局变量用uniform关键字修饰,uniform只能在app中修改,vertex和fragment shader只可以使用,GLSL1.3和1.4都一样 如果uniform变量在vertex和fragment两者之间声明方式完全一样,则它可以在vertex和fragment共享使用,uniform变量一般用来表示:变换矩阵,材质光照参数和颜色等信息 查询当前活动的uniform变量: glGetProgramiv (programObject, GL_ACTIVE_UNIFORMS, &numUniforms); glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); 获取unitform变量: glGetUniformLocation (programId, "offset"); 设置app中设置的值: glUniform2f (offsetLocationId, fXOffset, fYOffset);这里不使用programId是因为在glUseProgram下工作,前面已经指定了当前的活动program指针对象。

    2)统一变量块,由统一变量缓冲区对象支持,程序间可共享

    所有类型,除了采样器,都允许放到一个uniform块中。此外,uniform块必须声明为全局作用域 Uniform块支持更多的顶点和片段数量,在统一缓冲区对象间切换,比单独加载一个个Uniform变量更加高效。 uniform块的布局限定符 shared 指定uniform块在多个程序对象之间共享(默认的共享设置)。 packed 布局uniform块使得使用的内存最小化,通常不允许跨程序共享。 std140 使用opengl规范中默认的布局。 row_major 使得uniform块中的矩阵按照行主序的方式存储。 column_major 指定矩阵应该按照列主序的方式存储(这是默认的排序)。 (1)uniform块的布局应用: layout(shared, row_major) uniform uniformBlockName {...}; // 只是声明该uniform块的布局 (2)影响所有后续的uniform布局可以使用: layout(shared, row_major) uniform;该行之后声明的所有uniform都会使用该布局,直到修改或覆盖uniform声明位置。 (3)layout也 可以在内部成员重载外部uniform块的布局例如: layout(std140) uniform TransformBlock { mat4 matViewProj; layout(row_major) mat3 matNormal; mat3 matTexGen; } shader内部访问uniform块变量: 访问uniform块中的变量,uniform块的uniform符号并不作为变量作用域,所以两个相同的Uniform块中声明相同的uniform变量将会导致错误, 故Shader内部访问uniform变量的时候没有必要使用uniform块的名字 程序app访问Shader中的uniform块变量: uniform块,通过块标识和查询到成员的,indice, offset, type, size信息,并进行memcpy赋值操作。 1) glGetUniformBlockIndex 获取uniform块索引号。 2)用 glGetActiveUniformBlockiv ()使用GL_UNIFORM_BLOCK_DATA_SIZE返回编译生成的uniform block 大小。 3)用malloc申请内存块,获取uniform块中的各个成员类型和大小,程序中的值填写malloc内存块 glGetUniformIndices (GLuint program, GLsizei uniformCount, // 输入的个数 const GLchar **uniformNames, // 输入的名字 GLuint *uniformIndices); // 输出的索引号 获取这个特定的索引的offset,size,type(或者每个成员的该值调用一次): glGetActiveUniformsiv — Returns information about several active uniform variables for the specified program object void glGetActiveUniformsiv( GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); 4) 使用glGenBuffers/glBindBuffer/glBufferData填充到缓冲区 5)调用glBindBufferRange/glBindBufferBase将unform块和帧缓冲区对象联系起来 6)如果有多个着色器程序共享一个uniform块,为了避免它为每个程序分配一个不同的块索引,在调用glLinkProgram之前要调用 glUniformBlockBinding ()来绑定块。 实例: "uniform MyUniforms = { vec3 translation; // 平移 float scale; // 缩放 vec4 rotation; //四元数 bool enabled; } // 顶点或片段着色器中直接应用translation即可,不需要MyUniforms限制。 " uboIndex = glGetUniformBlockIndex (program, "Uniforms"); glGetActiveUniformBlockiv (program, uboIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &uboSize); GLvoid *buffer = malloc(uboSize); const char *names[NumUniformMembers] = { "translation", "scale", "rotation", "enabled", }; enum { Translation, Scale, Rotation, Enabled, NumUniformMembers } // 得到每个成员的indices,size,offset, type;用于填充CPU中的memory buffer数据,然后用 glBufferData传输 到GPU VBO中。 GLuint indices[NumUniformMembers]; GLuint size[NumUniformMembers]; GLuint offset[NumUniformMembers]; GLuint type[NumUniformMembers]; glGetUniformIndices (program, NumUniformMembers, names, indices); glGetActiveUniformsiv (program, NumUniformMembers, indices, GL_UNIFORM_OFFSET, offset); glGetActiveUniformsiv(program, NumUniformMembers, indices, GL_UNIFORM_SIZE, offset); glGetActiveUniformsiv(program, NumUniformMembers, indices, GL_UNIFORM_TYPE, offset); 使用memcpy将CPU应用程序中的具体变量值通过size,type字节数赋值给buffer中。 void *memcpy(void *dest, const void *src, size_t n); memcpy(buffer + offset[Scale], &scale, size[Scale] * TypeSize(type[Scale])); memcpy(buffer + offset[Translation], &scale, size[Translation] * TypeSize(type[Translation])); memcpy(buffer+ offset[Rotation], &scale, size[Rotation] * TypeSize(type[Rotation])); memcpy(buffer+ offset[Enabled], &scale, size[Enabled] * TypeSize(type[Enabled])); 填充满buffer后。 glGenBuffers(1,&ubo); glBindBuffer(GL_UNIFORM_BUFFER, ubo); glBufferData(GL_UNIFORM_BUFFER, uboSize, buffer, GL_STATIC_RAW); glBindBufferBase(GL_UNIFORM_BUFFER, uboIndex, ubo);

    三、如何设置顶点,纹理以外的数据到Shader中

    例如光源信息(类型,位置,光照颜色,镜面反色顶点光泽度,衰减属性),雾信息(颜色,衰减距离,衰减模型),阴影需要的深度纹理或投影计算信息。除了计算出来的以外,光源和雾的信息CPU如何设置到Shader中? 答案:用Uniform变量传输,HLSL中也是通过常量表来传输,包括投影矩阵;光照的开启,雾开启关闭等OGL渲染状态信息直接写在CG/HLSL/GLSL中即可;阴影数据也是类似的uniform传输变量,主要是对顶点数据进行投影运算或用深度纹理处理,CG中有实现阴影算法直接在shader中开启。 例如: 光照的类型通过位置是否为w==0来判断是否为平行光,光源的diffuseColor, specularColor, ambientColor都通过颜色传递到Shader中,衰减系数,镜面反色光泽度都是通过常量表设置到Shader中的。 例如DX中: ToonWorldViewHandle     = ToonConstTable->GetConstantByName(0, "WorldViewMatrix"); ToonWorldViewProjHandle = ToonConstTable->GetConstantByName(0, "WorldViewProjMatrix"); ToonColorHandle         = ToonConstTable->GetConstantByName(0, "Color"); ToonLightDirHandle      = ToonConstTable->GetConstantByName(0, "LightDirection"); OutlineWorldViewHandle = OutlineConstTable->GetConstantByName(0, "WorldViewMatrix"); OutlineProjHandle      = OutlineConstTable->GetConstantByName(0, "ProjMatrix"); // // Set shader constants: // // Light direction: D3DXVECTOR4 directionToLight(-0.57f, 0.57f, -0.57f, 0.0f); ToonConstTable->SetVector( Device,  ToonLightDirHandle, &directionToLight); 固定管线中雾设置: glEnable(GL_FOG);    {       GLfloat fogColor[4] = {0.5, 0.5, 0.5, 1.0};       fogMode = GL_EXP;       glFogi (GL_FOG_MODE, fogMode);       glFogfv (GL_FOG_COLOR, fogColor);       glFogf (GL_FOG_DENSITY, 0.35);       glHint (GL_FOG_HINT, GL_DONT_CARE);       glFogf (GL_FOG_START, 1.0);       glFogf (GL_FOG_END, 5.0);    } Shader中雾的设置,绘制物体时候进行该设置即可,直到覆盖定义为止: FogVertexMode = LINEAR; // linear fog function         FogStart      = 50.0f;  // fog starts 50 units away from viewpoint         FogEnd        = 300.0f; // fog ends 300 units away from viewpoint         FogColor      = 0x00CCCCCC; // gray         FogEnable     = true;       // enable
    转载请注明原文地址: https://ju.6miu.com/read-834.html

    最新回复(0)