在OpenGL着色器编程中,uniform变量是一种特殊的全局变量,它们的作用是在图形渲染管线的不同阶段(如顶点着色器、片段着色器等)共享常量数据。uniform变量的值由CPU端的应用程序设置,并且在整个渲染调用过程中保持不变,这意味着所有在这个渲染批次中的顶点或片段都将使用同一组uniform变量的值。
特性与用途:
声明与类型:
在GLSL(OpenGL Shading Language)中,uniform变量通过关键字uniform
进行声明,并指定其数据类型,例如矩阵、向量、标量或者结构体。例如:
uniform mat4 modelMatrix; // 用于存储模型变换矩阵
uniform vec3 lightPosition; // 存储光源位置
uniform float time; // 表示当前时间
生命周期与作用域:
赋值与更新:
应用程序需要使用OpenGL API函数来为uniform变量分配内存地址并设置其值。这些函数包括glGetUniformLocation
获取uniform变量在着色器中的位置标识符,然后使用glUniform*
系列函数(如glUniform1f
, glUniform3fv
, glUniformMatrix4fv
等)来传递实际的数据。
性能优化:
限制:
OpenGL对uniform变量的数量和大小有限制,这取决于硬件支持。超出限制会导致无法创建更多的uniform变量或无法设置更大的uniform数组。现代OpenGL版本和扩展通常提供了更大容量和更灵活的方式来管理uniform和其他常量数据。
总结来说,uniform变量在OpenGL中扮演着非常重要的角色,它允许应用程序灵活地控制着色器的行为而不必修改和重新编译着色器代码,使得开发者能够实时调整渲染效果,比如变换、光照条件等。
在OpenGL着色器编程中,当提到“活动的uniform”时,它是指那些在当前链接的着色器程序中被实际使用的uniform变量。每个uniform变量在GLSL(OpenGL着色语言)源代码中都有一个声明,但在编译并链接成最终着色器程序后,并非所有声明的uniform都会被认为是“活跃”的。
活跃的uniform是指在任何片段着色器、顶点着色器或其他类型的着色器阶段中有用到的uniform变量,这些变量的值可以由应用程序在运行时通过API函数设置,并传递给GPU以便在渲染过程中使用。例如,如果一个光照模型中的某个参数是通过uniform变量传入的,而这个参数在实际的着色计算中发挥了作用,那么这个uniform就是活跃的。
glGetActiveUniform
函数就是为了查询这些在链接后的着色器程序中确实起作用的uniform变量的详细信息,包括其名称、类型、大小和数组元素的数量等。只有这些活跃的uniform需要应用程序关注并进行相应的数据上传。
int glGetUniformLocation( uint program, const char *name );
void glGetActiveUniformName( uint program, uint uniformIndex, sizei bufSize, sizei *length, char *uniformName );
void glGetUniformIndices( uint program, sizei uniformCount, const char * const *uniformNames, uint *uniformIndices );
void glGetActiveUniform( uint program, uint index, sizei bufSize, sizei *length, int *size, enum *type, char *name );
void glGetActiveUniformsiv( uint program, sizei uniformCount, const uint *uniformIndices, enum pname, int *params );
uint glGetUniformBlockIndex( uint program, const char *uniformBlockName );
void glGetActiveUniformBlockName( uint program, uint uniformBlockIndex, sizei bufSize, sizei length, char *uniformBlockName );
void glGetActiveUniformBlockiv( uint program, uint uniformBlockIndex, enum pname, int *params );
void glGetActiveAtomicCounterBufferiv( uint program, uint bufferIndex, enum pname, int *params );
int glGetUniformLocation( uint program, const char *name );
// is equivalent to
glGetProgramResourceLocation(program, UNIFORM, name);
void glGetActiveUniformName( uint program, uint uniformIndex, sizei bufSize, sizei *length, char *uniformName );
// is equivalent to
glGetProgramResourceName(program, UNIFORM, uniformIndex, bufSize, length, uniformName);
参数说明:
program
:一个有效的OpenGL着色器程序对象ID。uniformIndex
:要查询的uniform变量的索引,这个索引是从0开始,按照在着色器程序中定义的顺序排列的。bufSize
:传递给uniformName缓冲区的大小(以字符计),包括结束符\0的空间。length
:可选指针,用于接收实际写入uniformName缓冲区的字符数,不包括结束符。uniformName
:指向一个足够大的缓冲区,用来存储返回的uniform变量名。获取着色器程序中活动 uniform 变量名称的最大长度
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
void glGetUniformIndices( uint program, sizei uniformCount, const char * const *uniformNames, uint *uniformIndices );
// is equivalent (assuming no errors are generated) to:
for (int i = 0; i < uniformCount; i++) {
uniformIndices[i] = glGetProgramResourceIndex(program, UNIFORM, uniformNames[i]);
}
参数说明:
program
:要查询的着色器程序对象的标识符。uniformCount
:要查询的uniform变量名称数组的长度。uniformNames
:一个字符串数组,包含要查询的uniform变量的名称。uniformIndices
:用于存储uniform变量索引的数组。这些索引可以用于后续调用如glGetActiveUniformsiv、glGetUniformIndices和glUniformBlockBinding等函数。在现代OpenGL编程中,特别是在使用 Uniform Buffer Objects (UBOs) 或 Shader Storage Buffers (SSBOs) 时,索引对于间接访问 uniform 变量非常有用。
void glGetActiveUniform( uint program, uint index, sizei bufSize, sizei *length, int *size, enum *type, char *name );
// is equivalent (assuming no errors are generated) to:
const enum props[] = { ARRAY_SIZE, TYPE };
glGetProgramResourceName(program, UNIFORM, index, bufSize, length, name);
glGetProgramResourceiv(program, UNIFORM, index, 1, &props[0], 1, NULL, size);
glGetProgramResourceiv(program, UNIFORM, index, 1, &props[1], 1, NULL, (int *)type);
参数说明:
program
着色器程序对象的标识符。index
活跃uniform变量的索引,在 0 到 glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count) - 1 之间。bufSize
参数指定了存储 uniform 名称的缓冲区大小。length
参数返回实际的 uniform 名称的长度(不包括终止 null 字符)。size
参数返回 uniform 数组中的元素数量(如果不是数组,则为 1)。type
参数返回 uniform 变量的数据类型。name
参数是一个指向存储 uniform 名称的缓冲区的指针。GLuint program; // 要查询的着色器程序对象的标识符
GLint count;
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count); // 获取活动 uniform 变量数量
for (GLuint i = 0; i < count; ++i) {
GLsizei length;
GLint size;
GLenum type;
GLchar name[256]; // 假设 uniform 变量名称不超过 255 个字符
// 可以用这个查最大长度 GL_ACTIVE_UNIFORM_MAX_LENGTH
glGetActiveUniform(program, i, 256, &length, &size, &type, name); // 获取 uniform 变量信息
std::cout << "Uniform #" << i << ": " << name << ", Size: " << size << ", Type: " << type << std::endl;
}
void glGetActiveUniformsiv( uint program, sizei uniformCount, const uint *uniformIndices, enum pname, int *params );
// is equivalent (assuming no errors are generated) to:
GLenum prop;
for (int i = 0; i < uniformCount; i++) {
glGetProgramResourceiv(program, UNIFORM, uniformIndices[i], 1, &prop, 1, NULL, ¶ms[i]);
}
uniformCount
参数指定了 uniform 变量索引数组的长度。uniformIndices
参数是一个指向存储 uniform 变量索引的数组的指针。pname
参数指定了要查询的 uniform 变量的信息类型,比如大小、类型等,支持的值。
params
参数是一个用于存储查询结果的数组。数组的长度应该至少和 uniformCount 参数一样。GLuint program; // 要查询的着色器程序对象的标识符
GLsizei uniformCount = ...; // uniform 变量索引数组的长度
GLuint uniformIndices[] = {0, 1, 2, ...}; // uniform 变量索引数组
GLint params[uniformCount]; // 存储查询结果的数组
// 查询 uniform 变量的类型
glGetActiveUniformsiv(program, uniformCount, uniformIndices, GL_UNIFORM_TYPE, params);
// 现在 params 数组中包含了 uniform 变量的类型
uint glGetUniformBlockIndex( uint program, const char *uniformBlockName );
// is equivalent to
glGetProgramResourceIndex(program, UNIFORM_BLOCK, uniformBlockName);
在OpenGL编程中,可以将一组相关的uniform变量组织到一个统一块中,然后通过统一缓冲对象(Uniform Buffer Object, UBO)进行高效的数据传输。
如果未找到匹配的统一块,则返回GL_INVALID_INDEX。
void glGetActiveUniformBlockName( uint program, uint uniformBlockIndex, sizei bufSize, sizei length, char *uniformBlockName );
// is equivalent to
glGetProgramResourceName(program, UNIFORM_BLOCK, uniformBlockIndex, bufSize, length, uniformBlockName);
void glGetActiveUniformBlockiv( uint program, uint uniformBlockIndex, enum pname, int *params );
// is equivalent to
GLenum prop;
glGetProgramResourceiv(program, UNIFORM_BLOCK, uniformBlockIndex, 1, &prop, maxSize, NULL, params);
program
参数指定了要查询的着色器程序。
uniformBlockIndex
参数是要查询的 uniform 块的索引。
pname
参数指定了要查询的 uniform 块的信息类型。
params
参数是一个用于存储查询结果的数组。数组的长度应该符合查询的信息类型。
void glGetActiveAtomicCounterBufferiv( uint program, uint bufferIndex, enum pname, int *params );
// can be used to determine properties of active atomic counter buffer bindings used by program and is equivalent to
GLenum prop;
glGetProgramResourceiv(program, ATOMIC_COUNTER_BUFFER, bufferIndex, 1, &prop, maxSize, NULL, params);
program
参数指定了要查询的着色器程序。bufferIndex
参数是要查询的原子计数器缓冲区对象的索引。pname
参数指定了要查询的原子计数器缓冲区对象的信息类型。
params
参数是一个用于存储查询结果的数组。数组的长度应该符合查询的信息类型。GLuint program; // 要查询的着色器程序对象的标识符
GLuint bufferIndex; // 要查询的原子计数器缓冲区对象的索引
GLint numActiveCounters;
glGetActiveAtomicCounterBufferiv(program, bufferIndex, GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS, &numActiveCounters);
// 查询活动原子计数器的索引数组
GLint* activeCounterIndices = new GLint[numActiveCounters];
glGetActiveAtomicCounterBufferiv(program, bufferIndex, GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES, activeCounterIndices);
void glUniform{1234}{ifd ui}( int location, T value );
void glUniform{1234}{ifd ui}v( int location, sizei count, const T *value );
void glUniformMatrix{234}{fd}v( int location, sizei count, boolean transpose, const float *value );
void glUniformMatrix{2x3,3x2,2x4,4x2,3x4,4x3}{fd}v( int location, sizei count, boolean transpose, const float *value );
OpenGL中的Uniform命令用于将指定的数值加载到当前活动着色程序uniform变量中。当通过UseProgram绑定了一个非零的着色程序对象时,这个着色程序即为活动着色程序,其uniforms可以被更新;若未绑定任何着色程序,则当前管线对象通过ActiveShaderProgram设置的活动着色程序(若有)成为活动对象。
Uniform系列命令可按照不同数据类型和大小来更新uniform变量:
对于布尔型uniform变量及其数组,可以使用Uniformi{v}、Uniformui{v}和Uniform*f{v}命令进行赋值,系统会自动进行类型转换:0或0.0f表示FALSE,非零值表示TRUE,但使用的Uniform命令尺寸必须匹配shader中声明的uniform大小。
对于所有其他可由Uniform命令加载的uniform类型,所使用的命令必须严格匹配uniform在shader中声明的尺寸和类型,且不进行类型转换。
当对数组uniform的一部分(从位置k开始的N个元素)进行赋值时,数组中索引k至k+N-1的元素会被新值替换。如果尝试加载的数组元素超过了GetActiveUniform报告的最大数组索引,超出部分的值将被OpenGL忽略。
最后,如果location参数值为-1,Uniform*命令会静默地忽略传入的数据,而不改变当前uniform的值。
void glProgramUniform{1234}{ifd}( uint program, int location, T value );
void glProgramUniform{1234}{ifd}v( uint program, int location, sizei count, const T *value );
void glProgramUniform{1234}ui( uint program, int location, T value );
void glProgramUniform{1234}uiv( uint program, int location, sizei count, const T *value );
void glProgramUniformMatrix{234}{fd}v( uint program, int location, sizei count, boolean transpose, const T *value );
void glProgramUniformMatrix{2x3,3x2,2x4,4x2,3x4,4x3}{fd}v( uint program, int location, sizei count, boolean transpose, const T *value );
这些以"Program"开头的命令与上述不带"Program"前缀的相应命令功能相同,区别在于:它们不是更新当前活动的着色程序对象,而是直接根据第一个参数指定的program ID更新相应的程序对象中的uniform变量。在首个program参数之后的剩余参数与对应的非Program uniform命令的参数完全一致。
OpenGL中的Uniform Blocks允许开发者将一组uniform变量组织在命名的uniform块中,并从缓冲对象存储中提取它们的值。每个着色器程序可以声明多个uniform块,但如果超过实现依赖的限制(如MAX_VERTEX_UNIFORM_BLOCKS、MAX_FRAGMENT_UNIFORM_BLOCKS等),则程序链接将会失败。
当一个命名的uniform块在程序中的多个着色器中被声明时,它必须在每个着色器中以完全相同的方式声明,包括uniform的名字、类型、布局限定符及其顺序。
uniform块中的数据在存储到与之关联的Uniform Buffer Object (UBO)时,其内存表示遵循特定规则。SPIR-V中的Uniform存储类变量,如果用Block或BufferBlock进行装饰,必须使用Offset、ArrayStride和MatrixStride装饰来显式布局。
布局限制符 | 描述 |
---|---|
binding = N | 设置缓存的绑定位置,需要用到 OpenGL API |
shared | 设置uniform块是多个程序间共享的(这是默认的布局方式,与shared存储限制符不存在混淆) |
packed | 设置uniform块占用最小的内存空间,但是这样会禁止程序间共享这个块 |
std140 | 使用标准布局方式来设置uniform块或者着色器存储的buffer块,参见附录H |
std430 | 使用标准布局方式来设置uniform块,参见附录I |
offset = N | 强制设置成员变量位于缓存的 N 字节偏移处 |
align = N | 强制设置成员变量的偏移位置是 N 的倍数 |
row_major | 使用行主序的方式来存储uniform块中的矩阵 |
column_major | 使用列主序的方式来存储uniform块中的矩阵(这也是默认的顺序) |
例如,如果需要共享一个uniform块,并且使用行主序的方式来存储数据,那么可以使用下面的代码来声明:
layout ( shared, row_major ) uniform { ... };
如果需要对所有后继的uniform块设置同一种布局,那么可以使用下面的语句:
layout ( packed, column_major ) uniform;
这样一来,当前行之后的所有uniform块都会使用这种布局方式,除非再次改变全局的布局,或者对某个块的声明单独设置专属的布局方式。
使用Shader Storage Buffer Objects (SSBO) 进行多阶段渲染中的数据传递。我们将实现一个简单的场景,其中包含多个物体的位置信息,并且这些位置信息将在顶点着色器和几何着色器之间通过SSBO进行传递。
首先,在C++代码中创建、填充并绑定SSBO:
#include
#include
// 假设我们有一个结构体表示每个物体的变换矩阵
struct Transform {
GLfloat matrix[16];
};
std::vector<Transform> transforms; // 假设有多个物体的变换矩阵数据
GLuint ssbo;
// 初始化与填充
// ...
for (size_t i = 0; i < numObjects; ++i) {
// 填充transforms向量,计算每个物体的变换矩阵
}
// 创建并初始化SSBO
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
GLsizeiptr bufferSize = transforms.size() * sizeof(Transform);
glBufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, transforms.data(), GL_DYNAMIC_DRAW);
// 设置SSBO的绑定点
GLuint bindingPoint = 0;
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingPoint, ssbo);
// 解绑
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
然后,在GLSL顶点着色器中声明并读取SSBO:
#version 430
layout(std430, binding = 0) buffer Transforms {
mat4 transform[];
} transformBuffer;
in vec4 vertexPosition_modelspace;
uniform uint objectIndex;
void main() {
gl_Position = transformBuffer.transform[objectIndex] * vertexPosition_modelspace;
}
接下来,在几何着色器(或片段着色器,根据实际需求)中同样可以访问这个SSBO:
#version 430
layout(std430, binding = 0) buffer Transforms {
mat4 transform[];
} transformBuffer;
// ... 输入变量 ...
out vec4 color;
uniform uint objectIndex;
void main() {
// 访问当前物体的变换矩阵
mat4 objTransform = transformBuffer.transform[objectIndex];
// 使用变换矩阵对输出数据进行操作...
// ...
color = ...; // 根据变换计算颜色或其他属性
}
在这个例子中,我们创建了一个存储多个4x4变换矩阵的SSBO,并在顶点着色器中使用当前对象索引来查找并应用相应的变换。同样的数据也可以在几何着色器中被复用,用于进一步的处理和计算。这样实现了不同着色器阶段之间的数据共享,便于进行复杂的数据驱动渲染。
void glUniformBlockBinding( uint program, uint uniformBlockIndex, uint uniformBlockBinding );
用于指定着色器程序中的统一块(uniform block)与统一缓冲区对象绑定点的绑定关系。
// 假设我们已经有一个编译和链接好的着色器程序对象
GLuint program = ...; // 已经创建并成功链接的着色器程序
// 假设有如下着色器代码中的一个统一块:
// 在顶点或片段着色器中定义了名为"MyUniformBlock"的统一块
// layout (binding = 0) uniform MyUniformBlock { ... };
// 我们可以查询这个统一块在当前程序中的索引
GLint uniformBlockIndex = -1;
const GLchar *uniformBlockName = "MyUniformBlock";
glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, uniformBlockName, &uniformBlockIndex);
// 确保查询到了正确的索引
if (uniformBlockIndex != -1) {
// 假设我们想要将该统一块绑定到第3个统一缓冲区绑定点上
GLuint uniformBlockBindingPoint = 3;
// 调用UniformBlockBinding函数进行绑定
glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBindingPoint);
// 创建并填充一个统一缓冲区对象,并将其绑定到对应的绑定点上
GLuint uniformBufferObject;
glGenBuffers(1, &uniformBufferObject);
glBindBuffer(GL_UNIFORM_BUFFER, uniformBufferObject);
glBufferData(GL_UNIFORM_BUFFER, sizeof(MyUniformData), &myUniformData, GL_DYNAMIC_DRAW); // 假设MyUniformData是结构体类型,包含需要传递给统一块的数据
// 将统一缓冲区对象绑定到第3个统一缓冲区绑定点
glBindBufferBase(GL_UNIFORM_BUFFER, uniformBlockBindingPoint, uniformBufferObject);
}
// 此时,当执行此程序时,统一块“MyUniformBlock”将从绑定到第3个绑定点上的统一缓冲区对象中读取数据
void glShaderStorageBlockBinding( uint program, uint storageBlockIndex, uint storageBlockBinding );
用于在程序链接后指定 shader storage block 与缓冲区对象绑定点的对应关系。在OpenGL编程中,shader storage blocks 允许着色器直接读写存储在缓冲区对象(buffer objects)中的数据,并且可以被多个着色器阶段共享。
program
: 已经成功链接的OpenGL着色器程序对象的ID。storageBlockIndex
: 指定要更改其绑定点的 shader storage block 在程序中的索引位置。这个索引基于 shader storage block 在相应着色器代码中声明的顺序或通过布局限定符(layout qualifier)显式指定的 binding layout。storageBlockBinding
: 新的缓冲区对象绑定点值。当执行此程序时,将使用绑定到该绑定点上的缓冲区对象来访问和修改对应的 shader storage block 中的数据。以下是一个使用 glShaderStorageBlockBinding
函数的例子,假设我们有一个着色器程序,其中包含一个名为 “MySSBO” 的 shader storage block,并且我们在C++代码中创建了一个缓冲区对象用于存储数据。
首先,在 GLSL 着色器代码中声明 shader storage block:
#version 430
layout(std430, binding = 0) buffer MySSBO {
vec4 data[];
} myStorageBlock;
void main() {
// 访问或修改 myStorageBlock.data 数组
}
在 C++ 部分,创建并填充缓冲区对象,然后链接着色器程序:
GLuint shaderProgram = ...; // 已经成功链接的着色器程序
GLuint ssbo; // 创建的 shader storage buffer object
// 初始化和填充 SSBO
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec4) * dataSize, dataPtr, GL_DYNAMIC_DRAW);
// 假设我们希望将此 SSBO 绑定到第 2 个绑定点而不是默认的 0 号绑定点
glShaderStorageBlockBinding(shaderProgram, 0, 2); // 第二个参数为 MySSBO 在着色器程序中的索引(这里假定是 0)
// 解绑 SSBO 和 shader program
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
这里的调用 glShaderStorageBlockBinding(shaderProgram, 0, 2)
将原本在 GLSL 中通过 binding = 0
显式指定的绑定位置更改为 2。这意味着当执行该着色器时,会从绑定到第 2 个 shader storage buffer binding point 的缓冲区对象读取或写入数据。
后续在渲染过程中,可以通过如下方式绑定缓冲区对象:
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, ssbo); // 将 SSBO 绑定到第 2 个绑定点
这样,着色器就可以正确地访问与 “MySSBO” 相关联的数据了。
glUniformBlockBinding
和 glShaderStorageBlockBinding
是用于 OpenGL 中不同类型的着色器存储块的绑定函数。它们之间的区别在于它们用于不同类型的着色器存储块glUniformBlockBinding:
glUniformBlockBinding
函数,将程序对象中的 uniform 块与绑定点相关联,以便将缓冲对象绑定到该绑定点。glShaderStorageBlockBinding:
glShaderStorageBlockBinding
函数,将程序对象中的 shader storage 块与绑定点相关联,以便将缓冲对象绑定到该绑定点。总之,glUniformBlockBinding
用于 uniform 块,而 glShaderStorageBlockBinding
用于 shader storage 块,它们分别用于不同类型的着色器存储块,具有不同的特性和用途。
uint glGetSubroutineIndex( uint program, enum shadertype, const char *name );
void glGetActiveSubroutineName( uint program, enum shadertype, uint index, sizei count, sizei *length, char *name );
int glGetSubroutineUniformLocation( uint program, enum shadertype, const char *name );
void glGetActiveSubroutineUniformName( uint program, enum shadertype, uint index, sizei count, sizei *length, char *name );
void glGetActiveSubroutineUniformiv( uint program, enum shadertype, uint index, enum pname, int *values );
void glUniformSubroutinesuiv( enum shadertype, sizei count, const uint *indices );
uint glGetSubroutineIndex( uint program, enum shadertype, const char *name );
// 等价于
glGetProgramResourceIndex(program, programInterface, name);
program
: 已经成功链接的着色器程序对象的ID。shadertype
: 指定查询子程序的着色器类型,例如:GL_VERTEX_SHADER、GL_TESS_CONTROL_SHADER、GL_TESS_EVALUATION_SHADER、GL_FRAGMENT_SHADER、GL_GEOMETRY_SHADER 或 GL_COMPUTE_SHADER。name
: 子程序的名称,即在 GLSL 代码中声明的子程序函数名。void glGetActiveSubroutineName( uint program, enum shadertype, uint index, sizei count, sizei *length, char *name );
// 等价于
glGetProgramResourceName(program, programInterface, index, count, length, name);
program
: 已经成功链接的着色器程序对象的ID。shadertype
: 指定查询子程序的着色器类型,例如:GL_VERTEX_SHADER、GL_TESS_CONTROL_SHADER、GL_TESS_EVALUATION_SHADER、GL_FRAGMENT_SHADER、GL_GEOMETRY_SHADER 或 GL_COMPUTE_SHADER。index
: 子程序的索引,可以通过 glGetSubroutineIndex 函数或者通过查询 ACTIVE_SUBROUTINES 的值来获取。bufsize
: 提供的输出缓冲区 name 的大小,以字符数为单位。length
(可选):一个指向整型变量的指针,将存储实际写入 name 缓冲区的字符串长度(不包括终止符)。name
:指向一个字符数组的指针,用于接收子程序的名称。int glGetSubroutineUniformLocation( uint program, enum shadertype, const char *name );
// 等价于
glGetProgramResourceLocation(program, programInterface, name);
program
: 已经成功链接的着色器程序对象的ID。shadertype
: 指定查询子程序统一变量所在的着色器类型,例如:GL_VERTEX_SHADER、GL_TESS_CONTROL_SHADER、GL_TESS_EVALUATION_SHADER、GL_FRAGMENT_SHADER、GL_GEOMETRY_SHADER 或 GL_COMPUTE_SHADER。name
: 子程序统一变量在 GLSL 着色器代码中的名称。void glGetActiveSubroutineUniformName( uint program, enum shadertype, uint index, sizei count, sizei *length, char *name );
// 等价于
glGetProgramResourceName(program, programInterface, index, count, length, name);
program
: 已成功链接的着色器程序对象的ID。shadertype
: 指定查询子程序统一变量所在的着色器类型,如 GL_VERTEX_SHADER、GL_TESS_CONTROL_SHADER、GL_TESS_EVALUATION_SHADER、GL_FRAGMENT_SHADER、GL_GEOMETRY_SHADER 或 GL_COMPUTE_SHADER。index
: 激活子程序统一变量的索引。bufsize
: 提供的输出缓冲区 name 的大小,以字符数为单位。length
(可选): 一个指向整型变量的指针,将存储实际写入 name 缓冲区的字符串长度(不包括终止符)。name
: 指向一个字符数组的指针,用于接收激活子程序统一变量的名称。void glGetActiveSubroutineUniformiv( uint program, enum shadertype, uint index, enum pname, int *values );
// 等价于
glGetProgramResourceiv(program, programInterface, index, 1, &pname, maxSize, NULL, values);
program
: 已成功链接的着色器程序对象的ID。shadertype
: 指定查询子程序统一变量所在的着色器类型,如 GL_VERTEX_SHADER、GL_TESS_CONTROL_SHADER、GL_TESS_EVALUATION_SHADER、GL_FRAGMENT_SHADER、GL_GEOMETRY_SHADER 或 GL_COMPUTE_SHADER。index
: 激活子程序统一变量的索引。pname
: 要查询的属性名(二选一):values
: 存储查询结果的整型数组指针。void glUniformSubroutinesuiv( enum shadertype, sizei count, const uint *indices );
shadertype
: 指定要设置子程序的着色器类型,例如 GL_VERTEX_SHADER、GL_TESS_CONTROL_SHADER、GL_TESS_EVALUATION_SHADER、GL_FRAGMENT_SHADER、GL_GEOMETRY_SHADER 或 GL_COMPUTE_SHADER。count
: 要设置的子程序统一变量的数量。indices
: 一个指向无符号整数数组的指针,数组中的每个元素对应于一个子程序统一变量的位置,并给出了该位置应该绑定的子程序索引。GLuint shaderProgram;
GLint subroutineUniformLocation[2];
// 获取子程序索引
GLuint subroutineIndex0 = glGetSubroutineIndex(shaderProgram, GL_FRAGMENT_SHADER, "Subroutine0");
GLuint subroutineIndex1 = glGetSubroutineIndex(shaderProgram, GL_FRAGMENT_SHADER, "Subroutine1");
// 获取子程序统一变量的位置
subroutineUniformLocation[0] = glGetUniformLocation(shaderProgram, "subroutineUniform0");
subroutineUniformLocation[1] = glGetUniformLocation(shaderProgram, "subroutineUniform1");
// 创建一个数组来存储要设置的子程序索引
GLuint subroutineIndices[] = {subroutineIndex0, subroutineIndex1};
// 设置子程序统一变量关联的子程序
glUseProgram(shaderProgram);
glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 2, subroutineIndices);
// ... 渲染循环中可以根据需要切换子程序 ...
void glMemoryBarrier( bitfield barriers );
设置内存屏障(memory barrier),确保在图形管线中执行特定操作时满足特定的内存访问顺序。通过内存屏障,OpenGL 确保某些内存相关的操作(如缓冲区更新、纹理加载或原子操作)完成后再继续后续渲染步骤。
参数 barriers
是一个位域(bitfield),可以是以下标志的组合(使用逻辑或运算符 |
组合):
GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT
: 确保顶点属性数组的所有更改完成。GL_ELEMENT_ARRAY_BARRIER_BIT
: 确保元素数组缓冲的所有更改完成。GL_UNIFORM_BARRIER_BIT
: 确保所有统一变量的更改对着色器可见。GL_TEXTURE_FETCH_BARRIER_BIT
: 确保所有纹理读取操作完成,并且任何后续的纹理操作都会看到这些更改。GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
: 确保所有图像加载和存储操作完成。GL_COMMAND_BARRIER_BIT
: 确保先前的绘图命令完成,对于那些可能影响后续绘图命令结果的状态更改特别有用。GL_PIXEL_BUFFER_BARRIER_BIT
: 确保像素打包缓冲区的操作完成。GL_TEXTURE_UPDATE_BARRIER_BIT
: 确保纹理更新完成。GL_BUFFER_UPDATE_BARRIER_BIT
: 确保缓冲区数据更新完成。CLIENT_MAPPED_BUFFER_BARRIER_BIT
: 确保映射到客户端内存的缓冲区完成所有写入操作。QUERY_BUFFER_BARRIER_BIT
: 确保所有之前完成的查询操作(例如计时、样本计数、任何类型的OpenGL查询)的结果已经写入到相应的查询对象关联的缓冲区。GL_FRAMEBUFFER_BARRIER_BIT
: 确保帧缓冲的所有更新完成。GL_TRANSFORM_FEEDBACK_BARRIER_BIT
: 确保变换反馈操作完成。GL_ATOMIC_COUNTER_BARRIER_BIT
: 确保原子计数器操作完成。SHADER_STORAGE_BARRIER_BIT
: 确保所有之前由着色器写入到 shader storage block 的数据更新对后续可能访问这些存储块的所有着色器阶段都是可见和一致的。GL_ALL_BARRIER_BITS
: 设置所有上述障碍位。例如,如果你在 CPU 上修改了某个作为纹理使用的缓冲区数据,并希望在 GPU 执行下一次绘制调用时能看到这个更改,你可以在修改后调用 glMemoryBarrier
并指定适当的屏障类型:
glBufferData(GL_TEXTURE_BUFFER, size, data, GL_DYNAMIC_DRAW);
glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
// 在这之后的渲染指令会看到纹理缓冲区的更新
void glMemoryBarrierByRegion( bitfield barriers );
作用类似于 glMemoryBarrier
,但它只影响指定区域的内存访问。这在一些情况下可以提高性能,因为它只会影响到指定区域的内存操作,而不是全局范围内的所有操作。
该命令仅应用于可能被片段着色器读取或写入的内存事务。因此,只支持以下几种屏障位:
void glGetShaderiv( uint shader, enum pname, int *params );
uint shader
: 指定要查询其属性的着色器对象的句柄。enum pname
: 指定要查询的着色器属性类型,可以取以下值:
GL_SHADER_TYPE
:返回该着色器的类型(例如 GL_VERTEX_SHADER
、GL_FRAGMENT_SHADER
等)。GL_DELETE_STATUS
:如果着色器已被标记为删除,则返回 TRUE
,否则返回 FALSE
。GL_COMPILE_STATUS
:如果着色器上次编译或特殊化成功,则返回 TRUE
,否则返回 FALSE
。GL_INFO_LOG_LENGTH
:返回着色器信息日志(包括结束符 \0
)的长度。如果没有信息日志,则返回零。GL_SHADER_SOURCE_LENGTH
:返回组成着色器源代码的所有字符串连接后的总长度(包括结束符 \0
)。如果尚未定义源代码,则返回零。SPIR_V_BINARY
(注意:标准OpenGL规范中并未包含此选项):在支持SPIR-V二进制模块的相关扩展或版本中,如果通过 ShaderBinary
命令成功将SPIR-V二进制模块与着色器关联,则返回 TRUE
,否则返回 FALSE
。int *params
:一个指向整型变量的指针,用于接收查询结果。void glGetProgramiv( uint program, enum pname, int *params );
uint program
: 指定要查询其属性的程序对象的句柄。enum pname
: 指定要查询的程序属性类型,可以取以下值:
DELETE_STATUS
:如果程序已被标记为删除,则返回 TRUE
,否则返回 FALSE
。LINK_STATUS
:如果程序上次链接成功,则返回 TRUE
,否则返回 FALSE
。VALIDATE_STATUS
:如果上一次调用 ValidateProgram
成功,则返回 TRUE
,否则返回 FALSE
。INFO_LOG_LENGTH
:返回包含结束符 \0
的信息日志长度。若无信息日志,则返回零。ATTACHED_SHADERS
:返回已附加到程序的对象数量。ACTIVE_ATTRIBUTES
和 ACTIVE_ATTRIBUTE_MAX_LENGTH
:返回活动属性的数量和最长活动属性名称的长度。ACTIVE_UNIFORMS
和 ACTIVE_UNIFORM_MAX_LENGTH
:返回活动 uniforms 的数量和最长活动 uniform 名称的长度。TRANSFORM_FEEDBACK_BUFFER_MODE
:返回变换反馈时使用的缓冲模式。TRANSFORM_FEEDBACK_VARYINGS
和 TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH
:返回变换反馈模式下输出变量的数量和最长输出变量名称长度。ACTIVE_UNIFORM_BLOCKS
和 ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH
:返回包含活动 uniforms 的 uniform 块数量和最长活动 uniform 块名称长度。GEOMETRY_VERTICES_OUT
、GEOMETRY_INPUT_TYPE
和 GEOMETRY_OUTPUT_TYPE
:返回几何着色器的最大输出顶点数以及输入和输出类型。GEOMETRY_SHADER_INVOCATIONS
:返回每组原始图元的几何着色器调用次数。TESS_CONTROL_OUTPUT_VERTICES
、TESS_GEN_MODE
、TESS_GEN_SPACING
、TESS_GEN_VERTEX_ORDER
和 TESS_GEN_POINT_MODE
:返回曲面细分控制和评估的相关信息。COMPUTE_WORK_GROUP_SIZE
:返回计算着色器的工作组大小数组。PROGRAM_SEPARABLE
:返回程序是否可作为分离式程序对象使用。PROGRAM_BINARY_RETRIEVABLE_HINT
:返回程序二进制检索提示的状态。ACTIVE_ATOMIC_COUNTER_BUFFERS
:返回程序中使用的活动原子计数器缓冲区的数量。函数返回的是当前生效于程序的属性值,这些值可能与最近通过 LinkProgram
或 ProgramBinary
调用所设置但尚未生效的属性不同。如果自最后一次使属性更改生效的调用以来未发生任何更改,那么该函数将返回初始值。
void glGetProgramPipelineiv( uint pipeline, enum pname, int *params );
uint pipeline
: 指定要查询其属性的程序管线对象的句柄。enum pname
: 指定要查询的程序管线属性类型,可以取以下值:
ACTIVE_PROGRAM
:返回管线中当前活动的程序对象(用于更新统一变量)的名字。GL_VERTEX_SHADER
, GL_FRAGMENT_SHADER
等):返回与管线对应着色器阶段关联的当前程序对象的名字。VALIDATE_STATUS
:返回通过调用 ValidateProgramPipeline
计算出的管线验证状态。INFO_LOG_LENGTH
:返回包含结束符 \0
的管线信息日志长度。若无信息日志,则返回零。如果给定的 pipeline
名称是由 GenProgramPipelines
生成但尚未绑定过的程序管线对象,OpenGL 首先会以类似于调用 BindProgramPipeline
创建新程序管线对象的方式创建一个新的状态向量。
shaders
指向的无符号整数数组中void glGetAttachedShaders( uint program, sizei maxCount, sizei *count, uint *shaders );
uint program
: 指定要查询其附着着色器的程序对象句柄。sizei maxCount
: 表示shaders
数组能够容纳的最大着色器对象数量。sizei *count
: 输出参数,用于返回实际写入到shaders
数组中的着色器对象的数量。如果没有任何着色器附着到程序上,则count
被设置为0。如果不需要知道实际数量,可以传入NULL值。uint *shaders
: 一个足够大的缓冲区,用于存储着色器对象的名字(即着色器对象的句柄)。调用此函数时,它会尝试填充shaders
数组,但最多不会超过maxCount
个着色器对象。若想知道程序上实际附着了多少个着色器,可以通过调用GetProgramiv(program, ATTACHED_SHADERS, count)
来查询。这里的ATTACHED_SHADERS
是一个枚举值,表示要查询的是附着着色器的数量。
void GetShaderInfoLog(uint shader, sizei bufSize, sizei *length, char *infoLog);
void GetProgramInfoLog(uint program, sizei bufSize, sizei *length, char *infoLog);
void GetProgramPipelineInfoLog(uint pipeline, sizei bufSize, sizei *length, char *infoLog);
这些函数将返回对应类型对象的信息日志字符串到 infoLog
中。即使查询 INFO_LOG_LENGTH
返回零,这个字符串也会以空字符结束。实际写入到 infoLog
的字符数(不包括空字符终止符)将返回到 length
指向的位置。如果 length
为NULL,则不会返回长度。
通过参数 bufSize
可指定最多可写入 infoLog
的字符数,包括空字符终止符。分别可通过调用 GetShaderiv
(着色器对象)、GetProgramiv
(程序对象)或 GetProgramPipelineiv
函数,并设置 pname
参数为 INFO_LOG_LENGTH
来查询相应对象的信息日志长度。
shader
是一个着色器对象,GetShaderInfoLog
将返回一个空字符串或该对象最后一次编译或特殊化尝试的相关信息。program
是一个程序对象,GetProgramInfoLog
将返回一个空字符串或该对象最后一次链接尝试或最后一次验证尝试(参见11.1.3.11节)的相关信息。pipeline
是一个程序管线对象,GetProgramPipelineInfoLog
将返回一个空字符串或该对象最后一次验证尝试的相关信息。需要注意的是,信息日志通常仅在应用程序开发阶段有用,应用程序不应期望不同的GL实现生成完全相同的信息日志。
void glGetShaderSource( uint shader, sizei bufSize, sizei *length, char *source );
shader
:指定要查询其源代码的着色器对象的标识符。bufSize
:指定了可以写入到 source
缓冲区的最大字符数,包括终止空字符。length
:是一个指向整数变量的指针,该变量将接收实际写入 source
的字符数,不包括终止空字符。如果 length
为 NULL,则不会返回长度信息。source
:一个指向缓冲区的指针,其中将填充着色器的源代码字符串。这个字符串将以空字符终止。当调用此函数时,OpenGL 将把着色器对象对应的源代码复制到 source
指向的缓冲区中,并确保字符串以空字符结束。通过 length
参数可以获取实际复制的源代码长度(不包含空字符)。通过 bufSize
参数可以防止溢出,确保足够的空间容纳整个源代码字符串及其终止空字符。
着色器的源代码是由之前使用 ShaderSource
函数提交的一系列字符串连接而成的。可以通过调用 GetShaderiv
函数并设置参数 pname
为 SHADER_SOURCE_LENGTH
来查询着色器源代码的实际总长度。
void glGetShaderPrecisionFormat( enum shadertype, enum precisiontype, int *range, int *precision );
shadertype
: 必须是 VERTEX_SHADER
或 FRAGMENT_SHADER
,指定查询的目标着色器类型。precisiontype
: 必须是以下值之一:
LOW_FLOAT
MEDIUM_FLOAT
HIGH_FLOAT
LOW_INT
MEDIUM_INT
HIGH_INT
range
: 指向一个包含两个整数的数组,在调用该函数后,这两个整数将被填充为所查询格式的数值范围编码。如果 min
和 max
分别表示该格式可表示的最小和最大绝对值,则返回的值定义为:
range[0] = log2(|min|)
range[1] = log2(|max|)
precision
: 指向一个整数,该整数将填充为所查询格式的精度位数。如果能表示的最小大于1的数是 1 + ε
,那么 *precision
将包含 b - log2(ε)
,其中在这个范围内 [−2^(range[0]), 2^(range[1])]
的每一个数都能至少以 2^(*precision)
的精度表示。例如,对于 IEEE 单精度浮点数格式,会返回 range[0] = 127
, range[1] = 127
, 并且 *precision = 23
;而对于32位的二进制补码整数格式,将返回 range[0] = 31
, range[1] = 30
, 并且 *precision = 0
。
OpenGL 着色语言规范第4.7节(“Precision and Precision Qualifiers”)详细描述了对应于不同 precisiontype
值所需的各种格式的最低精度和范围要求。
void glGetUniformfv( uint program, int location,float *params );
void glGetUniformdv( uint program, int location,double *params );
void glGetUniformiv( uint program, int location,int *params );
void glGetUniformuiv( uint program, int location,uint *params );
void glGetnUniformfv( uint program, int location,sizei bufSize, float *params );
void glGetnUniformdv( uint program, int location,sizei bufSize, double *params );
void glGetnUniformiv( uint program, int location,sizei bufSize, int *params );
void glGetnUniformuiv( uint program, int location,sizei bufSize, uint *params );
这些命令用于获取程序对象(program)中默认统一块(uniform block)中指定位置(location)的统一变量(uniform)的值。根据uniform变量在location处的数据类型,函数会返回相应数量的值。
void GetUniformfv(uint program, int location, float *params);
params
指向的浮点数组中。void GetUniformdv(uint program, int location, double *params);
params
指向的双精度浮点数组中。void GetUniformiv(uint program, int location, int *params);
params
指向的整数数组中。void GetUniformuiv(uint program, int location, uint *params);
params
指向的无符号整数数组中。对于上述每个函数,还存在带 n
前缀的版本,例如:
void GetnUniformfv(uint program, int location, GLsizei bufSize, float *params);
GetUniformfv
,但允许指定缓冲区大小(bufSize),确保不会溢出用户提供的缓冲区。其他带 n
前缀的函数功能与对应的非 n
版本类似。
当需要查询一个数组类型的统一变量时,需要针对数组中的每一个元素分别调用 GetUniform* 命令。如果要查询的是矩阵类型的统一变量,则函数将按照列主序顺序返回矩阵的所有值。
void glGetUniformSubroutineuiv(enum shadertype, int location, uint *params);
enum shadertype
: 必须是以下枚举值之一,代表了不同的着色器阶段:
GL_VERTEX_SHADER
GL_TESS_CONTROL_SHADER
GL_TESS_EVALUATION_SHADER
GL_GEOMETRY_SHADER
GL_FRAGMENT_SHADER
int location
: 子程序统一变量在对应着色器阶段的位置标识符。uint *params
: 指向一个无符号整数数组的指针,该数组将被填充为当前绑定到给定位置的子程序索引。如果提供的位置是一个未使用的统一变量位置,则函数会返回 GL_INVALID_INDEX
,并且不会生成错误。这意味着开发者可以安全地查询任何位置而无需担心因位置无效而导致错误发生。通过此函数获得的子程序索引可以用于在运行时动态切换不同着色器中的子程序实现。
void glGetProgramStageiv(uint program, enum shadertype, enum pname, int *values);
uint program
: 表示你想查询的已链接的OpenGL着色器程序对象的ID。enum shadertype
: 指定着色器类型,如 GL_VERTEX_SHADER
, GL_FRAGMENT_SHADER
, GL_GEOMETRY_SHADER
等。enum pname
: 指定要查询的信息类型,可能的取值包括:
GL_ACTIVE_SUBROUTINE_UNIFORMS
: 返回此着色器阶段中活动(即使用了的)子程序统一变量的数量。GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS
: 返回此着色器阶段中活动的子程序统一变量位置的数量。GL_ACTIVE_SUBROUTINES
: 返回此着色器阶段中活动(即编译并链接到程序中的)子程序的数量。GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH
: 返回此着色器阶段中最长的子程序统一变量名称(包括结束符null)的长度。GL_ACTIVE_SUBROUTINE_MAX_LENGTH
: 返回此着色器阶段中最长的子程序名称(包括结束符null)的长度。int *values
: 存储查询结果的整数数组指针。如果给定的程序中不存在对应类型的着色器,那么函数会返回一个与没有任何子程序或子程序统一变量的着色器一致的结果。这意味着即使没有实际的子程序定义,函数也会给出有意义且正确的默认值。