This implementation of dynamic shadows with soft edges in 2D using OpenGL was part of a game concept I experimented with. In the end I decided not to go ahead with the game, but as the shadowing itself was pretty fun to do and as the concepts (and possibly the code) can easily be used for other games, I will detail my work here.
I had just finished implementing hard-edged shadows for rectangles when I found a GameDev.net article by Orangy Tang, where a technique for rendering soft shadow edges is described. The article is very thorough and I ended up using most of the ideas described there. Instead of starting from scratch, I will only explain what was done differently.
Click for a (2.5 MB) full-size version. The limited colors of the gif animation don’t do it justice, but I hope you can get a good impression of the shadow behaviour. Also notice the neat (and correct) colored lighting.
The main difference between the procedure presented in the article and the approach I chose is using a render to texture operation to accumulate the light. In the article, it is proposed to build lit and unlit areas in the alpha channel and then render the geometry modulated by the alpha value for every light.
The shadowed areas, including the ‘shadow fin’ regions, are essentially calculated in the same way and written to the alpha channel of a texture. Then, the light’s texture, modulated by the new alpha values, is rendered additively to the color channels. This is repeated for every light, until finally the color values at each pixel represent the amount of light of a given color at that location.
Now it is time to render the regular geometry. In contrast to the method from the article, any blend mode can be used. Thus it is fully possible to use the usual modes for transparency and similar. In a final step, the light color values from the texture are multiplied with the scene color values.
The advantages of the original approach are retained. In particular, it is still possible to have any shape of light simply by changing the light’s texture. Additionally, colored lighting works more intuitively and the presence of light and shadow does not affect the regular rendering of the scene at all.
It comes at the cost of requiring a render to texture type operation. On modern graphics chips, however, this feature is fast and well supported using framebuffer objects. Unfortunately, my notebook’s onboard graphics adapter does not provide the necessary extensions and so the downloadable code uses a slower copy of the light data to a texture instead.
For the implementation, I used the D programming language along with small parts of Arc 0.2 for Phobos. As said, the code was taken from a game prototype and as such was written to get things done quickly and not for extensibility or efficiency. Nevertheless, I have cleaned it and tried to add comments where they were missing, so it should be quite understandable.
There are a lot of possible enhancements: Spot lights and using the depth buffer to allow some geometry to be drawn over shadows instead of under (see the article) would be two worthwile additions. Significant improvements of the efficiency of shadow calculations are possible. Calculating shadows only for light blockers that are in the light’s radius, using the framebuffer objects extension, if available, and caching shadows for immobile lights and blockers should all improve performance significantly.
Finally, here are the sources.