BlendingSimple Transparency Most special effects in OpenGL rely on some type of blending. Blending is used to combine the color of a given pixel that is about to be drawn with the pixel that is already on the screen. How the colors are combined is based on the alpha value of the colors, and/or the blending function that is being used. Alpha is a 4th color component usually specified at the end. In the past you have used GL_RGB to specify color with 3 components. GL_RGBA can be used to specify alpha as well. In addition, we can use glColor4f() instead of glColor3f(). Most people think of Alpha as how opaque a material is. An alpha value of 0.0 would mean that the material is completely transparent. A value of 1.0 would be totally opaque. The Blending Equation If you are uncomfortable with math, and just want to see how to do transparency, skip this section. If you want to understand how blending works, this section is for you. (Rs Sr + Rd Dr, Gs Sg + Gd Dg, Bs Sb + Bd Db, As Sa + Ad Da) OpenGL will calculate the result of blending two pixels based on the above equation. The s and d subscripts specify the source and destination pixels. The S and D components are the blend factors. These values indicate how you would like to blend the pixels. The most common values for S and D are (As, As, As, As) (AKA source alpha) for S and (1, 1, 1, 1) - (As, As, As, As) (AKA one minus src alpha) for D. This will yield a blending equation that looks like this: (Rs As + Rd (1 - As), Gs As + Gd (1 - As), Bs As + Bd (1 - As), As As + Ad (1 - As)) This equation will yield transparent/translucent style effects. Blending in OpenGL We enable blending just like everything else. Then we set the equation, and turn off depth buffer writing when drawing transparent objects, since we still want objects behind the translucent shapes to be drawn. This isn't the proper way to blend, but most the time in simple projects it will work fine. Rui Martins Adds: The correct way is to draw all the transparent (with alpha < 1.0) polys after you have drawn the entire scene, and to draw them in reverse depth order (farthest first). This is due to the fact that blending two polygons (1 and 2) in different order gives different results, i.e. (assuming poly 1 is nearest to the viewer, the correct way would be to draw poly 2 first and then poly 1. If you look at it, like in reality, all the light comming from behind these two polys (which are transparent) has to pass poly 2 first and then poly 1 before it reaches the eye of the viewer. You should SORT THE TRANSPARENT POLYGONS BY DEPTH and draw them AFTER THE ENTIRE SCENE HAS BEEN DRAWN, with the DEPTH BUFFER ENABLED, or you will get incorrect results. I know this sometimes is a pain, but this is the correct way to do it. We'll be using the code from the last tutorial. We start off by adding two new variables to the top of the code. I'll rewrite the entire section of code for clarity. #include <windows.h> // Header File For Windows #include <stdio.h> // Header File For Standard Input/Output #include <gl\gl.h> // Header File For The OpenGL32 Library #include <gl\glu.h> // Header File For The GLu32 Library #include <gl\glaux.h> // Header File For The GLaux Library HDC hDC=NULL; // Private GDI Device Context HGLRC hRC=NULL; // Permanent Rendering Context HWND hWnd=NULL; // Holds Our Window Handle HINSTANCE hInstance; // Holds The Instance Of The Application bool keys[256]; // Array Used For The Keyboard Routine bool active=TRUE; // Window Active Flag Set To TRUE By Default bool fullscreen=TRUE; // Fullscreen Flag Set To Fullscreen Mode By Default bool light; // Lighting ON/OFF bool blend; // Blending OFF/ON? ( NEW ) bool lp; // L Pressed? bool fp; // F Pressed? bool bp; // B Pressed? ( NEW ) GLfloat xrot; // X Rotation GLfloat yrot; // Y Rotation GLfloat xspeed; // X Rotation Speed GLfloat yspeed; // Y Rotation Speed GLfloat z=-5.0f; // Depth Into The Screen GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f }; // Ambient Light Values GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f }; // Diffuse Light Values GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f }; // Light Position GLuint filter; // Which Filter To Use GLuint texture[3]; // Storage for 3 textures LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc Move down to LoadGLTextures(). Find the line that says: if (TextureImage[0]=LoadBMP("Data/Crate.bmp")). Change it to the line below. We're using a stained glass type texture for this tutorial instead of the crate texture. if (TextureImage[0]=LoadBMP("Data/glass.bmp")) // Load The Glass Bitmap ( MODIFIED ) Add the following two lines somewhere in the InitGL() section of code. What this line does is sets the drawing brightness of the object to full brightness with 50% alpha (opacity). This means when blending is enabled, the object will be 50% transparent. The second line sets the type of blending we're going to use. Rui Martins Adds: An alpha value of 0.0 would mean that the material is completely transparent. A value of 1.0 would be totally opaque. glColor4f(1.0f,1.0f,1.0f,0.5f); // Full Brightness, 50% Alpha ( NEW ) glBlendFunc(GL_SRC_ALPHA,GL_ONE); // Blending Function For Translucency Based On Source Alpha Value ( NEW ) Look for the following section of code, it can be found at the very bottom of lesson seven. if (keys[VK_LEFT]) // Is Left Arrow Being Pressed? { yspeed-=0.01f; // If So, Decrease yspeed } Right under the above code, we want to add the following lines. The lines below watch to see if the 'B' key has been pressed. If it has been pressed, the computer checks to see if blending is off or on. If blending is on, the computer turns it off. If blending was off, the computer will turn it on. if (keys['B'] && !bp) // Is B Key Pressed And bp FALSE? { bp=TRUE; // If So, bp Becomes TRUE blend = !blend; // Toggle blend TRUE / FALSE if(blend) // Is blend TRUE? { glEnable(GL_BLEND); // Turn Blending On glDisable(GL_DEPTH_TEST); // Turn Depth Testing Off } else // Otherwise { glDisable(GL_BLEND); // Turn Blending Off glEnable(GL_DEPTH_TEST); // Turn Depth Testing On } } if (!keys['B']) // Has B Key Been Released? { bp=FALSE; // If So, bp Becomes FALSE } But how can we specify the color if we are using a texture map? Simple, in modulated texture mode, each pixel that is texture mapped is multiplied by the current color. So, if the color to be drawn is (0.5, 0.6, 0.4), we multiply it times the color and we get (0.5, 0.6, 0.4, 0.2) (alpha is assumed to be 1.0 if not specified). Thats it! Blending is actually quite simple to do in OpenGL. Note (11/13/99) I ( NeHe ) have modified the blending code so the output of the object looks more like it should. Using Alpha values for the source and destination to do the blending will cause artifacting. Causing back faces to appear darker, along with side faces. Basically the object will look very screwy. The way I do blending may not be the best way, but it works, and the object appears to look like it should when lighting is enabled. Thanks to Tom for the initial code, the way he was blending was the proper way to blend with alpha values, but didn't look as attractive as people expected :) The code was modified once again to address problems that some video cards had with glDepthMask(). It seems this command would not effectively enable and disable depth buffer testing on some cards, so I've changed back to the old fashioned glEnable and Disable of Depth Testing. Alpha From Texture Map. The alpha value that is used for transparency can be read from a texture map just like color, to do this, you will need to get alpha into the image you want to load, and then use GL_RGBA for the color format in calls to glTexImage2D(). Questions? If you have any questions, feel free to contact me at stanis@cs.wisc.edu. Tom Stanis Jeff Molofee (NeHe) * DOWNLOAD Visual C++ Code For This Lesson. * DOWNLOAD Borland C++ Builder 6 Code For This Lesson. ( Conversion by Christian Kindahl )
|