iDevGames Forums
2D Dynamic Lighting in OpenGL! Sort of... - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: Graphics & Audio Programming (/forum-9.html)
+--- Thread: 2D Dynamic Lighting in OpenGL! Sort of... (/thread-1043.html)

Pages: 1 2


2D Dynamic Lighting in OpenGL! Sort of... - metacollin - Jul 8, 2009 04:08 PM

OK, first a little background. I was trying to add lighting via 2D textured blended quads, but that proved to be a headache: thread.

NelsonMandella suggested I implement my own dynamic lighting system using quads, and after procrastinating a while, I finally did! It looks great, much better than I was expecting, actually.

Instead of altering the luminosity of tiles directly however, I created an 'ambient light overlay'. Just a quad the size of the viewport with a grid of tiles that are selectively subdivided depending if they need to be lighted or not.

The lighting works by altering the alpha of these tiles, so an alpha of 1.0 is completely black, and an alpha of 0.0 is completely visible. This isn't true lighting I suppose, but I wanted variability in visibility, not actual amplification of the colors. And it is still fully possible to do a color multiplicative overlay in addition to this, so nothing lost.

My quads are 16x16 in coordinates, but 32x32 in pixels, so I subdivide 1 16x16 tile in to 16*4x4 tiles when they require lighting calculations. The problem is when I have two lights intersecting, I get a weird artifact, as seen here:

Light Artifacts

Here is how I calculate the alphas for each corner of each tile (oh, and feel free to use/steal this code, of course):

Code:
//NOTE: range = mahLight.radius*mahLight.radius
dx = (mahLight.pixelposx - vTiles[i][j].pixelposx); //delta x
dy = (mahLight.pixelposy - vTiles[i][j].pixelposy); //delta y
dist = dx*dx + dy*dy; // Distance squared

float blint, brint, tlint, trint; //Bottom Left, Bottom Right, Top Left, Top Right alpha intensity
blint = (dist - range) / range; //Linear falloff for an offset of 0,0

dx = (mahLight.pixelposx+4 - vTiles[i][j].pixelposx); //Offset delta x 4 pixels to the right
brint = (dx*dx + dy*dy);
brint = (brint-range) / range; //fall off for an offset of 4,0

dy = (mahLight.pixelposy+4 - vTiles[i][j].pixelposy); //Offset delta y by 4 pixels up
trint = (dx*dx + dy*dy);
trint = (trint-range) / range; //fall off for an offsent of 4,4

dx = (mahLight.pixelposx - vTiles[i][j].pixelposx); //Offset delta x back to 0
tlint = (dx*dx + dy*dy);
tlint = (tlint-range) / range; //fall off for an offset of 0,4


if((vTiles[i][j].a3 + blint) < vTiles[i][j].a3) //pick the brighter (small alpha) of the two
    vTiles[i][j].a3 += blint;

clamp(vTiles[i][j].a3, 0.0f, aii); //don't let it get darker than the ambient light

if((vTiles[i][j].a4 + brint) < vTiles[i][j].a4)
    vTiles[i][j].a4 += brint;

clamp(vTiles[i][j].a4, 0.0f, aii);

if((vTiles[i][j].a1 + trint) < vTiles[i][j].a1)
    vTiles[i][j].a1 += trint;

clamp(vTiles[i][j].a1, 0.0f, aii);

if((vTiles[i][j].a2 + tlint) < vTiles[i][j].a2)
    vTiles[i][j].a2 += tlint;

clamp(vTiles[i][j].a2, 0.0f, aii);

vTiles are subdivided tiles, i and j are grid coordinates. Note how I pick the lighter of the two values (smaller alpha) and clamp it between totally transparent and only as opaque as the ambient light allows. Also note that intensities within the radius will be negative, since the whole thing is inverted because I am using alphas.


Any thoughts on what I'm doing wrong in my calculations to cause this artifact? Thanks!


2D Dynamic Lighting in OpenGL! Sort of... - Skorche - Jul 8, 2009 08:42 PM

What is the reasoning for using tiles and subdividing? Couldn't you just render a quad with a lightmap texture for each light? It's very simple and looks just fine.


2D Dynamic Lighting in OpenGL! Sort of... - metacollin - Jul 8, 2009 10:33 PM

Sorry, I should have qualified this. I plan on doing more advanced lighting techniques later, such as animating it (already implemented, lighting equations can change etc. to create pulses and flickers that look much nicer than anything I could achieve by scaling a quad) as well as object light occlusion, color mixing, etc. etc., most of which isn't possible with the 2D quad solution. Also, when two light-mapped quads intersect, their are gradient artifacts due to the blending equation and there being only 256 levels of alpha, something that isn't a problem by doing the lighting procedurally.

I could be full of bullcrap though. Feel free to educate me. =)


2D Dynamic Lighting in OpenGL! Sort of... - _ibd_ - Jul 9, 2009 03:45 AM

Make the falloff of the light intensity exponential, not linear, and it should blend fine.


2D Dynamic Lighting in OpenGL! Sort of... - Skorche - Jul 9, 2009 07:50 AM

Well, you can't change the falloff curve easily without changing the texture, but you can still trivially modulate the size or brightness. Regenerating a texture with a different curve wouldn't exactly be more expensive than recalculating all lights every frame. You could easily get away with a 64x64 or 128x128 lightmap texture without anybody noticing too.

As far as blending, you most certainly can do fancier blending than just adding the two together. I like to emulate Photoshop's screen blending mode as it blends nicely without oversaturating.

You can also cast shadows easily using the GPU: http://www.gamedev.net/reference/programming/features/2dsoftshadow/ Hard edged shadows are pretty trivial if that's all you want.

Edit: Found my 100% fixed function GPU accelerated shadow mapping demo app. GPUShadow No shadow calculation is done on the CPU, and it's less than 150 lines long so it's pretty simple. Really just a quick shadow masking demo compatible with the iPhone. As such it just uses linear falloff and additive blending. I'd be willing to share the code on request, but not post it here.


2D Dynamic Lighting in OpenGL! Sort of... - metacollin - Jul 10, 2009 02:49 AM

Skorche, thanks a LOT for the info! You've more or less totally changed my mind about how I'm going to deal with my lighting, textured quads for the win - and the dynamic equation alterations are something I can live without, or at least futz together with multiple textures/multiple quads blended together.

Here's my problem: I messed around with screen blending in photoshop, and it looks quite nice for colored lights (especially color mixing...blue + red actually makes glowing purple). However, if I just want to increase the visibility of something (like in the screen shot), it doesn't work so well. White painted over it just looks like solid white.

One trick I know is just blending an image with itself with screen to lighten it, but I have no idea where to start on implementing screen blending.

Unfortunately, I have no idea how to implement screen blending within the confines of OpenGL blending. The equation(in OpenGL color space) is Cr = 1- ((1-Sc)*(1-Dc)) where Cr = the resulting color, Sc and Dc are the source and destination colors, correct? It would be easy if there was a GL_MULTIPLY for glBlendEquation(), but...

I suppose I could accomplish the visibility increase using something along the lines of Cr = 1- ((1-Sa*Dc)*(1-Dc)) where Sa = source alpha.

I have a sinking feeling I'm going to have to learn how to use GLSL to make this work. Tell me it isn't true! =)

Just to be clear on what I'm looking at doing:

1. Have white lights that brighten the scene without washing out the color. (In my original implementation in the earlier thread, I was only able to accomplish this by using multiple quads blending over each other, which caused artifacts).
2. Properly mixing color lights (a red, green, and blue spotlight would appear like a while light) would be nice.


Again, thanks for all the help everyone. I know nothing about optics or the physics of light for visual purposes, so you'll have to bear with me. This whole lighting thing was supposed to be simple....dim a scene, have a lantern (spot light like in the screenie) and eventually a directional flashlight (crazy light occlusion shadow stuff...I'll worry about that later/never), but it's sure giving me a headache.


2D Dynamic Lighting in OpenGL! Sort of... - Skorche - Jul 10, 2009 07:32 AM

Well, you don't exactly just blend the lights over the top of the image. You build a lightmap first, then multiply that against your scene. However, blending a second copy of your lights over the top of the final lit image makes for a nice foggy effect.

The simplest way to do it is to render the lightmap into a texture. You can even halve the resolution if you want to save on fillrate and it will still look OK. When doing shadow masking, you can eat up a lot of fillrate at full resolution when you have more than a couple lights. Once you have your lightmap, you can draw your scene normally (without any sort of lighting) then multiply the lightmap texture over the top of the scene.

If you want to avoid the render to texture for some reason, then render your lightmap into the framebuffer before drawing anything else. Then draw all of your scene elements from front to back, using a multiply blend mode. You'll have to use the z or stencil buffer to ensure that you never have any overdraw because you only have one copy of the lightmap and you are drawing over the top of it. Doing it this way, you can't use any alpha blended sprites.

A final nifty trick that you can do to add a bit more depth to your lighting is to draw background and midground elements with lighting, then draw your foreground over the top of them using just a constant ambient lighting value.

I've always wanted to use this lighting effect (and a certain physics engine) in a 2D side scroller, but don't have the design skills to make such a game. Annoyed It would totally be fantastic though I think.


2D Dynamic Lighting in OpenGL! Sort of... - metacollin - Jul 10, 2009 03:02 PM

Hmm, multiplying the a light map texture over the scene sounds interesting. Let me just make sure I have it in my head right:

1. Draw all lights currently visible in an empty frame buffer that has been cleared with an ambient light color (If I wanted a totally black scene except where there were lights, this would be black... totally visible, white, half intensity would be 0.5, 0.5, 0.5 etc.).

2. Write it to a texture using glCopyTexImage2D(), making sure to overwrite the old texture (save that VRAM!)

3. Render scene then blend the light map with glBlendFunc(GL_ZERO, GL_SRC_COLOR). Or glBlendFunc(GL_DST_COLOR, GL_ZERO).

I'm going to try it right now and see what happens.


2D Dynamic Lighting in OpenGL! Sort of... - Skorche - Jul 10, 2009 03:55 PM

Yep. That's pretty much it. I forgot to mention clear the lightmap with the ambient color, but you seem two be a step ahead of me.


2D Dynamic Lighting in OpenGL! Sort of... - metacollin - Jul 12, 2009 02:42 PM

Woot! Lighting Success!

Finally, everything is working as it should. Those are 3 spot lights, but using 1-time procedural generation and a variety of attenuation equation (lambertian looks nice) you can get some nice looking effects. Thanks a lot for that method, Skorche. It solved all my problems.

Just to reiterate for anyone who wants to implement this themselves:


1. Clear the screen with your ambient light color, noting that higher values mean higher visibility. Make sure the alpha is set to 0.

2. Render quads textured with light maps with their alpha determining their intensity. Use glBlendFunc(GL_SRC_ALPHA, GL_ONE);

4. Copy the current buffer to a texture using glCopyTexImage2D() to generate the initial texture, and glCopyTexSubImage2D() to replace it. This was a bit of a hiccup for me, as I read glCopyTexSubImage2D() was faster, so I was just using that, but you have to setup the color space with glCopyTexImage2D() (using GL_RGBA) first.

I haven't tried this yet, but even faster is to use a frame buffer object if your card supports that extension, but dealing with extensions is something for a later date for me.

5. Clear the depth and color buffers and render the scene as normal.

6. Draw a quad over the view port with the texture you generated in step 4. Blend it with glBlendFunc(GL_DST_COLOR, GL_ZERO); Modulate the colors of the quad if you want to multiply in an additional ambient tint at this point, otherwise just use white.

7. Enjoy an episode of the A-Team, you've earned it!

I bet other cool things can be done with this, like mapping a caustic texture using multitexturing in addition to this to create interesting looking underwater scenes or something. I'll have to play around with it.


2D Dynamic Lighting in OpenGL! Sort of... - maximile - Jul 12, 2009 03:09 PM

Awesome stuff. Might have to have a play around myself.


2D Dynamic Lighting in OpenGL! Sort of... - metacollin - Jul 12, 2009 03:44 PM

Just one last shameless plug:
Video of lighting in action


2D Dynamic Lighting in OpenGL! Sort of... - monteboyd - Jul 13, 2009 01:50 AM

Looks great metacollin! You could have a level set in a nightclub with that lighting! Wink


2D Dynamic Lighting in OpenGL! Sort of... - ThemsAllTook - Jul 13, 2009 06:05 AM

Fantastic. I'm looking forward to playing this. Grin


2D Dynamic Lighting in OpenGL! Sort of... - Skorche - Jul 13, 2009 09:01 AM

Now you just need some shadow casting and some pulsing or flickering lights and you're golden. Smile