Fast & beautiful 2D lighting in Unity

Håkan Edling
4 min readApr 9, 2018

--

I posted a short tweet for #unitytips on how to create flashy 2D lighting effects with Unity and got some requests to clarify exactly how it was implemented, so I thought I’d try to run through the setup.

Update

As I’ve gotten a lot of questions on this article, and the fact that I never actually had time to finish the game the screenshots were taken from (who ever finishes their games right) I’ve cleaned up the code a bit and pushed it to GitHub. Now you can all get it and try it for yourselves, but please note that the project includes a lot of other stuff as well, such as:

  • Map import from Tiled
  • Dynamic room prefabs with variable exits
  • Maze generation with simple path checking

If you have other questions about the code, you can open an issue on GitHub, or if you want to contribute something back to it, fork the repo and send me a Pull Request!

Now, let’s get back to the story!!!

In-game screenshot

In game screenshot with the lighting blended with the 2D game

Scene setup

How the scene is setup inside Unity

1. Layers

To make this work we need two extra layers. I’m going to call them shadowcaster and lighting in this article. Like the names indicate all objects casting shadows should be set to the shadowcaster layer, and all objects receiving the shadows should be set to the lighting layer. In my setup I only have a single plane receiving shadows to make it as efficient as possible.

2. Objects casting shadows

For every sprite based object that should cast a shadow, create a 3D object with an adequate shape and position it behind the sprite (i.e further away from the camera on the z-axis). Try to make sure that all objects end at the same z-value as you want to add a plane acting as a floor later on (unless you want objects floating in the air). Since we’re not using this object for anything but casting shadows, remove all components but the MeshRenderer.

Remember that blockers that are higher than the position of the light source will not get their top lit by the light source which will make them dark in the light layer. To address this, either lighten up the material color of the 3D object, or create a second light source higher up that only affects objects on the shadowcaster layer.

Shadow caster setup:

  • Layer: shadowcaster
  • Cast Shadows: On
  • Receive Shadows: Off

3. Creating the floor

The floor receiving the shadows can just be a simple plane. Again, make sure you only leave the MeshRenderer component for the object and that you position the plane behind the shadow casters on the z-axis.

Plane setup:

  • Layer: lighting
  • Cast Shadows: Off
  • Receive Shadows: On

Make the plane a child of the main camera and make it the same size as the camera viewport. If you want areas not hit by the light to be completely dark, set the color of the assigned material to black. The lighter you make the base color of the plane the more subtle the shadow effects will become.

4. Add the lights

Create a point light (or any other light source of your choice) and position it on the z-axis so that the light is fully, or partially blocked by the objects you created in section 2.

Light setup:

  • Culling mask: shadowcaster, lighting

5. Light camera

Now create a new camera and make it a child of your main camera. Make sure the camera has exactly the same settings in terms of orthographic size and so on as the main camera as otherwise the lighting texture won’t match the output of the main camera.

Camera setup:

  • Culling mask: shadowcaster, lighting
  • Render To Texture: On

6. Main camera

Now the main camera needs to blend the rendered texture from the light camera with it’s own output with a shader. Since I’m no shader expert I use the BlendImageEffect shader from SpriteLightKit by Prime31. You can find the repo here on GitHub:

https://github.com/prime31/SpriteLightKit

Camera setup:

  • Culling mask: Everything except shadowcaster & lighting

7. Conclusion

I hope this gives some insight exactly on how I forced Unity to give me nice real time shadows in 2D. Even though I use it in a classic Zelda style top-down game the approach works just as well with platformer game.

--

--

Håkan Edling

Software Engineer @Optimizely. Microsoft MVP. Founder of Piranha CMS. Musician, mixer, producer, husband & father of three.