Search code examples
qtopengltexturing

OpenGL Texturing Garbage


I have a problem with texturing – it loads the image correctly, but renders garbage onto the geometry. The geometry itself draws fine (a simple triangle), but no matter which texture I load it just spits random patterns onto the triangle.

I'm using g++ 4.2.1 on Mac OS X with Qt 4.7 and OpenGL

First of all, here's the console output:

BallGLWidget::initializeGL called
Image format is GL_RGB
Checking textures...
glGetError enum value:  GL_NO_ERROR

Also, my logging code for the shader initialization doesn't register any error.

The OpenGL initialization function:

void BallGLWidget::initializeGL()
{

    cout << "BallGLWidget::initializeGL called" << endl;

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    initializeShaders();



    checkOpenGLError();

    glEnableVertexAttribArray(VERTEX_POS_NUM);
    glEnableVertexAttribArray(TEX_POS_NUM);

    glBindAttribLocation(programHandle, VERTEX_POS_NUM, VERTEX_POS_ATTRIB_NAME);
    glBindAttribLocation(programHandle, TEX_POS_NUM, TEX_COORD_ATTRIB_NAME);

    //this MUST be called AFTER glBindAttribLocation
    glLinkProgram(programHandle);
//FIXME:-----------DEBUG-----------
printProgramInfoLog(programHandle);
//-----------END-DEBUG-----------
glUseProgram(programHandle);
//FIXME:-----------DEBUG-----------
printProgramInfoLog(programHandle);
//-----------END-DEBUG-----------


    checkOpenGLError();

    samplerUniformLocation = 
                glGetUniformLocation(programHandle, BALL_SAMPLER_NAME);

    glUniform1f(samplerUniformLocation, 0);
    glActiveTexture(GL_TEXTURE0);


    ball_texture_handle = loadTexture(BALL_IMAGE_PATH);




    //bind it in initialization because we're only using
    //1 texture in the program
    glBindTexture(GL_TEXTURE_2D, ball_texture_handle);
}

Here's the loadTexture function:

GLuint BallGLWidget::loadTexture(const char* filenamePtr)
{
    //create & prepare a temporary texture handle that will be copied to 
    //DesktopMain::ball_texture_handle after this function returns
    GLuint texHandle;
    glGenTextures(1, &texHandle);
    glBindTexture(GL_TEXTURE_2D, texHandle);

    QImage* img = new QImage();
    if(!img->load(filenamePtr))
    {
        //error loading image, handle error
        cerr << "ERROR LOADING TEXTURE" << endl;
    }


    //This is the Qt way- its commented out for conventional OpenGL code
    //bind the texture to the current context
    //GLuint texHandle = bindTexture(*img);

    GLenum openglImageFormat;
    QImage::Format imgFormat = img->format();
    switch(imgFormat)
    {
    case QImage::Format_RGB32:
        openglImageFormat = GL_RGB;

        cout << "Image format is GL_RGB" << endl;
        break;
    case QImage::Format_ARGB32:
        openglImageFormat = GL_RGBA;

        cout << "Image format is GL_RGBA" << endl;
        break;
    //handle this case the same as ARGB32
    case QImage::Format_ARGB32_Premultiplied:
        openglImageFormat = GL_RGBA;

        cout << "Image format is GL_RGBA (premultiplied)" << endl;
        break;
    case QImage::Format_Invalid:
        cerr << "ERROR:  INVALID IMAGE FORMAT" << endl;
        return -1;
        break;
    default:
        cerr << "ERROR:  UNRECOGNIZED IMAGE FORMT" << endl;
        return -1;
        break;
    }

    //use tightly packed pixel values
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    //use linear filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 
            img->width(), img->height(), 0, openglImageFormat,
            GL_UNSIGNED_BYTE, img->bits());

    cerr << "Checking textures..." << endl;
    checkOpenGLError();

    delete img;

    return texHandle;
}

The vertex shader:

attribute vec2 a_v_position;
attribute vec2 a_tex_position;

varying   vec2 tex_coord_output;


void main()
{
    //copy attributes to varyings for use in the frag shader
    tex_coord_output = a_tex_position;

    gl_Position = vec4(a_v_position, 0.0, 1.0);
}

The fragment shader:

varying vec2 tex_coord_output;

uniform sampler2D ballsampler;

void main()
{
    gl_FragColor = texture2D(ballsampler, tex_coord_output);
}

EDIT:

A screenshot of the program, as requested.

https://docs.google.com/open?id=0B8xCefwW3X4TY2Y3N2M0MGYtMDQ0NS00MDk4LWEzODgtNDc3OWFkODI3ZWE3

EDIT:

The attribute locations were off because apparently glBindAttribLocation only works if called BEFORE the program object is linked (http://www.opengl.org/sdk/docs/man/xhtml/glBindAttribLocation.xml). I changed the code above accordingly, but the program still looks like below (there is still a problem with the texturing...):

I get the following result: https://docs.google.com/open?id=0B8xCefwW3X4TNWE0YTQ5MTktZTA2Yy00YmI4LWJmMjMtYTlhOTYxMGNkMTk0


Solution

  • Break it down, try something simple:

    vert.glsl

    #version 120
    
    uniform mat4 projection;
    uniform mat4 modelview;
    
    attribute vec2 position;
    attribute vec2 texcoord;
    
    varying vec2 fragTexCoord;
    
    void main(void)
    {
        fragTexCoord = texcoord;
        gl_Position = projection * modelview * vec4( position, 0.0, 1.0 );
    }
    

    frag.glsl

    #version 120
    
    uniform sampler2D texture;
    
    varying vec2 fragTexCoord;
    
    void main(void)  
    {     
        gl_FragColor = texture2D( texture, fragTexCoord );    
    }
    

    main.cpp

    #include <GL/glew.h>
    #include <GL/glut.h>
    
    #include <cstdlib>
    #include <stdexcept>
    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <vector>
    
    using namespace std;
    
    
    GLuint CreateShader( const GLenum& aShaderType, const string& aShaderSource )
    {
        GLuint shader = glCreateShader( aShaderType );
    
        const GLchar* shaderString = aShaderSource.c_str();
        GLint shaderLength = aShaderSource.size();
        glShaderSource( shader, 1, &shaderString, &shaderLength );
    
        glCompileShader( shader );
    
        GLint compiled;
        glGetShaderiv( shader, GL_COMPILE_STATUS, &compiled );    
        if( GL_FALSE == compiled )
        {
            // compile failure, dump log
            GLint loglen;
            glGetShaderiv( shader, GL_INFO_LOG_LENGTH , &loglen);
    
            vector< char > log( loglen );
            glGetShaderInfoLog( shader, loglen, NULL, &log[0] );
    
            string type;
            switch( aShaderType )
            {
                case GL_VERTEX_SHADER:      type = "GL_VERTEX_SHADER";      break;
                case GL_FRAGMENT_SHADER:    type = "GL_FRAGMENT_SHADER";    break;
                default:                    type = "UNKNOWN SHADER";        break;
            }
    
            stringstream err;
            err << "*** " << type << " ***" << endl;
            err << aShaderSource;
            err << "*** Compilation Log ***" << endl;
            err << string( log.begin(), log.end() );
    
            throw std::logic_error( err.str() );         
        }
    
        return shader;
    }
    
    
    GLuint CreateProgram( const string& aVertexShader, const string& aFragmentShader )
    {
        GLuint vert = CreateShader( GL_VERTEX_SHADER, aVertexShader );
        GLuint frag = CreateShader( GL_FRAGMENT_SHADER, aFragmentShader );
    
        GLuint program = glCreateProgram();
        glAttachShader( program, vert );
        glAttachShader( program, frag );
        glLinkProgram( program );
    
        glDeleteShader( vert );
        glDeleteShader( frag );
    
        GLint linked;
        glGetProgramiv( program, GL_LINK_STATUS, &linked );    
        if( GL_FALSE == linked )
        {
            // link failure, dump log
            GLint loglen;
            glGetProgramiv( program, GL_INFO_LOG_LENGTH , &loglen);
    
            vector< char > log( loglen );
            glGetProgramInfoLog( program, loglen, NULL, &log[0] );
    
            stringstream err;
            err << "*** Link log ***" << endl;
            err << string( log.begin(), log.end() );
            throw std::logic_error( err.str() );         
        }
    
        return program;
    }
    
    
    string LoadFile( const string& filename )
    {
        ifstream infile(filename.c_str(), ios::binary);
        istreambuf_iterator<char> begin(infile), end;
        return string(begin, end);
    }
    
    
    GLuint prog = 0;
    GLuint tex = 0;
    void init()
    {
        GLenum glewError = glewInit();
        if( GLEW_OK != glewError )
        {
            stringstream err;
            err << "GLEW error: " << glewGetErrorString(glewError) << endl;
            throw std::logic_error( err.str() );      
        }    
    
        cout << "GLEW_VERSION : " << glewGetString(GLEW_VERSION) << endl;
        cout << "GL_VERSION   : " << glGetString(GL_VERSION) << endl;
        cout << "GLSL VERSION : " << glGetString(GL_SHADING_LANGUAGE_VERSION) << endl;
        cout << "GL_VENDOR    : " << glGetString(GL_VENDOR) << endl;
        cout << "GL_RENDERER  : " << glGetString(GL_RENDERER) << endl;    
    
        if( !GLEW_VERSION_2_1 )
        {
            stringstream err;
            err << "OpenGL 2.1 or better required for GLSL support." << endl;
            throw std::logic_error( err.str() ); 
        }
    
        // load shaders
        string vert = LoadFile( "vert.glsl" );
        string frag = LoadFile( "frag.glsl" );
        prog = CreateProgram( vert, frag );
    
        // create random texture
        const unsigned int width = 32;
        const unsigned int height = 32;
        const unsigned int channels = 3;
        unsigned char buffer[ width * height * channels ];
        for( unsigned int i = 0; i < width * height; ++i )
        {
            buffer[i*channels + 0] = rand()%255;
            buffer[i*channels + 1] = rand()%255;
            buffer[i*channels + 2] = rand()%255;
        }
    
        // upload texture data
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glGenTextures(1, &tex);
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        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, channels, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
    }
    
    
    struct Vertex
    {
        Vertex() : x(0), y(0), s(0), t(0) {}
        Vertex( float _x, float _y, float _s, float _t ) : x(_x), y(_y), s(_s), t(_t) {}
        float x, y;
        float s, t;
    };
    
    void display()
    {
        static float currentTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
        float newTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
        float frameTime = newTime - currentTime;
        currentTime = newTime;
    
        vector< Vertex > verts;
        verts.push_back( Vertex( -1, -1, 0, 0 ) );
        verts.push_back( Vertex(  1, -1, 1, 0 ) );
        verts.push_back( Vertex(  1,  1, 1, 1 ) );
        verts.push_back( Vertex( -1,  1, 0, 1 ) );
    
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
    
        static float angle = 0;
        angle += 60 * frameTime;
        glRotatef( angle, 0, 0, 1 );
        glScalef( 5, 5, 5 );
    
        glUseProgram( prog );
    
        // load uniforms
        GLfloat projection[16];
        glGetFloatv( GL_PROJECTION_MATRIX, projection );
        GLint projection_loc = glGetUniformLocation( prog, "projection" );
        glUniformMatrix4fv( projection_loc, 1, GL_FALSE, projection );
    
        GLfloat modelview[16];
        glGetFloatv( GL_MODELVIEW_MATRIX, modelview );
        GLint modelview_loc = glGetUniformLocation( prog, "modelview" );
        glUniformMatrix4fv( modelview_loc, 1, GL_FALSE, modelview );
    
        GLint texture_loc = glGetUniformLocation( prog, "texture" );
        glUniform1i( texture_loc, 0 );    
        glActiveTexture( GL_TEXTURE0 );
        glBindTexture( GL_TEXTURE_2D, tex );
    
        // load attributes
        GLint position_loc = glGetAttribLocation( prog, "position" );
        glVertexAttribPointer( position_loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &verts[0].x );
        glEnableVertexAttribArray( position_loc );
    
        GLint texcoord_loc = glGetAttribLocation( prog, "texcoord" );
        glVertexAttribPointer( texcoord_loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &verts[0].s );
        glEnableVertexAttribArray( texcoord_loc );
    
        // render
        glDrawArrays( GL_QUADS, 0, verts.size() );
    
        glDisableVertexAttribArray( position_loc );
        glDisableVertexAttribArray( texcoord_loc );
    
        glutSwapBuffers();
    }
    
    
    void reshape(int w, int h)
    {
        glViewport(0, 0, w, h);
    
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        double aspect = (double)w / (double)h;
        glOrtho(-10*aspect, 10*aspect, -10, 10, -1, 1);
    }
    
    
    int main(int argc, char **argv)
    {
        glutInit(&argc, argv);
        glutInitWindowSize(800,600);
        glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
        glutCreateWindow("GLSL");
    
        try
        {
            init();
        }
        catch( std::exception& e )
        {
            cout << "Init failure: " << endl << e.what() << endl;
            return EXIT_FAILURE;
        }
    
        glutDisplayFunc(display);
        glutIdleFunc(display);
        glutReshapeFunc(reshape);
        glutMainLoop();
        return EXIT_SUCCESS;
    }