Mega Code Archive

 
Categories / Delphi / Graphic
 

OpenGL IV Texture mapping

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