Faking multiple colored light sources in OpenGL ES

by ingvar 3. juli 2010 14:04

I wanted to make a little 2D game for the iPhone. I wanted the game play to be happening at night and for the player to be able to see anything at all I needed to some light into the game world. I wanted multiple light sources and if possible colored light. Also, I wanted to be able to easily switch to dawn or even full day light. I wanted to be able to ‘shape’ the light, like the light cones from a cars headlights and not just light spots. So I came up with the idea of rendering light sprites to a texture that is bound to a off screen frame buffer and then blend this texture onto the 2D rendered world. This method keeps the number of textures to a minimum with a large amount of light ‘sources’ - though they are still fakes.

Here is the two light textures used in this example. The one the left is used for the car and the one on the right is used for the three light spots. I have putted both textures on a blue background because they contain transparent areas. I used OpenGL to scale and change the color of when rederingen the three light spots.

Car head lights Spot""

Here is the rendered 2D world:

2D World

Here is the final rendered light texture:

Final light texture

Here is the final result:

Final combined result

So lets dive into the code. Below is a series of code fragments that shows how to implement the method I have just described.

Here is what you need to do to make the texture that we will render to:

/* Create the texture */
glGenTextures(1, &textureFramebufferTexture);
glBindTexture(GL_TEXTURE_2D, textureFramebufferTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

/* Create the offscreen framebuffer and bind this to the texture */
glGenFramebuffersOES(1, &textureFramebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFramebuffer);
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, textureFramebufferTexture, 0);
glBindTexture(GL_TEXTURE_2D, 0);

/* Now you can render to the textureFramebuffer like any other frame buffer */

This is how to render to that texture frame buffer. It is just like any other frame buffer:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFramebuffer);

/* Do your rendering - standard OpenGL ES */

 

This is how I rendered the light textures:

glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glBlendFunc(GL_ONE, GL_ONE);
/* Make the spot red */
glColor4f(1.0, 0.6, 0.6, 1.0);
glBindTexture(GL_TEXTURE_2D, spotTexture);

glLoadIdentity();
glTranslatef(spotX, spotY, 0.0);
glScalef(2.0, 2.0, 0.0);
glVertexPointer(2, GL_FLOAT, 0, spotVertices);
glNormalPointer(GL_FLOAT, 0, spotNormals);
glTexCoordPointer(2, GL_FLOAT, 0, spotTextureCoords); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

And finally how to render the texture onto the rendered 2D world. It is like rendering any other texture, so no surprises here:

/* Use normal back buffer */
glBindFramebufferOES(GL_FRAMEBUFFER_OES, frameBuffer);
glColor4f(1.0, 1.0, 1.0, 1.0);
glBindTexture(GL_TEXTURE_2D, textureFramebufferTexture);
glBlendFunc(GL_DST_COLOR, GL_ZERO);

/* Render the texture */
glVertexPointer(2, GL_SHORT, 0, vertices);
glNormalPointer(GL_FLOAT, 0, normals);
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

 

Tags: , ,

iPhone

About the author

Martin Ingvar Kofoed Jensen

Architect and Senior Developer at Composite on the open source project Composite C1 - C#/4.0, LINQ, Azure, Parallel and much more!

Follow me on Twitter

Read more about me here.

Read press and buzz about my work and me here.

Stack Overflow

Month List