Today, we take a look at very important and yet often overlooked feature of Unity – the Lighting System. Unity’s default 3D scene has lighting that is comprised of a Directional Light (to mimic the sun) a horizon (created by the procedural Skybox) and ambient lighting (calculated using the Skybox). This setup works for many projects, but there is SO MUCH MORE that the Lighting System can do, more personality that good lighting can add, and better performance if you understand the tradeoffs of your lighting decisions. Let’s dig in!
Real-Time Lighting
When you add a light to your scene, the default configuration is one that will casts a white light in Realtime mode. This setting means that the object will contribute direct light to the scene, and updates each frame. “Direct” light refers to light that is cast directly from a source to an object, illuminating the surface that it hits. There is also “indirect” light, that refers to light that is reflected from one surface to another as it “bounces” in the scene.
Realtime lighting can be configured to cast a shadow, and those shadows will also update during each frame. As this regular updating carries quite a cost, you will find that there is a limit to the number of lights that can simultaneously affect a single object (this is usually 4). Lights have a “range” setting which determines the distance that the light can reach and beyond which it will not affect objects. You may also notice that shadows come with a draw distance (meaning that if you are far away, the shadow will not be processed).
The three primary light types are Point, Spot, and Directional.
- Point – this casts a light out in all directions creating a spherical emission. This behaves like the light you would expect from a bulb or flame – steady in all directions. This is useful for lighting areas, especially if you have a visible light source such as a lamp, a fire, or some sort of energy emission.
- Spot – this type casts its light in a conical shape, spreading out from the origin point. The angle of this cone can be adjusted. This type is best used when you want to highlight a particular area, light a character in a particular way (especially during cutscenes), or cast light from a specific source like a recessed fixture.
- Directional – we have already seen this one at work – this is the shadow casting light in our scene when we open Unity. This light only has a direction and casts parallel light rays. It has no dimensions or fall-off, and so it is best used for general lighting or for simulating the sun.
In addition to these types, your scene is also being lit by your environment. You can see the environmental settings by opening the Light Settings window (Window > Rendering > Light Settings) and selecting the Environmental tag. By default, Unity will use the procedural Skybox as a light and reflection source for objects in your scene, but you can turn these off, and set the ambient light in your scene to a different skybox, a solid color, or a custom gradient. This ambient light is what forms the base of your rendered image, determining the color of objects that are not affected by a light source.
Baked Lighting
If there is one type of lighting that renders in “realtime” then what is the alternative? The answer is baked lighting, a process by which we can pre-render how light will affect objects in our scene ahead of time, and then apply that lighting solution to those surfaces as a map that does not need to be constantly recalculated. This allows us to run a one-time calculation of complex lighting solutions in advance – like the effects of emissive materials, large sized light sources, and indirect lighting – and skip the performance cost at runtime.
To use Baked Lighting, you set a light source’s Light Mode to Baked. This tells the system that you intend to remove this from the realtime lighting solution. (There is also a Mixed mode which I will discuss later). In class, we also looked at an Area Light, a lighting type that is ONLY available as a Baked light. This creates a light source in the shape of a Rectangle or Disc that can be sized. This type is useful for generating soft shadows such as though one might expect from flourescent light fixtures, skylights, and windows.
Because of the pre-calculation required for baked lighting, Unity assumes that affected GameObjects will be Static (that is, they are not intended to move). Unity has a number of different static flags, such as the “Navigation Static” flag that we saw in the lesson about NavMesh objects. The “Contribute GI” flag is what Unity’s lighting system will use to determine whether or not this object should receive “baked” lighting.
Once you have defined your static objects and your baked light, it is time to generate your solution. In your Light Settings panel under the Scene tab, select the Generate Lighting button at the bottom. (If you do not yet have a Light Settings Asset, you will need to create one first). There are three different Lightmappers that can be used – Enlighten (a third party realtime global illumination solution), Progressive CPU (the default) and Progressive GPU (which is in preview). I prefer to use Progressive GPU as it runs considerably faster, assuming that your system has a reasonable graphics card.
Once you hit Generate Lighting, prepare to wait for a few minutes while the system renders out the scene. You will see the lights start to appear, but this renders in chunks (hence the “progressive”) so don’t panic if the first visual looks incorrect or is missing chunks. You can view the progress in the blue bar at the bottom right of the screen.
Once your solution has run, you can see that your static objects now have their illumination mapped onto their surface, and you can move them in and out of light sources and it will retain its appearance. You will also see that dynamic GameObjects that enter these “baked” lights are unaffected. There are two approaches to help resolve this.
Mixed Lighting
If you want to bake a lighting solution but also want some objects to cast shadows and respond in realtime, a solution that you can use is to set your Lighting mode to Mixed. This creates a situation where static objects will receive both baked indirect lighting, and realtime direct lighting, allowing you to cast shadows. Whereas baked lighting will also bake shadows of static objects, the Mixed mode will only bake the indirect bounces, saving the shadow generation for real-time. You may find that lighting in this mode renders a little brighter than your original light, so this approach may take some trial and error.
Light Probes
But what about Area Lights? Since these are Baked only, how can I get those lights to affect objects in my scene? Thankfully, Unity provides a solution to this also, in the form of Light Probes. A Light Probe is an object that allows Unity to “capture” the lighting conditions (color, intensity, and direction) at a point in space and then apply that lighting solution to an object.
To create this, add a Light Probe Group to your scene (Create > Lighting > Light Probe Group). This will create a box 8 yellow spheres that you can reposition (and also add to and subtract from) to cover your scene. Expand the probe group to cover the areas you need, and consider adding more probes in areas of high traffic or with strong differences in lighting. Once you bake your lighting, you will see these spheres rendered with their individual lighting properties, and that your selected object will also have a sphere that is lit as a blend of the probes nearest to it. Don’t worry about placing a lot of these, they are incredibly performant!
Reflection Probes
One more thing that we need to account for is the reflectivity of materials. If you have materials that are even partially smooth or metallic (or both), you will see that the environment is rendered in them (by default the procedural skybox map). While this flat horizon may work well for outdoor environments, it would be unusual to see a vast sky reflected in objects that are located indoors. To account for this, Unity provides Reflection Probes. These are similar to Light Probes, except that they render a spherical scene from a particular point, and use that map as the reflection map for objects. They operate more independently from one another, and are designed to reflect a particular position or location, and for objects to appear in them, they must have their “Reflection Probe” static flag set to true. (If you are only interested in reflecting the properties of a large space like the floor, walls, and ceiling, you might want to ignore smaller objects such as furniture that may break the illusion when they remain static and steady in reflections despite moving through space.
To create a probe, go to Create > Lighting > Reflection Probe. You will see a sphere at the location, contained within a larger box. This box is the area of influence for this particular sphere, and outside of one of these, Unity will default to the general environment reflection map (by default the Skybox). You can overlap these influence areas with other probes, and the engine will blend between them if your object is located in the intersection of two more more spaces. Once you have placed your probe (try to keep it at the height that you need to reflect from – often times this may be waist-high) you can use the Bake button to render a view from the current scene. These will also automatically bake at the end of a generated lighting solution. Once it has baked you can select the sphere to see the map.