GLSL结构体中的sampler2D

7
在GLSL中,当我尝试将一个包含sampler2D属性的统一结构体传递给一个前向声明的函数时,似乎存在着着色器链接错误。如果我删除前向声明并将函数移动到main之上,代码能够正常工作。这是不合法的代码吗?
#version 330 core

in vec2 texcoords;
out vec4 color;

struct Material{
    sampler2D tex; // Sampler inside a struct
}; 

uniform Material material;

// Forward Declaration
vec4 add(Material m);

void main() {
    color = add(material);
}

// Function Definition
vec4 add(Material m) {
    return vec4(texture(m.tex, texcoords));
}

// C++
glUniform1f(glGetUniformLocation(shader, "material.tex"), 0);
glBindTexture(GL_TEXTURE_2D, texture);
编辑: 经过一番搜索,发现这是AMD驱动程序中的一个错误。我个人使用的是相当古老的ATI Mobility Radeon HD 4670,但它仍然可以运行OpenGL 3.3。在AMD的论坛上,我找到了一个类似的帖子,因此有趣的是要知道这对AMD图形卡有多大影响。因为如果您在英特尔或NVIDIA上进行开发,那么您怎么知道您的着色器不会在某些AMD图形卡上编译?我们应该保持安全,不使用具有采样器的结构体原型,甚至完全不将采样器放入结构体中吗?... 值得注意的是,WebGL甚至不允许在结构体内部使用采样器。

错误信息:

Vertex shader(s) failed to link, fragment shader(s) failed to link.
unexpected error.
unexpected error.

你说有一个链接错误。链接错误是由于将顶点着色器和片段着色器链接在一起,因此在这个阶段出现了问题。就我所知,这段代码本身看起来是正确的。你能否也发布一下顶点着色器代码? - Joey Dewd
顺便提一下,由于纹理位置是整数而不是浮点数,所以您应该使用 glUniform1i 而不是 glUniform1f - Joey Dewd
1
嘿,你就是那个Iggy!真巧啊 ;) 尽管如此,我的确认为片段着色器看起来很好 - 在GLSL中允许使用这样的函数原型。我不明白它为什么会抱怨(事实上,你的源代码在这里甚至编译得非常完美)。 - Joey Dewd
可能是的,不知道是什么导致了这种行为。 - Joey Dewd
实际上没关系,它还是会出现相同的错误信息!>< - Iggy
显示剩余6条评论
1个回答

7
这实际上不应该工作,因为您无法实例化包含不透明数据类型(sampler、image、原子计数器)的结构体。将不透明类型作为统一变量的结构体是可以接受的,但是您不能通过传递Material实例来实现add(...)函数。 数据类型(GLSL)- 不透明类型

不透明类型的变量只能以两种方式之一声明。它们可以在全局范围内声明为统一​​变量。这些变量可以是不透明类型的数组。它们可以声明为结构体的成员,但如果是这样,则该结构体仅可用于声明统一变量(或声明本身是统一变量的结构体/数组的成员)。 它们不能成为缓冲区支持的接口块或输入/输出变量的一部分,直接或间接地。


这个代码的更改应该适用于所有符合OpenGL标准的实现:
// Must be in, because you cannot assign values to an opaque type.
vec4 add (in sampler2D tex) {
  return vec4(texture(tex, texcoords));
}

这也可以工作,因为它使用了材质中的采样器 material.tex 统一变量:
vec4 add (void) {
  return vec4(texture(material.tex, texcoords));
}

你也可以使用预处理器宏来实现你现在尝试做的事情。例如,实现我在答案中写的第一种方式的add(...),然后使用#define ADD(m) add(m.tex)。然后你就可以在几乎任何GLSL实现上使用ADD(material)而不会出现问题。 - Andon M. Coleman
有趣的见解。然而,传递给 add(...) 的材料实例仍然是一致的;没有实例化正在进行。我仍然困惑的是,如果它在原地定义,你可以运行 add(Material)。只有在前向声明时才会出现问题。 - Iggy
在这个例子中,material.tex 实际上是一个统一的变量。结构体作为统一变量基本上只是一种名称空间的东西。虽然它没有前向声明,但它确实很有趣。 - Andon M. Coleman
结构体和命名空间都很合理。奇怪的是,前向声明版本在其他图形卡上可以工作。这可能是完全不同的问题。再次值得注意的是,这是一个链接错误。 - Iggy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接