# 2D - Smooth lighting with shadows for a tile-based game

for the last week or so I have been completely stuck on trying to implement efficient lighting. I've been researching and experimenting and I've looked everywhere that I have been capable of finding, and I just cannot figure it out. So, now I'm posting here, with the hope that someone can maybe help me. I'm trying to make a 2D tile-based game, similar to Terraria, and I'm trying to avoid Terraria's style of tile-based lighting and go for something smoother, like Starbound (here), except with more visual shadows. I have successfully implemented something to what I would like to see, but, sadly, it's incredibly inefficient. This is what I've tried so far:

1. Tiled based lighting that is put in a lightmap as a single grayscale pixel and is then scaled up and (with a shader?) smoothed out so that it looks non-tiled. This approach is similar to what I've seen (here), but is done in xna and I'm not sure if there is a BlendState equivalent thing. I also had never done anything with shaders and my attempt at smoothing out the tiles ended after I could only smooth them horizontally or vertically (not in a circle as it should be).

2. Raycasting and drawing each pixel with a certain alpha value for the distance it is away from the light source. This approach is the one that worked, but was inefficient. The first problem I encountered with this is that raycasting with around 360 rays per point light missed a bunch of pixels. I solved this by then using a shader to make a black pixel within the radius of a light the proper color if it was next to at least two non-black pixels, so that it wasn't a shadow. This worked, but sadly, it was far too inefficient, dropping about 300 FPS per light. But did bring me close to what I want, and I would post a picture, but sadly I'm limited to two links :(

and now I'm here, quite unsure of what to do next, are either of these two approaches that I've done before usable? Is there an even better route to take?

Thank you for taking the time to read my problem and hopefully help me out :)

P.S. I've heard of Box2D, and I really don't want to use it, mainly because this is a learning experience for me and I don't learn too much by using someone else's code.

Edit: I'm completely new to libGDX and OpenGL in general, and I know that there's a way to create a mesh using vertices and I was wondering if that might be the key, if I create a mesh from the raycasts' end points and somehow, using a shader, create an alpha gradient on the mesh? I really don't know how to create a mesh nor do I know how to create a gradient on a weirdly shaped object.

Edit2: This is a picture of what I want, and what I have, that is very inefficient :

Edit3: It works! For the most part, thank you for the help Felsir, but, of course, there are still problems, I've gotten the actual lighting to work, and it's actually MORE than efficient since I don't have to draw the tiles that are not lit up by light, but the problem is, well, darkening the rest of the screen, especially since there will be multiple lights. My first thought would be to not draw the tiles within the radius of the light, but since it creates triangles and not perfect squares like it's supposed to, this happens:

As you can see, it doesn't blacken out the tiles near the edges, and creates some other problems. My other idea was to make a shader that goes through the whole screen and uses a sampler so that if the alpha value is 0, set it black, but, obviously, this doesn't work since the screen doesn't have alpha values other than 1. My last idea would be to make a mesh that's inverted from the light, but that becomes absurdly complicated with multiple lights and probably inefficient, is there any way I can black out the non-lit screen?

Edit4: So, it's still not working, I've gotten the light to work, now I need the absence of light to work, and it's not. My new idea is if I can render all the tiles, then render a black mesh over the screen with an alpha value of one, I'm wondering if it's possible, using blendFuncs and blendEquations, to subtract the alpha of the black mesh from the mesh of the light. So the darkness is rendered, then the light afterwards, and I don't know if there's a way just to remove some of the alpha from some of the pixels that are overlapping using blend funcs. I really really need to get this or something to work, I want to complete my lighting. I decided to go through every single combination of blends functions, srcs, and dsts, since I know so little about how they work, and nothing worked, I'm probably doing something wrong, please help.

This is a post by @OrangePixel for one way to make efficient lightning with Libgdx.

It describes a simple technique. You can put alpha blended Dot above the map and make it seem like a lightning effect. The article is interesting and all the thanks go to the author!

http://techblog.orangepixel.net/2015/07/shine-a-light-on-it/

• Because links die all the time, it's best to summarise the key points in your answer, so that it's useful even if the linked resource later becomes unavailable. – DMGregory Aug 2 '15 at 13:20
• This is nice and all, but the problem I'm having is with shadows, lights are very easy to implement if they are not affected by anything in the game. – KingDolphin Aug 4 '15 at 15:24

Instead of casting rays in 360 degree fashion, cast rays to the corners of the tiles in range of the lightsource.

Sort the angles from the light source to the tile corners and do a 'sweep'. You can optimize the algorithm to look for the nearest tiles first and ignore angles for culled tiles:

    A--B E--F
o   |T1| |T2|
C--D G--H


Angles would be sorted: A, B, E, F, H, G ,D, C (draw lines from o to the corners). First tile T1 is checked, because it is closer. A is visible, B is not. C is visible and D is not. This results in tile T2: E, F, H, G angles being ignored because they're all between A and C.

If the lights are in fixed positions or rarely move, calculate the polygon only when the light is placed or moved. Store the polygon shape and use that to draw the light effect.

See: http://ncase.me/sight-and-light/ for an example of the light ray algorithm.

• This would be great for realistic-ish shadows that stop immediately, but the thing is, I want the light to sometimes bleed through tiles and lose some value while they do it, like in the picture I posted above, this technique might be able to do that, I just can't see how. Thank you for responding though, I hadn't seen that article before. – KingDolphin Aug 11 '15 at 16:12
• @KingDolphin I think if a ray only hits one corner only you can adjust the area to 'bleed'. If you draw the rays in your image it looks like that is true for the bleed? – Felsir Aug 11 '15 at 17:49
• @Flesir The way the one in the image works, is it literally draws the ray that it shoots, it doesn't make a polygon out of the hit points, like your answer suggests, and the reason I haven't done it with a polygon is because I'm not sure how I could calculate the lighting value of each pixel in the polygon. If you look closely at the image, the longer the ray is in a block, the darker it becomes. – KingDolphin Aug 11 '15 at 18:41
• If you create a polygon and define the vertice colors, the gradient is calculated by the videocard. Thus the centerpoint is white and the endpoints of the rays are black, a smooth gradient is generated. – Felsir Aug 11 '15 at 21:57
• how can I define the vertex colors? Sorry, I've never really dealt with OpenGL/LibGDX meshes and the most I've made is a triangle. But I also thought that the colors of the mesh were defined in the fragment shader, so how does the gradient color override the fragment shader that it has? – KingDolphin Aug 12 '15 at 20:27

Have you consider using chunk of pixel in your second point to get better performance?

If you search for a different aproach you can investigate Cellular automaton , starting from lights chunk for each neighbour lite them according to distance and material repeat for its neighbours ...

• Do you mean like almost changing the 'thickness' of the raycast so that I don't have to fire off as many? This could work but it would still miss some pixels or it would make the shadows really inaccurate, I think – KingDolphin Jul 29 '15 at 20:04