从 GLSL 着色器中获取常量

Posted

技术标签:

【中文标题】从 GLSL 着色器中获取常量【英文标题】:Getting a constant from a GLSL shader 【发布时间】:2014-10-24 03:44:22 【问题描述】:

我有一个用 GLSL 编写的着色器,其中包含用于保存光照数据的结构数组。我使用一个常量来声明数组大小,这是一种很好的做法。假设这个变量被声明为

const int NUM_POINT_LIGHTS = 100;

我如何使用 C++ 将这些数据从着色器中提取出来,以便我的 C++ 程序确切地知道它有多少灯可用?我尝试将其声明为

const uniform int NUM_POINT_LIGHTS = 100;

正如预期的那样,这不起作用(尽管很奇怪,似乎统一规范只是简单地覆盖了 const 规范,因为 OpenGL 抱怨说我正在使用非 const 值初始化一个数组)。我也试过了

const int NUM_POINT_LIGHTS = 100;
uniform numPointLights = NUM_POINT_LIGHTS;

这会起作用,除了 GLSL 优化了未使用的制服这一事实,所以我必须跟踪 glsl 以认为制服以某种方式被使用,以便能够获取数据。我无法找到任何其他方法来查询程序以获取常量值。有没有人知道我如何能够从着色器中提取一个常量,以便我的程序获取在着色器中进行功能编码的信息以供使用?

【问题讨论】:

一般来说,以这种方式编写的常量字面量在着色器编译后会变成制服。在 NV GLSL 实现上,它可能被称为 _main_NUM_POINT_LIGHTS。我建议您枚举程序中的所有制服,看看是否会发生这种情况。我不确定你最终要完成什么......如果你想要一个数据数组,你最好使用 UBO,因为它不受相同的约束(例如,制服的最大数量组件)作为制服。 我假设 UBO 的组件计入了统一组件的最大数量,尽管通过添加一个包含多个元素的数组相对容易确认它们没有片段着色器的 2048 个统一组件的 460m 上限。我实际上为此使用了 UBO,但即使使用 UBO,如果能够让 c++ 程序动态确定它可用的灯的数量,那就太好了。我会看看创建了哪些制服,尽管这听起来像是一个可能无法移植到其他硬件的解决方案。 没有这样的运气,我的常数没有制服。 【参考方案1】:

我认为你不能直接获取常量的值。但是,我认为您必须使用常量的值,很可能是统一数组的大小。如果是这种情况,您可以获取统一数组的大小,从而间接获取常量的值。

假设你的着色器包含这样的东西:

const int NUM_POINT_LIGHTS = 100;
uniform vec3 LightPositions[NUM_POINT_LIGHTS];

那么你可以先得到这个制服的索引:

const GLchar* uniformName = "LightPositions";
GLuint uniformIdx = 0;
glGetUniformIndices(program, 1, &uniformName, &uniformIdx);

使用此索引,您可以检索此制服的属性:

const int nameLen = strlen("LightPositions") + 1;
const GLchar name[nameLen];
GLint uniformSize = 0;
GLenum uniformType = GL_NONE;
glGetActiveUniform(program, uniformIdx, nameLen, NULL,
                   &uniformSize, &uniformType, name);

uniformSize 应该是NUM_POINT_LIGHTS 常量的值。请注意,我没有尝试过这个,但我希望我根据文档得到了正确的论点。

一个有点难看但可能非常实用的解决方案当然是从着色器源代码中解析出值。由于在将其传递给glShaderSource() 之前无论如何都需要阅读它,因此选择常量值应该很容易。

如果您的主要目标是避免在多个位置使用常量,则另一种选择是在您的 C++ 代码中定义它,并在读取着色器代码之后和传递之前将常量定义动态添加到着色器代码中它去glShaderSource()

【讨论】:

+1 用于将#define NUM_POINT_LIGHTS 动态注入着色器源 不幸的是,您不能在结构数组上使用 glGetUniformIndices,因为每个单独的元素都有自己的索引,而不是数组有一个。但是,无论是从源中解析值还是将其动态添加到源中,都可以作为实现此功能的真正好方法。动态添加它实际上可能是我最好的选择,因为它允许使用变量编写多个着色器并始终与程序功能保持同步。【参考方案2】:

您不能只从 GLSL 程序中查询常量。 GLSL 规范中没有定义这样的东西。

统一缓冲区对象可能是解决统一优化问题的一种方法。 https://www.opengl.org/wiki/Uniform_Buffer_Object

【讨论】:

不幸的是,UBO 成员不能有初始化程序。 :( 我认为你也在努力让一切充满活力。你完全可以做依赖于供应商的解决方法,甚至在你读入着色器时查看它本身的源代码,但是你在 C++ 中也定义了常量,你会失去什么?任何解决方法都将非常复杂。

以上是关于从 GLSL 着色器中获取常量的主要内容,如果未能解决你的问题,请参考以下文章

顶点纹理获取(在顶点着色器中读取纹理)

为啥我的着色器中的 GLSL 纹理坐标不是线性的?

如果未绑定,统一值是不是保留在 GLSL 着色器中?

GLSL 像素化着色器中的边框伪影

GLSL索引绘图并设置纹理案例

一次调用可以在 glsl 计算着色器中进行乒乓传播吗?