Let There Be Light!

Chico Queiroz

February 2016

In this article, we will cover:

  • Using lights and cookie textures to simulate a cloudy outdoor
  • Adding a custom Reflection map to a scene
  • Creating a laser aim with Projector and Line Renderer
  • Reflecting surrounding objects with Reflection Probes
  • Setting up an environment with Procedural Skybox and Directional Light
  • Lighting a simple scene with Lightmaps and Light Probes

(For more resources related to this topic, see here.)

Introduction

Whether you're willing to make a better-looking game, or add interesting features, lights and effects can boost your project and help you deliver a higher quality product. In this article, we will look at the creative ways of using lights and effects, and also take a look at some of Unity's new features, such as Procedural Skyboxes, Reflection Probes, Light probes, and custom Reflection Sources.

Lighting is certainly an area that has received a lot of attention from Unity, which now features real-time Global Illumination technology provided by Enlighten. This new technology provides better and more realistic results for both, Real-time and Baked lighting. For more information on Unity's Global Illumination system, check its documentation at http://docs.unity3d.com/Manual/GIIntro.html.

The Big Picture

There are many ways of creating light sources in Unity. Here's a quick overview of the most common methods.

Lights

Lights are placed into the scene as game objects, featuring a Light component. They can function in Realtime, Baked, or Mixed modes. Among the other properties, they can have their Range, Color, Intensity, and Shadow Type set by the user. There are four types of lights:

  • Directional Light: It is normally used to simulate the sunlight
  • Spot Light: It works like a cone-shaped spot light
  • Point Light: This is a bulb lamp-like, omnidirectional light
  • Area Light: This baked-only light type is emitted in all directions from a rectangle-shaped entity, allowing for a smooth, realistic shading

For an overview of the light types, check Unity's documentation at http://docs.unity3d.com/Manual/Lighting.html.

Different types of lights

Environment lighting

Unity's Environment Lighting is often achieved through the combination of a Skybox material and a sunlight defined by the scene's Directional Light. Such combination creates an ambient light that is integrated into the scene's environment, and which can be set as Realtime or Baked into Lightmaps.

Emissive materials

When applied to static objects, materials featuring the Emission colors or maps will cast light over surfaces nearby, in both—Realtime and Baked modes, as shown in the following screenshot:

Projector

As its name suggests, a Projector can be used to simulate projected lights and shadows, basically by projecting a material and its texture map onto the other objects.

Lightmaps and Light Probes

Lightmaps are basically texture maps generated from the scene's lighting information and applied to the scene's static objects, in order to avoid the use of processing-intensive real-time lighting.

Light Probes are a way of sampling the scene's illumination at specific points, in order to have it applied onto dynamic objects without the use of real-time lighting.

The Lighting window

The Lighting window, which can be found through navigating to the Window | Lighting menu, is the hub for setting and adjusting the scene's illumination features, such as Lightmaps, Global Illumination, Fog, and much more. It's strongly recommended that you take a look at Unity's documentation on the subject, which can be found at http://docs.unity3d.com/Manual/GlobalIllumination.html.

Using lights and cookie textures to simulate a cloudy outdoor

As it can be seen in many first-person shooters and survival horror games, lights and shadows can add a great deal of realism to a scene, helping immensely to create the right atmosphere for the game. In this recipe, we will create a cloudy outdoor environment lighting using cookie textures. Cookie textures work as masks for lights. It functions by adjusting the intensity of the light projection to the cookie texture's alpha channel. This allows for a silhouette effect (just think of the bat-signal) or, as in this particular case, subtle variations that give a filtered quality to the lighting.

Getting ready

If you don't have access to an image editor, or prefer to skip the texture map elaboration in order to focus on the implementation, please use the image file called cloudCookie.tga, which is provided inside the 1362_06_01 folder.

How to do it...

To simulate a cloudy outdoor environment, follow these steps:

  1. In your image editor, create a new 512 x 512 pixel image.
  2. Using black as the foreground color and white as the background color, apply the Clouds filter (in Photoshop, this is done by navigating to the Filter | Render | Clouds menu).

    Image editors usually have a filter or command that renders clouds. If your image editor doesn't have such capability, you can either paint it manually, or use the image file called cloudCookie.tga, provided in the 1362_06_01 folder).

  3. Select your entire image and copy it.
  4. Open the Channels window (in Photoshop, this can be done by navigating to the Window | Channels menu).
  5. There should be three channels: Red, Green, and Blue. Create a new channel. This will be the Alpha channel.
  6. In the Channels window, select the Alpha 1 channel and paste your image into it.
  7. Save your image file as cloudCookie.PSD or TGA.
  8. Import your image file to Unity and select it in the Project view.
  9. From the Inspector view, change its Texture Type to Cookie and its Light Type to Directional. Then, click on Apply, as shown:
  10. We will need a surface to actually see the lighting effect. You can either add a plane to your scene (via navigating to the GameObject | 3D Object | Plane menu), or create a Terrain (menu option GameObject | 3D Object | Terrain) and edit it, if so you wish.
  11. Let's add a light to our scene. Since we want to simulate sunlight, the best option is to create a Directional Light. You can do this through the drop-down menu named Create | Light | Directional Light in the Hierarchy view.
  12. Using the Transform component of the Inspector view, reset the light's Position to X: 0, Y: 0, Z: 0 and its Rotation to X: 90; Y: 0; Z: 0.
  13. In the Cookie field, select the cloudCookie texture that you imported earlier. Change the Cookie Size field to 80, or a value that you feel as more appropriate to the scene's dimension. Please leave Shadow Type as No Shadows.
  14. Now, we need a script to translate our light and, consequently, the Cookie projection. Using the Create drop-down menu in the Project view, create a new C# Script named MovingShadows.cs.
  15. Open your script and replace everything with the following code:
    using UnityEngine;
    using System.Collections;
    
    public class MovingShadows : MonoBehaviour{
    public float windSpeedX;
    public float windSpeedZ;
    private float lightCookieSize;
    private Vector3 initPos;
    
    void Start(){
       initPos = transform.position;
       lightCookieSize = GetComponent<Light>().cookieSize;
    }
    
    void Update(){
       Vector3 pos = transform.position;
       float xPos= Mathf.Abs (pos.x);
       float zPos= Mathf.Abs (pos.z);
       float xLimit = Mathf.Abs(initPos.x) + lightCookieSize;
       float zLimit = Mathf.Abs(initPos.z) + lightCookieSize;
    
       if (xPos >= xLimit)
         pos.x = initPos.x;
    
       if (zPos >= zLimit)
         pos.z = initPos.z;
    
       transform.position = pos;
       float windX = Time.deltaTime * windSpeedX;
       float windZ = Time.deltaTime * windSpeedZ;
       transform.Translate(windX, 0, windZ, Space.World);
    }
    }
  16. Save your script and apply it to the Directional Light.
  17. Select the Directional Light. In the Inspector view, change the parameters Wind Speed X and Wind Speed Z to 20 (you can change these values as you wish, as shown).
  18. Play your scene. The shadows will be moving.

How it works...

With our script, we are telling the Directional Light to move across the X and Z axis, causing the Light Cookie texture to be displaced as well. Also, we reset the light object to its original position whenever it traveled a distance that was either equal to or greater than the Light Cookie Size. The light position must be reset to prevent it from traveling too far, causing problems in real-time render and lighting. The Light Cookie Size parameter is used to ensure a smooth transition.

The reason we are not enabling shadows is because the light angle for the X axis must be 90 degrees (or there will be a noticeable gap when the light resets to the original position). If you want dynamic shadows in your scene, please add a second Directional Light.

There's more...

In this recipe we have applied a cookie texture to a Directional Light. But what if we were using the Spot or Point Lights?

Creating Spot Light cookies

Unity documentation has an excellent tutorial on how to make the Spot Light cookies. This is great to simulate shadows coming from projectors, windows, and so on. You can check it out at http://docs.unity3d.com/Manual/HOWTO-LightCookie.html.

Creating Point Light Cookies

If you want to use a cookie texture with a Point Light, you'll need to change the Light Type in the Texture Importer section of the Inspector.

Adding a custom Reflection map to a scene

Whereas Unity Legacy Shaders use individual Reflection Cubemaps per material, the new Standard Shader gets its reflection from the scene's Reflection Source, as configured in the Scene section of the Lighting window. The level of reflectiveness for each material is now given by its Metallic value or Specular value (for materials using Specular setup). This new method can be a real time saver, allowing you to quickly assign the same reflection map to every object in the scene. Also, as you can imagine, it helps keeping the overall look of the scene coherent and cohesive. In this recipe, we will learn how to take advantage of the Reflection Source feature.

Getting ready

For this recipe, we will prepare a Reflection Cubemap, which is basically the environment to be projected as a reflection onto the material. It can be made from either six or, as shown in this recipe, a single image file.

To help us with this recipe, it's been provided a Unity package, containing a prefab made of a 3D object and a basic Material (using a PNG as Diffuse map), and also a JPG file to be used as the reflection map. All these files are inside the 1362_06_02 folder.

How to do it...

To add Reflectiveness and Specularity to a material, follow these steps:

  1. Import batteryPrefab.unitypackage to a new project. Then, select battery_prefab object from the Assets folder, in the Project view.
  2. From the Inspector view, expand the Material component and observe the asset preview window. Thanks to the Specular map, the material already features a reflective look. However, it looks as if it is reflecting the scene's default Skybox, as shown:
  3. Import the CustomReflection.jpg image file. From the Inspector view, change its Texture Type to Cubemap, its Mapping to Latitude + Longitude Layout (Cylindrical), and check the boxes for Glossy Reflection and Fixup Edge Seams. Finally, change its Filter Mode to Trilinear and click on the Apply button shown as follows:
  4. Let's replace the Scene's Skybox with our newly created Cubemap, as the Reflection map for our scene. In order to do this, open the Lighting window by navigating to the Window | Lighting menu. Select the Scene section and use the drop-down menu to change the Reflection Source to Custom. Finally, assign the newly created CustomReflection texture as the Cubemap, shown as follows:
  5. Check out for the new reflections on the battery_prefab object.

How it works...

While it is the material's specular map that allows for a reflective look, including the intensity and smoothness of the reflection, the refection itself (that is, the image you see on the reflection) is given by the Cubemap that we have created from the image file.

There's more...

Reflection Cubemaps can be achieved in many ways and have different mapping properties.

Mapping coordinates

The Cylindrical mapping that we applied was well-suited for the photograph that we used. However, depending on how the reflection image is generated, a Cubic or Spheremap-based mapping can be more appropriate. Also, note that the Fixup Edge Seams option will try to make the image seamless.

Sharp reflections

You might have noticed that the reflection is somewhat blurry compared to the original image, the reason is because we have ticked the Glossy Reflections box. To get a sharper-looking reflection, deselect this option, in which case, you can also leave the Filter Mode option as default (Bilinear).

Maximum Size

At 512 x 512 pixels, our reflection map will probably run fine on the lower-end machines. However, if the quality of the reflection map is not so important in your game's context, and the original image dimensions are big (say, 4096 x 4096), you might want to change the texture's Max Size at the Import Settings, to a lower number.

Creating a laser aim with Projector the and Line Renderer

Although using GUI elements, such as a cross-hair, is a valid way to allow players to aim, replacing (or combining) it with a projected laser dot might be a more interesting approach. In this recipe, we will use the Projector and Line components to implement this concept.

Getting ready

To help us with this recipe, it's been provided a Unity package containing a sample scene featuring a character holding a laser pointer, and also a texture map named LineTexture. All files are inside the 1362_06_03 folder. Also, we'll make use of the Effects assets package provided by Unity (which you should have installed when installing Unity).

How to do it...

To create a laser dot aim with a Projector, follow these steps:

  1. Import BasicScene.unitypackage to a new project. Then, open the scene named mecanim. This is a basic scene, featuring a player character whose aim is controlled via mouse.
  2. Import the Effects package by navigating to the Assets | Import Package | Effects menu. If you want to import only the necessary files within the package, deselect everything in the Importing package window by clicking on the None button, and then check the Projectors folder only. Then, click on Import, as shown:
  3. From the Inspector view, locate the ProjectorLight shader (inside the Assets | Standard Assets | Effects | Projectors | Shaders folder). Duplicate the file and name the new copy as ProjectorLaser.
  4. Open ProjectorLaser. From the first line of the code, change Shader "Projector/Light" to Shader "Projector/Laser". Then, locate the line of code – Blend DstColor One and change it to Blend One One. Save and close the file.

    The reason for editing the shader for the laser was to make it stronger by changing its blend type to Additive. Shader programming is a complex subject, which is beyond the scope of this book. However, if you want to learn more about it, check out Unity's documentation on the subject, which is available at http://docs.unity3d.com/Manual/SL-Reference.html, and also the book called Unity Shaders and Effects Cookbook, published by Packt.

  5. Now that we have fixed the shader, we need a material. From the Project view, use the Create drop-down menu to create a new Material. Name it LaserMaterial. Then, select it from the Project view and, from the Inspector view, change its Shader to Projector/Laser.
  6. From the Project view, locate the Falloff texture. Open it in your image editor and, except for the first column of pixels that should be black, paint everything white. Save the file and go back to Unity.
  7. Change the LaserMaterial's Main Color to red (RGB: 255, 0, 0). Then, from the texture slots, select the Light texture as Cookie and the Falloff texture as Falloff.
  8. From the Hierarchy view, find and select the pointerPrefab object (book | mixamorig:Hips | mixamorig:Spine | mixamorig:Spine1 | mixamorig:Spine2 | mixamorig:RightShoulder | mixamorig:RightArm | mixamorig:RightForeArm | mixamorig:RightHand | pointerPrefab). Then, from the Create drop-down menu, select Create Empty Child. Rename the new child of pointerPrefab as LaserProjector.
  9. Select the LaserProjector object. Then, from the Inspector view, click the Add Component button and navigate to Effects | Projector. Then, from the Projector component, set the Orthographic option as true and set Orthographic Size as 0.1. Finally, select LaserMaterial from the Material slot.
  10. Test the scene. You will be able to see the laser aim dot as shown:
  11. Now, let's create a material for the Line Renderer component that we are about to add. From the Project view, use the Create drop-down menu to add a new Material. Name it as Line_Mat.
  12. From the Inspector view, change the shader of the Line_Mat to Particles/Additive. Then, set its Tint Color to red (RGB: 255;0;0).
  13. Import the LineTexture image file. Then, set it as the Particle Texture for the Line_Mat, as shown:
  14. Use the Create drop-down menu from Project view to add a C# script named LaserAim. Then, open it in your editor.
  15. Replace everything with the following code:
    using UnityEngine;
    using System.Collections;
    public class LaserAim : MonoBehaviour {
    
    public float lineWidth = 0.2f;
    public Color regularColor = new Color (0.15f, 0, 0, 1);
    public Color firingColor = new Color (0.31f, 0, 0, 1);
    public Material lineMat;
    private Vector3 lineEnd;
    private Projector proj;
    private LineRenderer line;
    
    void Start () {
       line = gameObject.AddComponent<LineRenderer>();
       line.material = lineMat;
       line.material.SetColor("_TintColor", regularColor);
       line.SetVertexCount(2);
       line.SetWidth(lineWidth, lineWidth);
       proj = GetComponent<Projector> ();
    }
    
    void Update () {
       RaycastHit hit;
       Vector3 fwd = transform.TransformDirection(Vector3.forward);
    
       if (Physics.Raycast (transform.position, fwd, out hit))
    {
         lineEnd = hit.point;
          float margin = 0.5f;
         proj.farClipPlane = hit.distance + margin;
    
       } else {
         lineEnd = transform.position + fwd * 10f;
       }
       line.SetPosition(0, transform.position);
       line.SetPosition(1, lineEnd);
    
       if(Input.GetButton("Fire1")){
         float lerpSpeed = Mathf.Sin (Time.time * 10f);
         lerpSpeed = Mathf.Abs(lerpSpeed);
         Color lerpColor = Color.Lerp(regularColor,
    firingColor, lerpSpeed);
         line.material.SetColor("_TintColor", lerpColor);
    
       }
       if(Input.GetButtonUp("Fire1")){
         line.material.SetColor("_TintColor", regularColor);
       }
    }
    }
  16. Save your script and attach it to the LaserProjector game object.
  17. Select the LaserProjector GameObject. From the Inspector view, find the Laser Aim component and fill the Line Material slot with the Line_Mat material, as shown:
  18. Play the scene. The laser aim is ready, and looks as shown:

    In this recipe, the width of the laser beam and its aim dot have been exaggerated. Should you need a more realistic thickness for your beam, change the Line Width field of the Laser Aim component to 0.05, and the Orthographic Size of the Projector component to 0.025. Also, remember to make the beam more opaque by setting the Regular Color of the Laser Aim component brighter.

How it works...

The laser aim effect was achieved by combining two different effects: a Projector and Line Renderer.

A Projector, which can be used to simulate light, shadows, and more, is a component that projects a material (and its texture) onto other game objects. By attaching a projector to the Laser Pointer object, we have ensured that it will face the right direction at all times. To get the right, vibrant look, we have edited the projector material's Shader, making it brighter. Also, we have scripted a way to prevent projections from going through objects, by setting its Far Clip Plane on approximately the same level of the first object that is receiving the projection. The line of code that is responsible for this action is—proj.farClipPlane = hit.distance + margin;.

Regarding the Line Renderer, we have opted to create it dynamically, via code, instead of manually adding the component to the game object. The code is also responsible for setting up its appearance, updating the line vertices position, and changing its color whenever the fire button is pressed, giving it a glowing/pulsing look.

For more details on how the script works, don't forget to check out the commented code, available within the 1362_06_03 | End folder.

Reflecting surrounding objects with Reflection Probes

If you want your scene's environment to be reflected by game objects, featuring reflective materials (such as the ones with high Metallic or Specular levels), then you can achieve such effect using Reflection Probes. They allow for real-time, baked or even custom reflections through the use of Cubemaps.

Real-time reflections can be expensive in terms of processing, in which case you should favor Baked reflections, unless it's really necessary to display dynamic objects being reflected (mirror-like objects, for instance). Still, there are some ways real-time reflections can be optimized. In this recipe, we will test three different configurations for reflection probes:

  • Real-time reflections (constantly updated)
  • Real-time reflections (updated on-demand) via script
  • Baked reflections (from the Editor)

Getting ready

For this recipe, we have prepared a basic scene, featuring three sets of reflective objects: one is constantly moving, one is static, and one moves whenever it is interacted with. The Probes.unitypackage package that is containing the scene can be found inside the 1362_06_04 folder.

How to do it...

To reflect the surrounding objects using the Reflection probes, follow these steps:

  1. Import Probes.unitypackage to a new project. Then, open the scene named Probes. This is a basic scene featuring three sets of reflective objects.
  2. Play the scene. Observe that one of the systems is dynamic, one is static, and one rotates randomly, whenever a key is pressed.
  3. Stop the scene.
  4. First, let's create a constantly updated real-time reflection probe. From the Create drop-down button of the Hierarchy view, add a Reflection Probe to the scene (Create | Light | Reflection Probe). Name it as RealtimeProbe and make it a child of the System 1 Realtime | MainSphere game object. Then, from the Inspector view, the Transform component, change its Position to X: 0; Y: 0; Z: 0, as shown:
  5. Now, go to the Reflection Probe component. Set Type as Realtime; Refresh Mode as Every Frame and Time Slicing as No time slicing, shown as follows:
  6. Play the scene. The reflections will be now be updated in real time. Stop the scene.
  7. Observe that the only object displaying the real-time reflections is System 1 Realtime | MainSphere. The reason for this is the Size of the Reflection Probe. From the Reflection Probe component, change its Size to X: 25; Y: 10; Z: 25. Note that the small red spheres are now affected as well. However, it is important to notice that all objects display the same reflection. Since our reflection probe's origin is placed at the same location as the MainSphere, all reflective objects will display reflections from that point of view.
  8. If you want to eliminate the reflection from the reflective objects within the reflection probe, such as the small red spheres, select the objects and, from the Mesh Renderer component, set Reflection Probes as Off, as shown in the following screenshot:
  9. Add a new Reflection Probe to the scene. This time, name it OnDemandProbe and make it a child of the System 2 On Demand | MainSphere game object. Then, from the Inspector view, Transform component, change its Position to X: 0; Y: 0; Z: 0.
  10. Now, go to the Reflection Probe component. Set Type as Realtime, Refresh Mode as Via scripting, and Time Slicing as Individual faces as shown in the following screenshot:
  11. Using the Create drop-down menu in the Project view, create a new C# Script named UpdateProbe.cs.
  12. Open your script and replace everything with the following code:
    using UnityEngine;
    using System.Collections;
    
    public class UpdateProbe : MonoBehaviour {
    private ReflectionProbe probe;
    
    void Awake () {
       probe = GetComponent<ReflectionProbe> ();
       probe.RenderProbe();
    }
    
    public void RefreshProbe(){
       probe.RenderProbe();
    }
    }
  13. Save your script and attach it to the OnDemandProbe.
  14. Now, find the script named RandomRotation, which is attached to the System 2 On Demand | Spheres object, and open it in the code editor.
  15. Right before the Update() function, add the following lines:
    private GameObject probe;
    private UpdateProbe up;
    void Awake(){
    probe = GameObject.Find("OnDemandProbe");
    up = probe.GetComponent<UpdateProbe>();
    }
  16. Now, locate the line of code called transform.eulerAngles = newRotation; and, immediately after it, add the following line:
    up.RefreshProbe();
  17. Save the script and test your scene. Observe how the Reflection Probe is updated whenever a key is pressed.
  18. Stop the scene. Add a third Reflection Probe to the scene. Name it as CustomProbe and make it a child of the System 3 On Custom | MainSphere game object. Then, from the Inspector view, the Transform component, change its Position to X: 0; Y: 0; Z: 0.
  19. Go to the Reflection Probe component. Set Type as Custom and click on the Bake button as shown:
  20. A Save File dialog window will show up. Save the file as CustomProbe-reflectionHDR.exr.
  21. Observe that the reflection map does not include the reflection of red spheres on it. To change this, you have two options: set the System 3 On Custom | Spheres GameObject (and all its children) as Reflection Probe Static or, from the Reflection Probe component of the CustomProbe GameObject, check the Dynamic Objects option as shown, and bake the map again (by clicking on the Bake button).
  22. If you want your reflection Cubemap to be dynamically baked while you edit your scene, you can set the Reflection Probe Type to Baked, open the Lighting window (the Assets | Lighting menu), access the Scene section, and check the Continuous Baking option as shown. Please note that this mode won't include dynamic objects in the reflection, so be sure to set System 3 On Custom | Spheres and System 3 On Custom | MainSphere as Reflection Probe Static.

How it works...

The Reflection Probes element act like omnidirectional cameras that render Cubemaps, and apply them onto the objects within their constraints. When creating Reflection Probes, it's important to be aware about how the different types work:

  • Realtime Reflection Probes: Cubemaps are updated at runtime. The Realtime Reflection Probes have three different Refresh Modes: On Awake (Cubemap is baked once, right before the scene starts); Every frame (Cubemap is constantly updated); Via scripting (Cubemap is updated whenever the RenderProbe function is used).
    Since Cubemaps feature six sides, the Reflection Probes features Time Slicing, so each side can be updated independently. There are three different types of Time Slicing: All Faces at Once (renders all faces at once and calculates mipmaps over 6 frames. Updates the probe in 9 frames); Individual Faces (each face is rendered over a number of frames. It updates the probe in 14 frames. The results can be a bit inaccurate, but it is the least expensive solution in terms of frame-rate impact); No Time Slicing (The Probe is rendered and mipmaps are calculated in one frame. It provides high accuracy, but it also the most expensive in terms of frame-rate).
  • Baked: Cubemaps are baked during editing the screen. Cubemaps can be either manually or automatically updated, depending whether the Continuous Baking option is checked (it can be found at the Scene section of the Lighting window).
  • Custom: The Custom Reflection Probes can be either manually baked from the scene (and even include Dynamic objects), or created from a premade Cubemap.

There's more...

There are a number of additional settings that can be tweaked, such as Importance, Intensity, Box Projection, Resolution, HDR, and so on. For a complete view on each of these settings, we strongly recommend you to read Unity's documentation on the subject, which is available at http://docs.unity3d.com/Manual/class-ReflectionProbe.html.

Setting up an environment with Procedural Skybox and Directional Light

Besides the traditional 6 Sided and Cubemap, Unity now features a third type of skybox: the Procedural Skybox. Easy to create and setup, the Procedural Skybox can be used in conjunction with a Directional Light to provide Environment Lighting to your scene. In this recipe, we will learn about different parameters of the Procedural Skybox.

Getting ready

For this recipe, you will need to import Unity's Standard Assets Effects package, which you should have installed when installing Unity.

How to do it...

To set up an Environment Lighting using the Procedural Skybox and Directional Light, follow these steps:

  1. Create a new scene inside a Unity project. Observe that a new scene already includes two objects: the Main Camera and a Directional Light.
  2. Add some cubes to your scene, including one at Position X: 0; Y: 0; Z: 0 scaled to X: 20; Y: 1; Z: 20, which is to be used as the ground, as shown:
  3. Using the Create drop-down menu from the Project view, create a new Material and name it MySkybox. From the Inspector view, use the appropriate drop-down menu to change the Shader of MySkybox from Standard to Skybox/Procedural.
  4. Open the Lighting window (menu Window | Lighting), access the Scene section. At the Environment Lighting subsection, populate the Skybox slot with the MySkybox material, and the Sun slot with the Directional Light from the Scene.
  5. From the Project view, select MySkybox. Then, from the Inspector view, set Sun size as 0.05 and Atmosphere Thickness as 1.4. Experiment by changing the Sky Tint color to RGB: 148; 128; 128, and the Ground color to a value that resembles the scene cube floor's color (such as RGB: 202; 202; 202). If you feel the scene is too bright, try bringing the Exposure level down to 0.85 shown as follows:
  6. Select the Directional Light and change its Rotation to X: 5; Y: 170; Z: 0. Note that the scene should resemble a dawning environment, something like the following scene:
  7. Let's make things even more interesting. Using the Create drop-down menu in the Project view, create a new C# Script named RotateLight. Open your script and replace everything with the following code:
    using UnityEngine;
    using System.Collections;
    public class RotateLight : MonoBehaviour {
    public float speed = -1.0f;
    void Update () {
       transform.Rotate(Vector3.right * speed * Time.deltaTime);
    }
    }
  8. Save it and add it as a component to the Directional Light.
  9. Import the Effects Assets package into your project (via the Assets | Import Package | Effects menu).
  10. Select the Directional Light. Then, from Inspector view, Light component, populate the Flare slot with the Sun flare.
  11. From the Scene section of the Lighting window, find the Other Settings subsection. Then, set Flare Fade Speed as 3 and Flare Strength as 0.5, shown as follows:
  12. Play the scene. You will see the sun rising, and the Skybox colors changing accordingly.

How it works...

Ultimately, the appearance of Unity's native Procedural Skyboxes depends on the five parameters that make them up:

  • Sun size: The size of the bright yellow sun that is drawn onto the skybox, is located according to the Directional Light's Rotation on the X and Y axes.
  • Atmosphere Thickness: This simulates how dense the atmosphere is for this skybox. Lower values (less than 1.0) are good for simulating the outer space settings. Moderate values (around 1.0) are suitable for the earth-based environments. Values that are a bit above 1.0 can be useful when simulating air pollution and other dramatic settings. Exaggerated values (like more than 2.0) can help to illustrate extreme conditions or even alien settings.
  • Sky Tint: It is the color that is used to tint the skybox. It is useful for fine-tuning or creating stylized environments.
  • Ground: This is the color of the ground. It can really affect the Global Illumination of the scene. So, choose a value that is close to the level's terrain and/or geometry (or a neutral one).
  • Exposure: This determines the amount of light that gets in the skybox. The higher levels simulate overexposure, while the lower values simulate underexposure.

It is important to notice that the Skybox appearance will respond to the scene's Directional Light, playing the role of the Sun. In this case, rotating the light around its X axis can create dawn and sunset scenarios, whereas rotating it around its Y axis will change the position of the sun, changing the cardinal points of the scene.

Also, regarding the Environment Lighting, note that although we have used the Skybox as the Ambient Source, we could have chosen a Gradient or a single Color instead—in which case the scene's illumination wouldn't be attached to the Skybox appearance.

Finally, also regarding the Environment Lighting, please note that we have set the Ambient GI to Realtime. The reason for this was to allow the real-time changes in the GI, promoted by the rotating Directional Light. In case we didn't need these changes at runtime, we could have chosen the Baked alternative.

Lighting a simple scene with Lightmaps and Light Probes

Lightmaps are a great alternative to Realtime lighting, as they can provide the desired look to an environment without being processor-intensive. There is one downside, though—since there is no way of baking Lightmaps onto the dynamic objects, the lighting of the important elements of the game (such as player characters themselves) can look artificial, failing to match the intensity of the surrounding area. The solution? Light Probes.

Light Probes work by sampling the light intensity over the location that they are placed at. Dynamic objects, once Light Probe-enabled, will be lit according to the interpolation of the nearest probes around them.

Getting ready

For this recipe, we have prepared a basic scene, including a simple game environment and an instance of Unity's Rollerball sample asset, which will be used as the player character. The geometry for the scene was created using ProBuilder 2.0, an extension developed by ProCore, and was sold at Unity's Asset Store and at ProCore's website (http://www.protoolsforunity3d.com). ProBuilder is a fantastic level design tool that speeds up the design process considerably for both, simple and complex level design.

The LightProbes.unitypackage package, containing the scene and all necessary files can be found inside the 1362_06_06 folder.

How to do it...

To reflect the surrounding objects using the Reflection Probes, follow these steps:

  1. Import LightProbes.unitypackage to a new project. Then, open the scene named LightProbes. The scene features a basic environment and a playable Rollerball game sequence.
  2. First, let's set up the light from our scene. From the Hierarchy view, select the Directional Light. Then, from the Inspector view, set Baking as Baked. Also, at the top of the Inspector, to the right of the object's name, check the Static box, shown as follows:
  3. Now, let's set up the Global Illumination for the scene. Open the Lighting window (via the menu Window | Lighting) and select the Scene section. Then, from the Environment Lighting subsection, set SkyboxProbes (available from the Assets) as Skybox, and the scene's Directional Light as Sun. Finally, change the Ambient GI option from Realtime to Baked as shown in the following screenshot:
  4. Lightmaps can be applied onto static objects only. From the Hierarchy view, expand the Level game object to reveal the list of the children objects. Then, select every child and set them as Static, as shown:
  5. Imported 3D meshes must feature Lightmap UV Coordinates. From the Project view, find and select the lamp mesh. Then, from the Inspector view, within the Model section of the Import Settings, check the Generate Lightmap UVs option, and click on the Apply button to confirm changes, shown as follows:
  6. Scroll down the Import Settings view and expand the lamp's Material component. Then, populate the Emission field with the texture named lamp_EMI, available from the Assets folder. Finally, change the Global Illumination option to Baked. This will make the lamp object emit a green light that will be baked into the Lightmap.
  7. Open the Lighting window. By default, the Continuous Baking option will be checked. Uncheck it, as shown, so we can bake the Lightmaps on demand.
  8. Click on the Build button and wait for the Lightmaps to be generated.
  9. From the Hierarchy view, select the RollerBall. Then, from the Inspector view, find the Mesh Renderer component and check the Use Light Probes option, as shown:
  10. Now, we need to create the Light Probes for the scene. From the Hierarchy view, click on the Create drop-down menu and add a Light Probe Group to the scene (Create | Light | Light Probe Group).
  11. To facilitate the manipulation of the probes, type Probe into the search field of the Hierarchy view. This will isolate the newly created Light Probe Group, making it the only editable object on the scene.
  12. Change your viewport layout to 4 Split by navigating toWindow | Layouts | 4 Split. Then, set viewports as Top, Front, Right, and Persp. Optionally, change Top, Front and Right views to the Wireframe mode. Finally, make sure they are set to orthographic view, as shown in the following screenshot. This will make it easier for you to position the Light Probes.
  13. Position the initial Light Probes at the corners of the top room of the level. To move the Probes around, simply click and drag them, as shown:
  14. Select the four probes to the left side of the tunnel's entrance. Then, duplicate them by clicking on the appropriate button on the Inspector view or, alternatively, use the Ctrl/Cmd + D keys. Finally, drag the new probes slightly to the right, to a point that they are no longer over the shadow that is projected by the wall, shown as follows:
  15. Repeat the last step, this time duplicating the probes next to the tunnel's entrance and bringing them inward towards the group. To delete the selected probes, either use the respective button on the Light Probe Group component, or use the Ctrl/Cmd + Backspace keys.
  16. Duplicate and reposition the four probes that are nearest to the tunnel, repeating the operation five times and conforming each duplicate set to the shadow, projected by the tunnel.
  17. Use the Add Probe button to place the three probes over well-lit areas of the scene.
  18. Now, add Light Probes within the shadow that is projected by the L-shaped wall.
  19. Since the Rollerball is able to jump, place the higher probes even higher, so that they will sample the lighting above the shadowed areas of the scene.
  20. Placing too many Light Probes on a scene might be memory intensive. Try optimizing the Light Probes Group by removing the probes from the regions that the player won't have access to. Also, avoid overcrowding the regions of continuous lighting conditions by removing the probes that are too close to the others in the same lighting condition.
  21. To check out which Light Probes are influencing the Rollerball at any place, move the Rollerball game object around the scene. A polyhedron will indicate which probes are being interpolated at that position, as shown:
  22. From the bottom of the Lighting window, click on the Build button and wait for the Lightmaps to be baked.
  23. Test the scene. The Rollerball will be lit according to the Light Probes.
  24. Keep adding Probes until the level is completely covered.

How it works...

Lighmaps are basically texture maps including scene lights/shadows, global illumination, indirect illumination, and objects featuring the Emissive materials. They can be generated automatically or on demand by Unity's lighting engine. However, there are some points that you should pay attention to, such as:

  • Set all the non-moving objects and lights to be baked as Static
  • Set the game lights as Baked
  • Set the scene's Ambient GI as Baked
  • Set the Global Illumination option of the emissive materials as Baked
  • Generate Light UVs for all 3D meshes (specially the imported ones)
  • Either Build the Lightmaps manually from the Lighting window, or set the Continuous Baking option checked

Light Probes work by sampling the scene's illumination at the point that they're placed at. A dynamic object that has Use Light Probes enabled has its lighting determined by the interpolation between the lighting values of the four Light Probes defining a volume around it (or, in case there are no probes suited to define a volume around the dynamic object, a triangulation between the nearest probes is used).

It is important to notice that even if you are working on a level that is flat, you shouldn't place all your probes on the same level, as Light Probe Groups will form a volume in order to the interpolation to be calculated correctly. This and much other information on the subject can be found in the Unity's documentation at http://docs.unity3d.com/Manual/LightProbes.html.

There's more...

In case you can spare some processing power, you can exchange the use of Light probes for a Mixed Light. Just delete the Light Probe Group from your scene, select the Directional Light and, from the Light component, change Baking to Mixed. Then, set Shadow Type as Soft Shadows and Strength as 0.5, as shown in the following screen. Finally, click on the Build button and wait for the Lightmaps to be baked. The real-time light/shadows will be cast into/from the dynamic objects such as Rollerball.

Summary

This article aimed to present you with some of the Unity's new features in lighting, and occasionally teaches you a few tricks with lights and effects. By now, you should be familiar with some of the concepts introduced by Unity 5, comfortable with a variety of techniques and, hopefully, willing to explore deeper some of the functionalities discussed throughout the recipes.

As always, Unity's documentation on the subject is excellent, so we encourage you to go back to the recipes and follow the provided URLs.

You can learn more about Unity 2D by referring to:

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Unity 5.x Cookbook

Explore Title
comments powered by Disqus