Multiple textures in GLSL - only one works
Asked Answered
D

5

23

My problem is getting more than one texture accessible in a GLSL shader. Here's what I'm doing:

Shader:

uniform sampler2D sampler0;
uniform sampler2D sampler1;
uniform float blend;
void main( void )
{
    vec2 coords = gl_TexCoord[0];
    vec4 col = texture2D(sampler0, coords);
    vec4 col2 = texture2D(sampler1, coords);
    if (blend > 0.5){
        gl_FragColor = col;
    } else {
        gl_FragColor = col2;
    }
};

So, I simply choose between the two color values based on a uniform variable. Simple enough (this is a test), but instead of the expected behavior, I get all black when blend <= 0.5.

OpenGL code:

m_sampler0location = m_shader.FindUniform("sampler0");
m_sampler1location = m_shader.FindUniform("sampler1");
m_blendlocation = m_shader.FindUniform("blend");

glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
m_extensions.glUniform1iARB(m_sampler0location, 0);
glBindTexture(GL_TEXTURE_2D, Texture0.Handle);  
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
m_extensions.glUniform1iARB(m_sampler1location, 1);
glBindTexture(GL_TEXTURE_2D, Texture1.Handle);
glBegin(GL_QUADS);
    //lower left
    glTexCoord2f(0, 0);
    glVertex2f(-1.0, -1.0);
    //upper left
    glTexCoord2f(0, maxCoords0.t);
    glVertex2f(-1.0, 1.0);
    //upper right
    glTexCoord2f(maxCoords0.s, maxCoords0.t);
    glVertex2f(1.0, 1.0);
    //lower right
    glTexCoord2f(maxCoords0.s, 0);
    glVertex2f(1.0, -1.0);
glEnd()

The shader is compiled and bound before all this. All the sanity checks in that process indicate that it goes ok. As I said, the value of col in the shader program reflects fragments from a texture; the value of col2 is black. The texture that is displayed is the last active texture - if I change the last glBindTexture to bind Texture0.Handle, the texture changes. Fixed according to Bahbar's reply.

As it is, the scene renders all black, even if I add something like gl_FragColor.r = blend; as the last line of the shader. But, if I comment out the call glActiveTexture(GL_TEXTURE1);, the shader works again, and the same texture appears in both sampler0 and sampler1.

What's going on? The line in question, glActiveTexture(GL_TEXTURE1);, seems to work just fine, as evidenced by a subsequent glGetIntegerv(GL_ACTIVE_TEXTURE, &anint). Why does it break everything so horribly? I've already tried upgrading my display drivers.

Dungdungan answered 25/8, 2010 at 18:54 Comment(2)
Have you found a solution to your problem? I have the exact same issue.Stucco
Yes, the tagged solution worked for me.Dungdungan
W
24

Here's a basic GLUT example (written on OS X, adapt as needed) that generates two checkerboard textures, loads a shader with two samplers and combines them by tinting each (one red, one blue) and blending. See if this works for you:

#include <stdio.h>
#include <stdlib.h>

#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>

#define kTextureDim 64

GLuint t1;
GLuint t2;

/* adapted from the red book */
GLuint makeCheckTex() {
    GLubyte image[kTextureDim][kTextureDim][4]; // RGBA storage

    for (int i = 0; i < kTextureDim; i++) {
        for (int j = 0; j < kTextureDim; j++) {
            int c = ((((i & 0x8) == 0) ^ ((j & 0x8)) == 0))*255;
            image[i][j][0]  = (GLubyte)c;
            image[i][j][1]  = (GLubyte)c;
            image[i][j][2]  = (GLubyte)c;
            image[i][j][3]  = (GLubyte)255;
        }
    }

    GLuint texName;
    glGenTextures(1, &texName);    
    glBindTexture(GL_TEXTURE_2D, texName);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureDim, kTextureDim, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);

    return texName;
}

void loadShader() {
#define STRINGIFY(A) #A

    const GLchar* source = STRINGIFY(

                                     uniform sampler2D tex1;
                                     uniform sampler2D tex2;

                                     void main() {
                                         vec4 s1 = texture2D(tex1, gl_TexCoord[0].st);
                                         vec4 s2 = texture2D(tex2, gl_TexCoord[0].st + vec2(0.0625, 0.0625));
                                         gl_FragColor = mix(vec4(1, s1.g, s1.b, 0.5), vec4(s2.r, s2.g, 1, 0.5), 0.5);
                                     }

                                     );

    GLuint program = glCreateProgram();
    GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(shader, 1, &source, NULL);
    glCompileShader(shader);

    GLint logLength;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar* log = (GLchar*)malloc(logLength);
        glGetShaderInfoLog(shader, logLength, &logLength, log);
        printf("Shader compile log:\n%s\n", log);
        free(log);
    }

    glAttachShader(program, shader);  
    glLinkProgram(program);

    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar* log = (GLchar*)malloc(logLength);
        glGetProgramInfoLog(program, logLength, &logLength, log);
        printf("Program link log:\n%s\n", log);
        free(log);
    }

    GLuint t1Location = glGetUniformLocation(program, "tex1");
    GLuint t2Location = glGetUniformLocation(program, "tex2");

    glUniform1i(t1Location, 0);
    glUniform1i(t2Location, 1);

    glUseProgram(program);
}


void init()
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_FLAT);

    t1 = makeCheckTex();
    t2 = makeCheckTex();

    loadShader();
}


void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, t1);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, t2);

    glBegin(GL_QUADS);
    //lower left
    glTexCoord2f(0, 0);
    glVertex2f(-1.0, -1.0);
    //upper left
    glTexCoord2f(0, 1.0);
    glVertex2f(-1.0, 1.0);
    //upper right
    glTexCoord2f(1.0, 1.0);
    glVertex2f(1.0, 1.0);
    //lower right
    glTexCoord2f(1.0, 0);
    glVertex2f(1.0, -1.0);
    glEnd();

    glutSwapBuffers();
}


void reshape(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-2, 2, -2, 2, -2, 2);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}


int main(int argc, char **argv)
{
    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);

    glutInitWindowSize(512, 512);
    glutInitWindowPosition(0, 0);

    glutCreateWindow("GLSL Texture Blending");

    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glutIdleFunc(display);

    init();

    glutMainLoop();
    return 0;
}

Hopefully the result will look something like this (you can comment out the glUseProgram call to see the first texture drawn without the shader): alt text

Whitewood answered 27/8, 2010 at 15:6 Comment(4)
Ok, this works. Thanks so much, now I have a base to work from in trying to figure out the problem in my own program.Dungdungan
Why are the uniforms inside the shader called "tex0, tex1", but when calling "glGetUniformLocation" they are named "tex1, tex2"?Concentre
A better question might be: how come this works, even though the return location of one of the uniforms is "-1"? I have tested setting the uniform names to any string and it will still work - anyone could explain this, please ?Concentre
Nice catch, that's an error. I imagine it works (though it's by no means guaranteed to) b/c the last texture bound is t2.Whitewood
T
13

Just to help other people who might be interested in using multiple textures and will feel hopeless after days searching for an answer. I found that you need to call

glUseProgram(program);

GLuint t1Location = glGetUniformLocation(program, "tex1");
GLuint t2Location = glGetUniformLocation(program, "tex2");

glUniform1i(t1Location, 0);
glUniform1i(t2Location, 1);

In that order for it to work (glUseProgram is the last instruction for 99% of the sample code I've found online). Now it may be only the case for me and not affect anyone else on Earth, but just in case I thought I'd share.

Threw answered 28/3, 2014 at 12:23 Comment(3)
that's right, the man page says "glUniform operates on the program object that was made part of the current state by calling glUseProgram()"Checky
this didn't work for me (in OpenGL 2.0 / GLSL 1.2), but glGetUniformLocation/glUseProgram/glUniform1i did - https://mcmap.net/q/400656/-how-can-i-pass-multiple-textures-to-a-single-shaderVelutinous
Thanks so much! I had the same problem. This is super helpfulAzzieb
B
10

Quite late reply, but for anybody encountering this - I encountered same problem and after short fiddling, I realized that calling glActiveTexture(GL_TEXTURE0) fixes the issue. It seems something down the line gets confused if active texture unit is not 'reset' to zero. This could be system-dependent behavior; I'm running 64bit Archlinux with Mesa 8.0.4.

Bensen answered 22/8, 2012 at 1:2 Comment(2)
This fixed an odd related problem I was having as well. Very bizarroFuthark
An absolute live saver! I ran through my code, deconstructing it 'til I was crosseyed and searching everywhere in docs and online, and I couldn't find a single error. I'd upvote you x 100 if I could, John :^) (win7 64bit GTX780)Sheridansherie
W
2

When compiling your shader to test, I found two errors:

  1. coords should be assigned the st portion of the 4-component gl_TexCoord, e.g.

    vec2 coords = gl_TexCoord[0].st;
    
  2. The shader should not end with a semicolon.

Are you checking anywhere in your main program that shader compiles correctly? You may want to look at GL_COMPILE_STATUS via glGetShader and glGetShaderInfoLog.

Whitewood answered 26/8, 2010 at 17:4 Comment(1)
Ok, well I changed the assignment but it had no effect. The semicolon was a typo, so I have removed it from the original message. I already check against GL_COMPILE_STATUS, and the shader compiles and binds correctly in all cases, but only actually works if I leave out the line glActiveTexture(GL_TEXTURE1); - even if I replace the whole thing with a oneliner that just outputs white.Dungdungan
Y
0

It sounds like your glActiveTexture call does not work. Are you sure you set up the function pointer correctly ?

Verify by calling glGetIntegerv(GL_ACTIVE_TEXTURE, &anint) after having called your glActiveTexture(GL_TEXTURE1).

Also glEnable(GL_TEXTURE_2D) are not useful. The shader itself specifies what texture units to use, and which target of each unit to "enable".


Edit to add:

Well, your new situation is peculiar. The fact that you can't even show red is weird, in particular (well, did you set alpha to 1 just to make sure ?).

That said, you should restore GL_TEXTURE0 as the glActiveTexture after you're done setting the texture unit 1 (i.e. after the glBindTexture call).

Yanyanaton answered 25/8, 2010 at 19:27 Comment(6)
Maybe he is mixing enums from the ARB extension with core functionality (the function)? Strange indeed.Overspill
Thank you for your speedy response. You were right - GL_ACTIVE_TEXTURE was not set correctly. I have now fixed the function pointer and the value changes as it should - but now, all I get is black. I have updated the question with my new situation.Dungdungan
@Mads Elvheim: clarified the last sentence.Yanyanaton
@Yanyanaton : "Also glEnable(GL_TEXTURE_2D) are not useful. The shader itself specifies what texture units to use, and which target of each unit to "enable"" I'm 90% sure it's false, do you have any reference please ? I've always enabled the target when changing the active texture...Curassow
@Calvin1602: The shader_objects extension issue 25 covers it indirectly. the spec 3.0 (introducing deprecation) list this as deprecated (p409, first bullet, "Enable targets of all dimensionalities"). More recent core specs (4.0, e.g.) don't even mention that glEnable used to take GL_TEXTURE_2D (since it was controlling fixed function).Yanyanaton
@Bahbar: I believe it's not an alpha issue since the same shader works fine if I just comment out the glActiveTexture(GL_TEXTURE1);. I added the call to restore the active texture unit to 0 (thanks for the reminder!), but the issue persists. Uh, since this looks like it belongs in the "it should not do that" category, is there any example code for sampling multiple textures from a shader (incl. the OpenGL setup part) somewhere?Dungdungan

© 2022 - 2024 — McMap. All rights reserved.