神刀安全网

OpenGL ES 3.0编程指南:第四章. Shaders and Programs — (二)Uniforms and Attributes

[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布局规定如下:

  1. 标量 :基础对齐量是标量类型的大小,例如sizeof(GLint)
  2. 二元向量 :基础对齐量是标量类型大小的两倍
  3. 三元或四元向量 : 基础对齐量是标量类型大小的四倍
  4. 标量数组或向量数组 : 基础对齐量和数组跨距都是其单个元素的基础对齐量。整个数组被填充为vec4大小的整数倍,不足则在末尾补齐
  5. C列R行的列优先矩阵 : 储存为一个由C个具有R个分量的向量组成的数组,数组规则符合规则4
  6. 由M个C列R行的列优先矩阵组成的数组 : 储存为一个由M * C个具有R个分量的向量组成的数组,数组规则符合规则4
  7. C列R行的行优先矩阵 : 储存为一个由R个具有C个分量的向量组成的数组,数组规则符合规则4
  8. 由M个C列R行的行优先矩阵组成的数组 : 储存为一个由M * R个具有C个分量的向量组成的数组,数组规则符合规则4
  9. 单个结构体 : 偏移和大小根据前面的规则计算。结构体的大小为vec4大小的整数倍,不足不足则在末尾补齐
  10. 结构体组成的数组 : 基础对齐量需要考虑单个结构体的对齐和补齐

和上文提到的一个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信息外,还可以用来查询顶点属性信息,具体在第六章讲述。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » OpenGL ES 3.0编程指南:第四章. Shaders and Programs — (二)Uniforms and Attributes

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址