39

I am using freeglut, GLEW and DevIL to render a textured teapot using a vertex and fragment shader. This is all working fine in OpenGL 2.0 and GLSL 1.2 on Ubuntu 14.04.

Now, I want to apply a bump map to the teapot. My lecturer evidently doesn't brew his own tea, and so doesn't know they're supposed to be smooth. Anyway, I found a nice-looking tutorial on old-school bump mapping that includes a fragment shader that begins:

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

What they don't mention is how to pass two textures to the shader in the first place.

Previously I

//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);

so now I

//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

but this doesn't work. The texture disappears completely (effectively the teapot is white). I've tried GL_TEXTURE_2D_ARRAY, glActiveTexture and few other likely-seeming but fruitless options.

After sifting through the usual mixed bag of references to OpenGL and GLSL new and old, I've come to the conclusion that I probably need glGetUniformLocation. How exactly do I use this in the OpenGL cpp file to pass the already-populated texture handles to the fragment shader?

(This is homework so please answer with minimal code fragments (if at all). Thanks!)

Failing that, does anyone have a tea cosy mesh?

fintelia
  • 1,201
  • 6
  • 17
lofidevops
  • 15,528
  • 14
  • 79
  • 119
  • I am using multiple textures in GLSL from python. Here is my [repo with screenshots](https://github.com/cprogrammer1994/heightmap-multitexture-terrain) – Szabolcs Dombi Jun 30 '17 at 14:04

2 Answers2

75

It is very simple, really. All you need is to bind the sampler to some texture unit with glUniform1i. So for your code sample, assuming the two uniform samplers:

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)

In your initialization code:

// 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);

OK, shader uniforms set, now we render. To do so, you will need the usual glBindTexture plus 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.

And in the shader, you will use the textures samplers just like you already do:

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

Note: For techniques like bump-mapping, you only need one set of texture coordinates, since the textures are the same, only containing different data. So you should probably pass texture coordinates as a vertex attribute.

glampert
  • 4,371
  • 2
  • 23
  • 49
  • 4
    excellent! it was the order of glGetUniformLocation/glUseProgram/glUniform1i that was my main point of confusion - as mentioned in http://stackoverflow.com/a/12065077/236081 I had to `glActiveTexture(GL_TEXTURE0 + 0);` after the final glBindTexture for it to work - and thanks for the khronos links – lofidevops Aug 11 '14 at 22:17
  • How do glGenSampler and GlBindSampler involve in this procedure? – dudu Mar 25 '19 at 01:46
  • 4
    `GL_TEXTURE0 + 1 == GL_TEXTURE1` for anyone wondering – Asad-ullah Khan Jun 25 '22 at 04:29
4

instead of using:

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

in your code, you can have:

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)

in your shader. That also mean you don't have to query for the location.

merak
  • 83
  • 3
  • 4
    This is not correct for GLSL 1.2 because the "layout(location=0)" does not exist. – GenericPtr Jan 07 '18 at 03:32
  • 3
    I would argue this is elegant solution in modern Opengl. Using the "layout" binding gives one the ability to bind texture cords and the texture index from your vertex data (glVertexAttribPointer()), older openGL standards are VERY far out of favor. and doing this gives your shader the ability to select the correct index out of a group of samplers. – Pnelego Apr 14 '20 at 04:05
  • Perhaps and I generally agree with you, @Pnelego, but to be fair, we mean very modern--layout (binding) wasn't introduced until GLSL version 4.2. I'm personally working on a project where I'm targeting 3.3, just for the sake of flexibility, and can't count on that. – Michael Macha Nov 30 '20 at 21:58
  • Also OpenGL 4.5 adds the simpler glBindTextureUnit() instead of having to set glActiveTexture() each time before your glBindTexture() calls. – GazTheDestroyer Mar 18 '22 at 14:48