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.
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);
}
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.
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.