This page describes some of the features of Modern OpenGL used in the TextureBmpModern program. This is part of a series of programs illustrating features of Modern OpenGL. On this page, we only discuss aspects of the software with are new to TextureBmpModern over the earlier programs SimpleDrawModern and BasicDrawModes.

The code fragments listed below are taken from the programs TextureBmpModern.cpp. It is recommended to refer to the source code as you read this web page to see how the code fits together. In some cases, the code examples given below are slightly modified to make this web page more readable. We cannot cover all features of commands here, and it strongly recommended to search for on-line documentation for the OpenGL commands and keywords to learn more about their functionality.

TextureBmpModern draws a single texture map onto a rectangle formed from two triangles.

TextureBmpModern defines a C++ class RgbImage to hold texture maps loaded from 24-bit bitmap files (BMP files). BMP files can be created with a variety of programs. On Windows, the Paint program can convert a variety of image file formats to a BMP file.

I. Reading the texture map from the bitmap file

The following line of code creates an RgbImage object, and reads the texture map from the bitmap file RedLeavesTexture.bmp into the RgbImage object.

    RgbImage theTexMap("RedLeavesTexture.bmp");

II. Binding a texture map and setting its parameters

The code below is used to generate a texture name, bind that texture to be the the active texture number 0, and set the texture map's parameters.

    glGenTextures(1, &textureName);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureName);

    // Pixel alignment: each row is word aligned (aligned to a 4 byte boundary)
    //    Therefore, no need to call glPixelStore( GL_UNPACK_ALIGNMENT, ... );

    // Set wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // Set the best quality filtering options.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

The call to glGenTextures generates an array of length one containing a new texture name. A texture name is an unsigned integer used to identify a texture.

glActiveTexture makes "texture unit" number 0 the currently active texture unit. Texture units are the mechanism by which textures are passed as parameters to shader programs: a texture is assigned to a texture unit, and shader programs access textures via having them assigned to texture units. Since TextureBmpModern uses only a single text map, it is convenient to use texture unit 0. (The total number of texture unit can vary, but there should be at least 80 texture units available.)

The call to glBindTexture does double-duty: First it means that henceforth, the named texture is the one assigned to the active texture unit, namely texture unit 0. Second, it specifies that the texture is a 2D texture (the most common kind). In addition, it also means that subsequent calls to glTexParameteri change the parameters of the named texture.

TextureBmpModern uses glTexParameteri to set the wrap mode to be GL_REPEAT so that the texture will wrap if texture coordinates are outside the range [0,1]. Other possible wrap modes are GL_CLAMP and GL_CLAMP_TO_EDGE.

The parameters for GL_TEXTURE_MAG_FILTER and GL_TEXTURE_MIN_FILTER control how the texture and its mipmap levels are sampled. The options used in TextureBmpModern, GL_LINEAR and GL_LINEAR_MIPMAP_LINEAR, give the best quality results albeit at some modest extra computational time.

The parameters assigned by glTexParameteri are assigned to the current texture, not to the active texture unit. (To be precise, the parameters control how the texture is sampled, and they are assigned to the default "sampler" that is associated with the texture map.) When you use multiple textures, you must make calls glTexParameteri for each one.

III. Loading the texture map data, and building mipmaps.

"Mipmapping" is a technique that creates multiple levels of resolution, called "mipmap levels"; this aids in rendering textures at different resolutions. The next two lines of code load the texture map data into OpenGL's memory, and cause OpenGL to build the mipmap levels.

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureWidth, textureWidth, 0, GL_RGB, GL_UNSIGNED_BYTE, theTexMap.ImageData());
    glGenerateMipmap(GL_TEXTURE_2D);

IV. Associating a sampler to a texture unit.

The fragment shader for TextureBmpModern uses a 2D sampler named theTextureMap to access the texture map. The call to glGetUniformLocation(shaderProgram1, "theTextureMap") obtains the binding location of this sampler in the linked shader program. The call to glUniform1i then specifies that this sampler uses the texture assigned to texture unit 0.

    // Make sure that the shaderProgram1 uses the GL_TEXTURE_0 texture.
    glUseProgram(shaderProgram1);
    glUniform1i(glGetUniformLocation(shaderProgram1, "theTextureMap"), 0);

This assignment is the default, so the above two lines are not really necessary, but it is probably a good idea to always make this explicit.

V. Specifying texture coordinates at vertices.

The rectangle is specified by its four vertices. The following array is used in TextureBmpModern to specify the vertices' (x,y) positions, and the vertices' (s,t) texture coordinates.

    // Specify four vertices, with a position (x,y), and texture coordinates (s,t), for each point.
    float rectVerts[] = {
        // Position (x,y)        // texture coordinates (s,t)
        -0.8f, -0.8f,            0.0f, 0.0f,                // First point
         0.8f, -0.8f,            1.0f, 0.0f,                // Second point
         0.8f,  0.8f,            1.0f, 1.0f,                // Third point
        -0.8f,  0.8f,            0.0f, 1.0f,                // Fourth point
    };

This data is then loaded in a VBO with glBufferData, and calls to glVertexAttribPointer and glEnableVertexAttribArray are used to specify the locations of this data in the VBO and how it is given to the vertex shader. This is done in the same way that SimpleAnimModern specified position and color data at each vertex; see the documentation for SimpleAnimModern for a description of this.

VI. Rendering and the fragment shader.

Rendering is done by a call to glDrawElements. Since there is only one texture map, and it is already bound to texture unit 0, it does not require giving any more information about the texture map.

The fragment shader, in the file TextureBmpModern.glsl, uses the following code to apply the texture map:

#version 330 core
in vec3 theColor;       // Color value came from the vertex shader (smoothed)
in vec2 theTexCoord;    // Texture coordinates (interpolated from vertex shader)
uniform sampler2D theTextureMap;
out vec4 FragColor; // Color that will be used for the fragment
void main()
{
   FragColor = texture(theTextureMap,theTexCoord)*vec4(theColor, 1.0f);   // Multiply color and texture color
}

The declaration uniform sampler2D theTextureMap; specifies theTextureMap as a 2D texture map. The call to texture(theTextureMap,theTexCoord) returns a vec3 object which has the RGB components of the texture map sample. The (s,t) texture coordinates of the vec2 object theTexCoord specify where to sample the texture map: these texture coordinates are obtained by interpolation from the texture coordinates of the vertices (that is, by Gouraud interpolation).

The texture coordinates at the vertices are those obtained from the vertex attribute data in the VBO (as described in Section V above). The earlier-described calls to glTexParameteri control the details of how the sampler computes a texture value.