Part of my latest roguelike project started with my playing around with 2D shadows for the fun of it. There are some great resources out there for examples of how to do 2D shadows; Catalin in particular did a bunch of the heavy lifting to figure out how to do soft shadows in 2D using Shaders. I got a version of that up an running, but ultimately couldn’t get the performance I wanted out of the algorithm. In particular older iOS devices and their lack of MRTs and painful fill rate absolutely killed my implementation of Catalin’s approach, which is very much shader and fill bound.
After squeezing every bit of perf I could out of that approach and not getting there, I gave up and took a few steps back. I drew countless mockups of 2D shadows up on my whiteboard, convinced there must be a better way;
Then, epiphany time: staring at one of those mockups, I realized that it had the same general structure as a 3d overhead view with an extremely high fov. Picture looking down at a city street, surrounded by skyscrapers looming up and out of your field of vision.
Here’s what I mean – take the screenshot below:
And now imagine that each wall cell was actually a 3D object that stretched up and outside your field of view. It’s easier to show than describe:
Notice how the shape of the lines in the lower picture directly matches the shape of the shadows in the upper picture?
To get that to work, I oriented a projection matrix on the 2D display, cranked the FOV way up, and placed 3D cubes to line up with the 2D wall tiles. Set up a shader to just paint black (play with blending a bit) and et voila, you have 2D shadows with just a few 3D shapes. I did some work to combine nearby cubes to reduce the total number of surfaces drawn, and now the actual casting of shadows is taking a negligible amount of time in my render loop. I built a ton on top of that, including bump mapping, separating mob and wall shadows, orthographic projection “up” mobs and walls (so that the shadows can be cast up them correctly), multi-phase particle rendering and more – more on all that later.
At some point I’ll write up a more technical walkthrough of the above algorithm. I’ll also have to dig up an older copy of the Catalin implementation (I did versions in both XNA and OGL) and a prototype build.