I have a 2-D square grid, and I'd like to do a shadow casting / lighting algorithm. However, I haven't found one that takes advantage of the fact that I'm using a 2-D grid and not arbitrary polygons, while still retaining a non-grid lighting.
Like this:
{"name":"Monaco-whats-yours-is-mine-01.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/f\/afc95cb0114aecdf1d72b835e9dd49fb.jpg","w":1440,"h":900,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/f\/afc95cb0114aecdf1d72b835e9dd49fb"}
Not this:
{"name":"maxresdefault.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/9\/49b4bf903de99c158710e8fff1b85368.jpg","w":1920,"h":1080,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/9\/49b4bf903de99c158710e8fff1b85368"}
Bonus points if it shows how to do soft edges like in the first image.
My way is like this: first you want to get a polygon with the lit area. A very simple approach is to just create a fixed circle with, say, 1024 vertices. By using a fixed polygon instead of trying to figure out the exact shadow polygon (which is also possible) it gets rather trivial - just loop through the number of vertices and cast a ray for each, connecting the polygon to the first tilemap intersection (or the end of the ray if there is none).
As an example with just 6 vertices you get 6 collision rays for your light source:
\ / \ / ----*---- / \ / \
And the resulting light polygon with no shadow tiles:
___ /. .\ / . . \ ....*.... \ . . / \.___./
And with some shadow tiles:
___ / | /__ \S S| * \S | / |___ /
It won't be perfect, a shadow tile between two rays is ignored and the shadow never exactly follows the tiles but just the ray intersections. But you won't notice, I think when I implemented it I only had 256 vertices even.
As a next step, to get the soft border, add a border inside the polygon. For each vertex add another one which is, say, 10 pixels closer to the light. It should be trivial, there are no special cases - the first polygon always has exactly 1024 vertices no matter how many collisions - and you now add another 1024 vertices. Now create 1024 fully opaque triangles by connecting the inner vertices with a vertex in the center. And then fill the strip between the outer and inner polygon with 2048 triangles where the outer vertices are fully transparent.
Finally there's many ways to use this polygon to create the shadow effect - a very easy one is to use two bitmaps, one where you draw the shadow (e.g. white on black), one where you draw your fully lit tilemap. Then use a shader to combine them. The gradient in the shadow bitmap decides whether the tilemap bitmap is drawn unmodified (or with that extra color effect they seem to have in the screenshot) or gets dark and gray.
You could even completely skip shader and extra bitmaps for a somewhat reduced effect - just draw the shadow polygon first (white on dark gray). Then draw your tilemap but use multiplicative blending - fully white areas will have the unmodified lit tilemap, shadowed areas will get darkened, including the soft border. (But there shouldn't be any performance gain anymore and if you want the grayscale effect you need a shader anyway.)
[edit:]
Tried it out quick. My ray casting seems to have a small bug, probably due to the shortcut I took combining left/right and up/down. And my smoothing isn't enough because it obviously doesn't smooth at the sides. So instead of just moving the inner vertices towards the light source should probably take the normal of one of the adjacent edges into account and move in the opposite direction of that a bit.
I took a similar approach to Elias's, but what bothered me with the "fixed polygon"-approach was the slight jittering that's visible at the shadow edges when moving.
I've also tried this algorithm. It isn't optimized for square grids though, but this could probably be done in some way.
I've also tried this algorithm
So basically, instead of shooting a fixed amount of rays, he collects all edges within the light radius which could possibly throw a shadow. Then sorts their vertices by angle to the light source. And then does the same algorithm as with the fixed angles, except shooting rays along those sorted angles. I think that is indeed worth the extra complexity, and with some clever collecting of edges can probably even optimize the sorting a bit.
Just leaves the question for making the soft edges. One approach might be to only calculate the original shadow polygon but after drawing it blur the whole shadow buffer. If done clever this can be achieved in the shader without requiring an extra pass I guess - instead of taking a single sample from the shadow buffer take several samples and average, therefore smoothing out the edges.
He doesn't cap the distance (and always encloses the area with obstacles), so you'll have to generate nice arcs between the rays / angles that went beyond the light source radius, but that's no big deal.
I didn't quite like the idea of doing it with angles and atan2, but didn't play around with it any longer.
Also, t-junctions could be a problem when the resulting triangle fan is drawn with transparency at some point (i. e. two triangles have part of a common edge, but the vertices are not the same).