I’m happy to say I’ve got the first major building block of lighting and shadows into the game. Here’s an example of the final result:
There were quite a few stages involved in getting these lights working, with a lot of thanks to Amit Patel’s excellent article on 2D visibility. Amit’s brilliant interactive tutorials helped me a great deal and I’m not sure I would have achieved these results without them. I also need to thank Wholehog Game’s article on lighting a 2D game for the clear explanation of using normal maps in 2D and lights as geometry.
A picture is worth a thousand words so before I explain the technique here’s a debug screenshot from the game:
This technique uses 3 (or in this case 4) frame buffer objects, textures that are written to as though they’re the main screen.
The top-left shows the diffuse (e.g. unlit coloured sprites) colour, which is how the King under the Mountain has been rendered until now.
The top-right is the normal map version of this view, drawn in the same way but using normal maps encoded to RGB rather than the sprites more usual colouring.
The bottom-left is the lighting buffer, showing the accumulated light from all light sources in view (currently just point lights, or in this case, a single point light). A polygon is rendered in the shape of the “visible” area as discussed above, and then a shader is used which uses the information in the normal map to calculate how much light strikes each pixel fragment. You’ll notice that in the final version the walls are not very visible so I’m hoping to improve this soon – I plan to encode the “height” of the walls using the alpha transparency of the normal map sprites so the light source should interact with the wall edge more realistically. Currently the point lights have a radius of 7 tiles but this falls off quite sharply so I’m going to adjust the algorithm used so they project light over a wider area without having to calculate a larger visibility polygon.
Finally the bottom-right texture is the final output, which for now is just the output of combining the diffuse and lighting buffers. Hopefully the changes I just mentioned should make this a bit brighter as its too dark as things stand. I’m also going to greatly increase the global ambient lighting – the goal is that the player can see what is in unlit areas although it should always be preferable to include some light source.
I put together a video showing the complete process from start to finish (at least in terms of development stages)
There’s a bug in the video that the point light is stretched into the aspect ratio of the window, so the light source appears oval-shaped rather than circular. After a bit of head scratching I fixed this in the shader. It occurs to me that due to the information in the wall sprite normal maps which define how it is lit, I no longer need the “top” of wall edges as part of the light visibility polygon – instead the far sides of a wall from a light source will not be lit due to the normal mapping. I’m going to take this out which should give the exact same results but be a huge performance increase, allowing for many more lights than the current solution in the same amount of processing time. I’ve not tried conclusively performance testing the point light system but as it stands the engine should handle thousands, maybe about 10,000 point lights, on-screen without any slowdown in the framerate. I’ll have to see what it can handle with a large amount of lights moving around the map at once, but I’m confident it can handle *a lot*, more than will be realistically needed in the game on-screen at once.
From here, the next major steps I’m going to be working on are global illumation – “outdoor” lighting that also illuminates indoor areas that have doors and windows. I’d also like to get some more art assets into the game to expand on the absolute basics that have been created so far. Having said that, I’m getting married next week and then I’m on honeymoon for the best part of a month so unfortunately King under the Mountain will be on hold for a while – I don’t think spending the holiday on game development would make a very good impression with my wife!
As part of working on the lighting system, and more specifically the normal map sprites, I’ve put the basics in place for ensuring that King under the Mountain is fully data-driven. That is, the game is not hard-coded to know about different kinds of walls, floors, materials and other information, instead this is loaded as the game starts from a set of data files. What this really means is that the game is completely moddable – everything you see or interact with in the game can easily be overridden in a user-created mod just by including some sprites or data files in the correct place. Ease of creating mods is a top priority for me in designing the game engine and this is going to factor into every aspect of the game’s development. The goal is that total conversion mods should be possible (and even better, easy) to implement and use the game engine for a completely different aesthetic entirely.