如何将多个纹理传递给单个着色器?

39

我正在使用freeglut、GLEW 和DevIL渲染一个带纹理的茶壶, 并使用顶点和片段着色器。在Ubuntu 14.04上,这一切都在OpenGL 2.0和GLSL 1.2上正常工作。

现在,我想为茶壶应用一个凹凸贴图。我的讲师显然不亲自泡茶,所以他不知道它们应该是光滑的。无论如何,我找到了一个看起来不错的旧式凹凸贴图教程,其中包括一个以以下代码开头的片段着色器:

uniform sampler2D DecalTex; //The texture
uniform sampler2D BumpTex; //The bump-map 

他们没有提及如何将两个纹理传递给着色器。
以前我
//OpenGL cpp file
glBindTexture(GL_TEXTURE_2D, textureHandle);

//Vertex shader
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;

//Fragment shader
gl_FragColor = color * texture2D(DecalTex,gl_TexCoord[0].xy);

所以现在我

//OpenGL cpp file
glBindTexture(GL_TEXTURE_2D, textureHandle);
glBindTexture(GL_TEXTURE_2D, bumpHandle);

//Vertex shader
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
gl_TexCoord[1] = gl_TextureMatrix[1] * gl_MultiTexCoord1;

//Fragment shader
gl_FragColor = color * texture2D(BumpTex,gl_TexCoord[0].xy);
//no bump logic yet, just testing I can use texture 1 instead of texture 0

但是这并不起作用。纹理完全消失了(实际上茶壶是白色的)。我尝试过GL_TEXTURE_2D_ARRAY、glActiveTexture和其他一些看似有希望但毫无结果的选项。
在查阅了通常混杂着OpenGL和GLSL新旧参考资料之后,我得出结论,我可能需要使用glGetUniformLocation。我应该如何在OpenGL cpp文件中使用它,将已填充的纹理句柄传递给片段着色器? (This is homework so please answer with minimal code fragments (if at all). Thanks!)
如果不行的话,有人有茶壶套网吗?

我正在使用Python中的GLSL多个纹理。这是我的repo with screenshots - Szabolcs Dombi
2个回答

76

其实非常简单。你所需要做的就是使用 glUniform1i 将采样器绑定到某个纹理单元即可。所以对于你的代码示例来说,假设有两个统一采样器:

uniform sampler2D DecalTex;  // The texture  (we'll bind to texture unit 0)
uniform sampler2D BumpTex;   // The bump-map (we'll bind to texture unit 1)
在你的初始化代码中:
// Get the uniform variables location. You've probably already done that before...
decalTexLocation = glGetUniformLocation(shader_program, "DecalTex");
bumpTexLocation  = glGetUniformLocation(shader_program, "BumpTex");

// Then bind the uniform samplers to texture units:
glUseProgram(shader_program);
glUniform1i(decalTexLocation, 0);
glUniform1i(bumpTexLocation,  1);

好的,着色器 uniform 设置完毕,现在我们开始渲染。为此,您需要使用通常的glBindTexture以及glActiveTexture

glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0
glBindTexture(GL_TEXTURE_2D, decalTexHandle);

glActiveTexture(GL_TEXTURE0 + 1); // Texture unit 1
glBindTexture(GL_TEXTURE_2D, bumpHandle);

// Done! Now you render normally.

在着色器中,您将像以前一样使用纹理采样器:

vec4 a = texture2D(DecalTex, tc);
vec4 b = texture2D(BumpTex,  tc);

注意:对于像凸凹映射(bump-mapping)这样的技术,只需要一个纹理坐标系即可,因为纹理是相同的,只包含不同的数据。因此,你应该将纹理坐标作为顶点属性传递。


4
非常好!我的主要困惑点在于glGetUniformLocation/glUseProgram/glUniform1i的顺序-正如https://dev59.com/wXA65IYBdhLWcg3w6DFO#12065077所述,我必须在最后的glBindTexture之后使用`glActiveTexture(GL_TEXTURE0 + 0);`才能使其工作-感谢提供的khronos链接。 - lofidevops
在这个过程中,glGenSampler和GlBindSampler如何参与其中? - dudu
5
GL_TEXTURE0 + 1 == GL_TEXTURE1 对于任何想知道的人 - Asad-ullah Khan

4

使用以下内容代替:

glUniform1i(decalTexLocation, 0);
glUniform1i(bumpTexLocation,  1);

在你的代码中,你可以有:

layout(binding=0) uniform sampler2D DecalTex;  
// The texture  (we'll bind to texture unit 0)
layout(binding=1)uniform sampler2D BumpTex;   
// The bump-map (we'll bind to texture unit 1)

在你的着色器中,这也意味着你不需要查询位置。


4
"layout(location=0)" 在 GLSL 1.2 中并不存在,因此这不是正确的。 - GenericPtr
4
我认为这是现代OpenGL中优雅的解决方案。使用“layout”绑定可以让您将纹理坐标和纹理索引从顶点数据(glVertexAttribPointer())绑定到着色器,旧的OpenGL标准非常不受欢迎。这样做可以使您的着色器能够从一组采样器中选择正确的索引。 - Pnelego
或许我基本上同意你的想法,@Pnelego,但公正起见,我们指的是非常现代的 - 布局(binding)直到GLSL版本4.2才被引入。就我个人而言,我正在开发一个项目,针对3.3版本进行开发,只是为了更灵活,并且不能依赖此特性。 - Michael Macha
此外,OpenGL 4.5 添加了更简单的glBindTextureUnit()函数,而不必在每次调用glBindTexture()之前设置glActiveTexture()。 - GazTheDestroyer

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