Mapping a texture in OpenGL

Viewed 59

I am trying to map a texture to a simple quad for the first time, but all it won't render. I am using freeglut for the implementation, and the stb_image.h header to load the texture. The code:

#include <GL/glut.h>   
#include <stb_image.h>
#include <iostream>
int ww = 500, wh = 500;

void display()
{
    glClearColor(1.0, 1.0, 0.7, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glBegin(GL_QUADS);
    glEnable(GL_TEXTURE_2D);
    glTexCoord2f(0.0, 0.0); glVertex3f(10,10,0);
    glTexCoord2f(0.0, 1.0); glVertex3f(10,wh-10,0);
    glTexCoord2f(1.0, 1.0); glVertex3f(ww-10,wh,0);
    glTexCoord2f(1.0, 0.0); glVertex3f(ww-10,10,0);
    glDisable(GL_TEXTURE_2D);
    glEnd();
    glFlush();
}


void myinit()
{   
    glViewport(0, 0, ww, wh);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)ww, 0.0, (GLdouble)wh);
    glMatrixMode(GL_MODELVIEW);

    unsigned int texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    int width, height, nrChannels;
    unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);
}
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
    glutInitWindowSize(ww, wh);
    glutCreateWindow("texture-try");
    glutDisplayFunc(display);
    myinit();
    glutMainLoop();
    return 0;
}

It successfully loads the texture, but the quad appears just white. What am I missing?

Edit:

I moved the glBegin(GL_QUADS); and glEnd(); inside the glEnable(GL_TEXTURE_2D); and glDisable(GL_TEXTURE_2D); commands as was suggested, but it still won't render the texture.

I also checked the glGetError command, but it returns GL_NO_ERROR.

2 Answers

From the OpenGL documentation for glBegin:

Only a subset of GL commands can be used between glBegin and glEnd. The commands are glVertex, glColor, glSecondaryColor, glIndex, glNormal, glFogCoord, glTexCoord, glMultiTexCoord, glVertexAttrib, glEvalCoord, glEvalPoint, glArrayElement, glMaterial, and glEdgeFlag. Also, it is acceptable to use glCallList or glCallLists to execute display lists that include only the preceding commands. If any other GL command is executed between glBegin and glEnd, the error flag is set and the command is ignored.

In other words, you can't enable/disable texturing within a glBegin/glEnd block.

void display()
{
    glClearColor(1.0, 1.0, 0.7, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    // move to before glBegin
    glEnable(GL_TEXTURE_2D);
    
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(10,10,0);
    glTexCoord2f(0.0, 1.0); glVertex3f(10,wh-10,0);
    glTexCoord2f(1.0, 1.0); glVertex3f(ww-10,wh,0);
    glTexCoord2f(1.0, 0.0); glVertex3f(ww-10,10,0);
    glEnd();

    // move to after glEnd
    glDisable(GL_TEXTURE_2D);

    glFlush();
}

Several things:

  • The aforementioned "don't use prohibited functions within a glBegin()/glEnd() pair" issue; as of posting this hasn't been fixed in the question code.
  • GL_LINEAR_MIPMAP_LINEAR is being used without providing any mipmaps. Drop to GL_LINEAR or provide some mipmaps.
  • Pass 3 to stbi_load()'s desired_channels parameter (instead of the current 0) to guarantee you get 3-channel image data instead of just assuming nrChannels is 3 for all input images.
  • Using GL_RGB with the default GL_UNPACK_ALIGNMENT of 4 can result in wonky display if your image width isn't a nice multiple of 4 so drop that down to 1 via glPixelStorei(GL_UNPACK_ALIGNMENT, 1) before calling glTexImage2D().
  • Switch to GLUT_DOUBLE & glutSwapBuffers() instead of using GLUT_SINGLE and glFlush().

All together:

#include <GL/glut.h>   
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <iostream>
int ww = 500, wh = 500;

void display()
{
    glClearColor(1.0, 1.0, 0.7, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glEnable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(10,10,0);
    glTexCoord2f(0.0, 1.0); glVertex3f(10,wh-10,0);
    glTexCoord2f(1.0, 1.0); glVertex3f(ww-10,wh,0);
    glTexCoord2f(1.0, 0.0); glVertex3f(ww-10,10,0);
    glEnd();
    glDisable(GL_TEXTURE_2D);
    glutSwapBuffers();
}


void myinit()
{   
    glViewport(0, 0, ww, wh);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)ww, 0.0, (GLdouble)wh);
    glMatrixMode(GL_MODELVIEW);

    unsigned int texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    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_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    int width, height, nrChannels;
    // https://commons.wikimedia.org/wiki/File:Container.JPG
    unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannels, 3);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);
}
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowSize(ww, wh);
    glutCreateWindow("texture-try");
    glutDisplayFunc(display);
    myinit();
    glutMainLoop();
    return 0;
}

screenshot

Related