Lighting with 2D normal maps

beginner

By: Pablo Fonovich

Normal maps allow you to add volume and details to any sprite or 3D object’s surface. A normal map’s pixels encode the direction the surface is facing, allowing the engine to fake volume when interacting with lights.

In this tutorial, you will learn to add normal maps to your 2D art in Godot and make it interact with lights.

You will learn to add normal maps to:

  • Sprites.
  • Seamless textures.
  • Animated characters.
  • AnimatedSprite nodes.
  • Tilemaps.

To download the sprites we’ll use in the following examples, click here. Each image comes with a corresponding normal map named with the _n suffix. For instance, azagaya_n.png is the normal map for azagaya.png.

We’re going to focus on using normal maps in Godot here. To learn more about how normal maps work under the hood, read What are normal maps? in Laigter’s documentation.

Adding a normal map to a sprite

Let’s see how to add a normal map to a sprite to get started.

Open Godot, create a new scene, and add a Sprite node. Assign the image azagaya.png to its Texture property.

Sprite nodes have a Normal Map property you can set in the Inspector. Drag an image from the File System dock and drop it on the sprite’s Normal Map property.

Nothing changes because normal maps only affect lit surfaces. Add a Light2D node to the scene.

Assign the LightSprite.png texture to it, move the light over the sprite, and increase its Range -> Height property. The light’s Height controls how far the light is from our sprite on the Z-axis. Increasing the value accentuates the lighting effect.

The sprite should look embossed. However, the scene may be too bright overall, making the shading not look believable. You can use a CanvasModulate and set its Color property to a dark gray for a better result. In this case, the CanvasModulate node allows you to simulate the ambient light property of 3D environments, tinting the entire scene.

Seamless texture with a normal map

You can use normal maps with seamless textures as with any sprite. To do so, select both the color texture and the normal map, and head to the Import dock. Enable Repeat and click reimport to make the pictures tile.

Create a new scene with the same nodes as the previous one, but use simplebrick.png and simplebrick_n.png for the sprite’s Texture and Normal Map, respectively.

Also, enable the Region property and set the Rect to something bigger than the original texture.

Provided both the normal map and the texture are set to Repeat, you should end up with something like this:

This example shows that Godot uses the same UV coordinates for the texture and normal map.

Animations with Normal Maps

In the following examples, you will learn some tips about using normal maps with animations.

There are two ways to create animations from a sprite sheet in Godot. The first is setting each frame in an AnimationPlayer node, and the second is using an AnimatedSprite.

Using an AnimationPlayer

Once again, create a new scene with a Sprite, Light2D, and CanvasModulate nodes. Add the spritesheet.png sprite sheet and its normal map to the sprite node’s corresponding properties.

Set the Animation -> Hframes property to 11, our sprite sheet’s columns.

Add an AnimationPlayer node and create a new animation called “Idle” lasting 0.8 seconds. Enable looping and add a keyframe for the sprite’s Animation -> Frame property. You can add one for each of the eleven frames.

And that’s it! Each frame will use the same region for the sprite’s texture and normal map.

You should see something like this:

Using an AnimatedSprite

Depending on the kind of game you’re creating the AnimatedSprite node may be more convenient than using an AnimationPlayer. Unfortunately, that node doesn’t have a Normal Map property.

To assign a normal map to animated sprites, we have to use a shader.

Add an AnimatedSprite node to the scene. In the Inspector, add a new SpriteFrames resource in the Frames property.

Click on the newly created resource to open the SpriteFrames bottom panel. Click on the grid-like button with the tooltip “Add frames from Spritesheet”. In the popup file dialog, double-click spritesheet.png and set the Horizontal and Vertical settings to 11 and 1, respectively.

Click “Select all frames” in the window’s top-right and click “Add 11 Frames”. Set the FPS setting in the SpriteFrames editor to 12.

If you turn on Playing in the Inspector, you should see the idle animation playing on your AnimatedSprite.

Lets add support for the normal map. Expand the AnimatedSprite node’s Material in the Inspector and create a new Shader Material and add a new Shader in the corresponding property. Click the Shader to open the shader editor and paste this code into it:

// Mandatory line to define 2D shaders
shader_type canvas_item;

// Allows us to assign a normal map to the shader in the Inspector
uniform sampler2D normal_map;

void fragment(){
	// Converts the texture data into a unit vector, with each channel in the [-1, 1] range

NORMAL = 2.0 * texture(normal_map, UV).rgb - 1.0;
}

In the fragment() function, we are sampling the normal map texture, transforming each sampled value to a valid unit vector, and assigning that unit vector to the NORMAL built-in attribute. After setting the normal map in the Inspector to the uniform, you should end up with a lit character, similar to the version using the AnimationPlayer.

Normal Maps with Skeleton Deform

For this example, we are going to use azagaya.png once again. If you are not familiar with 2D Skeleton Deform, read the 2D Skeletons page from Godot’s reference manual.

Create a new scene and add a Polygon2D node. Assign azagaya.png to it as a texture. Add a Skeleton2D with a Bone2D child, and an AnimationPlayer to create the animation. Also, add Light2D and CanvasModulate nodes. Your scene should look like this:

Scene tree for skeleton example

You can create a simple animation for testing purposes. Here’s the one I designed:

You surely noticed that Polygon2D doesn’t have a Normal Map attribute. As with the AnimatedSprite node, you need to add the same shader to it. Do so, assign a normal map to the shader material, and you should end up with a lit character:

Normal maps with TileMap nodes

Now you know how to use normal maps with sprites, you probably want your environment to react to lighting similarly.

Learning to create tilesets and using tilemaps is beyond the scope of this tutorial. To learn how to make one, check out our video:

Once you created a tileset and at least one tile, you should see a group named Selected Tile in the Inspector. You can assign a normal map to the corresponding property there. Note that in Godot 3.2, you need to assign the normal map of the whole tileset to each tile manually. This is a bit tedious, but that’s how it is for now.

We included a tileset for you to try this out, tile.png.

If you did it right, you should see something like this:

Become a better game developer

Join our weekly newsletter and get our latest game creation tutorials, tips, and open-source tools right in your inbox.

🔒 No spam. Unsubscribe anytime.

Made by

Our tutorials are the result of careful teamwork to ensure we produce high quality content. The following team members worked on this one:

Pablo Fonovich

Tutor

Nathan Lovato

Founder

Related products

Banner image

Godot secrets bundle 120$

A bundle with our four secrets courses at the price of three! It contains 2D Secrets, Shader Secrets, VFX Secrets, and PCG Secrets.

Banner image

Godot 2D Secrets 40$

Learn to create professional 2D games with the Godot game engine.