This page describes some of the features of Modern OpenGL used in the FourTexturesModern 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 FourTexturesModern over the earlier programs SimpleDrawModern, BasicDrawModes and TextureBmpModern.

The code fragments listed below are taken from the programs FourTexturesModern.cpp. It is recommended to refer to the source code as you read this web page to see how the code fits together. 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.

FourTexturesModern draws four texture maps onto four rectangles, each formed from two triangles.

I. Load texture maps from bitmap files into OpenGL

The following code reads the four texture maps from bitmap files, loads into OpenGL's texture map storage, builds the mipmaps, and sets the sampling properties. See the explanations for TextureBmpModern for how these commands function.

First the following global variables are defined:

const int NumTextures = 4;
unsigned int TextureNames[NumTextures];     // Texture names generated by OpenGL
const char* TextureFiles[NumTextures] = {
    "RedLeavesTexture.bmp",
    "LightningTexture.bmp",
    "WoodGrain.bmp",
    "IvyTexture.bmp",
};

Then the texture maps are loaded by:

    RgbImage texMap;
    
    glGenTextures(NumTextures, TextureNames);
    for (int i = 0; i < NumTextures; i++) {
        texMap.LoadBmpFile(TextureFiles[i]);            // Read i-th texture from the i-th file.
        glBindTexture(GL_TEXTURE_2D, TextureNames[i]);  // Bind (select) the i-th OpenGL 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_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

        // Store the texture into the OpenGL texture named TextureNames[i]
        int textureWidth = texMap.GetNumCols();
        int textureHeight = texMap.GetNumRows();
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureWidth, textureWidth, 0, GL_RGB, GL_UNSIGNED_BYTE, texMap.ImageData());
        glGenerateMipmap(GL_TEXTURE_2D);
    }

II. Data for the VAO and VBO and EBO.

A single VAO is used to hold the vertex data for all four rectangles. The vertex data is:

    float rectVerts[] = {
        // Position (x,y)        // texture coordinates (s,t)
        -0.8f, -0.8f,            0.0f, 0.0f,                // First point
        -0.1f, -0.8f,            1.0f, 0.0f,                // Second point
        -0.1f,  -0.1f,           1.0f, 1.0f,                // Third point
        -0.8f,  -0.1f,           0.0f, 1.0f,                // Fourth point
        // Position (x,y)        // texture coordinates (s,t)
         0.1f, -0.8f,            0.0f, 0.0f,                // First point
         0.8f, -0.8f,            1.0f, 0.0f,                // Second point
         0.8f,  -0.1f,           1.0f, 1.0f,                // Third point
         0.1f,  -0.1f,           0.0f, 1.0f,                // Fourth point
         // Position (x,y)       // texture coordinates (s,t)
         -0.8f, 0.1f,            0.0f, 0.0f,                // First point
         -0.1f, 0.1f,            1.0f, 0.0f,                // Second point
         -0.1f, 0.8f,            1.0f, 1.0f,                // Third point
         -0.8f, 0.8f,            0.0f, 1.0f,                // Fourth point
         // Position (x,y)       // texture coordinates (s,t)
         0.1f, 0.1f,             0.0f, 0.0f,                // First point
         0.8f, 0.1f,             1.0f, 0.0f,                // Second point
         0.8f, 0.8f,             1.0f, 1.0f,                // Third point
         0.1f, 0.8f,             0.0f, 1.0f,                // Fourth point
    };

The four rectangles could now be rendered with four calls to glDrawElements, but we use a fancier method instead that allows using a single EBO for all four rectangles. This EBO specifies how a single rectangle is drawn as a triangle strip with two triangles.

    unsigned int rectElements[] = { 1, 2, 0, 3 };

As is described next, glDrawElementsBaseVertex() is able to use this single array rectElements to render four different rectangles.

By the way: If you do not wish to use glDrawElementsBaseVertex() as described next, you can instead make a larger element array and use four calls glDrawElements() as done in the earlier sample programs.

III. Rendering with glDrawElementsBaseVertex:

The calls to glDrawElementsBaseVertex() have the form. Each call to glDrawElementsBaseVertex() is preceded by a call glBindTexture().

    glBindTexture(GL_TEXTURE_2D, TextureNames[0]);
    glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, (void*)0);

    glBindTexture(GL_TEXTURE_2D, TextureNames[1]);
    glDrawElementsBaseVertex(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, (void*)0, 4);

    glBindTexture(GL_TEXTURE_2D, TextureNames[2]);
    glDrawElementsBaseVertex(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, (void*)0, 8);

    glBindTexture(GL_TEXTURE_2D, TextureNames[3]);
    glDrawElementsBaseVertex(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, (void*)0, 12);

The parameters to glDrawElementsBaseVertex() are the same as the parameters for glDrawElements(), except there is a final "base vertex" which specifies an extra offset into the vertex data in the VBO.. For instance, the second rectangle is rendered with glDrawElementsBaseVertex(..., 4). The base vertex value 4 means that 4 is added to each member of the EBO before indexing into the vertex data; in other words, it effectively uses 5, 6, 4, 7 as the elements instead of the values 1, 2, 0, 3 which are stored in the EBO.