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
}
  • March 25, 2010
  • 9