RayLight

Using RayLight to achieve 2D shaft lighting in Unity3D.

Warning: This set of scripts is currently untested on anything other than very small scenes. Use at your own risk.

Below you will find some examples of what the code is capable of to help determine if it is correct for your project. This is only a limited subset of what is possible, so let your mind run wild. You can click any of the images to expand them.

Static Scene Dynamic Objects Dynamic Light Dynamic Light and Objects
Static Scene
Dynamic Objects
Dynamic Light
Dynamic Everything!
  • C#RayLight.cs
    • Identifies a GameObject as a light source
  • C#RayLightVertex.cs
    • Marks a GameObject as an endpoint that light rays should be cast towards
  • C#RayLightRenderer.cs
    • The script attached to a Camera which performs the actual light rendering

There are four steps (one for each script and a some general guidelines) that must be taken for RayLight to work correctly:

  1. Light Setup
  2. Vertex Setup
  3. Camera Setup
  4. Final Steps

Lighting setup is straightforward; the RaycastLight.cs script must be applied to a GameObject from which light should be cast.
It is best apply this to an empty GameObject as the transform of the object is used to determine the light source.

Properties:

  • Mat The Material that will be used as the texture for this light.

  • Range The radius that the light source will cast light.

  • Layer Mask The layer mask can be used to select which layers in the scene should interrupt the light.

In order for the lights to render they have to know the endpoints of objects that they will intercept. This is done in two ways, by either assigning the RayLightVertex.cs script to vertices that should act as endpoints, or to a parent object whose children should act as the endpoints (recommended).

The vertexes that will be used for endpoints should be placed on the logical vertices of the object casting the shadow. For example, if using a square you would place the vertices at the four corners of the square.

Note: At this time circle shapes are not supported. You can attempt to simulate the behavior by placing vertexes around the edge of the circle, but results and performance may vary.

Properties:

  • Use Children If checked the transform of each child of the GameObject will be used as a lighted endpoint, otherwise the transform of the object itself will be used.

For lighting to correctly render under the scene (as opposed to on top of the scene) it must be rendered on a separate camera. The simplest way to achieve this effect is by attaching a second Camera with the RayLightRenderer.cs script to the Main Camera.

For correct rendering some changes must be made to the clear flags on the camera. On the Main Camera change Clear Flags to Don't Clear; this will allow for the scene to be drawn on top of the lights, which will already have rendered at the time the main scene is rendered.

On the child camera (the one with the script attached) set the settings to what you normally use for your camera, except set Culling Mask to Nothing. This will prevent the child camera rendering anything except for the lights.

Note: I am far from knowledgeable about cameras in Unity. There is probably a good way to achieve some cool effects (such as lights drawing over certain objects, but not others) but it is up to you to figure them out.

There are some important considerations when setting up your scene for RayLight lighting.

First off, lights do not take each other into consideration. This means that if you place your lights so that their rays will overlap, that will be evident in the scene, assuming the light texture uses transparency.

Secondly, the light will only cast towards vertices as specified by the RayLightVertex.cs script. The lights will, however, try to draw in a circular fashion. This means if you do not define some boundary for your light area strange results might occur. This is best remedied by making sure you place your lights in an area with clear bounds. The bounds do not need to be visible in your play area, they simply need to be in range of the camera.
You can see an example of how this might be done below. Note how even though level bounds exist they are in range of the light but not visible to the camera.

Scene Example

Because of these considerations it is best to try to keeps lights separated in to distinct play areas, such as separate rooms. It is also theoretically possible to attach light endpoints to the edge of the viewport of the camera, to make sure the lights will always fill out the visible area of the screen. This may have varying results and has not been tested.

I hope you find these scripts useful. If you run into any issues please report/fork on GitHub.