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.