OpenGL4.5 - 绑定多个纹理和采样器

12

enter image description here

我正在尝试理解OpenGL 4.5中的纹理、纹理单元和采样器。我附上一张图片,展示我正在努力理解的内容。我认为在我的例子中一切都是正确的,但是我不太确定右侧带有问号的1D采样器是否正确。

所以,我知道OpenGL提供了许多纹理单元/绑定点,可以将纹理和采样器绑定在一起。

每个绑定点可以支持一个纹理目标(在我的情况下,我将目标GL_TEXTURE_2D和GL_TEXTURE_1D绑定到绑定点0,另一个GL_TEXTURE_2D绑定到绑定点1)。

此外,采样器也可以以类似的方式绑定到这些绑定点(我在图片中将一个2D采样器绑定到绑定点0)。

执行这些操作的函数是glBindTextureUnit和glBindSampler。

我的初步想法是将1D采样器绑定到绑定点0,并在着色器中根据绑定点和采样器类型进行匹配:

layout (binding = 0) uniform sampler1D tex1D;
layout (binding = 0) uniform sampler2D tex2D;

引用来源:
每个纹理图像单元都支持所有目标的绑定。 因此,可以将2D纹理和数组纹理绑定到同一图像单元,或者在两个不同的图像单元中绑定不同的2D纹理而不会相互影响。 那么渲染时使用哪个纹理呢? 在GLSL中,这取决于使用此纹理图像单元的采样器类型。
但是我发现以下声明:
听起来很可疑,好像您可以使用相同的纹理图像单元用于不同的采样器,只要它们具有不同的纹理类型即可。 不要这样做。 规范明确禁止此操作; 如果两个不同的GLSL采样器具有不同的纹理类型,但与同一纹理图像单元相关联,则渲染将失败。 为每个采样器提供不同的纹理图像单元。
因此,我的问题是,如果最终只会将一个采样器绑定到绑定点,强制您进行选择,那么将不同的纹理目标绑定到同一绑定点的目的是什么?
我引用的信息:https://www.khronos.org/opengl/wiki/Texture#Texture_image_units
2个回答

11

那么这是为什么呢?

从前,OpenGL没有纹理单元(这就是为什么glActiveTexture 是独立于 glBindTexture 的一个函数)。事实上,在OpenGL 1.0中甚至没有纹理对象。但是仍然需要不同种类的纹理。你仍然需要能够为2D纹理和3D纹理创建数据。所以他们想出了纹理目标区分,并使用glEnable来确定在渲染操作中使用哪个目标。

当纹理对象出现在GL 1.1中时,他们必须决定纹理对象与目标之间的关系。他们决定一旦对象绑定到目标上,它将永久与该目标相关联。由于需要多个不同类型的纹理,旧的启用功能决定每个目标代表一个单独的对象绑定点。并且让你在glBindTexture中重复绑定点,以便清楚地向代码读者表示您正在干扰哪个绑定点的数据。

切换到OpenGL 1.2,多重纹理出现了。因此,现在他们需要您能够将多个相同目标的纹理绑定到不同的“单元”。但是他们不能更改glBindTexture以指定特定的单元;那将是一种不兼容的更改。

现在,他们可以完全重写纹理的工作方式,为多重纹理等创建一个新的绑定函数。但是OpenGL ARB喜欢向后兼容性;他们喜欢使旧的API函数起作用,无论API的结果看起来如何。因此,他们决定纹理单元将成为整个集合的绑定,每个集合都有一个启用状态,表示要使用哪个目标。而你使用 glActiveTexture 在不同的单元之间切换。

当然,一旦着色器出现,你就可以看到这一切发生了变化。启用状态变成了着色器中的采样器类型。因此现在没有明确的代码描述哪个纹理目标已启用;它只是着色器的东西。所以他们不得不制定一个规则,如果两个采样器是不同类型,则不能使用相同的单元。

这就是为什么每个纹理单元有多个独立的绑定点:OpenGL致力于向后兼容。

最好忽略该功能的存在。绑定你特定着色器需要的正确纹理。所以专注于使用这些函数,不要担心你可能会把两个纹理绑定到同一个目标上。如果你想确保不会意外地使用错误的纹理,可以使用glBindTexturesglBindTextureUnit,并将纹理名称设置为0,这将取消绑定特定纹理单元中的所有目标。


2
@Ripi2:标题的第一个单词从字面上讲就是“OpenGL4.5”。因此,很明显,OP是在询问OpenGL 4.5版本,所以没有必要提供3.3的替代方案。此外,这个问题被标记了“opengl-4”,所以在这里也没有3.3的意义。 - Nicol Bolas
1
@Ripi2:此外,GL 4.4 核心中还添加了 multi-bind 功能。它是一个扩展,也支持一些 3.3 级别的硬件。即使 ARB_DSA 在 3.3 硬件上也有一定的支持 - Nicol Bolas
1
@NicolBolas感谢您的回答。简而言之,将单个纹理和单个采样器分别连接到一个单位纹理中,两者必须在维度上匹配。这正确吗?我已经拿到了4.5编程指南,并尝试遵循它(实际上它甚至没有提到glActiveTexture),并且glBindTextureUnit的描述没有说它会取消绑定给定纹理单元的所有其他目标(您在第一段中提到的第二个效果)。 - Carlos Romero
我明白了,谢谢!这样就清楚了。有一件小事 - 我认为 glBindTextures 是向后兼容的?那么正确的做法(如果坚持使用4.5 / 那本厚厚的书)应该是调用 glBindTextureUnit(unit, 0) 吗? - Carlos Romero
1
@CarlosRomero:glBindTextures可以一次绑定多个纹理,与glBindTextureUnit的作用相同。 - Nicol Bolas
显示剩余2条评论

-2

Let's say you have two GLSL programs:

in progA:

uniform sampler1D progA_sampler1D;
uniform sampler2D progA_sampler2D;

in progB:

uniform sampler1D progB_sampler1D;
uniform sampler2D progB_sampler2D;

And you have several textures with names text1D_1, text1D_2, text1D_3,... text2D_1, text2D_2, etc

Now let's suppose you want progA to sample from text1D_1 and text2D_1 and progB to sample from text1D_2 and text2D_2

You already know that each sampler must be associated with a texture unit, not with a texture name. We can not use the same texture unit for both samplers progA_sampler1D and progA_sampler2D

FIRST OPTION: four texture units

glUseProgram(progA);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_1D, text1D_1);
glUniform1i(locationProgA_forSampler1D, 1); // Not glUniform1i(locationProgA_forSampler1D, GL_TEXTURE0 + 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_1);
glUniform1i(locationProgA_forSampler2D, 2);

glUseProgram(progB);
glActiveTexture(GL_TEXTURE0 + 3);
glBindTexture(GL_TEXTURE_1D, text1D_2);
glUniform1i(locationProgA_forSampler1D, 3);
glActiveTexture(GL_TEXTURE0 + 4);
glBindTexture(GL_TEXTURE_2D, text2D_2);
glUniform1i(locationProgA_forSampler2D, 4);

SECOND OPTION: two texture units

glUseProgram(progA);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_1D, text1D_1);
glUniform1i(locationProgA_forSampler1D, 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_1);
glUniform1i(locationProgA_forSampler2D, 2);

glUseProgram(progB);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_1D, text1D_2);
glUniform1i(locationProgA_forSampler1D, 2);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, text2D_2);
glUniform1i(locationProgA_forSampler2D, 1);

Note that unit GL_TEXTURE0 + 1 has bound two textures text1D_1 and text2D_2 with different types.
On the same way GL_TEXTURE0 + 2 has bound two textures, of types GL_TEXTURE_2D and GL_TEXTURE_1D

WRONG OPTION: two texture units

glUseProgram(progA);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_1D, text1D_1);
glUniform1i(locationProgA_forSampler1D, 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_1);
glUniform1i(locationProgA_forSampler2D, 2);

glUseProgram(progB);
glActiveTexture(GL_TEXTURE0 + 1);
//Next is wrong: two textures (text1D_1 and text1D_2) of same type GL_TEXTURE_1D
glBindTexture(GL_TEXTURE_1D, text1D_2);
glUniform1i(locationProgA_forSampler1D, 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_2);  //Wrong: two textures of same type GL_TEXTURE_2D
glUniform1i(locationProgA_forSampler2D, 2);


2
glUniform1i(locationProgA_forSampler1D, GL_TEXTURE0 + 1); 这不是正确的用法。即使我们忽略了这个错误,你所说的是一些绝不能做的事情。那是非常容易出错的代码。 - Nicol Bolas
@NicolBolas 感谢您指出这个错误。现在已经修复了。当然,“第二个选项”是容易出错的。这只是对OP关于那些表面上矛盾的句子的解释。 - Ripi2

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