views:

228

answers:

3

Hi, There's been similar threads before, but I could not find a solution in them. 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.

Thank you for your time.

+1  A: 

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).

Bahbar
Maybe he is mixing enums from the ARB extension with core functionality (the function)? Strange indeed.
Mads Elvheim
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.
dep
@Mads Elvheim: clarified the last sentence.
Bahbar
@Bahbar : "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...
Calvin1602
@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).
Bahbar
@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?
dep
@Bahbar : thanks, I'll investigate this.
Calvin1602
+1  A: 

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.

bosmacs
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.
dep
A: 

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 tex0;
                                     uniform sampler2D tex1;

                                     void main() {
                                         vec4 s1 = texture2D(tex0, gl_TexCoord[0].st);
                                         vec4 s2 = texture2D(tex1, 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 commend out the glUseProgram call to see the first texture drawn without the shader): alt text

bosmacs
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.
dep