[ Nomad Programming, Graphics, and Algorithms Tutorial ] [ Compiler : MS Visual C++ 6.0 ] [ Part 15 - Extensions / Multitexturing ] [ http://nomad.openglforums.com ] <<------------------------------------------------------>> [ DISCLAIMER ] I will hold no responsibility to whatever happens to you, your computer, your sanity, your pet, or whatever that may happen to your existence for your reading these texts and for the outcome of the various source code(s) given in each tutorial. So in short, read at your own risk! [ INTRODUCTION ] Multitexturing...this topic might sound so advanced to some of you, but in reality, if you can texturemap the way I told you to in Part 14, you could multitexture in no time at all...;). So what is multitexturing exactly? Well, it is when you apply two or more textures onto a polygon! Simple as that! Before anything else, it is of paramount importance that you know basic texturemapping before proceeding here. Also, Part 15 is not for everyone...Why? Well, not everyone has a video card that supports OpenGL 1.2 (Why version 1.2? Because OpenGL multitexturing support is only found in version 1.2 or later)...so unless you have one (if you could play the 3D games these days, you probably have OpenGL 1.2 or later already), you might not be able to try out multitexturing right away...:(. SideNote: It is always good practice to update always your video card driver. For example, nVidia (the makers of the oh so popular GeForce card series) has updated drivers that (as of this writing) supports OpenGL version 1.4...;). [ TUTORIAL ] Before we begin, let me tell you that this tutorial assumes you have the glext.h file. The latest version can be found in the OpenGL.org website. A copy is (but of course) included with the souce code of this tutorial...:). So what's the glext.h file for? It conveniently holds information needed for us to load extensions. More on extensions below...:). Well, some of you might be impatiently asking: "OK, I am impatient right now, how do we start already?!?" To impatiently answer that impatiently asked question, you must first ensure that your video card is impatie...err, I mean, you must first ensure that your video card supports the GL_ARB_multitexture extension. First, what is an extension? Well, an extension is what it says it is, an 'extension' to the OpenGL API (they are the functions, #defines, etc. that are new or found only in OpenGL 1.2 or later). Thanks to Microsoft's non-commitment to update its OpenGL SDK (which currently stands at version 1.1), OpenGL programmers have to access the functions new in later versions of OpenGL through extension-loading. Now exactly what is GL_ARB_multitexture? It is a string that you check/compare with when you get from OpenGL the extensions supported by your video card. Here's how you might go about it: //----8<----[ CODE BEGIN ]--------// char *myString; myString = (char*)glGetString(GL_EXTENSIONS); if(strstr(myString, "GL_ARB_multitexture") != NULL) { // There is support for multitexture! Yey! } else { // There is NO support for multitexture! *Sob! } //----8<----[ CODE END ]--------// Simple! The function glGetString(GL_EXTENSION) returns a long string that contains the names of different extensions supported by your video card. Oh yeah, I almost forgot, you must first explicitly declare the function pointers of the functions that you plan to use. Do not worry, just copy and paste from the code...:). They are: //----8<----[ CODE BEGIN ]--------// PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL; PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB = NULL; PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = NULL; //----8<----[ CODE END ]--------// You would most probably put these functions declarations at the top of your file (see source code). You might be overwhelmed after reading: o PFNGLACTIVETEXTUREARBPROC o PFNGLMULTITEXCOORD2FARBPROC o PFNGLCLIENTACTIVETEXTUREARBPROC While they're easier to copy and paste than read, they are actually words that are products of some sort of coding convention. For example, PFNGLACTIVETEXTUREARBPROC means it is a "Pointer to a function named glActiveTextureARB". The last four letters ("PROC") probably means it's a procedure/function (which strikes me as redundant, since they've used "FN" already, which means function. If you believe I am mistaken, please let me know). The others are read the same way also...;). Just think of them as normal data types, like char, float, etc. if you are overwhelmed by the long words...;). Well, we have now a code that checks if multitexturing is supported and codes that creates "variables" that is supposed to point to the start of their respective function. What we need now is to actually let these function point to the start of the address of where it's located at...You do it like so: //----8<----[ CODE BEGIN ]--------// char *myString; myString = (char*)glGetString(GL_EXTENSIONS); if(strstr(myString, "GL_ARB_multitexture") != NULL) { glActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) \ wglGetProcAddress("glActiveTextureARB"); glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) \ wglGetProcAddress("glMultiTexCoord2fARB"); glClientActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) \ wglGetProcAddress("glClientActiveTextureARB"); if(!glActiveTextureARB || !glMultiTexCoord2fARB || !glClientActiveTextureARB) { // Error getting procedure/function's starting address. return; } } else { // There is NO support for multitexture! *Sob! } //----8<----[ CODE END ]--------// Don't worry with the bad-looking code, it's not too bad. All we did was call wglGetProcAddress() to let it return to us the respective function starting address that we need. That was all that was added...;). Now with all the initializations out of the way, let's go to the fun stuff! Now, we load our textures the same way we do in Part 14. In fact, I will be using the exact same function that loads bitmap in Part 14 (that'll be the BitmapLoad() function...;) ). Obviously, we'll need two or more textures for multitexturing to work (you COULD use one texture and multitexture...but honestly, I do not know of any reason why you'd do this...;) ). Note that multitexturing can support up to 32 textures! But then again, the more texture you multitexture with, the more strain it will be for your video card...so use them wisely! Now, say our two texture IDs are named: TEXTURE_0 and TEXTURE_1. (You should know what a texture ID is by now. If you need to refresh your memory, review Part 14...:) ). The first thing we'd have to do is make our textures active, like so: //----8<----[ CODE BEGIN ]--------// glActiveTextureARB(GL_TEXTURE0_ARB); glBindTexture(GL_TEXTURE_2D, TEXTURE_0); glActiveTextureARB(GL_TEXTURE1_ARB); glBindTexture(GL_TEXTURE_2D, TEXTURE_1); //----8<----[ CODE END ]--------// If it's not obvious enough, glActiveTextureARB(GL_TEXTURE0_ARB) tells our video card that we want texture number 0 (the count starts at zero you see) to be active when our video card renders our scene (remember that OpenGL is a state-machine, meaning, the output of your scene depends on what state OpenGL is in...what we're doing is "activating" the "texture 0" flag so when OpenGL renders our scene, it knows that it should use texture 0). glBindTexture() binds the corresponding texture ID with GL_TEXTURE0_ARB. The same is true for GL_TEXTURE1_ARB...;). Now for the last step... In Part 14, we used the function glTexCoord2f() to apply a texture to a polygon. Now we replace this function with glMultiTexCoord2fARB() which works similarly with glTexCoord2f() except that it accepts one more parameter. The following code will show what I mean: //----8<----[ CODE BEGIN ]--------// glBegin(GL_QUADS); { glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f); glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f); glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f); glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f); glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); } glEnd(); //----8<----[ CODE END ]--------// The code is a bit self-explanatory (that is, assuming you know how to use glTexCoord2f() already...). Well, that's all. Simple! Now go try it out yourself (like adding multitexturing to your existing project!). [ ENDING ] And that's that! Not hard eh? If there is anything that's not clear to you, your mind, or to that part of you that lets you comprehend the things here, you may email me at (according to priority): willietang@hehe.com or willietang@yahoo.com Like I said before, if you know texturemapping, then multitexturing shouldn't be a problem...;). Note that I did not cover every aspect of multitexturing here. There are other topics such as setting up glTexEnv() which can modify how texture blend together and other function pointers which might be useful to you someday. Perhaps in the future, when I have the luxury of time, I will delve further into multitexturing. Part 16 will be on how to use OpenGL matrices. Briefly, the OpenGL matrix is a 4x4 array of floats that stores information that affects where and how objects appear in your scene. A quick primer on matrix math will be done, so check out Part 16! [ NOMAD.SOURCE ] Unfortunately, it was very difficult (and very awkward) to place codes here. So it was eventually decided that the source code be downloaded on my website at: http://nomad.openglforums.com Unzip the file and click on gfxOpenGL5.dsw to see the source codes (I AM, of course, assuming you're using Visual C++ 6.0 here...). <<------------------------------------------------------>> [ Written By: Willie Tang ] [ Written On: 04.20.2003 ]