Graphic Delphi

Title: OpenGL IV: Texture mapping
Question: Texture mapping: (quote from my OpenGL book)
"The process of applying an image (the texture) to a primitive. Texture mapping is often used to add realism to a scene. For example, you can apply a picture of a building facade to a polygon representing a wall."
On this article I will show you how to do texture mapping so your 3D objects become more "realistic"
Answer:
We have seen the basics of OpenGL
OpenGL I: Hello World, Setup a Delphi OpenGL Application
OpenGL II: Moving and rotating 2D shapes
OpenGL III: Moving and rotating 3D shapes
Also you can keep checking here for future articles on OpenGL
First of all we are going to need a new delphi unit and a DLL: GLAux,
I will provide these files for you to download
Then we need to use 2 new OpenGL procedures, they are on the opengl32.dll so,
we need to declare them at the beginning of our program:
procedure glGenTextures(n: GLsizei; var textures: GLuint); stdcall; external opengl32;
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;
Before we start with the code there are a some very important things you need to know
about the images you plan to use as textures.
- The image height and width MUST be a power of 2. 64*64, 128*128... etc
- For compatability reasons, shouldn't be more than 256 pixels.
If the image you want to use is not a power of 2 on the width or height, resize it in an
art program. There are ways around this limitation, but for now we'll just stick
to standard texture sizes.
Ok, here we go, first we need a variable to hold our texture, for OpenGL you need to declare
a variable like this, I also added other common variables:
texture: array [0..0] of GLuint; // Storage For One Texture ( NEW )
zoom:GLFloat; // This will control how far the camera is
xrot, yrot, zrot:GLFloat; // These are to add some rotation
Then we need to add the LoadGLTextures procedure to the initialization process
this is a procedure that we will create. Next step is to enable Textures,
If you forget to enable texture mapping your object will usually appear solid white,
which is definitely not good.
try commenting out the glEnable(GL_TEXTURE_2D) and you'll see =o)
here's the InitGL function for our application:
function InitGL(Width, Height: Glsizei): Bool;
{ All Setup For OpenGL Goes Here }
var
fWidth, fHeight: GLfloat;
begin
glClearColor(0.0, 0.0, 0.0, 0.0);//Black Background
glClearDepth(1.0); //Depth Buffer Setup
glDepthFunc(GL_LESS); //Text
glEnable(GL_DEPTH_TEST); //Enables Depth Testing
glShadeModel(GL_SMOOTH); //Enables Smooth Color Shading
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
fWidth := Width;
fHeight := Height;
gluPerspective(45.0, fWidth/fHeight, 0.1, 100); //Calculate Aspect Ratio Of The Window
glMatrixMode(GL_MODELVIEW);
LoadGLTextures; //we load our Textures here
glEnable(GL_TEXTURE_2D); //and very important, ENABLE TEXTURES!!! (new)
zoom :=-10.0; // 10 units into the screen depth
xrot := 0.0; // initialize the angles
yrot := 0.0; // initialize the angles
zrot := 0.0; // initialize the angles
Result:=True
end;
LoadGLTextures and glEnable(GL_TEXTURE_2D) are the changes on the initialization that
allow us to do texture mapping
Now let's see how we load the textures in memory for later use:
// Load Bitmaps And Convert To Textures
procedure LoadGLTextures;
Procedure LoadATexture(Var Where:GLuint; Const FName:String);
var
texture1: PTAUX_RGBImageRec;
Begin
texture1 := auxDIBImageLoadA(PChar(FName)); //Load the actual image to memory
if (Assigned(texture1)) then
Begin
glBindTexture(GL_TEXTURE_2D, Where); // Typical Texture Generation Using Data From The Bitmap
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
//generate the texture
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1^.sizeX, texture1^.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1^.data);
End
End;
begin
glGenTextures(1, texture[0]); // Create The Texture
LoadATexture(texture[0], 'data\terracrusher.bmp');
end;
Now let me explain a little be what each instruction does:
glGenTextures(1, texture[0]) tells OpenGL we want to generate one texture
name (increase the number if you load more than one texture). Remember at the
very beginning of this tutorial we created room for one texture with the line
texture: array [0..0] of GLuint;
glBindTexture(GL_TEXTURE_2D, texture[0]) tells OpenGL to bind the named texture texture[0]
to a texture target.
The main function of glBindTexture is to assign a texture name to texture data.
In this case we're telling OpenGL there is memory available at texture[0].
When we create the texture, it will be stored in the memory that texture[0] references.
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
These two lines tell OpenGL what type of filtering to use when the image is larger (GL_TEXTURE_MAG_FILTER)
or stretched on the screen than the original texture, or when it's smaller (GL_TEXTURE_MIN_FILTER)
on the screen than the actual texture.
Using GL_LINEAR for both makes the texture look smooth way in the distance, and when it's up close to the screen.
Using GL_LINEAR requires alot of work from the processor/video card, so if your system is slow,
you might want to use GL_NEAREST. A texture that's filtered with GL_NEAREST will appear blocky
when it's stretched. You can also try a combination of both. Make it filter things up close,
but not things in the distance.
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1^.sizeX, texture1^.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1^.data);
Next we create the actual texture. glTexImage2D tells OpenGL the texture will be a 2D texture (GL_TEXTURE_2D).
- Zero represents the images level of detail, this is usually left at zero.
- Three is the number of data components. Because the image is made up of red data, green data and blue data,
there are three components.
- TextureImage[0]^.sizeX is the width of the texture.
If you know the width, you can put it here, but it's easier to let the computer figure it out for you.
- TextureImage[0]^.sizey is the height of the texture.
- zero is the border. It's usually left at zero.
- GL_RGB tells OpenGL the image data we are using is made up of red, green and blue data in that order.
- GL_UNSIGNED_BYTE means the data that makes up the image is made up of unsigned bytes,
- and texture1^.data tells OpenGL where to get the texture data from.
In this case it points to the data stored in the texture1 record.
Ok, now that we have our texture in memory, let's use it
function DrawGLScene(): Bool; { All Rendering Done Here }
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); //Clear Screen and Depth Buffer
glLoadIdentity(); //Reset The View (move to 0, 0, 0)
glTranslatef(0.0,0.0, zoom); //move into the screen depth zoom units
glRotatef(xrot,1.0,0.0,0.0); //rotate in x axis
glRotatef(yrot,0.0,1.0,0.0); //rotate in y axis
glRotatef(zrot,0.0,0.0,1.0); //rotate in z axis
glBindTexture(GL_TEXTURE_2D, texture[0]); //select our texture!!! (new)
glBegin(GL_QUADS);
// Front Face
glNormal3f( 0.0, 0.0, 1.0);
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, 1.0);
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, 1.0);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0);
// Back Face
glNormal3f( 0.0, 0.0,-1.0);
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);
glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, 1.0, -1.0);
glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);
// Top Face
glNormal3f( 0.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 1.0);
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, 1.0, 1.0);
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, -1.0);
// Bottom Face
glNormal3f( 0.0,-1.0, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0);
glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, -1.0, -1.0);
glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, 1.0);
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);
// Right face
glNormal3f( 1.0, 0.0, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, -1.0);
glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, 1.0, 1.0);
glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, 1.0);
// Left Face
glNormal3f(-1.0, 0.0, 0.0);
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);
glEnd();
xrot := xrot + 1.0; // add to the x angle
yrot := yrot + 0.3; // add to the y angle
zrot := zrot + 0.3; // add to the z angle

DrawGLScene := True
end;
The only new line here was:
glBindTexture(GL_TEXTURE_2D, texture[0]); //select our texture!!! (new)
This line of code selects which texture we want to use.
If there was more than one texture you wanted to use in your scene, you would select the texture
using glBindTexture(GL_TEXTURE_2D, texture[number of texture to use]).
If you wanted to change textures, you would bind to the new texture.
One thing to note is that you can NOT bind a texture inside glBegin() and glEnd(),
you have to do it before or after glBegin().
that's it, it wasn't too hard, was it?
as always, try playing with the code, comment this or that line and see what happens
you can download the code for the article here:
And the GLAux unit and dll here:
keep up coding
salu2
EberSys