OpenGL Texture Mapping

Hey folks,
I need a piece of Delphi4-code that makes use of plain OpenGL-syntax and does the following:
A rectangle with a texture on it (loaded from a BMP) turning e.g. around the z-axis with a background image (also loaded from a BMP) behind the scene.
The main problem is the texture-mapping. It does not work for me.

I know this is an easy task for you OpenGL-freaks (greetings to Lischke)!

Thanks for your help.
LVL 1
tierAsked:
Who is Participating?

[Webinar] Streamline your web hosting managementRegister Today

x
 
LischkeConnect With a Mentor Commented:
Ah guy, you wanna have the whole story... Even 300 points wouldn't make up for the work which stands behind all that stuff. Here's the part about texture loading from my FAQs. How you can apply textures is shown above. Regarding FViewport and FBackgroundTexture. I thought it would be obvious: FViewport is the viewport (left, top, right, bottom) of your window and FBackgroundTexture is a texture object created with       glGenTextures(1, @FBackgroundTexture); glBindTexure ensures the texture object is active (you need this call also when loading the texture from file). Here is the complete description:

How to do texturing?

Texturing is a way to map a one, two or three-dimensional bitmap onto a polygonal surface, to increase image quality dramatically. Let's consider a 2D bitmap and how it is used as a texture, since this image format is widely used under Windows. The main problem while texturing a polygon is to map pixels of the texture image to 3D coordinates of the given polygon. In order to avoid irritations the bitmpap coordinates are named S and T instead of X, Y and Z for coordinates in 3D space (for one dimension only S is used and for three dimensions S, T and Q are used). The orientation of each of the coordinates can be considered as you want, but if you imaging the texture image staying upward in front of you, S is mostly used for horizontal and T for vertical direction.
Independent of the actual image size S and T are always in the range 0..1 for one occurence of the image on the polygon. Values < 0 or > 1 will either set a border color, repeat the first (last) image pixel or repeat the entire image, depending on the state flags. The easiest way to specify how the image has to be mapped is to give each vertex of the polygon you want to texture a specific texture coordinate:

glBegin(GL_POLYGON);
  glTexCoord2f(0,0);
  glVertex3f(0,0,0);

  glTexCoord2f(1,0);
  glVertex3f(1,0,0);

  glTexCoord2f(1,1);
  glVertex3f(1,1,0);

  glTexCoord2f(0,1);
  glVertex3f(0,1,0);
glEnd;

As you can see the texture coordinate must be given before the actual vertex. This small example maps an image independent of its size onto a square of unit size. If the image has not an aspect ratio of 1 (say 320x256 instead of 128x128) it will not be scaled uniformly. BTW: OpenGL requires all image pixel sizes to be a power of two (2,4,8,16,32...) and has usually an upper limit (MS's and SGI's DLLs have 1024x1024 pixels as limit, but that's quite large, you should use smaller images). With the broad introduction of texture compression this size will be grow larger.

To actual do texturing with Delphi (and not only there) you need 4 steps:
- load an image into host memory
- transfer the image to your OpenGL rendering context
- set the desired options (don't forget to enable texturing) and
- specify texture coordinates while issuing vertices

As always in OpenGL you need an active rendering context before any gl call. Without any doubts is loading a bitmap into OpenGL the hardest problem. So I provide you my loader routine, which can easily adapted to your needs:

procedure TTexture.PrepareImage;

// load texture to OpenGL subsystem

type PPixelArray = ^PPixelArray;
     TPixelArray = array [0..0] of Byte;

var Data        : PPixelArray;
    BMInfo      : TBitmapInfo;
    I,ImageSize : Integer;
    Temp        : Byte;
    MemDC       : HDC;

begin
  with BMinfo.bmiHeader do
  begin
    // make image data available (i.e load a picture into Image)
    Image.DataNeeded;
    // let Windows do any conversion to OpenGL's internal format we use,
    // create a description of the required image format
    FillChar(BMInfo,SizeOf(BMInfo),0);
    biSize:=sizeof(TBitmapInfoHeader);
    biBitCount:=24;
    biWidth:=RoundUpToPowerOf2(Image.Width);
    biHeight:=RoundUpToPowerOf2(Image.Height);
    ImageSize:=biWidth*biHeight;
    biPlanes:=1;
    biCompression:=BI_RGB;
    // a dummy DC for the GetDIBits call below
    MemDC:=CreateCompatibleDC(0);
    // a piece of memory to place the bitmap bits into
    GetMem(Data,ImageSize*3);
    try
      // get the actual bits of the image
      GetDIBits(MemDC,Image.Bitmap,0,biHeight,Data,BMInfo,DIB_RGB_COLORS);

      // now set the bits depending on the features supported by OpenGL
      if GL_EXT_bgra then
          // BGR extension avoids color component swapping
          glTexImage2d(GL_TEXTURE_2D,0,3,biWidth,biHeight,0,GL_BGR_EXT,GL_UNSIGNED_BYTE,Data)
        else
        begin
          // no BGR support, so we must swap the color components by hand
          // switch off range checking for color swapping, make sure we restore the original state
          {$ifopt R+} {$define RangeCheck} {$R-} {$endif}
          for I:=0 TO ImageSize-1 do //swap blue with red to go from bgr to rgb
          begin
            Temp:=Data[I*3];
            Data[I*3]:=Data[I*3+2];
            Data[I*3+2]:=Temp;
          end;
          // restore range check, if previously activated
          {$ifdef RangeCheck} {$undef RangeCheck} {$R+} {$endif}
          glTexImage2d(GL_TEXTURE_2D,0,3,biWidth,biHeight,0,GL_RGB,GL_UNSIGNED_BYTE,Data);
        end;
    finally
      FreeMem(Data);
      DeleteDC(MemDC);
      Image.ReleaseData;
    end;
  end;  
end;

My TTexture class has an Image property which is a simple TPicture class. By calling Image.LoadFromFile(FileName) you can load a bitmap from disk into main memory. This is (in TTexture) done by calling Image.DataNeeded. In opposition Image.ReleaseData empties the Image since we don't need it any longer after it has been transfered to OpenGL. The procedure above should handle most if not all of the possible pixel formats available.

One problem while loading an image is that OpenGL expects its color components in RGB order (remember we use a 24 bit image), which is the way a pixel is described in video memory. Microsoft has, for what ever reason, introduced the BGR format. I guess you see what I'm trying to say. All pixels of our image (which is stored in a Windows HBitmap and thus in BGR format) must be changed to RGB. Since SGI's OpenGL for Windows there's an extension for that case. You can check for that extension while context activation:

  Buffer:=StrPas(PChar(glGetString(GL_EXTENSIONS)));
  if Pos('GL_EXT_bgra',Buffer) > 0 then GL_EXT_bgra:=True
                                   else GL_EXT_bgra:=False;

The interface unit in OpenGL12.zip handles extension detection automatically. WIth the release of OpenGL version 1.2 (probably this year) you don't have to deal with extions like the one above, because there will be many new common pixel formats (RGB 555, BGR 565 etc.).

For those of you who have problems to implement the rounding function used in the loader routine here one solution:

function TTexture.RoundUpToPowerOf2(Value: Integer): Integer;
var LogTwo : Extended;
begin
  LogTwo:=log2(Value);
  if Trunc(LogTwo) < LogTwo then Result:=Trunc(Power(2,Trunc(LogTwo)+1))
                            else Result:=value;
end;

At this point we almost have what's needed for texturing. The last step is setting up how OpenGL should apply the texture while rendering:

procedure TTexture.PrepareParams;
begin
  glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
  glPixelStorei(GL_UNPACK_ALIGNMENT,4);
  glPixelStorei(GL_UNPACK_ROW_LENGTH,0);
  glPixelStorei(GL_UNPACK_SKIP_ROWS,0);
  glPixelStorei(GL_UNPACK_SKIP_PIXELS,0);

  case FTextureWrap of
    twBoth       : begin
                     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
                     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
                   end;
    twNone       : begin
                     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
                     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
                   end;
    twHorizontal : begin
                     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
                     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
                   end;
    twVertical   : begin
                     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
                     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
                   end;
  end;

  case FMinFilter of
    miNearest              : glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    miLinear               : glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    miNearestMipmapNearest : glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_NEAREST);
    miLinearMipmapNearest  : glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
    miNearestMipmapLinear  : glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_LINEAR);
    miLinearMipmapLinear   : glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
  end;

  case FMagFilter of
    maNearest : glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
    maLinear  : glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  end;

  case FTextureMode of
    tmDecal    : glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
    tmModulate : glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
    tmBlend    : glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_BLEND);
    tmReplace  : glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
  end;
end;

It would be far out the scope to describe what all the options do. I recommend that you read OpenGL.hlp in the Delphi\Help directory and experiment with the parameters in the procedures. The variables FTextureWrap, FMinFilter, FMagFilter and FTextureMode are simple sets of the values in the case statements above they belong to. If you use one of the mipmap options in FMinFilter you have to  adjust the loader function as well. Good start parameters are miNearest/maNearest for the filters and tmModulate (with light calculations) or tmDecal (without) for the texture mode.

If you want to load more than one texture and quickly switch between them, you should consider using texture objects. These objects are simple integer numbers describing a specific set of parameters and a texture image (bitmap). Texture objects are very similar to display lists and can even be shared between different contexts, but have a different name space. All you have to do is to bind such a texture object to the current context by calling glBindTexture(GL_TEXTURE_2D,FHandle) every time you either want to setup the object or to use it. To get a valid texture object call glGenTextures(1,@FHandle) and with glDeleteTextures(1,@FHandle) you can free it. Don't search in OpenGL.hlp for any of the texture object calls. They are quite new and not covered in the current version of the help file (even in Delphi 4).

To sum up all said until now the following sequence enables the use of textures in your OpenGL program:

- glGenTextures(1,@FHandle)
- glBindTexture(GL_TEXTURE_2D,FHandle)
- PrepareImage
- PrepareParams
- glEnable(GL_TEXTURE_2D)
- .... any glTexCoord and glVertex call combination
- glDeleteTextures(1,@FHandle) (will free used texture memory as well)

Ciao, Mike
0
 
brainwareCommented:
Kinda few 3D Freaks in the EE - Delphi Area :(( so far i have only seen me and SpaceBrain who really work with 3D :)

But if any more.. Come out of the dark please :))

Tier: Ill see if i get time to night, aka to write a good Sample for ya..
0
 
LischkeCommented:
Hi Tier,

actually two question in one and the background picture thingy is one of the little "secrets" :-) Anyway first the background code:

        glDisable(GL_LIGHTING);
        glDisable(GL_DEPTH_TEST);

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix;
        glLoadIdentity;
        glMatrixMode(GL_PROJECTION);
        glPushMatrix;
        glLoadIdentity;
        with FViewport do
        begin
          glOrtho(0, Width - 1, 0, Height - 1, 0, 100);

          glFrontFace(GL_CCW);
          glEnable(GL_TEXTURE_2D);
          glBindTexture(GL_TEXTURE_2D, FBackgroundTexture);
          glBegin(GL_QUAD_STRIP);
            glTexCoord2f(0, 0);
            glVertex2f(0, 0);
            glTexCoord2f(1, 0);
            glVertex2f(Width - 1, 0);
            glTexCoord2f(0, 1);
            glVertex2f(0, Height - 1);
            glTexCoord2f(1, 1);
            glVertex2f(Width - 1, Height - 1);
          glEnd;
          end;
        end;
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix;
        glMatrixMode(GL_PROJECTION);
        glPopMatrix;

        glEnable(GL_LIGHTING);
        glEnable(GL_DEPTH_TEST);
        glClear(GL_DEPTH_BUFFER_BIT); // clear only depth buffer

This code essentially switchs to orthogonal mapping where one unit in OpenGL corresponds directly to a pixel on the screen. As you can see here I use a rectangle which fits exactly the whole window and uses very straight texture mapping. With very little adjustment you can use the same code to create your front rectangle. I'd suggest, though, that you use usual perspective projection for this second rectangle (looks better when rotating in 3D space).

I'm not sure about the question for texture mapping as such. Do you have already code to load a texture into OpenGL or do you need some? What are your problems with texture mapping?

Ciao, Mike

PS: brainware, seems you haven't seen my homepage (www.lischke-online.de). Look at my 3DS import library or my OpenGL class library GLScene :-)
0
Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
tierAuthor Commented:
I am not sure where my problem is concerning texture mapping.  It just did not work at all.
Please give me the whole solution with bitmap loading.
Thanks...
0
 
tierAuthor Commented:
Btw, where did you get the FViewport and FBackgroundTexture fields? I need the whole explanation, because I am an OpenGL beginner.
0
 
LischkeCommented:
Correction:

type
  PPixelArray = ^TPixelArray;
  TPixelArray = array [0..0] of Byte;


Note definition of PPixelArray!

Ciao, Mike
0
 
brainwareCommented:
Wow.. That OpenGL is more complex than coding own 3D Engine with Perspective Texture Mapping, Dynamic Lights etc :)
jk.. :)
Lischke.. looks like u know more about that api than i do :)  Don't surpose u got some good Delphi 3 samples/ Faqs etc? using OpenGL and not Glu/Glut.
0
 
LischkeCommented:
Do you really think it's more complex? Well, maybe, but there are so many features. It's an universal API and leading for years in industry 3D visualization...

Regarding D3 samples etc.: See my homepage (www.lischke-online.de) for GLScene. This lib contains many tricks in this field (lens flares, hierarchical scene managament, just to name two). Additionally, there's my own OpenGL header translation for version 1.2 (usable, but not finished yet) which you can get from www.delphi-jedi.org (AFAIK the server is down at the moment, try it tomorrow). The zip file contains a very simple example to enable OpenGL in a Delphi application. Try also famous sites like:

- Earl F. Glynn's computer lab (http://www.efg2.com/lab/)
- Richey's Delphi Box (http://inner-smile.com/delphit.htm)

Ciao, Mike
0
 
LischkeCommented:
tier, what now?
0
 
rwilson032697Commented:
Listening :-)
0
 
brainwareCommented:
Danm me.. i lost a URL i ran into..
it had like 100+ professional Samples and Libs for OpenGL ..

mabye some of ya use it offen.. it had some yellow and blue graphics as Left menu and at bottom it sayd "Powered by OpenGL"

also a url worth posting here..
0
All Courses

From novice to tech pro — start learning today.