cocos2d-x dynamic light tutorial

What you are going to learn

Create your normal maps

The easiest way to create normal maps for your sprites is to use SpriteIlluminator. For a quick start you simply

Create the sprite sheets

With TexturePacker you can pack sprites + normal maps on two different sprite sheets, using the same layout:

Setup your Cococ2D-x project

To create a new Cocos2d-x project we can use the cocos command, with the option -l cpp we create a C++ "hello world" project.

~/Frameworks/cocos2d-x $ cocos new LightingDemo -l cpp
Running command: new
> Copy template into /Users/joachim/Frameworks/cocos2d-x/LightingDemo
> Copying cocos2d-x files...
> Rename project name from 'HelloCpp' to 'LightingDemo'
> Replace the project name from 'HelloCpp' to 'LightingDemo'
> Replace the project package name from 'org.cocos2dx.hellocpp' to 'org.cocos2dx.LightingDemo'
> Replace the mac bundle id from 'org.cocos2dx.hellocpp' to 'org.cocos2dx.LightingDemo'
> Replace the ios bundle id from 'org.cocos2dx.hellocpp' to 'org.cocos2dx.LightingDemo'
~/Frameworks/cocos2d-x $

Open the new project using Xcode or VisualStudio.

Load sprite sheet and add an animation

We can replace the init() method of HelloWorldScene.cpp by our own: First we have to load the sprite sheet data file (the one with the .plist extension) into the SpriteFrameCache. The texture image file is automatically loaded, its name is composed by replacing the .plist suffix with .png:

bool HelloWorld::init()
    if (!Layer::init())
        return false;

    auto spritecache = SpriteFrameCache::getInstance();

Now we can fetch the individual sprite frames of our animation from the cache:

    Vector<SpriteFrame*> animFrames;
    char str[100];
    for(int i = 1; i <= 8; i++)
        sprintf(str, "character/%02d.png", i);

Finally we create a sprite, set the Animate action on it, and add the sprite to the scene:

    auto sprite = Sprite::createWithSpriteFrame(animFrames.front());

    Animation *animation = Animation::createWithSpriteFrames(animFrames, 1.0f/8);
    sprite->setPosition(Director::getInstance()->getWinSize() / 2);


    return true;

If you start the demo application, the (still unlit) animation is played in the center of the screen.

Simple sprite sheet animation in cocos2d-x

Creating a point light effect using a fragment shader

The brightness of each sprite pixel depends on the angle between the light vector and the normal vector (stored in the normal map). This calculation is done by an OpenGL fragment shader:

You can clone all sources from GitHub.

Adding the light effect to the Sprite

First of all you have to modify the code from above, as we want to use an EffectSprite instead of a Sprite:

    auto sprite = EffectSprite::createWithSpriteFrame(animFrames.front());

Now we create an instance of the LightEffect and configure some properties.

    _effect = LightEffect::create();

    Vec3 lightPos(200, 200, 100);

The LightEffect creates a point light. A point light is a light source that emits light from a single spot in all directions. Think of it as a candle, torch, light bulb.

The effect class lets you set several parameters for the effect:

The position of the light in the scene. The light source can be placed in 3 dimensions. That means that you can place it between the player and the screen — to light the scene. The z-position can dramatically change the effect you get from the light effect.
Sets the color of the light source
Sets the brightness of the light: with a value of 1.0 and a white light color a rendered pixel will never get brighter than its original value in the texture. With brightness values >1 the pixels can get overexposed.
The radius at which the light source does not have any effect on the sprite.
The radius at which the light's intensity decreases to 50%. The value range is [0 … 1], relative to the cut-off radius. A value of 0.5 will give you a soft light, a value of 1 a light with hard edges.
Sets the color of the background light. This is a non-directional light which lights the sprite from all sides.
Light with hard and soft cut-off

The newly created LightEffect can be set on the EffectSprite. The sprite sheet with the _n suffix contains the normal maps for all sprites. It is important that this sheet is packed with the same layout as the sprites!

    sprite->setEffect(_effect, "spritesheet_n.png");

After adding these few lines our application displays a lit animation, but we are not yet able to move the light position. We are going to change this in the next section.

Adding a light effect to the cocos2d-x animation

Adding a light bulb

Let's add a sprite symbolizing the light source:

   _lightSprite = Sprite::create("lightbulb.png");
   _lightSprite->setPosition(lightPos.x, lightPos.y);

To allow the user to move around the light bulb we register some touch callbacks:

    auto listerner = EventListenerTouchAllAtOnce::create();
    listerner->onTouchesBegan = CC_CALLBACK_2(HelloWorld::handleTouches, this);
    listerner->onTouchesMoved = CC_CALLBACK_2(HelloWorld::handleTouches, this);
    listerner->onTouchesEnded = CC_CALLBACK_2(HelloWorld::handleTouches, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listerner, this);

The implementation of handleTouches() is quite simple, we update the position of the light bulb sprite, and tell the effect where our light is positioned now:

void HelloWorld::handleTouches(const std::vector &touches, cocos2d::Event *)
    for (auto &touch: touches)
        Point pos = touch->getLocation();
        _lightPos.set(pos.x, pos.y, _lightPos.z);


In the demo project on GitHub we've also added some background images:

Simple sprite sheet animation in cocos2d-x

Did you like the tutorial? Please share!

Source code available for download

The source code is available on GitHub. Clone it using git:

git clone

or download one of the archives: cocos2d-x-dynamic-lighting.tar.gz