overview
- 用
glCreateShader
创建一个shader,可以指定shader的类型等信息。 - 通过
glShaderSource
导入shader的源文件。 - 通过
glCompileShader
编译指定的shader。 - 通过
glCreateProgram
创建一个program。 - 通过
glAttachShader
来将一对shader绑定到一个program中。 - 通过
glLinkProgram
来将这对shader链接到该program中。 - 通过
glUseProgram
来使用该program。 - 如果shader使用了默认的uniform块,用
glGetUniformLocation
来查询uniform变量的地址,并且通过glUniform
来设置它们的值。 - 如果shader使用了命名的uniform块,用
glGetUniformBlockIndex
和glGetActiveUniformBlockiv
来查询该uniform块的信息。查询uniform块中uniform变量的偏移值是通过glGetActiveUniformsiv
来得到的。uniform块和绑定一些点(points)的buffer object的关联是通过glUniformBlockBinding
来完成的。且,buffer object到这些点的绑定是通过glBindBufferRange
来完成的。 - 如果vertex shader使用了用户定义的变量,application必须提供之,这是通过opengl api的调用来定位vertex attribute位置的。在一个vertex shader中,在这些属性数据被传给shader之前,泛型顶点属性的索引应该和一个变量相关联,这通常有2种方法。application可以显式创建之,通过在链接(linking)之前调用
glBindAttribLocation
。或者,如果没有显式的关联被创建,opengl会在linking这步自动关联。一个application可以查询这种关联,通过调用glGetAttribLocation
。所以,泛型的顶点属性可以通过glVertexAttrib
来传递给vertex shader;或者使用glVertexAttribPointer
和glEnableVertexArrayPointer
结合标准的opengl命令来描画vertex arrays。 - 如果fragment shader使用了用户定义的输出(out)变量,application可能要创建用户定义的变量和fragment数据索引之间的关联,这通过在linking前调用
glBindFragDataLocation
来完成。或者,如果没有显式的关联,opengl会在linking时自动进行关联。一个application可以查询赋值,通过glGetFragDataLocation
。 - 一个application可能选择性地记录vertex shader的输出(out)变量到一个或多个可以tranform feedback的buffer。在vertex shader输出变量被记录前,vertex shader输出变量通过
glTransformFeedbackVarying
和绑定一些点的feedback buffer相关联;且buffer对象和这些点的绑定是通过glBindBufferRange
来完成的。
7.1 obtaining version information
OpenGL和OpenGL shading language的版本号均可通过glGetString
来获得。
void getGlVersion(int *major, int *minor)
{
const char *verstr = (const char *) glGetString(GL_VERSION);
if ((verstr == NULL) || (sscanf(verstr, "%d.%d", major, minor) != 2))
{
*major = *minor = 0;
fprintf(stderr, "Invalid GL_VERSION format!!!\n");
}
}
void getGlslVersion(int *major, int *minor)
{
int gl_major, gl_minor;
getGlVersion(&gl_major, &gl_minor);
*major = *minor = 0;
if (gl_major == 1)
{
const char *extstr = (const char *) glGetString(GL_EXTENSIONS);
if ((extstr != NULL) &&
(strstr(extstr, "GL_ARB_shading_language_100") != NULL))
{
*major = 1;
*minor = 0;
}
}
else if (gl_major >= 2)
{
const char *verstr = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
if ((verstr == NULL) ||
(sscanf(verstr, "%d.%d", major, minor) != 2))
{
*major = *minor = 0;
fprintf(stderr, "Invalid GL_SHADING_LANGUAGE_VERSION format!!!\n");
}
}
}
7.2 creating shader objects
创建shader的函数原型:
GLuint glCreateShader(GLenum shaderType) // GL_VERTEX_SHADER/GL_FRAGMENT_SHADER
然后指定shader的源文件:
void glShaderSource(GLuint shader,
GLsizei count, // 源码字符串数组的个数
const GLchar **string, // 源码字符串数组
const GLint *length) // 如果为NULL,源码字符串以null为终止符;否则指定了字符串数组中指定字符串的大小。
7.3 compiling shader objects
当源码字符串被导入shader object后,源码必须被编译,且编译结果被shader object保持:
void glCompileShader(GLuint shader)
编译结果为GL_TRUE
或者GL_FALSE
,它作为一部分来存储在shader object中。该状态可通过以GL_COMPILE_STATUS
为参数调用glGetShader
来查询。
一个shader的编译可能有词法、语法、语义的错误。无论编译是否正确,编译信息可通过glGetShaderInfoLog
来查看,但不可作为编译成功与否的说明。只是保留了编译shader的编译信息而已。
7.4 linking and using shaders
每个shader object都是被独立编译的。为了创建一个program,application需要制定需要被绑定(attach)进来的shader object的清单。
先创建一个program object:
GLuint glCreateProgram(void)
program object会检查这些shader的完整性等。不再使用的shader object可以从program中解绑定(detach)。
void glAttachShader(GLuint program,
GLuint shader)
绑定的shader数目没有天生的限制,绑定可以在shader被导入或者被编译前被绑定。换句话说,该函数只是简单的制定了被链接的shader清单而已,依赖检查等工作由后续函数完成。
为了创建一个有效的program,所有被绑定的shader object必须被编译,且program object自己也必须链接进来。链接操作分配了uniform变量的位置,初始化用户定义的uniform,解决独立编译的shader object间的引用依赖,并且检查确保vertex shader和fragment shader相互兼容。
void glLinkProgram(GLuint program)
同样的,查询该阶段的结果状态可通过glGetProgram
来完成,查询参数为GL_LINK_STATUS
。成功链入后,所有属于该program object的active的用户定义的uniform变量都被初始化为0。并且所有active的uniform变量都被分配了位置,该位置可通过glGetUniformLocation
来查询。
成功链入后,所有属于program的active的用户定义的uniform变量会被初始化为0,且每个program对象的active的uniform变量会被分配一个location,该location可用glGetUniformLocation
来查询。并且,任何还没有绑定到一个generic vertex attribute index的active的用户定义的attribute变量会在这时进行绑定。
如果program没有成对包含顶点和片元shader,会链接到隐式的固定管线以凑成一对。
如果链接成功,glLinkProgram
还会install产生的可执行程序作为渲染状态的一部分。然后通过glUseProgram
来使用该链接后的program。如果当前使用的program对象relink不成功,其链接状态被设置为GL_FALSE
,但是之前生成的可执行程序和相关的状态将会保持,直到再次调用glUseProgram
才会移除它们。只有成功relink后,可执行程序和相关的状态才会作为当前状态的一部分。
链接program对象失败的几种原因:
- active attribute变量超出数量
- active uniform变量超出数量
- shader缺少main函数
- fragment shader的in变量类型与对应的vertex shader的out变量类型不匹配
- 对函数或变量的引用没有解析
- 共享的全局变量被声明为两种不同的类型或不同的初始化值
- 一个或多个被attach的shader对象没有被成功编译
- 绑定一个generic attribute矩阵导致矩阵的一些行超出GL_MAX_VERTEX_ATTRIBS
- 没有足够的顶点属性槽来绑定属性矩阵
program对象的信息log在link的时候被更新。如果link操作成功,一个program就会被创建。它可能包含一个顶点处理器的可执行程序,一个片元处理器的可执行程序,或者二者全部。无论link操作成功与否,上一次link操作的信息和可执行程序都会丢失。link操作之后,application就可以自由改变绑定的shader对象,编译绑定的shader对象,解绑shader对象,绑定额外的shader对象。这些操作均不会影响信息log或该program直到该program对象的下一次link操作。
关于link操作的信息可以通过以program为参数调用glGetProgramInfoLog
来得到。如果该program对象被成功链接,信息log或者是一个空字符串或者link操作的信息。如果program对象没有成功链接,信息log包含发生的任意link错误,还有警告信息和linker提供的信息。
当link操作成功完成,其包含的program会被安装,并作为当前渲染状态的一部分。这是通过如下命令来完成的:
void glUseProgram(GLuint program)
如果program为0,可编程处理器则被禁止,固定管线功能将用于替代顶点和片段处理器。
成功install可编程处理器会导致OpenGL的固定管线功能被禁止。
当一个program正在被使用的时候,application可以自由地修改绑定的shader对象,编译绑定的shader对象,绑定附加的shader对象,解绑shader对象,删除任意绑定的shader对象,或者删除program对象自身。这些操作均不会影响当前状态的执行。然而,relink这个当前正被使用的program对象会install这个program以成为新的当前渲染状态,如果link操作成功的话。当一个program对象正在被使用的时候,控制这些被禁止的固定管线功能的状态也能通过一般的OpenGL调用被更新。
7.5 cleaning up
当不再被使用的时候,对象需要被删除,这通过一下命令来完成:
void glDeleteShader(GLuint shader)
这个函数将会释放内存并使shader值可再用。它和glCreateShader
的效果刚好相反。
如果一个要被删除的shader对象绑定到了一个program对象,该函数将会标记它以将来删除,但是直到它不再被绑定到任何渲染上下文的任何program对象,它才被真正删除(例如,它在被删除前,必须在其被绑定的地方解绑)。
shader值为0的时候,该函数只是简单地忽略它。
为了明确一个shader对象是否被标记为待删除,可以以GL_DELETE_STATUS
为参数来调用glGetShader
而得到。
void glDeleteProgram(GLuint program)
这个函数将会释放内存并使program值可再用。它和glCreateProgram
的效果刚好相反。
如果一个program对象作为当前渲染状态正在被使用,该函数将会标记其为待删除,但是直到它不再作为任意渲染上下文的当前状态时,它才真正被删除。如果一个要删除的program有shader对象绑定到它,这些shader对象会自动解绑但不会被删除,除非它们用glDeleteShader
标记为待删除。
为了明确一个program对象是否被标记为待删除,可以以GL_DELETE_STATUS
为参数来调用glGetProgram
而得到。
当一个shader对象不再需要绑定到一个program对象的时候,它可以用如下命令来解绑:
void glDetachShader(GLuint program, GLuint shader)
该函数效果与glAttachShader
正好相反。
如果shader已经用glDeleteShader
被标记为待删除,并且没有被绑定到任何其他program对象,它会在解绑后被删除。
一个可能很有用的编程小贴士就是,在shader对象被绑定到一个program对象后尽快删除shader对象。它们不会在此时被删除,但当他们不再被引用的时候,他们被标记为待删除。稍后,为了清除干净,application只需要删除program对象。所有绑定的shader对象会自动解绑,并且,因为他们被标记为待删除,它们此时会自动被删除。
7.6 query functions
OpenGL shading language API包含几个查询对象状态的函数。为了获得一个shader对象的信息,使用如下命令:
void glGetShaderiv(GLuint shader, GLenum pname, GLint *params)
- GL_SHADER_TYPE GL_VERTEX_SHADER/GL_FRAGMENT_SHADER
- GL_DELETE_STATUS GL_TRUE/GL_FALSE
- GL_COMPILE_STATUS GL_TRUE/GL_FALSE
- GL_INFO_LOG_LENGTH info log的长度,包含null;可以为0
- GL_SHADER_SOURCE_LENGTH shader source的长度,包含null;可以为0
同样地,
void glGetProgramiv(GLuint program, GLenum pname, GLint *params)
- GL_DELETE_STATUS GL_TRUE/GL_FALSE
- GL_LINK_STATUS GL_TRUE/GL_FALSE
- GL_VALIDATE_STATUS GL_TRUE/GL_FALSE
- GL_INFO_LOG_LENGTH info log的长度,包含null;可以为0
- GL_ATTACHED_SHADERS 绑定的shader数量
- GL_ACTIVE_ATTRIBUTES active的属性变量数量
- GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 最大属性名的长度,包含null;可以为0
- GL_ACTIVE_UNIFORMS active的uniform变量数量
- GL_ACTIVE_UNIFORM_MAX_LENGTH 最大uniform名的长度,包含null;可以为0
从当前shader对象获取shader字符串的命令是:
void glGetShaderSource(GLuint shader,
GLsizei bufSize, // 最大缓存值
GLsizei *length, // 真实的源码长度,不包含null;可以为0
GLchar *source)
shader源码字符串是以null结尾的。最大缓存值可以通过以GL_SHADER_SOURCE_LENGTH
为参数来调用glGetShader
来获得。
获取shader和program的info log的命令分别如下:
void glGetShaderInfoLog(GLuint shader,
GLsizei maxLength,
GLsizei *length,
GLchar *infoLog)
void glGetProgramInfoLog(GLuint program,
GLsizei maxLength,
GLsizei *length,
GLchar *infoLog)
一个使用范例:
void printShaderInfoLog(GLuint shader) {
int infologLen = 0;
int charsWritten = 0;
GLchar *infoLog;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLen);
printOpenGLError();
if (infologLen > 1) {
infoLog = (GLchar*) malloc(infologLen);
if (infoLog == NULL) {
printf("ERROR: Could not allocate InfoLog buffer\n");
exit(1);
}
glGetShaderInfoLog(shader, infologLen, &charsWritten, infoLog);
printf("InfoLog:\n%s\n\n", infoLog);
free(infoLog);
}
printOpenGLError();
}
你还可以以GL_CURRENT_PROGRAM
来调用glGet
函数来获取当前正在使用的program对象。
查询绑定到一个特定的program对象的shader对象清单的函数是:
void glGetAttachedShaders(GLuint program,
GLsizei maxCount,
GLsizei *count,
GLuint *shaders)
可以以GL_ATTACHED_SHADERS
为参数来调用glGetProgram
从而获取真实的绑定的shader数量。
还有两个判断是否是shader和program对象的函数:
GLboolean glIsShader(GLuint shader)
GLboolean glIsProgram(GLuint program)
7.7 specifying vertex atributes
OpenGL提供了一小组泛型位置,被称为“当前顶点状态”(CURRENT VERTEX STATE),用于传递顶点属性,将v赋值给index指定的每个顶点。
void glVertexAttrib{1|2|3|4}{s|f|d}(GLuint index, TYPE v)
void glVertexAttrib{1|2|3}{s|f|d}v(GLuint index, const TYPE *v)
void glVertexAttrib4{b|s|i|f|d|ub|us|ui}v(GLuint index, const TYPE *v)
还有一组归一化版本的相关函数:
void glVertexAttribNub(GLuint index, TYPE v)
void glVertexAttribN{b|s|i|f|d|ub|us|ui}v(GLuint index, const TYPE *v)
对于mat2,mat3,mat4,也可以导入。例如,mat4需要以列为单位导入4次。
顶点数组API允许泛型顶点属性以顶点数组的形式导入:
void glVertexAttribPointer(GLuint index,
GLint size, // 1,2,3,4
GLenum type,
GLboolean normalized,
GLsizei stride,
const GLvoid *pointer)
type: GL_BYTE,GL_UNSIGNED_BYTE,GL_SHORT,GL_UNSIGNED_SHORT,GL_INT,GL_UNSIGNED_INT,GL_FLOAT,GL_DOUBLE
开启和禁止这些顶点属性需要以下函数:
void glEnableVertexAttribArray(GLuint index)
void glDisableVertexAttribArray(GLuint index)
顶点属性数组主要用于以下函数:
glArrayElement
glDrawArrays
glDrawElements
glDrawRangeElements
glMultiDrawArrays
glMultiDrawElements
那么这些数据如何在shader中使用呢?那就是要将数据绑定到shader中的一个in变量。怎么绑定呢?有两种方式:
- linker自动绑定。
- 手工绑定。
手工绑定通过以下函数:
void glBindAttribLocation(GLuint program,
GLuint index,
const GLchar *name)
可以将同一index指定的数据绑定到不同name,但反之不行。如果name是一个矩阵的变量名,则index指定的是数据的第一列,其他数据则会自动绑定到index+i。例如:对于mat2,则为index+1;mat3则为index+1和index+2;mat4则为index+1,index+2和index+3。
glBindAttribLocation
可以在顶点shader对象被绑定到特定program对象前调用;还可以绑定永远不会使用的属性变量名到一个顶点shader中。
glBindAttribLocation
可以在任意时刻来绑定变量名和数据。属性绑定不会发生作用,直到glLinkProgram
被调用,所以任何属性绑定操作需要在glLinkProgram
之前进行。当一个program对象被成功链接,属性绑定的index的值保持不变,直到下一次link操作。
GLint glGetAttribLocation(GLuint program,
const GLchar *name)
如果name是一个矩阵,则返回的index值引用绑定数据的第一列。如果指定的program对象中,name指定的属性变量不是active的或者name以保留前缀“gl_”开头,-1被返回。
例子:
// 绑定,发生在link时
glBindAttribLocation(myProgram, 1, "Opacity");
// 对相关index赋值
glVertexAttrib1f(1, opacity);
// 或者可以调用glEnableVertexAttribArray来使传递好值的绑定生效
glEnableVertexAttribArray(1)
又例如:
glBindAttribLocation(myProgram, 1, "Opacity");
glBindAttribLocation(myProgram, 2, "Binormal");
glBindAttribLocation(myProgram, 3, "MyData");
当一个顶点shader被执行的时候,可访问到的属性变量被称为“ACTIVE ATTRIBUTES”。可以使用以下命令来获取active attribute的信息:
void glGetActiveAttrib(GLuint program,
GLuint index,
GLsizei bufSize,
GLsizei *length,
GLint *size,
GLenum *type,
GLchar *name)
可以通过参数GL_ACTIVE_ATTRIBUTES
调用glGetProgram
来获得active的属性数量。index为0会导致该函数返回第一个active的属性信息。index的有效值为[0,N-1]。
program参数要求,需要在之前调用一次glLinkProgram
。
bufSize可以通过参数GL_ACTIVE_ATTRIBUTE_MAX_LENGTH
调用glGetProgram
来获取,从而得到属性名字符串的最大长度。属性名字符串以null结尾。
type返回属性变量的数据类型,包括GL_FLOAG
,GL_FLOAT_VEC2
,GL_FLOAT_VEC3
,GL_FLOAT_VEC4
,GL_FLOAT_MAT2
,GL_FLOAT_MAT3
,GL_FLOAT_MAT4
。
size返回类型为type的数据的单位大小。
active attribute变量清单可能包含内置的属性变量名,例如以gl_
开头的,同时也包含用户定义的属性变量名。
如果链接步骤出错,length可能为0;如果有错误发生,返回值length,size,type,name是不会被修改的。
glGetActiveAttrib
在shader开发和application开发分开进行的时候是非常有用的。这是因为在shader开发侧和application开发侧的属性变量命名习惯上有一致的地方,application侧可以在运行时通过该函数来查询,从而确定该使用哪个属性变量。
查询特殊的泛型顶点属性的状态,可以通过下面的函数来得到:
void glGetVertexAttribfv(GLuint index,
GLenum pname,
GLfloat *params)
void glGetVertexAttribiv(GLuint index,
GLenum pname,
GLint *params)
void glGetVertexAttribdv(GLuint index,
GLenum pname,
GLdouble *params)
下面的panme枚举值,除了GL_CURRENT_VERTEX_ATTRIB
,均表示client端的状态。
- GL_VERTEX_ATTRIB_ARRAY_ENABLED 0代表false;非0代表true;初始值为GL_FALSE
- GL_VERTEX_ATTRIB_ARRAY_SIZE 1,2,3,4;初始值为4
- GL_VERTEX_ATTRIB_ARRAY_STRIDE 初始值为0
- GL_VERTEX_ATTRIB_ARRAY_TYPE 见上;初始值GL_FLOAT
- GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0代表false;非0代表true;初始值为GL_FALSE
- GL_CURRENT_VERTEX_ATTRIB 当index为0会发生错误;初始值(0,0,0,1)
另外,
void glGetVertexAttribPointerv(GLuint index,
GLenum pname,
GLvoid **pointer)
pname的唯一参数为GL_VERTEX_ATTRIB_ARRAY_POINTER
。
7.8 specifying uniform variables
对于属性变量,application可以在link发生前指定属性位置;相反地,uniform的位置或偏移值不能被application指定。他们只能由OpenGL在链接时决定,从而application需要在链接发生后再查询uniform的位置或偏移值。
uniform变量被集中到uniform块来管理。有两种uniform块,一种是默认的,一种是命名的。
7.8.1 default uniform block
默认uniform快包含所有没有包含在命名uniform块的uniform变量。任何sampler类型的uniform必须在默认uniform块中。
默认uniform块的一大优点就是uniform存储与program对象相关,所以默认uniform块对于shader里的独有uniform变量是最佳的。一大缺点就是改变uniform变量的值相对昂贵,所以默认uniform块里的uniform变量最好不经常改变。
为了更新用户定义的uniform变量,application需要得到它的位置,然后才能赋值。uniform变量的位置是在link时确定的,并且不会改变,直到下一次link。
GLint glGetUniformLocation(GLuint program,
const GLchar *name)
name以null结尾,并且中间不能有空白字符。name不能是一个结构体,结构体数组,向量的子元素或矩阵的名字。
以下情况,该函数会返回-1:
- name没有和program的一个active uniform变量对应
- name与一个命名uniform块关联
- name以保留前缀“gl_"开头
name中可以使用”[]“和"."以查询数组和结构体。
导入用户定义的uniform值只可能当program对象正在使用时才会发生。所有用户定义的uniform变量都会在program对象被成功链接时初始化,或者是显式给定的默认值或者为0。用户定义的uniform值是当前program对象状态的一部分。他们的值只可能当program对象是当前渲染状态的一部分时才会被修改,但是program会在换入/换出当前渲染状态的时候保留其值。见如下函数:
void glUniform{1|2|3|4}{f|i}(GLint location, TYPE v)
对于bool值,f|i均可;0/0.0为false,其他为true。
void glUniform{1|2|3|4}{f|i}v(GLint location,
GLuint count,
const TYPE v)
void glUniformMatrix{2|3|4}fv(GLint location,
GLuint count,
GLboolean transpose,
const GLfloat *v)
transpose为GL_FALSE
,列序;GL_TRUE
行序。
glUniform1i
和glUniform1iv
是唯一的两个来导入sampler类型的uniform变量的函数。试图用其他函数来导入会导致一个错误。
如下几点原因会导致glUnifom
出错:
- 没有正在使用的program对象
- 对于当前program对象,location是不合法的uniform变量位置
- count超出uniform变量/数组的界限
- shader中定义的uniform变量的类型和大小与该函数中值的不一致
如下函数可以查询指定位置的uniform变量的当前值:
void glGetUniformfv(GLuint program,
GLint location,
GLfloat *params)
void glGetUniformiv(GLuint program,
GLint location,
GLint *params)
例如:
uniform struct {
struct {
float a;
float b[10];
} c[2];
vec2 d;
} e;
loc1 = glGetUniformLocation(progObj, "e.d"); // is valid
loc2 = glGetUniformLocation(progObj, "e.c[0]"); // is not valid
loc3 = glGetUniformLocation(progObj, "e.c[0].b"); // is valid
loc4 = glGetUniformLocation(progObj, "e.c[0].b[2]"); // is valid
glUniform2f(loc1, 1.0f, 2.0f); // is valid
glUniform2i(loc1, 1, 2); // is not valid
glUniform1f(loc1, 1.0f); // is not valid
glUniform1fv(loc3, 10, floatPtr); // is valid
glUniform1fv(loc4, 10, floatPtr); // is not valid
glUniform1fv(loc4, 8, floatPtr); // is valid
当一个shader被执行的时候,可被访问到的uniform变量称为”ACTIVE UNIFORMS“。
如下函数获得一个program对象的active uniform变量的清单:
void glGetActiveUniform(GLuint program,
GLuint index,
GLsizei bufSize,
GLsizei *length,
GLint *size,
GLenum *type,
GLchar *name)
uniform变量名的最大字符串长度可以通过参数GL_ACTIVE_UNIFORM_MAX_LENGTH
调用glGetProgram
来得到。
type可以为:GL_FLOAT,GL_FLOAT_VEC2,GL_FLOAT_VEC3,GL_FLOAT_VEC4,GL_INT,GL_INT_VEC2,GL_INT_VEC3,GL_INT_VEC4,GL_BOOL,GL_BOOL_VEC2,GL_BOOL_VEC3,GL_BOOL_VEC4,GL_FLOAT_MAT2,GL_FLOAT_MAT3,GL_FLOAT_MAT4,GL_SAMPLER_1D,GL_SAMPLER_2D,GL_SAMPLER_3D,GL_SAMPLER_CUBE,GL_SAMPLER_1D_SHADOW,GL_SAMPLER_2D_SHADOW
使用glGetActiveUniform
,application开发者可以编程查询在shader中真实使用的uniform变量,并且自动创建用户接口以使最终用户可以修改这些uniform变量。
7.8.2 named uniform blocks
OpenGL 3.1加入了命名uniform块和相关的OpenGL shading language API。命名uniform块是以uniform buffer对象为支撑的。这样用buffer数据操作命令或buffer对象映射操作命令,更新uniform就会更高效。一个完整的uniform buffer对象可以快速被改变,这是通过改变与其绑定的buffer对象来达到的。
uniform变量集合(除了sampler类型的uniform)可以被聚集到一个命名的uniform块中。例如:
uniform MatrixBlock {
uniform mat4 MVmatrix;
uniform mat4 MVPmatrix;
uniform mat3 NormalMatrix;
};
命名uniform块的名字是MatrixBlock
,它包含了3个uniform变量。
要想更新命名uniform块中的uniform变量,需要知道它们的偏移值,然后才能赋值。偏移值是在link时赋值的,且不会改变,直到下一次link。
GLuint glGetUniformBlockIndex(GLuint program,
const GLchar* name)
如果name没有成功指定一个active的uniform块,GL_INVALID_INDEX
将会返回。
uniform块的信息也可被查询:
GLuint glGetUniformBlockiv(GLuint program,
GLuint index,
GLenum pname,
GLint *params)
pname参数为:
- GL_UNIFORM_BLOCK_BINDING
- GL_UNIFORM_BLOCK_DATA_SIZE
- GL_UNIFORM_BLOCK_NAME_LENGTH
- GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS
- GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES
- GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER
- GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER
然后,在命名uniform块中每个uniform变量的信息就可以查询了。
GLuint glGetActiveUniformsiv(GLuint program,
GLsizei count,
const GLuint *indices,
GLenum pname,
GLint *params)
pname参数如下:
- GL_UNIFORM_TYPE
- GL_UNIFORM_SIZE
- GL_UNIFORM_NAME_LENGTH
- GL_UNIFORM_BLOCK_INDEX
- GL_UNIFORM_OFFSET
- GL_UNIFORM_ARRAY_STRIDE
- GL_UNIFORM_MATRIX_STRIDE
- GL_UNIFORM_MATRIX_IS_ROW_MAJOR
link一个shader后,每个命名uniform块可能被绑定到一个uniform buffer绑定点:
void glUniformBlockBinding(GLuint program,
GLuint index,
GLuint index,
GLuint binding)
index必须是一个active的命名uniform块,否则INVALID_VALUE
就会产生。
7.9 samplers
glUniform1i
和glUniform1iv
导入定义为sampler类型的uniform变量(例如,sampler1D/sampler2D/sampler3D/samplerCube/sampler1DShadow/sampler2DShadow)。它们可以定义在顶点或片段shader中。
sampler的值用于访问特定的纹理map,其值应小于通过参数GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS
或GL_MAX_TEXTURE_IMAGE_UNITS
调用glGet
查询到的数值。如果超出了上限,link的时候会失败。对于顶点和片段shder二者的数值上限可以通过GL_COMBINED_TEXTURE_IMAGE_UNITS
来查询到。
当一个program正在执行的时候,可被访问到的sampler被称为“ACTIVE SAMPLERS”。
7.10 multiple render targets
OpenGL 2.0添加的一个特性就是可以同时渲染多个buffer。OpenGL shading language以用户定义的片段shader的out变量来支持这个功能。一个片段shader的数据buffer的数量是与实现相关的,但是不能少于4个。
应用此功能,application可以开发片段shader以使得每个片段shader可以计算多个值并且将它们存储到离屏的内存中。这些值可以在将来的渲染中被访问到。这可以被充分利用从而使得applicaiton实现复杂的多值传递算法并且使用图形硬件用于一般目的的计算。
void glDrawBuffers(GLsizei n, const GLenum *bufs)
该函数定义了片段shader的数据写入的buffer数组。
bufs的数值为:
- GL_NONE
- GL_COLOR_ATTACHMENTi
用参数GL_MAX_DRAW_BUFFERS
调用glGet
以获取最大的draw buffer数量。
用户定义的片段shader的out变量可能与数据buffer绑定,这通过如下命令实现:
void glBindFragDataLocation(GLuint program,
GLuint index,
const GLchar *name)
index必须是active的用户定义的out变量,否则INVALID_VALUE
会产生。
7.11 development aids
void glValidateProgram(GLuint program)
该函数产生的信息存储在program的信息log中。用参数GL_VALIDATE_STATUS
来调用glGetProgram
可以查看该函数的运行结果,GL_TRUE/GL_FALSE。典型地,该函数在开发的时候非常有用。
7.12 implementation-dependent API values
以下是一些实现中的限制,均可用glGet
查询,有些是在OpenGL 3.0加入的。
GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
: 顶点处理器和片段处理器绑定的用于访问纹理map的数量,最小值32。
GL_MAX_DRAW_BUFFERS
: 片段处理器的out变量所写入的最大数量的buffer,最小值为8,OpenGL 3.0更倾向于是1。
GL_MAX_FRAGMENT_UNIFORM_COMPONENTS
: 片段shader中uniform变量可访问到的最大的component。最小值1024,过去是64。
GL_MAX_TEXTURE_IMAGE_UNITS
: 片段shader中能访问到的最大的纹理map硬件单元。最小值是16,过去是2。
GL_MAX_VARYING_COMPONENTS
: 最小值64,过去是32位浮点值。
GL_MAX_VERTEX_ATTRIBS
: 最大active的顶点属性。最小值16。
GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS
: 最小值16,过去是0。
GL_MAX_VERTEX_UNIFORM_COMPONENTS
: 顶点shader默认uniform块可访问到的最大的component的数量。最小值1024,过去为512。
GL_MAX_CLIP_DISTANCES
: 每个program可使用的clip distance的数量。最小值8。
GL_MAX_COMBINED_UNIFORM_BLOCKS
: 每个program可使用的uniform块的数量。最小值24。
GL_MAX_FRAGMENT_UNIFORM_BLOCKS
: 针对fragment,其他同上。
GL_MAX_VERTEX_UNIFORM_BLOCKS
: 针对vertex,其他同上。
GL_MAX_UNIFORM_BLOCK_SIZE
: 每个uniform块的大小(byte),最小值16384。
7.13 application code for brick shaders
一个demo程序ogl2brick
。第6章已经见过“install”shader的代码,在讨论install代码之前,先看如何设置uniform变量。
GLint getUniLoc(GLuint program, const GLchar *name) {
GLint loc;
loc = glGetUniformLocation(program, name);
if (loc == -1) {
printf("No such uniform named \"%s\"\n", name);
}
printOpenGLError();
return loc;
}
传递给OpenGL的shader是字符串。下面的函数,我们假设每个shader都是字符串。这个函数的工作有,load,compile,link,install我们的brick shader。
int installBrickShaders(const GLchar *brickVertex,
const GLchar *brickFragment)
{
GLuint brickVS, brickFS, brickProg;
GLint vertCompiled, fragCompiled;
GLint linked;
brickVS = glCreateShader(GL_VERTEX_SHADER);
brickFS = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(brickVS, 1, &brickVertex, NULL);
glShaderSource(brickFS, 1, &brickFragment, NULL);
glCompileShader(brickVS);
printOpenGLError();
glGetShaderiv(brickVS, GL_COMPILE_STATUS, &vertCompiled);
printShaderInfoLog(brickVS);
glCompileShader(brickFS);
printOpenGLError();
glGetShaderiv(brickFS, GL_COMPILE_STATUS, &fragCompiled);
printShaderInfoLog(brickFS);
if (!vertCompiled || !fragCompiled) {
return 0;
}
brickProg = glCreateProgram();
glAttachShader(brickProg, brickVS);
glAttachShader(brickProg, brickFS);
glLinkProgram(brickProg);
printOpenGLError();
glGetProgramiv(brickProg, GL_LINK_STATUS, &linked);
printProgramInfoLog(brickProg);
if (!linked) {
return 0;
}
glUseProgram(brickProg);
glUniform3f(getUniLoc(brickProg, "BrickColor"), 1.0, 0.3, 0.2);
glUniform3f(getUniLoc(brickProg, "MortarColor"), 0.85, 0.86 , 0.0.84);
glUniform3f(getUniLoc(brickProg, "BrickSize"), 0.30 , 0.15);
glUniform3f(getUniLoc(brickProg, "BrickPct"), 0.90 , 0.85);
glUniform3f(getUniLoc(brickProg, "LightPosition"), 0.0, 0.0, 4.0);
return 1;
}