Dynamic Lighting

author:
Daniel Sperl
description:
Use normal maps for realistic light effects
lastupdate:
2015-11-30
compatible:
v2.0
tag:
normal maps, lighting, illumination
homepage:
https://github.com/Gamua/Starling-Extension-Dynamic-Lighting
download:
https://github.com/Gamua/Starling-Extension-Dynamic-Lighting/archive/master.zip

Overview

These classes add support for dynamic lighting to the Starling Framework. This allows you to use normal maps for realistic lighting effects of 2D objects.

This extension requires Starling Framework 2.0 or higher. Furthermore, be careful with the Context3D profile: the lowest one, baselineConstrained, can only cope with one point/directional light. So be sure to test your project with all profiles you want to support.

Demo

The three symbols at the top represent the light sources, which can be turned on and off. The circle with the “a” in the center represents the ambient light: movement doesn't affect the outcome.

Usage

Creating Normal Maps

To use the extension, you first need to create a set of normal maps for your objects. The recommended way to do that is with the excellent SpriteIlluminator from Andreas Löw (the author of the popular “TexturePacker”).

So, let's say you've got a conventional (colored) texture for your character, and one with the normal maps, like here:

Creating the LightStyle

What you're going to do now is create an instance of the “LightMeshStyle” class that's part of this extension, and attach it to any of your Images/Quads/Meshes in Starling. The style also contains some properties that configure how it will appear when hit by light.

var texture:Texture = ...;
var normalMap:Texture = ...;
 
var image:Image = new Image(texture);
var style:LightStyle = new LightStyle(normalMap);
style.ambientRatio = 0.3;
style.diffuseRatio = 0.7;
style.specularRatio = 0.5;
style.shininess = 8.0;
image.style = style;

The properties have the following meaning:

The math behind the lighting calculations is the Phong reflection model. The image above shows you how the different components add up to the final color of each pixel.

The ambient and diffuse ratio should always sum up to “1.0”. Otherwise, the object will quickly become unnaturally bright in some areas.

Adding Light Sources

Finally, add at least one light source; otherwise, you won't be able to see your object. There are several kinds of light sources:

To create a light source, use the factory methods on the LightSource class:

var ambientLight:LightSource = LightSource.createAmbientLight(Color.WHITE);
addChild(ambientLight);
 
var pointLight:LightSource = LightSource.createPointLight(Color.RED);
pointLight.x = 200;
pointLight.y = 150;
pointLight.z = -100;
addChild(pointLight);
 
var directionalLight:LightSource = LightSource.createDirectionalLight(Color.YELLOW);
directionalLight.rotationZ = Math.PI / 2.0;
directionalLight.rotationY = -1;
addChild(directionalLight);

Note that light sources inherit from the Sprite3D class, which means that they may be placed in a 3D space (even if the rest of your scene is typically 2D). Every light that's part of the display list will be picked up automatically by the style.

At development time, it's often useful to have a visual representation of the light. To do that, just enable the showLightBulb property:

pointLight.showLightBulb = true;

You can drag the light symbol around on the screen; hit “Shift” on the Keyboard while moving the mouse to change rotation (useful for directional light) and z-Position.

Directional lights default to shining along the positive x-Axis. To change the direction, adjust the rotationX/Y/Z properties on the light.

Performance Considerations

Almost all the magic of this extension is happening on the fragment and vertex shaders, i.e. on the GPU. So this extension doesn't cause any significant load on the CPU.

However, the fragment shader code is relatively extensive, especially if you add multiple light sources. On modern mobile hardware, that's not an issue, but be sure to add a fall-back for older devices.

Changelog

User Comments

Feel free to edit this part of the page if you want to add information that's lacking in the above description.
Questions are better asked in the forum, though.