glMultiTexCoord2fARB(GL_TEXTURE0_ARB, data[5*i], data[5*i+1]);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, data[5*i]+c[0], data[5*i+1]+c[1]);
glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
}
// Left Face
n[0]=-1.0f;
n[1]=0.0f;
n[2]=0.0f;
s[0]=0.0f;
s[1]=0.0f;
s[2]=1.0f;
t[0]=0.0f;
t[1]=1.0f;
t[2]=0.0f;
for (i=20; i<24; i++) {
c[0]=data[5*i+2];
c[1]=data[5*i+3];
c[2]=data[5*i+4];
SetUpBumps(n,c,l,s,t);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, data[5*i], data[5*i+1]);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, data[5*i]+c[0], data[5*i+1]+c[1]);
glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
}
glEnd();
• Use the base-texture
• Enable Lighting
• No offset texturre-coordinates → reset GL_TEXTURE-matrix
• Reset texture environment to GL_MODULATE in order to do OpenGLLighting (doesn't work otherwise!)
This will render our complete bump-mapped cube.
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
if (!emboss) {
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D,texture[filter]);
glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
glEnable(GL_BLEND);
glEnable(GL_LIGHTING);
doCube();
}
• Update Geometry (esp. rotations)
• Do The Logos
xrot+=xspeed;
yrot+=yspeed;
if (xrot>360.0f) xrot-=360.0f;
if (xrot<0.0f) xrot+=360.0f;
if (yrot>360.0f) yrot-=360.0f;
if (yrot<0.0f) yrot+=360.0f;
/* LAST PASS: Do The Logos! */
doLogo();
return true; // Keep Going
}
Finally, a function to render the cube without bump mapping, so that you can see what difference this makes!
bool doMeshNoBumps(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity(); // Reset The View
glTranslatef(0.0f,0.0f,z);
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
if (useMultitexture) {
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
}
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D,texture[filter]);
glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
glEnable(GL_LIGHTING);
doCube();
xrot+=xspeed;
yrot+=yspeed;
if (xrot>360.0f) xrot-=360.0f;
if (xrot<0.0f) xrot+=360.0f;
if (yrot>360.0f) yrot-=360.0f;
if (yrot<0.0f) yrot+=360.0f;
/* LAST PASS: Do The Logos! */
doLogo();
return true; // Keep Going
}
All the drawGLScene() function has to do is to determine which doMesh-function to calclass="underline"
bool DrawGLScene(GLvoid) // Here's Where We Do All The Drawing {
if (bumps) {
if (useMultitexture && maxTexelUnits>1) return doMesh2TexelUnits();
else return doMesh1TexelUnits();
} else return doMeshNoBumps();
}
Kills the GLWindow, not modified (thus omitted):
GLvoid KillGLWindow(GLvoid) // Properly Kill The Window
>…<
Creates the GLWindow, not modified (thus omitted):
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
>…<
Windows main-loop, not modified (thus omitted):
LRESULT CALLBACK WndProc(HWND hWnd, // Handle For This Window
UINT uMsg, // Message For This Window
WPARAM wParam, // Additional Message Information
LPARAM lParam) // Additional Message Information
>…<
Windows main-function, added some keys:
• E: Toggle Emboss / Bumpmapped Mode
• M: Toggle Multitexturing
• B: Toggle Bumpmapping. This Is Mutually Exclusive With Emboss Mode
• F: Toggle Filters. You'll See Directly That GL_NEAREST Isn't For Bumpmapping
• CURSOR-KEYS: Rotate The Cube
int WINAPI WinMain(HINSTANCE hInstance, // Instance
HINSTANCE hPrevInstance, // Previous Instance
LPSTR lpCmdLine, // Command Line Parameters
int nCmdShow) // Window Show State {
>…<
if (keys['E']) {
keys['E']=false;
emboss=!emboss;
}
if (keys['M']) {
keys['M']=false;
useMultitexture=((!useMultitexture) && multitextureSupported);
}
if (keys['B']) {
keys['B']=false;
bumps=!bumps;
}
if (keys['F']) {
keys['F']=false;
filter++;
filter%=3;
}
if (keys[VK_PRIOR]) {
z-=0.02f;
}
if (keys[VK_NEXT]) {
z+=0.02f;
}
if (keys[VK_UP]) {
xspeed-=0.01f;
}
if (keys[VK_DOWN]) {
xspeed+=0.01f;
}
if (keys[VK_RIGHT]) {
yspeed+=0.01f;
}
if (keys[VK_LEFT]) {
yspeed-=0.01f;
}
}
}
}
// Shutdown
KillGLWindow(); // Kill The Window
return (msg.wParam); // Exit The Program
}
Now that you managed this tutorial some words about generating textures and bumpmapped objects before you start to program mighty games and wonder why bumpomapping isn't that fast or doesn't look that good:
• You shouldn't use textures of 256×256 as done in this lesson. This slows things down a lot. Only do so if demonstrating visual capabilities (like in tutorials).
• A bumpmapped cube is not usual. A rotated cube far less. The reason for this is the viewing angle: The steeper it gets, the more visual distortion due to filtering you get. Nearly all multipass algorithms are very affected by this. To avoid the need for high-resolution textures, reduce the minimum viewing angle to a sensible value or reduce the bandwidth of viewing angles and pre-filter you texture to perfectly fit that bandwidth.