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

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

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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值