A texture is a form of data storage that allows convenient access not just to particular data entries, but also to sample points mixing (interpolating) multiple entries together.
In OpenGL textures can be used for many things, but most commonly it's mapping an image to a polygon (for example a triangle). In order to map the texture to a triangle (or another polygon) we have to tell each vertex which part of the texture it corresponds to. We assign a texture coordinate to each vertex of a polygon and it will be then interpolated between all fragments in that polygon. Texture coordinates typically range from 0 to 1 in the x and y axis as shown in the image below:
The texture coordinates of this triangle would look like this:
GLfloat texCoords[] = {
0.0f, 0.0f, // Lower-left corner
1.0f, 0.0f, // Lower-right corner
0.5f, 1.0f // Top-center corner
};
Put those coordinates into VBO (vertex buffer object) and create a new attribute for the shader. You should already have at least one attribute for the vertex positions so create another for the texture coordinates.
First thing to do will be to generate a texture object which will be referenced by an ID which will be stored in an unsigned int texture:
GLuint texture;
glGenTextures(1, &texture);
After that it has to be bound so all subsequent texture commands will configure this texture:
glBindTexture(GL_TEXTURE_2D, texture);
To load an image you can create your own image loader or you can use an image-loading library such as SOIL (Simple OpenGL Image Library) in c++ or TWL's PNGDecoder in java.
An example of loading image with SOIL would be:
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
Now you can assign this image to the texture object:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
After that you should unbind the texture object:
glBindTexture(GL_TEXTURE_2D, 0);
As seen above, the lower left corner of the texture has the UV (st) coordinates (0, 0) and the upper right corner of the texture has the coordinates (1, 1), but the texture coordinates of a mesh can be in any range. To handle this, it has to be defined how the texture coordinates are wrapped to the the texture.
The wrap parameter for the texture coordinate can be set with glTextureParameter using GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
and GL_TEXTURE_WRAP_R
.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
The possible parameters are:
GL_CLAMP_TO_EDGE
causes the texture coordinates to be clamped to the range [1/2N, 1 - 1/2N], where N is the size of the texture in the direction.
GL_CLAMP_TO_BORDER
does the same as GL_CLAMP_TO_EDGE
, but in cases where clamping, the fetched texel data is substituted with the color specified by GL_TEXTURE_BORDER_COLOR
.
GL_REPEAT
causes the integer part of the texture coordinate to be ignored. The texture is tiled.
GL_MIRRORED_REPEAT
: If the integer part of the texture coordinate is even, then the it is ignored. In contrast to, if the integer part of the texture coordinate is odd, then the texture coordinate is set to 1 - frac(s). fract(s) is the fractional part of the texture coordinate. That causes the texture to be mirrored every 2nd time.GL_MIRROR_CLAMP_TO_EDGE
causes the the textue coordinate to be repeated as for GL_MIRRORED_REPEAT
for one reptition of the texture, at which point the coordinate to be clamped as in GL_CLAMP_TO_EDGE
.Note the default value for GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
and GL_TEXTURE_WRAP_R
is GL_REPEAT
.
The last thing to do is to bind the texture before the draw call:
glBindTexture(GL_TEXTURE_2D, texture);