16. OpenGL Texturing (Version 2.0)

Introduction to Texturing in OpenGL

Textures are an essential part of 3D graphics. They allow you to wrap detailed images around your objects, making them look more realistic. This tutorial will guide you through setting up textures in OpenGL, focusing on Windows for simplicity.

To get started with texturing, there are a few key steps:
1. Load the texture image into your program.
2. Configure the texture parameters.
3. Apply the texture to your objects using texture coordinates.
4. Clean up when you’re done to free system resources.

Let’s dive into each of these steps.

Loading a Texture

The first step is loading the texture image. In this tutorial, we’ll use a RAW file, which is a simple uncompressed image format. You’ll need the following function to load the texture into OpenGL:

GLuint LoadTexture(const char *filename, int width, int height) {
    GLuint texture;
    unsigned char *data;
    FILE *file;

    // Open the file
    file = fopen(filename, "rb");
    if (file == NULL) return 0;

    // Allocate memory for the texture
    data = (unsigned char *)malloc(width * height * 3);

    // Read the file data
    fread(data, width * height * 3, 1, file);
    fclose(file);

    // Generate and bind the texture
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    // Set texture environment parameters
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    // Set texture filters
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // Set texture wrapping
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // Generate the texture
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

    // Free the texture data from memory
    free(data);

    return texture;
}

Texture Filters and Wrapping

When working with textures, you can control how they appear using filters and wrapping options:

  • Texture Filters: Control the quality of the texture when viewed up close (GL_TEXTURE_MAG_FILTER) or far away (GL_TEXTURE_MIN_FILTER). Options include:
    • GL_NEAREST (lowest quality)
    • GL_LINEAR
    • GL_LINEAR_MIPMAP_NEAREST
    • GL_LINEAR_MIPMAP_LINEAR (highest quality)
  • Texture Wrapping: Determines how the texture behaves at the edges. Options include:
    • GL_REPEAT (repeats the texture)
    • GL_CLAMP_TO_EDGE (clamps the texture to the edge)

These settings are applied when generating the texture, as shown in the code above.

Texture Coordinates

To apply a texture to an object, you must define texture coordinates for each vertex. Texture coordinates map points on the texture to points on the object.

Here’s how texture coordinates are arranged:

  0,1   —–   1,1
       |     |
       |     |
       |     |
  0,0   —–   1,0

With 0,0 being the bottom left and 1,1 being the top right. Using normalized values (0.0 to 1.0) ensures the texture is mapped to fit the object.

If you change the coordinates to values like 0.0 to 10.0, the texture will repeat multiple times. This is controlled by the GL_REPEAT wrapping parameter, which is great for things like brick walls. If the wrapping is set to GL_CLAMP_TO_EDGE, the texture will not repeat, and only the edges will be stretched.

Here’s an example of how texture coordinates are assigned to vertices:

void square(void) {
    glBindTexture(GL_TEXTURE_2D, texture); // Bind the texture
    glRotatef(angle, 1.0f, 1.0f, 1.0f); // Rotate the object

    glBegin(GL_QUADS);
    glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, -1.0); // Bottom-left
    glTexCoord2d(1.0, 0.0); glVertex2d(1.0, -1.0);  // Bottom-right
    glTexCoord2d(1.0, 1.0); glVertex2d(1.0, 1.0);   // Top-right
    glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, 1.0);  // Top-left
    glEnd();
}

Cleaning Up

When you’re done using a texture, it’s important to free the resources to avoid memory leaks. Use the following function to delete the texture:

void FreeTexture(GLuint texture) {
    glDeleteTextures(1, &texture);
}

The Full Code

Here’s the complete code for this tutorial. It includes everything from loading a texture to applying it to a rotating square:

#include <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

GLuint texture;
GLfloat angle = 0.0;

GLuint LoadTexture(const char *filename, int width, int height) {
    GLuint texture;
    unsigned char *data;
    FILE *file;

    file = fopen(filename, "rb");
    if (file == NULL) return 0;
    data = (unsigned char *)malloc(width * height * 3);
    fread(data, width * height * 3, 1, file);
    fclose(file);

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    free(data);

    return texture;
}

void FreeTexture(GLuint texture) {
    glDeleteTextures(1, &texture);
}

void square(void) {
    glBindTexture(GL_TEXTURE_2D, texture);
    glRotatef(angle, 1.0f, 1.0f, 1.0f);
    glBegin(GL_QUADS);
    glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, -1.0);
    glTexCoord2d(1.0, 0.0); glVertex2d(1.0, -1.0);
    glTexCoord2d(1.0, 1.0); glVertex2d(1.0, 1.0);
    glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, 1.0);
    glEnd();
}

void display(void) {
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    glEnable(GL_TEXTURE_2D);
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    square();
    glutSwapBuffers();
    angle++;
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("OpenGL Texturing Example");
    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutReshapeFunc(reshape);

    texture = LoadTexture("texture.raw", 256, 256);

    glutMainLoop();
    FreeTexture(texture);

    return 0;
}

If you have any questions, feel free to email me at swiftless@gmail.com. Happy coding!

Download Texture(.RAW file)

Download Texture(.BMP file)

  • March 25, 2010
  • 35