Welcome to the new Friends-of-FPC!

Here you can find all kinds of information about the FreePascal Compiler. We have many tutorials and howtos as well as a selection of tools to help you with your programming. We also have some example codes for you. And if you want to contribute some information/ sources/ tools yourself you can do so.
Also we have finally relaunched the FoFPC forum. It's your chance for some Q&A about everything FreePascal.

Friends-of-FPC

Tutorials: Learn how to code with FreePascal.

Source Codes: A collection of examples, miscellaneous source codes and open source stuff.

Tools and Help Files: Intro- duction of some tools that might help you with FPC.

Community

Forum: Ask or answer questions about the FreePascal Compiler, programming or just babble about coding.

Contribute! Contribute your own Tutorial, Source Codes or Tools and send them to us!

Website

About: Information about Friends-of-FPC.org.

OpenGL/ FPC - Chapter 20 - by Mr.WereWolf

My name is Peter, also known as Mr.WereWolf and I will be your guide in this tutorial. I am a Free Pascal fan and I believe that the Free Pascal Compiler can do at least as much as any other compiler under windows or other OS. As the title said, I will cover the extensions in OpenGL. I assume that you know what's OpenGL and you already checked out the OpenGL tutorials from Delax. It covers the basic stuff you should know. I also assume that you know what texture mapping is and the usage of it in OpenGL. These are very basic things, without them you won't understand a bit.

What's an extension in OpenGL?

Probably, by now you are already familiar with the most important OpenGL functions. You know glBegin, glEnd, glVertex, glColor, etc... You should know the most important constants too: GL_TRIANGLES, GL_TEXTURE_2D, GL_COLOR_BUFFER_BIT, GL_RGB, etc... These are part of every OpenGL implementation. But, sometimes the basic (unextended) OpenGL can't handle everything. Maybe we could do anything, using the basic implementation, with several passes and massive calculations, but the high quality graphics are not our only objective, we need speed,speed,speed, we need hardware support. So the basic OpenGL implementation was extended. An extension is specially created for a purpose, bringing new functions and constants. Unfortunately the extensions are platform dependent, so your hardware may not support the extension you want to use. Using extensions has advantages and disadvantages. You should use extensions for speed or to use your card's extra features, but your program may not run with some video cards. That's why before making a program for people using an extension you have to check out how many cards support it.

If everything is clear by now, let's see how can we get informations about your video card. The glGetString function has this job. Accepts the follwing constants: GL_VENDOR, GL_RENDERER, GL_VERSION, GL_EXTENSIONS and returns a pchar string for these.

 Version:=glGetString(GL_VERSION);
  //Returns the OpenGL version supported by your video card
 Renderer:=glGetString(GL_RENDERER);
  //Returns you video card's name
 Vendor:=glGetString(GL_VENDOR);
  //Returns your video card's company
 Extensions:=glGetString(GL_EXTENSIONS);
  //Returns the extensions supported by your video card

Right now, only the Extensions string is important for us. This string now contains a list of every extension supported by your card, separated by a space. Your video card should support at least 7-8 extensions. A modern video card may have more than 100 extensions. That depends on it's age. The GL_EXTENSIONS string should return something like this:

"GL_ARB_depth_texture GL_ARB_imaging GL_ARB_multisample GL_ARB_multitexture GL_ARB_point_parameters GL_ARB_shadow GL_ARB_shadow_ambient GL_ATI_texture_mirror_once GL_EXT_abgr GL_EXT_bgra GL_EXT_blend_color GL_EXT_blend_func_separate GL_EXT_blend_logic_op GL_EXT_blend_minmax GL_EXT_blend_subtract GL_EXT_clip_volume_hint GL_EXT_convolution GL_EXT_compiled_vertex_array GL_EXT_fog_coord GL_EXT_histogram GL_EXT_packed_pixels ... "

This string could get very long, I just intended to give you an example. As you see every extension has a name and begins with "GL_". I have to mention that there are "WGL_" extensions too, mostly for window operations, like controlling the frame rate or using other buffers. Next comes a field describing the type of the extension. "ARB" or "EXT" are approved, mostly standard extensions. There are also specific extensions for some video cards or workstations, like "ATI","NV","IBM","HP","SGI","KTX", and others. These extensions are supported only on a limited number of systems, so try to use ARB and EXT extensions as far as you can. For example there are very useful "NV" extensions for different effects, but these are only available on NVIDIA cards. Some of the EXT extensions may also have ARB equivalents (Ex. GL_ARB_texture_env_combine = GL_EXT_texture_env_combine).

I hope it is clear until now, cause we are going to learn the usage of the multitexture extension.

The GL_ARB_multitexture extension.

This is a very important and anticipated extension. This should be supported by every video card made since 2000. We have to check if the "GL_ARB_multitexture" string is present in the Extensions string. If it is, then your videocard has support for multitexture. As I said before, an extension is not part of the basic OpenGL implementation. That means that our gl or gl_sl unit does not contain stuff needed by us, so we have to include the glext unit too, which contains constants and functions for several extensions. In this example we won't use the glext unit, so we will define only constants and procedures needed in our program.

Here are the ARB_multitexture specific constants (or tokens) we are going to use:

 GL_ACTIVE_TEXTURE_ARB = $84E0;
 GL_MAX_TEXTURE_UNITS_ARB = $84E2;
 GL_TEXTURE0_ARB = $84C0;
 GL_TEXTURE1_ARB = $84C1;
 GL_TEXTURE2_ARB = $84C2;
 GL_TEXTURE3_ARB = $84C3;
 GL_TEXTURE4_ARB = $84C4;
 GL_TEXTURE5_ARB = $84C5;
 GL_TEXTURE6_ARB = $84C6;
 GL_TEXTURE7_ARB = $84C7; 

We will also need:

 glActiveTextureARB:procedure(terget:GLenum); 
 glMultitexcoord2fARB:procedure(target:GLenum;s:GLfloat;t:GLfloat);

ARB_multitexture has other functions and constants too, as you can see in the glext unit.

Probably you have heard of the opengl32.dll which contains the OpenGL functions. This library actually contains just the basic OpenGL stuff, that means that our procedures required by extensions are not there! Where are they?

wglGetProcAddress will tell us! The wglGetProcAddress function returns the address of an OpenGL extension function we ask for:

 glActiveTextureARB:=wglGetProcAddress('glActiveTextureARB'); 
 glMultiTexCoord2fARB:=wglGetProcAddress('glMultiTexCoord2fARB'); 

We also have to make shure that wglGetProcAddress returned the procedure adresses, otherwise the program could crash!

if not assigned(glActiveTextureARB) then error('GL_ARB_multitexture not supported!');
if not assigned(glMultiTexCoord2fARB) then error('GL_ARB_multitexture not supported!');

If we got here then we should check that how many texture units are supported...

 glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,@max_tex_unit);

If you have not used it yet, the glGetIntegerv function is part of the basic OpenGL implementation and can return us many informations about our video card. As you can see, if multitexture is supported then we can pass to glGetIntegerv an extension specific value. The max_tex_unit is a longint, and after this function should have a value equal or greater than 2. Older video cards support only two textures at time, but modern video cards can handle up to four-eight or even more texture units.

So, let's see what is actually the rendering of two textures at a time. As you know the texture is a representation of a material, in most cases. Sometimes rendering walls with only the material texture could give a very poor graphic quality. The walls also need effects, shadows or different shading on them. So beside the material texture you could use another texture for this purpose. You could render once again the whole polygon and blend it over the first one, but this would be a slow solution cause every pixel would be drawn twice. This is why multitexture is needed: to render all these with a single pass. Multitexture is mostly used in 3D games for lightmapping (lighting and shadows represented by a texture), detail-texturing (a much smaller, repeated texture pattern, that gives "detail" to the surface), environment-mapping (fake reflection, using a pre-rendered texture as the environment), bump-mapping (a texture that represents the roughness of a surface, related to the light sources), etc. I won't get into details about these cause we're talking only about extensions.

Let's see what's the difference between normal texturing and multitexture in our code. When we were rendering a normal texture-mapped quad we did this:

 //....
 //(The textures are already loaded)

 glenable(GL_TEXTURE_2D); //We enable 2D texture mapping 
 glbindTexture(GL_TEXTURE_2D,0); //We bind the texture we want to use

 glbegin(GL_QUADS); //We start rendering a quad
  gltexcoord2f(0,0);//We set the texture coordinate for the vertex
   glvertex3f(-10,10,0); //Then we set the vertex coordinate

 //The same for all vertices...
  gltexcoord2f(1,0);
   glvertex3f(10,10,0); 
  gltexcoord2f(1,-1); 
   glvertex3f(10,-10,0); 
  gltexcoord2f(0,-1); 
   glvertex3f(-10,-10,0);
  glend; //Finish the quad 

The above code must be familiar. The multitexturing is not that difficult either:

    glActiveTextureARB(GL_TEXTURE0_ARB); //Make active the first texture unit

    glenable(GL_TEXTURE_2D); //Enable 2D-texture mapping for this unit
    glbindTexture(GL_TEXTURE_2D,0); //Bind a texture for this unit
    glActiveTextureARB(GL_TEXTURE1_ARB); //Make active the second texture unit

    glenable(GL_TEXTURE_2D); //Enable 2D-texture mapping for this unit
    glbindTexture(GL_TEXTURE_2D,1); //Bind a texture for this unit

    //Done with texture binding, let's begin the quad
    glbegin(GL_QUADS);
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0,0);
	 //Set the texture coordinate for the first texture unit
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0,0);
	 //Set the texture coordinate for the second texture unit
  glvertex3f(-10,10,0); //Set the vertex

  //The same goes for all vertices...
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB,1,0);
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB,1,0);
  glvertex3f(10,10,0);

  glMultiTexCoord2fARB(GL_TEXTURE0_ARB,1,-1);
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB,1,-1);
  glvertex3f(10,-10,0);

  glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0,-1);
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0,-1);
  glvertex3f(-10,-10,0);
    glend; //Render the quad

Phew, we're done! I really hope that you understood the thing. Basically it's simple: Bind a texture for all units, and when rendering, instead of using gltexcoord2f, we use glMultiTexCoord2fARB twice, for both texture units. If we would like to use four texture units we would do the same thing, but four times, with GL_TEXTURE0_ARB, GL_TEXTURE1_ARB, GL_TEXTURE2_ARB and GL_TEXTURE3_ARB. In this example the second texture unit will multiply the first one (by default), we could also add, subtract, replace or do other operations with the texture units, but this is another story...

As I said there are lots of extensions, I just gave you an example. Other extensions are used just like this one, of course whith it's own functions and procedures. If you understand what's going on, you should go and search other useful extensions. First, you have to know which extensions are supported by your video card. For this I recommend you the GLView tool (OpenGL Extension Viewer), which can give you important informations about your video hardware, it's free and you can download it from the following site:http://www.realtech-vr.com/glview/. Every OpenGL developer should have it!

If you are interested about an extension you must take a look at the OpenGL Extension Registry, at http://oss.sgi.com/projects/ogl-sample/registry/. Here you can find documentations for all extensions.

You may also like to take a look at how many cards have support for the desired extension.Go to http://www.delphi3d.net/hardware/allexts.php select the extension and a list will appear with the cards supporting it.

Try using extensions with the greatest compatibility, if you want your program to run properly on the most of the systems.

Before closing I would like to make some notes on extensions and drivers. As the video card needs a video driver, the driver could change some of the extensions and it's possible that some extensions are available on some driver versions and not on others. It is also possible that if an extension is not supported entirely by the hardware, the driver will simulate it by software, which results in a massive slow-down. I also noticed that on an old video card, the GL_ARB_multitexture was only present in the extensions string with certain drivers, while the card had full support for multitexture with two units. Even if the "GL_ARB_multitexture" was not in the extensions string, the procedures returned ok, the program was working fine. Weird thing I know, this is one of the reasons I haven't included a routine to check if the name is in the string, cause if multitexture is not supported, procedures will return empty anyway. I don't know what to say about drivers and versions, it is very confusing, because I tested lots of programs and benchmarks, and my conclusion is that the latest version is not always the best.

This is my very first document for public. Many informations are based on my own experiments, so if you find something wrong please mail me. You also have to take in consideration that this tutorial if written for beginners, so I skipped some things, just to make it as simple as possible. The text may also contain grammar and expression mistakes, try to look over them, I'm not a native english speaker... Sorry for these. The example program should be successully compiled with any version of FPC above 1.0.10, but try using the latest version as possible. As about the source code, the only part that should be used for learning are the routines using multitexturing, the window initialization and the window procedure were reduced to the simplest as possible. For your own programs you should use the windows stuff from the Delax's sources, those are more safer.

If this lesson was useful for you, then please mail me and give me your opinion or suggestion for future subjects. I don't want my work to be a waste of time...

Good luck!

Dani Peter, // Mr.WereWolf
[dpsoftware@personal.ro]

The source files can be found here

Back to previous page

Useful Links









Link to us