Dynamic 2D Lighting

[UPDATE 27.01.15] Interestingly enough, while cruising these old posts, I found a link to this post which also might have some useful explanations on the concept.

So I finally got down and dirty last night, writing a new dynamic lighting library from scratch, and really trying to do it right. Well, today I have the results! :) I will still be optimizing the lighting engine further by using memory access, which should speed up line drawing considerably, but all in all, it’s running pretty fast!

You can see a demo here: (There aren’t any actual objects, only shadows)

Dynamic Lighting Demo (Click the picture for a live demo)

It has 3 lights and 100 squares placed randomly on the stage (2 of the lights are moving). The 100 squares are technically polygons, so you can construct any polygon you want from a series of points, convex or concave, or it doesn’t even need to be a closed polygon, it can be just a series of lines.

The way I made this is actually really simple:

First I store a canvas for each light, on this canvas all the shadows for that light will be drawn, and it will be used as an alpha mask for the copyPixels() of the lights texture onto the master canvas. When drawing an object for a light, I first tell the object to draw itself onto the light’s canvas, and to return all of it’s edge points:

For each point in the polygon, I get the vectors from the light to the current point ( vc), a vector from the light to the next point ( vn), and a vector from the light to the previous point ( vp). To determine if vc is an edge point, I need to check if vectors vn and vp are on the same side of vc, or on opposite sides. I can do this by using a 2D cross product between vc and vn, and between vc and vp. If vp and vn are on opposite sides, their cross products with vc will be of opposite signs, the same if they’re on the same side. So for a tiny bit of math:

We can further use the result of crossN to see whether the segment we are going to draw from vc to vn is facing the light source or not. If the vertices of the polygon were specified in a clockwise order, then crossN will be negative if the segment is facing the light source, positive if it is not. In the light demo above, I only draw the segments that are facing away from the light source, so that most of the object is exposed to the light, but this can be easily changed.

After the edge points are found, I draw a line from the edge point, to the edge of the light’s canvas, essentially a “shadow” of the point from the light source. These lines combined with the lines of the object, will form a polygon of shadows on the light’s canvas. From there, we can floodFill() in the center of the canvas to fill the lighted area, and from there, copyPixels() the appropriate part of the light’s canvas to the main canvas (which would generally be the size of your screen).

Some improvements to be made are to support circles as shadow casters, as well as optimizing floodFill(), since that is the current bottleneck, not sure if that will be possible though.

I realize that I suck at explaining stuff like this, so if there are any questions, ask away! And I’ll try sticking this lighting engine in the Game Jam game this weekend, hopefully it will make stuff look a bit better. :)