OpenGL 的 glGetActiveUniform 和 glGetUniformLocation

OpenGL 的 GLSL 中声明的 Uniform 变量用于应用程序向 GLSL 程序传递参数。glGetActiveUniform 函数通过索引号取得 GLSL 中声明的 Uniform 变量的名称、类型、是否是数组等信息,而 glGetUniformLocation 函数则是通过变量名称取得 Location 值,Location  可以视为 GLSL 程序编译后,各个 Uniform 变量在 GPU 内存中的布局的顺序号。

Uniform 变量可以使用单个变量、数组、结构体变量、结构体数组等。令人比较疑惑的是数组元素和结构体成员与 Location 值的关系,因此我通过实验,得出结果如下:

1、GLSL 内置类型的变量,如 int、float、vec2 等等所有类型,如果声明为变量,则取得的名称就是直接是变量名字符串。如果声明为数组,则取得的名称是包含数组名和零号元素下标的字符串。但如果变量未在GLSL代码中使用,则视为不存在,数组无论声明为多少个元素,只会从0开始至GLSL使用到的最大下标有效。

2、如果是自定义结构体,则名称是包含结构体和成员名在内的字符串,但结构体成员如果是数组,则无论是否使用到,都会在布局中存在。

//这是GLSL的,没有什么实际功能,只是把声明的变量用上,防止编译优化时被忽略掉。
uniform sampler2D qt_Texture0;    //这个采样器没在代码中使用,编译后就不存在了。
varying vec4 qt_TexCoord0;
uniform int _intVar;
uniform int _intArr[5];
uniform float _floatVar;
uniform float _floatArr[6];
uniform vec2 _vec2ver;
uniform vec2 _vec2Arr[7];
uniform ivec2 _ivec2ver;
uniform ivec2 _ivec2Arr[8];
uniform vec3 _vec3Arr[1];
struct StrUct
{
    float fa;
    float fArr[3];
    int   ia;
    int   iArr[4];
    vec2  va;
    vec2  vArr[5];
    mat4  mArr[6];
};
uniform StrUct _sVar;
uniform StrUct _sArr[6];
void main(void)
{
    float a = float(_intVar) + float(_intArr[0]) + float(_intArr[1]) + float(_intArr[2]);
    float b = _floatVar + _floatArr[0] + _floatArr[1] + _floatArr[2] + _vec3Arr[0].x;
    a += _vec2ver.x;
    b += _vec2Arr[6].y;
    int c = _ivec2ver.r + _ivec2Arr[5].r + _ivec2Arr[6].r + _ivec2Arr[7].r;
    float d = float(c) * a * b;
    float e = _sVar.fa + _sVar.fArr[1];
    float f = float(_sVar.ia + _sVar.iArr[1]) * _sArr[1].va.x + _sArr[0].vArr[3].g;
    gl_FragColor.x = float(c);
    gl_FragColor.y = d;
    gl_FragColor.z = e;
    gl_FragColor.a = f;
}
//这是一段取得 Uniform 变量数量和最大名称字符串长度之后,枚举每个变量信息的 C++ 代码。
       GLintcount = 0;
        GLintbufSize = 0;
        GLintlength = 0;
        std::stringname;
        glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &count);
        glGetProgramiv(programId, GL_ACTIVE_UNIFORM_MAX_LENGTH, &bufSize);
        if (count && bufSize)
        {
            for (int32_t i = 0; i < count; ++i)
            {
                GLenum symbolicConstant;
                name.resize(static_cast<size_t>(bufSize));
                glGetActiveUniform(programId, i, bufSize, &length, &elementCount, &symbolicConstant, &name.front());
                name.resize(static_cast<size_t>(length));
                location = glGetUniformLocation(programId, name.c_str());
                if (location < 0) continue;
                fprintf( stderr, "%d,  Location:%d\tsize:%d,  type:%04X\tname:%s\n", i, location, elementCount, symbolicConstant, name.c_str() );
            }
        }
C++代码输出的内容:
0,  Location:0    size:1,  type:8B5C    name:qt_ModelViewProjectionMatrix
1,  Location:1    size:1,  type:1404    name:_intVar
2,  Location:2    size:3,  type:1404    name:_intArr[0]
3,  Location:5    size:1,  type:1406    name:_floatVar
4,  Location:6    size:3,  type:1406    name:_floatArr[0]
5,  Location:9    size:1,  type:8B50    name:_vec2ver
6,  Location:10    size:7,  type:8B50    name:_vec2Arr[0]
7,  Location:17    size:1,  type:8B53    name:_ivec2ver
8,  Location:18    size:8,  type:8B53    name:_ivec2Arr[0]
9,  Location:26    size:1,  type:8B51    name:_vec3Arr[0]
10,  Location:27    size:1,  type:1406    name:_sVar.fa
11,  Location:28    size:3,  type:1406    name:_sVar.fArr[0]
12,  Location:31    size:1,  type:1404    name:_sVar.ia
13,  Location:32    size:4,  type:1404    name:_sVar.iArr[0]
14,  Location:36    size:1,  type:8B50    name:_sVar.va
15,  Location:37    size:5,  type:8B50    name:_sVar.vArr[0]
16,  Location:42    size:6,  type:8B5C    name:_sVar.mArr[0]
17,  Location:48    size:1,  type:1406    name:_sArr[0].fa
18,  Location:49    size:3,  type:1406    name:_sArr[0].fArr[0]
19,  Location:52    size:1,  type:1404    name:_sArr[0].ia
20,  Location:53    size:4,  type:1404    name:_sArr[0].iArr[0]
21,  Location:57    size:1,  type:8B50    name:_sArr[0].va
22,  Location:58    size:5,  type:8B50    name:_sArr[0].vArr[0]
23,  Location:63    size:6,  type:8B5C    name:_sArr[0].mArr[0]
24,  Location:69    size:1,  type:1406    name:_sArr[1].fa
25,  Location:70    size:3,  type:1406    name:_sArr[1].fArr[0]
26,  Location:73    size:1,  type:1404    name:_sArr[1].ia
27,  Location:74    size:4,  type:1404    name:_sArr[1].iArr[0]
28,  Location:78    size:1,  type:8B50    name:_sArr[1].va
29,  Location:79    size:5,  type:8B50    name:_sArr[1].vArr[0]
30,  Location:84    size:6,  type:8B5C    name:_sArr[1].mArr[0]

凡是数组,取得的名称中都有”[0]”,表示是数组的第一个元素。

在使用 glGetUniformLocation 去获取 Location 值时,可以包含下标,也可以不包含下标。

如   “_intArr” 和 “_intArr[0]” 都返回相同的Location值,”_intArr[1]”、”_intArr[2]”分别表示下标为1和2的元素。注意输出信息中_intArr的size只有3,也就是说数组只有3个元素,尽管GLSL中是声明的 _intArr[5],但由于在代码中最大只用到了[2],因此没有使用到的就被GLSL编译器优化掉了。_vec2Arr最大用到了7,尽管没有使用到 0、1、2……等,但明显GLSL编译器是认为它们存在的。

但是也不应该使用size值是否大于1来判断是否是数组,比如 _vec3Arr 这个数组,size 就为1。

也不应该假设数组的各个下标对应的Location值就是 0号元素的 Location 值加下标值,因为一些资料上说不同的GPU实现可能不一样,数组各个下标的 Location 值不一定和下标的顺序一致,那么正确的方式还是要通过完整的”数组名[下标]”来取得对应的Location值。

对于结构体,又有所不同,名字并不只是结构体的名字,还包含了所有成员的名字。结构体的数组也和普通的数组一样,虽然_sArr这个数组声明了6个元素,但由于在代码中只用到了0和1号元素,因此有效的就只有0和1。至于结构体中的数组成员,又和普通的数组有一些区别,无论数组成员是否被使用到,它都是存在的,并且长度和声明的一致。

转载请注明:《OpenGL 的 glGetActiveUniform 和 glGetUniformLocation

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注