16. OpenGL Texturing (Version 2.0)
Introduction to Texturing in OpenGL
Textures are an essential part of 3D graphics. They allow you to wrap detailed images around your objects, making them look more realistic. This tutorial will guide you through setting up textures in OpenGL, focusing on Windows for simplicity.
To get started with texturing, there are a few key steps:
1. Load the texture image into your program.
2. Configure the texture parameters.
3. Apply the texture to your objects using texture coordinates.
4. Clean up when you’re done to free system resources.
Let’s dive into each of these steps.
Loading a Texture
The first step is loading the texture image. In this tutorial, we’ll use a RAW file, which is a simple uncompressed image format. You’ll need the following function to load the texture into OpenGL:
GLuint LoadTexture(const char *filename, int width, int height) { GLuint texture; unsigned char *data; FILE *file; // Open the file file = fopen(filename, "rb"); if (file == NULL) return 0; // Allocate memory for the texture data = (unsigned char *)malloc(width * height * 3); // Read the file data fread(data, width * height * 3, 1, file); fclose(file); // Generate and bind the texture glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); // Set texture environment parameters glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // Set texture filters glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Set texture wrapping glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Generate the texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); // Free the texture data from memory free(data); return texture; }
Texture Filters and Wrapping
When working with textures, you can control how they appear using filters and wrapping options:
- Texture Filters: Control the quality of the texture when viewed up close (
GL_TEXTURE_MAG_FILTER
) or far away (GL_TEXTURE_MIN_FILTER
). Options include:- GL_NEAREST (lowest quality)
- GL_LINEAR
- GL_LINEAR_MIPMAP_NEAREST
- GL_LINEAR_MIPMAP_LINEAR (highest quality)
- Texture Wrapping: Determines how the texture behaves at the edges. Options include:
- GL_REPEAT (repeats the texture)
- GL_CLAMP_TO_EDGE (clamps the texture to the edge)
These settings are applied when generating the texture, as shown in the code above.
Texture Coordinates
To apply a texture to an object, you must define texture coordinates for each vertex. Texture coordinates map points on the texture to points on the object.
Here’s how texture coordinates are arranged:
0,1 —– 1,1 | | | | | | 0,0 —– 1,0
With 0,0 being the bottom left and 1,1 being the top right. Using normalized values (0.0 to 1.0) ensures the texture is mapped to fit the object.
If you change the coordinates to values like 0.0 to 10.0, the texture will repeat multiple times. This is controlled by the GL_REPEAT
wrapping parameter, which is great for things like brick walls. If the wrapping is set to GL_CLAMP_TO_EDGE
, the texture will not repeat, and only the edges will be stretched.
Here’s an example of how texture coordinates are assigned to vertices:
void square(void) { glBindTexture(GL_TEXTURE_2D, texture); // Bind the texture glRotatef(angle, 1.0f, 1.0f, 1.0f); // Rotate the object glBegin(GL_QUADS); glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, -1.0); // Bottom-left glTexCoord2d(1.0, 0.0); glVertex2d(1.0, -1.0); // Bottom-right glTexCoord2d(1.0, 1.0); glVertex2d(1.0, 1.0); // Top-right glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, 1.0); // Top-left glEnd(); }
Cleaning Up
When you’re done using a texture, it’s important to free the resources to avoid memory leaks. Use the following function to delete the texture:
void FreeTexture(GLuint texture) { glDeleteTextures(1, &texture); }
The Full Code
Here’s the complete code for this tutorial. It includes everything from loading a texture to applying it to a rotating square:
#include <GL/gl.h> #include <GL/glut.h> #include <windows.h> #include <stdio.h> #include <stdlib.h> GLuint texture; GLfloat angle = 0.0; GLuint LoadTexture(const char *filename, int width, int height) { GLuint texture; unsigned char *data; FILE *file; file = fopen(filename, "rb"); if (file == NULL) return 0; data = (unsigned char *)malloc(width * height * 3); fread(data, width * height * 3, 1, file); fclose(file); glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); free(data); return texture; } void FreeTexture(GLuint texture) { glDeleteTextures(1, &texture); } void square(void) { glBindTexture(GL_TEXTURE_2D, texture); glRotatef(angle, 1.0f, 1.0f, 1.0f); glBegin(GL_QUADS); glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, -1.0); glTexCoord2d(1.0, 0.0); glVertex2d(1.0, -1.0); glTexCoord2d(1.0, 1.0); glVertex2d(1.0, 1.0); glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, 1.0); glEnd(); } void display(void) { glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glEnable(GL_TEXTURE_2D); gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); square(); glutSwapBuffers(); angle++; } void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE); glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow("OpenGL Texturing Example"); glutDisplayFunc(display); glutIdleFunc(display); glutReshapeFunc(reshape); texture = LoadTexture("texture.raw", 256, 256); glutMainLoop(); FreeTexture(texture); return 0; }
If you have any questions, feel free to email me at swiftless@gmail.com. Happy coding!
Is there any way to Blend Textures??
My searches have all shown that .RAW is the modern camera’s equivalent to a negative but nothing has revealed by what means I might convert an image to that filetype. Should I figure out how to use BMP instead?
Many thanks
can u tell me y u allocated hiegth*width*3 bytes of memry??……..especialyy ” *3 “??
Because each pixel color are stored 3 bytes ( RGB ), so, Height x Width = Dimension, Dimension x 3 = Total bytes
Because it uses the RGB system, one byte for each primary color, red,green and blue.
Hi Swiftless,
Your tutorials are awesome and have helped me alot!
Thank you very much for all your great work.
I don’ jnow about the others, but when I try to open the archives of the textures, WinRar tells me they’re corrupted or damaged… Can you upload correctly the two files?
you tutorial 100% rate and
i want function load bmp like load file.raw
hey em writing ur code as it is but it gives a yellow window screen as output. can u plz tell me what is the problem?
Hi Swiftless!
I wrote a class-based image loader, but I have many problems with that.
First, it loads the RAW file with its parameters, but if I run my program I just see a blank white screen.
Second, if I don’t set the parameters of the bitmap outside of the load-function, every parameters will be 0.
Here is the code:
// ctexture.h
#include
#include
using namespace std;
class CTexture
{
public:
int Load(char* Filename, int Width, int Height);
void SetActive();
void Delete();
unsigned int GetID();
int Width;
int Height;
unsigned int ID;
int GetWidth();
int GetHeight();
private:
GLuint Texture;
unsigned char* Data;
};
int CTexture::Load(char* Filename, int Width, int Height)
{
FILE* File;
File=fopen(Filename,”rb”);
Data=(unsigned char*)malloc(Width*Height*3);
fread(Data,Width*Height*3,1,File);
fclose(File);
glGenTextures(1,&Texture);
glBindTexture(GL_TEXTURE_2D,Texture);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,Width,Height,0,GL_RGB,GL_UNSIGNED_BYTE,Data);
free(Data);
return Texture;
}
void CTexture::SetActive()
{
glBindTexture(GL_TEXTURE_2D,ID);
}
void CTexture::Delete()
{
glDeleteTextures(1,&ID);
}
unsigned int CTexture::GetID()
{
return ID;
}
int CTexture::GetWidth()
{
return Width;
}
int CTexture::GetHeight()
{
return Height;
}
// main.cpp
#include
#include
#include
#include
#include
using namespace std;
CTexture Default;
GLvoid InitTextures(GLvoid)
{
glEnable(GL_TEXTURE_2D);
Default.Width=256;
Default.Height=256;
Default.ID=34;
Default.Load(“default.raw”,Default.Width,Default.Height);
cout<<"Image width is: "<<Default.Width<<endl;
cout<<"Image height is: "<<Default.Height<<endl;
cout<<"ID: "<<Default.ID<<endl;
}
void Display(void)
{
InitTextures();
glClearColor(0.0f,0.0f,0.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT);
Default.SetActive();
glBegin(GL_QUADS);
glTexCoord2f(0.0f,0.0f); glVertex2f(-1.0f,1.0f);
glTexCoord2f(1.0f,0.0f); glVertex2f(1.0f,1.0f);
glTexCoord2f(1.0f,1.0f); glVertex2f(1.0f,-1.0f);
glTexCoord2f(0.0f,1.0f); glVertex2f(-1.0f,-1.0f);
glEnd();
glFlush();
}
int main(int argc,char** argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE);
glutCreateWindow("OpenGL");
glutDisplayFunc(Display);
glutMainLoop();
}
Otherwise, I use Microsoft Visual C++ 2008 for programming.
I don't know what is the problem.
Thank you very much!
I found the solution for the problem, I don’t need any help now.
hi after running this sample code i got only white square which one is rotating. i placed raw file to same folder.but there is no error and no texture.
Hi, Swiftless!
(sorry for my english because i’m hungarian and i’m 13 years old)
This is a great tutorial. I tried, it works. I tried it on flat surface and cube. But this program can read only one texture file. How can I do that read more than one texture file? Thanks!
Hi Janus,
That’s a good effort, I started OpenGL at about that age.
To make it work with multiple textures, you need to create another variable to store the new texture. You then call virtually the exact same line of code, only this time you set the new texture variable and provide the path to the new texture.
Eg:
GLuint texture1 = loadTexture(first_path);
GLuint texture2 = loadTexture(second_path);
Cheers,
Swiftless
Hi, Swiftless!
This idea is very good.
It works perfectly.
Thank you very much.
(Nagyon szépen köszönöm! :D)
Hi!
How can I change the texture image?
If I change the path-name, I’ll get either get a blank white square or the same texture.bmp file that I downloaded from this site…
Can anyone help me please..
Hi Nikhi,
It sounds like you are typing your path wrong. If the texture exists, and it is a .raw file, then you will see it. Unless there is something horribly wrong with your drivers. But I’d be more willing to put my money on a minor programming error.
Cheers,
Swiftless
First sorry for my bad english. Second, congratulations for tutorial, very good. For loading bitmaps with your code increase before “fread(data, width * height * 3, 1, file)” a ‘jump’ in bitmap file:
example:
unsigned long size = width * height * 3;
fseek(file, 52, SEEK_CUR);
fread(data, size, 1, file);
‘file = bitmap’
’52 = size of header (only bitmaps)’
‘SEEK_CUR = relation of moviment the cursor ‘
after you corrige the color (BGR for RGB)
char aux;
for(int bit_pixel = 0; bit_pixel < size; bit_pixel += 3){
aux = data[bit_pixel];
data[bit_pixel] = data[bit_pixel + 2];
data[bit_pixel + 2] = aux;
}
continue with your work, very good.
by
Hey Bruno,
That makes sense, usually when working with BMP files, I use the header to get the width and height, but if you know all the details, then you can definitely skip the header and adjust the format from BGR to RGB.
Cheers,
Swiftless
unsigned long size = width * height * 3;
fseek(file, 51, SEEK_CUR); // <- here must be 51, not 52
fread(data, size, 1, file);
after some playing with constants, i've caught right one, one my machine this causing to be a right colored bitmap
the texture.raw file, where do i get that file to run this tutorial?
Under the source code there is a link:
Download Texture(.RAW file)
http://www.swiftless.com/tutorials/opengl/texture_under_windows.html/cplusplus/texture.zip
I came across this site a while ago, and I’ve been reading your tutorials. I already made a texture loader, and stumbled across your code. I noticed how it’s very similar, but yours is also somewhat defective. In my computer, as well as others, if not all, BMP image data is stored in BGR format, instead of RGB. Your code loads a BMP, but miscolored. Here’s my code, which fixes this issue, allowing properly colored BMP textures:
GLuint ltex( const char * filename, int width, int height ){
GLuint t;
char * data = new char[width * height * 3];
FILE * file;
file = fopen( filename, “rb” );
if ( file == NULL ) return 0;
fread( data, width * height * 3, 1, file );
fclose( file );
char * data2 = new char[width * height * 3];
for(int i = 0; i < width * height * 3; i+=3){
data2[i] = data[i + 2];
data2[i+1] = data[i + 1];
data2[i+2] = data[i];
}
glGenTextures( 1, &t );
glBindTexture( GL_TEXTURE_2D, t );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,GL_RGB, GL_UNSIGNED_BYTE, data2);
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data2 );
delete[] data;
delete[] data2;
return t;
}
Sorry, ignore this. I posted the wrong one. This one is from a different project, and has several errors…
Hi Daniel,
My code doesn’t load a bitmap file, it loads a RAW file which is stored in RGB interleaved format. If your code above does read a BMP file correctly, I would have to ask where the BITMAP HEADER is read in? Otherwise you will get extra data at the start of your file which will be loaded into your texture.
Cheers,
Swiftless
Dear Swiftless,
could you please explain to us about the mismatch colour and how to fix it, I am experiencing the same problem. Thanks.
Hi, great tutorial :D.
Just one thing though…Mine turns up as just a blank white square. So, I’m wondering, where am I supposed to put the texture I want to use so that it will load correctly?
Sorry if it’s a silly question :X
Hey Barry,
The textures can be wherever you like, the file paths can be either relative or absolute, but for this tutorial, I place them in the same folder as the executable.
Cheers,
Swiftless
glEnable(GL_TEXTURE_2D);
For future readers: make sure the file path has forward slashes (/) instead of the standard (\). This is a really easy mistake that may save you some headache.
Btw, started learning opengl here. Now I have an ios app out. Check out “Fission HD” on YouTube. Thanks
About my last comment: I just read your tutorial “OpenGL Texture Coordinate Generation” and everything is a little bit more clear to me now! Thanks!
Hi Swiftless!
Great tutorial, as always!.
I have a sugestion for your OpenGL tutorials. I (humble beginner) still don’t understand why this texturing example won’t work on a cube, sphere or torus (the mapping seems to be different) so when I try any of these shapes I get flat colors. Maybe you could post some very basic examples of different mappings for different shapes? Just a suggestion though. 🙂
Beatriz / future readers:
Trying something like that given below to map the texture to different objects. The rest of the code can remain the same. I hope it makes things a little clearer.
shader.vert:
varying vec2 Pos;
void main() {
// Set the position of the current vertex
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
Pos = vec2(gl_Vertex.x, gl_Vertex.y);
}
shader.frag:
uniform sampler2D color_texture;
varying vec2 Pos;
void main() {
// Set the output color of our current pixel
gl_FragColor = texture2D(color_texture, Pos);
}