[TOC]
摘要
Uniform是由应用程序传给shader的只读常量。
Uniform的集合有两类。第一类是命名uniform block,uniform的值由一个缓冲区对象储存。每个命名uniform block被分配了一个uniform block索引。示例:
uniform TransformBlock
{
mat4 matViewProj;
mat3 matNormal;
mat3 matTexGen;
};
第二类是默认的uniform block,用于在命名uniform block之外声明的unifrom。默认的uniform block没有名字和索引。示例:
uniform mat4 matViewProj;
uniform mat3 matNormal;
uniform mat3 matTexGen;
如果一个uniform在顶点shader和片段shader中都有声明,那么它们的类型必须相同,它们的值也会相同。在链接阶段,链接器会为默认uniform block中的每个活动的uniform指定位置,应用程序会通过这些位置来为uniform加载数值。链接器也会为命名uniform block中的活动uniform分配偏移和跨距(offsets and strides)。
1.Getting and Setting Uniforms
一个uniform如果在程序中被使用,则认为这个uniform是活动的(active),否则如果只是被声明而没有被使用,则在链接阶段很可能就会被优化去除。
通过用参数 GL_ACTIVE_UNIFORMS 和 GL_ACTIVE_UNIFORM_MAX_LENGTH 调用 glGetProgramiv 函数,可以分别获得当前 program对象中激活的uniform的数量 和 uniform名字字符数的最大值,一旦这两个值获得以后,就可以查询uniform的详细信息:
void glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name)
- program : 要查询的program对象
- index : 要查询的uniform索引(也就是第几个uniform)
- bufsize : uniform名字的字符数量(上面查询的字符最大值,按照字面意思理解,也就是要分配的缓冲区大小)
- length : 如果这个值不是NULL,会被写入uniform名字的字符数(不包括终止字符)
- size : 如果要查询的uniform是一个数组,那么这个值会被写入程序中使用的最大数组元素(加1),如果不是一个数组,则该值为1
- type : 被写入该uniform的类型,可取值为
GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3, GL_FLOAT_VEC4,
GL_INT, GL_INT_VEC2, GL_INT_VEC3, GL_INT_VEC4,
GL_UNSIGNED_INT, GL_UNSIGNED_INT_VEC2, GL_UNSIGNED_INT_VEC3, GL_UNSIGNED_INT_VEC4,
GL_BOOL, GL_BOOL_VEC2, GL_BOOL_VEC3, GL_BOOL_VEC4,
GL_FLOAT_MAT2, GL_FLOAT_MAT3, GL_FLOAT_MAT4,
GL_FLOAT_MAT2x3, GL_FLOAT_MAT2x4,
GL_FLOAT_MAT3x2, GL_FLOAT_MAT3x4,
GL_FLOAT_MAT4x2, GL_FLOAT_MAT4x3,
GL_SAMPLER_2D, GL_SAMPLER_3D,
GL_SAMPLER_CUBE, GL_SAMPLER_CUBE_SHADOW,
GL_SAMPLER_2D_SHADOW, GL_SAMPLER_2D_ARRAY, GL_SAMPLER_2D_ARRAY_SHADOW,
GL_INT_SAMPLER_2D, GL_INT_SAMPLER_3D,
GL_INT_SAMPLER_CUBE, GL_INT_SAMPLER_2D_ARRAY,
GL_UNSIGNED_INT_SAMPLER_2D, GL_UNSIGNED_INT_SAMPLER_3D,
GL_UNSIGNED_INT_SAMPLER_CUBE, GL_UNSIGNED_INT_SAMPLER_2D_ARRAY
- name : 被写入uniform的名字,最多有bufsize个字符,以终止字符结尾
void glGetActiveUniformsiv (GLuint program, GLsizei count, const GLuint * indices, GLenum pname, GLint * params)
- program : 要查询的program对象
- count : indices数组元素数量
- indices : uniform索引列表
- 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_IS_ROW_MAJOR- params : 查询结果
查询到uniform的名字之后,就可以根据名字查询uniform的位置,注意,命名uniform block里的uniform不会被分配一个位置(猜测应该是整个uniform block会被分配一个位置,而里面的每个uniform不会再被单独分配一个):
GLint glGetUniformLocation (GLuint program, const char * name)
- program : 相应的program对象
- name : 要查询位置的uniform名字
- 如果这个uniform是非激活状态,返回值为-1
查询到uniform的位置之后,在根据位置和上面查询到的size和type,就可以为这个uniform加载数据:
void glUniform1f (GLint location, GLfloat x)
void glUniform1fv (GLint location, GLsizei count, const GLfloat * value)
void glUniform1i (GLint location, GLint x)
void glUniform1iv (GLint location, GLsizei count, const GLint * value)
void glUniform1ui (GLint location, GLuint x)
void glUniform1uiv (GLint location, GLsizei count, const GLuint * value)
void glUniform2f (GLint location, GLfloat x, GLfloat y)
void glUniform2fv (GLint location, GLsizei count, const GLfloat * value)
void glUniform2i (GLint location, GLint x, GLint y)
void glUniform2iv (GLint location, GLsizei count, const GLint * value)
void glUniform2ui (GLint location, GLuint x, GLuint y)
void glUniform2uiv (GLint location, GLsizei count, const GLuint * value)
void glUniform3f (GLint location, GLfloat x, GLfloat y, GLfloat z)
void glUniform3fv (GLint location, GLsizei count, const GLfloat * value)
void glUniform3i (GLint location, GLint x, GLint y, GLint z)
void glUniform3iv (GLint location, GLsizei count, const GLint * value)
void glUniform3ui (GLint location, GLuint x, GLuint y, GLuint z)
void glUniform3uiv (GLint location, GLsizei count, const GLuint * value)
void glUniform4f (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
void glUniform4fv (GLint location, GLsizei count, const GLfloat * value)
void glUniform4i (GLint location, GLint x, GLint y, GLint z, GLint w)
void glUniform4iv (GLint location, GLsizei count, const GLint * value)
void glUniform4ui (GLint location, GLuint x, GLuint y, GLuint z, GLuint w)
void glUniform4uiv (GLint location, GLsizei count, const GLuint * value)
void glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
void glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
void glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
void glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
void glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
void glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
void glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
void glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
void glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
- location : uniform的位置
- count : 需要加载数据的数组元素的数量或者需要修改的矩阵的数量
- transpose : 指明矩阵是列优先(column major)矩阵(GL_FALSE)还是行优先(row major)矩阵(GL_TRUE)
- x, y, z, w : uniform的值
- value : 指向由count个元素的数组的指针
上面的这些 glUniformXXX 方法并不用指定一个program作为参数,因为这些方法总是作用在当前使用的program上,由当前的program对象保存这些uniform的值。也就是说,当你在一个program1对象上给一个uniform1赋予了一个值,那么即使你使用另一个program2对象,uniform1的值仍然在program1里。
Example 4-3
GLint maxUniformLen;
GLint numUniforms;
char * uniformName;
GLint indenx;
glGetProgramiv (progObj, GL_ACTIVE_UNIFORMS, &numUniforms);
glGetProgramiv (progObj, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen);
uniformName = malloc ( sizeof(char) * maxUniformLen);
for (index = 0; index < numUniforms; index++)
{
GLint size;
GLenum type;
GLint locationl
glGetActiveUniform (progObj, index, maxUniformLen, NULL, &size, &type, uniformName);
location = glGetUniformLocation (progObj, uniformName);
switch (type)
{
case GL_FLOAT:
break;
case GL_FLOAT_VEC2:
break;
default:
break;
}
}
2.Uniform Buffer Objects
通过使用uniform缓冲对象来储存uniform数据,我们可以在程序之间或者shader之间共享uniform。
可以通过使用glBufferData, glBufferSubData, glMapBufferRange 和 glUnmapBuffer来修改uniform缓冲对象的内容,不能使用上文介绍的glUniformXXX方法。
在uniform缓存对象中,uniform在内存中按照如下方式储存:
- 类型为bool,int,uint,float的变量在内存中按照单个uint,int,uint,float以指定的偏移储存
- bool,int,uint,float类型的向量以指定的偏移开始储存在连续内存位置中,其中第一个分量储存在最低偏移处
- C列R行的列优先矩阵被当做一个有C个列向量的数组,每个向量包含R个分量;类似的,R行C列的行优先矩阵,被当做一个有R个行向量的数组,每个向量包含C个分量。列向量或者行向量是连续储存的,但是之间可能有缺口(偏移)。矩阵中两个向量之间的偏移被称为列跨距或行跨距(GL_UNIFORM_MATRIX_STRIDE),可以通过glGetActiveUniformsiv来查询。
- 标量数组、向量数组和矩阵数组在内存中按照元素的顺序储存,索引为0的成员储存在最低偏移处。数组中每对元素之间的偏移是一个常数,被称为数组跨距(GL_UNIFORM_ARRAY_STRIDE),可以通过glGetActiveUniformsiv查询。
除非使用默认的std140 uniform block布局,否则需要查询获得字节偏移和跨距,以便在uniform缓冲对象中设置uniform数据。std140布局使用有OpenGL ES 3.0明确规范定义的布局规范来进行特定打包,因此,使用std140布局,可以在不同的OpenGL ES 3.0实现之间共享uniform block。其他打包格式可能允许一些OpenGL ES 3.0实现以比std140更紧凑的方式打包数据。
使用std140布局的命名uniform block示例:
layout (std140) uniform LightBlock
{
vec3 lightDirection;
vec3 lightPosition;
};
std140布局规定如下:
- 标量 :基础对齐量是标量类型的大小,例如sizeof(GLint)
- 二元向量 :基础对齐量是标量类型大小的两倍
- 三元或四元向量 : 基础对齐量是标量类型大小的四倍
- 标量数组或向量数组 : 基础对齐量和数组跨距都是其单个元素的基础对齐量。整个数组被填充为vec4大小的整数倍,不足则在末尾补齐
- C列R行的列优先矩阵 : 储存为一个由C个具有R个分量的向量组成的数组,数组规则符合规则4
- 由M个C列R行的列优先矩阵组成的数组 : 储存为一个由M * C个具有R个分量的向量组成的数组,数组规则符合规则4
- C列R行的行优先矩阵 : 储存为一个由R个具有C个分量的向量组成的数组,数组规则符合规则4
- 由M个C列R行的行优先矩阵组成的数组 : 储存为一个由M * R个具有C个分量的向量组成的数组,数组规则符合规则4
- 单个结构体 : 偏移和大小根据前面的规则计算。结构体的大小为vec4大小的整数倍,不足不足则在末尾补齐
- 结构体组成的数组 : 基础对齐量需要考虑单个结构体的对齐和补齐
和上文提到的一个uniform的位置用来表示一个uniform类似,一个uniform block index用来表示一个uniform block。
获得uniform block的名字:
void glGetActiveUniformBlockName (GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * blockName)
- program : program对象
- index : 要查询的索引
- bufSize : 名字的(最大)字符数
- length : 如果这个值不是NULL,会被写入uniform名字的字符数(不包括终止字符)
- name : 被写入uniform的名字,最多有bufsize个字符,以终止字符结尾
获得uniform block的其他属性:
void glGetActiveUniformBlockiv (GLuint program, GLuint index, GLenum pname, GLint * params)
- program : program对象
- index : 要查询的索引
- pname : 要查询的属性,可取值为
GL_UNIFORM_BLOCK_BINDING 返回uniform block的最后一个绑定点(如果uniform block不存在,则为0)
GL_UNIFORM_BLOCK_DATA_SIZE 返回包含uniform block中所有uniform的缓冲对象的最小尺寸
GL_UNIFORM_BLOCK_NAME_LENGTH 返回uniform block名字的总长度(包括终止字符)
GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 返回uniform block中活动的uniform的数量
GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 返回uniform block中活动的uniform的索引列表
GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 返回uniform block是否由顶点shader引用
GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 返回uniform block是否由片段shader引用- params : 查询结果
根据名字获得uniform block index:
GLuint glGetUniformBlockIndex (GLuint program, const GLchar * blockName)
- program : program对象
- blockName : uniform block的名字
然后可以将uniform block index和program中的一个binding point进行绑定:
void glUniformBlockBinding (GLuint program, GLuint blockIndex, GLuint blockBinding)
- program : program对象
- blockIndex : uniform block index
- blockBinding : uniform缓冲对象绑定点
最后,将一个uniform buffer object和这个binding point绑定:
void glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)
void glBindBufferBase (GLenum target, GLuint index, GLuint buffer)
- target : 必须是 GL_UNIFORM_BUFFER 或者是 GL_TRANSFORM_FEEDBACK_BUFFER
- index : 绑定索引
- buffer : 缓冲对象
- offset : 缓冲对象的起始偏移字节数
- size : 能从缓冲对象读取或者写入缓冲对象的数据量
当编程使用到uniform时,有以下限制要注意:
- 顶点shader或片段shader能使用的活动的uniform block是有数量限制的,最大值可以通过glGetIntegerv调用GL_MAX_VERTEX_UNIFORM_BLOCKS或者GL_MAX_FRAGMENT_UNIFORM_BLOCKS来查询。任何实现的最大值都不会小于12。
- 一个program对象中所有shader能使用的活动的uniform block也是有数量限制的,最大值可以通过glGetIntegerv调用GL_MAX_COMBINED_UNIFORM_BLOCKS查询。任何实现的最大值都不会小于24。
- 每个uniform缓冲对象的最大可用储存量的大小可以通过glGetInteger64v来查询。任何实现的最大值都不会小于16KB。
以下示例展示了如何用前面描述的命名uniform block LightBlock来建立一个uniform缓冲对象:
GLuint blockId, bufferId;
GLint blockSize;
GLuint bindingPoint = 1;
GLfloat lightData[] =
{
// lightDirection (padded to vec4 based on std140 rule)
1.0f, 0.0f, 0.0f, 0.0f
// lightPosition
0.0f, 0.0f, 0.0f, 1.0f
};
// Retrieve the uniform block index
blockId = glGetUniformBlockIndex (program, "LightBlock")
glUniformBlockBinding (program, blockId, bindingPoint)
glGetActiveUniformBlockiv (program, blockId, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize)
glGenBuffers (1, &bufferId);
glBindBuffer (GL_UNIFORM_BUFFER, bufferId);
glBufferData (GL_UNIFORM_BUFFER, blockSize, lightData, GL_DYNAMIC_DRAW);
glBindBufferBase (GL_UNIFORM_BUFFER, bindingPoint, bufferId);
3.Getting and Setting Attributes
通过program对象除了可以查询uniform信息外,还可以用来查询顶点属性信息,具体在第六章讲述。