9. OpenGL Blending (Version 2.0)
Introduction
Blending is a nifty little ability which lets you take a fully opaque object and make it transparent. To do this, we introduce a new colour variable which we will A, giving us a selection of RGBA to set our colours. This A variable is a value between 0 and 1 which determines how transparent the object is, 0 being 100% transparent and 1 being completely opaque. Luckily GLUT gives us the ability to enable this in our context with a simple GLUT_RGBA, but after that there is a little work to be done. There’s a great explanation of the blending methods available on the opengl.org website, linked here <http://www.opengl.org/sdk/docs/man/xhtml/glBlendFunc.xml>, but I’ll give you a quick rundown of what we are going to use in this tutorial and some useful information on blending.
Drawing Order
One of the most important things to keep in mind when blending is the order in which you choose to draw objects in your scene. With the blending method we are going to use, we need to blend in order of furthest item first and nearest object last. Let me explain. When rendering, OpenGL is going to take the current colour in the render buffer and apply that with the current object, if you draw the closest object (which we will call Object 1) first then the only colour in the buffer to include in the transparency is the clear colour (or any other items you have drawn first). If you then draw the object behind that (Object 2), the colour of the first item drawn won’t be updated as OpenGL has finished with Object 1. So Object 1 is in front of Object 2, Object 1 has the colour of the render buffer combined with itself. Object 2 drawn after but behind Object 1 will have the colour of the render buffer (clear colour + Object 1) and will then combine that with Object 2, causing abnormalities.
To fix this, all transparent objects in the scene have to be sorted according to distance to the camera and transparent objects rendered from furthest to nearest. This sorting is unavoidable as the location of the camera is always moving, which is unfortunate but there are some fairly efficient sorting methods available as well as being able to send things off to the GPU if you know what you’re doing with tools like OpenCL and Cuda.
Blending Functions
To achieve the above transparency, we are going to use the most widely used blending methods within OpenGL and for most applications you won’t have to touch anything else. OpenGL has a blending method called glBlendFunc which takes in a source and a destination variable. For our source, we are going to send in the OpenGL constant GL_SRC_APLHA. GL_SRC_ALPHA is the incoming alpha value. So if we want to bring in Object 1 with an alpha value of 0.5 (50% transparent), GL_SRC_ALPHA will do that. Then for the destination value we are going to set GL_ONE_MINUS_SRC_ALPHA, which takes the opposite value to what we have sent for the incoming value. So if we send 0.2 as our incoming value, GL_ONE_MINUS_SRC_ALPHA is going to give us 1.0 – 0.2 = 0.8, so we want 20% of our current object and 80% of our existing render.
Coding
We’re going to do some initial changes in preparation of this looking half decent. First off, we need to change the clear colour to white. This won’t affect the blending, but will change how everything looks in the end and it’s simply not nice to blend things on a red background.
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Clear the background of our window to white
Next off, we need to change our glutInitDisplayMode to support the fourth colour, the alpha channel. This is done by including GLUT_RGBA inside of our glutInitDisplayMode:
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA); // Set up a basic display buffer (now double buffered)
Great, the next step is to enable OpenGL’s blending capabilities and set the blending modes as described above. We are going to use the GL_SRC_ALPHA and GL_ONE_MINUS_SRC_ALPHA parameters as these are the most commonly used and provide nice results when blending one object over another.
At the start of our display() method, include the following two lines and feel free to play with the parameters provided to get a feel for what blending capabilities are provided.
glEnable(GL_BLEND); // Enable the OpenGL Blending functionality glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Set the blend mode to blend our current RGBA with what is already in the buffer
Now we are finally ready to start drawing our shapes. First off, we are going to draw a green cube which is fully opaque, eg: an alpha value of 1.0f. We are going to draw this straight after we call our initial glTranslatef as we want it to stay static on the screen:
glColor4f(0.0f, 1.0f, 0.0f, 1.0f); // Set the colour to green and fully opaque glutSolidCube(2.0f); // Render the primitive
Great! If you run it now, you will have a white background and a green cube (it will look like a flat square).
Now we are going to set a colour for our moving cube and change it from a wire cube to a solid cube. We are going to set it to the colour red, with a 50% transparency.
So find glutWireCube(2.0); inside of the display method and replace it with the following:
glColor4f(1.0f, 0.0f, 0.0f, 0.5f); // Set the colour to red and 50% transparent glutSolidCube(2.0f); // Render the primitive
Go ahead and experiment with the other blending types. There are a number of different options available, each with their own unique results. The trick is learning how the alpha channel affects everything.
If you have any questions, please email me at swiftless@gmail.com
Tutorial Code
#include <GL/glew.h> // Include the GLEW header file #include <GL/glut.h> // Include the GLUT header file bool* keyStates = new bool[256]{0}; // Create an array of boolean values of length 256 (0-255) bool movingUp = false; // Whether or not we are moving up or down float yLocation = 0.0f; // Keep track of our position on the y axis. float yRotationAngle = 0.0f; // The angle of rotation for our object void keyOperations (void) { if (keyStates[GLUT_KEY_LEFT]) { // If the left arrow key has been pressed // Perform left arrow key operations } } void display (void) { keyOperations(); glEnable(GL_BLEND); // Enable the OpenGL Blending functionality glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Set the blend mode to blend our current RGBA with what is already in the buffer glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Clear the background of our window to white glClear(GL_COLOR_BUFFER_BIT); //Clear the colour buffer (more buffers later on) glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations glTranslatef(0.0f, 0.0f, -5.0f); // Push eveything 5 units back into the scene, otherwise we won't see the primitive glColor4f(0.0f, 1.0f, 0.0f, 1.0f); // Set the colour to green and fully opaque glutSolidCube(2.0f); // Render the primitive glTranslatef(0.0f, yLocation, 0.0f); // Translate our object along the y axis glRotatef(yRotationAngle, 0.0f, 1.0f, 0.0f); // Rotate our object around the y axis glColor4f(1.0f, 0.0f, 0.0f, 0.5f); // Set the colour to red and 50% transparent glutSolidCube(2.0f); // Render the primitive glutSwapBuffers(); // Swap our buffers if (movingUp) // If we are moving up yLocation -= 0.05f; // Move up along our yLocation else // Otherwise yLocation += 0.05f; // Move down along our yLocation if (yLocation < -3.0f) // If we have gone up too far movingUp = false; // Reverse our direction so we are moving down else if (yLocation > 3.0f) // Else if we have gone down too far movingUp = true; // Reverse our direction so we are moving up yRotationAngle += 0.1f; // Increment our rotation value if (yRotationAngle > 360.0f) // If we have rotated beyond 360 degrees (a full rotation) yRotationAngle -= 360.0f; // Subtract 360 degrees off of our rotation } void reshape (int width, int height) { glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up) gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly } void keyPressed (unsigned char key, int x, int y) { keyStates[key] = true; // Set the state of the current key to pressed } void keyUp (unsigned char key, int x, int y) { keyStates[key] = false; // Set the state of the current key to not pressed } int main (int argc, char **argv) { glutInit(&argc, argv); // Initialize GLUT glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA); // Set up a basic display buffer (now double buffered) glutInitWindowSize (500, 500); // Set the width and height of the window glutInitWindowPosition (100, 100); // Set the position of the window glutCreateWindow ("Your first OpenGL Window"); // Set the title for the window glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering glutIdleFunc(display); // Tell GLUT to use the method "display" as our idle method as well glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events glutMainLoop(); // Enter GLUT's main loop }
I personally think this looks better for the moving cube:
glColor4f(1.0f, 0.0f, 0.0f, 1.0f); // Set the colour to red and 0% transparent
glutWireCube(2.0f); // Render the primitive
glColor4f(1.0f, 0.0f, 0.0f, 0.5f); // Set the colour to red and 50% transparent
glutSolidCube(2.0f); // Render the primitive
It gives it a border so it actually looks like a cube instead of a distorted shape.
There’s a problem in your tutorial. Everything’s right except for in the code the smaller cube should be written first, otherwise the blending doesn’t take effect. It confused me for a while.
There is a big problem with the code that kind of undermines the lesson. Since the smaller cube is drawn second it doesn’t blend with the big cube, it just sits on top. To fix this you have to switch the order that the cubes are drawn or else it doesn’t blend at all, and then it makes no difference whether the blend options are on or not.
I can’t read the code… the advertisements kind of push it all into the right and then it’s cut off so basically I just see this:
|Line 21: glClear (GL_COLOR_BUFFER_ …
this is NOT a good thing… because I’m actually trying to follow a tutorial, not GUESS what is actually there. Why is it like this anyway? :] Would you please fix it?
NVM: I installed AdBlock, now I can see perfectly. Thanks anyway.
Middle mouse button drag, and be thankful next time someone teaches you C++.
Just remember that objects that are drawn on the
screen are drawn in the order in which you choose to draw them. So if you
draw a square (square1), before another square (square2), then if square1
is brought infront of square2 while square1 has an alpha value, then
square1 will not show square2 behind it.
I think this is the heart of this tutorial and I couldnt get it.
lol
#include”GL/glew.h” not # include “GL/gl.h”
Hey kalin,
This was an old tutorial back before I started tidying up code and moved to glew. Although with the #include “GL\glut.h” line, the #include “GL\gl.h” line becomes redundant.
Cheers,
Swiftless