2D light effect with alpha mask: problem with background.

Hello to everyone,
[sorry for my english. It’s a disaster]
I’m trying to generate a 2D light area with the precious help of our friend “alpha mask”. Now, the process is very simple but I’m an opengl’s newbie and I hope that someone of you can just direct me to the right way/technique.
I’ve a certain amount of 2D textures (my sprites) witch composing my map: background, items, boxes … etc … etc. Once I’ve painted the map I use a large big black texture painted over the map as the foreground: this generate darkness.

Now, before painting the “darkness” image, I paint a “light texture” (a white image, completely visible from external to completely invisible to the center) using the following declaration:


glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ZERO);

The darkness image instead is preceded by the following declaration:


glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA);

The natural consequence of this is this image:

As you can see the cone of light effect is good (I need to adjust the transparency of my “light” texture, but it’s ok) but the alpha channel of others semi-transparent elements from map are involved in the equation generated by the function:


glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA);

This is a big problem! The render code is this:


	{…}
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        foreach(singleton, &cengine::environment) {
	    /* every graphical element is formed by one or more textures */
            while ((texture = singleton->get()) != EE_NULLTEXTURE) { 
                glLoadIdentity();
                glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texture);
                glPushMatrix();
                glTranslatef(...);
                if (singleton->flipped[EFLIPDIRECTION_VERTICAL])
                    glRotatef(180.0, 1.0, 0.0, 0.0);
                if (singleton->flipped[EFLIPDIRECTION_HORIZONTAL])
                    glRotatef(180.0, 0.0, 1.0, 0.0);
                glRotatef(fmod(singleton->rotation, 360), 0.0, 0.0, 1.0);
                glBegin(GL_QUADS);
                {
                    glTexCoord2i(...);
                    glVertex3f(...);
                    glTexCoord2i(...);			
                    glVertex3f(...);
                    glTexCoord2i(...);
                    glVertex3f(...);
                    glTexCoord2i(...);	
                    glVertex3f(...);
                }
                glEnd();
                glPopMatrix();
            }
        }
        glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ZERO);
        singleton = light;
	/* LIGHT CONE (just a test) */
        while ((texture = singleton->get()) != EE_NULLTEXTURE) {
             	glLoadIdentity();
                glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texture);
                glPushMatrix();
                glTranslatef(...);
                if (singleton->flipped[EFLIPDIRECTION_VERTICAL])
                    glRotatef(180.0, 1.0, 0.0, 0.0);
                if (singleton->flipped[EFLIPDIRECTION_HORIZONTAL])
                    glRotatef(180.0, 0.0, 1.0, 0.0);
                glRotatef(fmod(singleton->rotation, 360), 0.0, 0.0, 1.0);
                glBegin(GL_QUADS);
                {
                    glTexCoord2i(...);
                    glVertex3f(...);
                    glTexCoord2i(...);			
                    glVertex3f(...);
                    glTexCoord2i(...);
                    glVertex3f(...);
                    glTexCoord2i(...);	
                    glVertex3f(...);
                }
                glEnd();
                glPopMatrix();
        }
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA);
        singleton = imgbackground;
	/* FOREGROUND DARKNESS (just a test) */
        while ((texture = singleton->get()) != EE_NULLTEXTURE) {
            	glLoadIdentity();
                glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texture);
                glPushMatrix();
                glTranslatef(...);
                if (singleton->flipped[EFLIPDIRECTION_VERTICAL])
                    glRotatef(180.0, 1.0, 0.0, 0.0);
                if (singleton->flipped[EFLIPDIRECTION_HORIZONTAL])
                    glRotatef(180.0, 0.0, 1.0, 0.0);
                glRotatef(fmod(singleton->rotation, 360), 0.0, 0.0, 1.0);
                glBegin(GL_QUADS);
                {
                    glTexCoord2i(...);
                    glVertex3f(...);
                    glTexCoord2i(...);			
                    glVertex3f(...);
                    glTexCoord2i(...);
                    glVertex3f(...);
                    glTexCoord2i(...);	
                    glVertex3f(...);
                }
                glEnd();
                glPopMatrix();
        }
        glDisable(GL_BLEND);
        glutSwapBuffers();
        glFlush();
	{…}

So, my question is: there is a technique to, for instance, applying a mask (or masks, in this case the cone of light) to a single texture (in this case to the foreground) in a fast way?
Thanks in advice guys, really.
[/sorry for my english. It’s a disaster]

Do you know how to use stencils?

With a stencil buffer you could do the following

  1. render you map

  2. enable drawing to the stencil buffer only

  3. render your light cone to the stencil buffer

  4. with the stencil buffer testing enabled, render a full screen rect in black (with what ever alpha)

The stencil will stop any black being draw where the cone value exists.

Thank you so much!

Mh, just another simply question (so that I know I’m on the right path). Did stencils support the “fade out” effect or just visible/invisible pixel? I mean, with stencils can I generate the same effect of the image before (the cone of light)?
Thanks for help!

I think it either suceeds or fails. You would need to have a dither pattern to give the illusion of fading on the edges. The alternate I thought of is to render the light cone to a texture buffer and then use a shader to check this texture when rendering the black layer. This would in effect give you a alpha mask - hope that makes sense

I am not sure I understand your exact problem, but staying with blending, I propose :

  • edit the darkness texture with the light texture using additive blending :

glBlendFunc(GL_ONE, GL_ZERO);

  • now in this mask black means black and white mean same color
  • then apply the resulting mask to the color texture, using multiplicative blending :

glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);

@tonyo_au: Thank you for your reply! Applying a pattern to the stencil is a good choice if I’ve a single cone of light, thanks! Unfortunately I’ve two or more cone of lights that, eventually, intersecate each others (and in this case the result is not what I’m looking for). Shader is a way, thanks again! I will look around for more informations.

@ZbuffeR: Thank you for your reply! Yeah, that’s what I’m looking for: applying one/two/more masks (cone of light) to a single texture (black one). How can I mask only that texture (black) with cone of light (one or more) using glBlendFunc saving the result in a “output” texture and then blending the output texture over the map? Thanks!