Normal map lighting for 2D Pixel Art sprites

Hi! :wave:

I am playing now with shaders to apply a lighting/shadow effect on my pixel art sprites for Witchcrafter. This use case is very specific, but the idea could be used for high res sprites and high res normal maps. The main issue with this is lack of support for multiple textures for sprites - therefore the common approach to have two sprites - normal map and color sprite forces us to utilize 2 draw calls and join the data in the render target :confused:

But let’s be a little bit more clever and economical with draw calls!

In Defold it is a little bit hard. To make lighting in one draw call, you need to do it in one fragment program of a sprite and you need to provide all the data needed as uniforms.


Changing different part of sprites - outfit

Moreover, because I am making an RPG, my previous fragment program for hero sprite is using some color coding for changing part of outfits to match currently worn inventory:

The color palette is specially selected, to easily distinguish the values of RGB in fp - cloak is orange, gloves are cyan, boots are magenta. Such pixel art is even more convenient to animate in traditional frame to frame animation, because the selected colors are very distinguishable.

You may notice above picture has already even shading on them, but this assumes that the main source of light is always in front/left side of the sprite. When sprite is flipped to go left, the shading is still static, so the main light source looks like is from front/right. This is accepted in many games, but for a witchcrafter who can create custom, dynamic sources of lights with fire spells, this looks unnatural, when a campfire is on the right side of such sprite - it was bothering me a lot :confounded:

Anyway, the whole sprite is then changed by fp to this according to given data passed to fp in uniforms, so I could have custom colors of each part:


Normal maps

But to have normal-map-like data I would need to change the approach. Normal maps utilize 3 channels (RGB) to represent the vectors normal to the surface being lit up:

The Red value increases goinf from left to right, Blue increases goling from bottom to top and green increases to the left bottom corner. With this you can create another sprite, so called “normal map”, that will represent a 3D-like structure of the object, that you can draw yourself or use a software that helps you draw it, like Sprite Dlight or, as below, Sprite Illuminator:

Screenshot from 2022-05-21 17-00-09
With instant preview in Sprite Illuminator:

For example an awesome game - Eagle Island is doing such trick for main character’s sprite:

Or stealth game The Siege and the Sandfox for every sprite and tilemaps:

As you can see above the two sprites can be merged in the game engine and given a data about lighting (either with other texture with drawn lights or some other uniforms-based solution) you can achieve a fully lit, dynamic object. Unfortunately, for pixel arts using a full scale normal map it outputs very high res lit object, which looks smooth, but it does not ressemble any hand-drawn pixel art, because it has two many tones in the color pallete :art:

To reduce the smoothness you can use a reduced color pallete for normal map, but you need to also polish your normal maps, preferrably all by your hand! :paintbrush:


Proof of Concept

For the Proof of Concept in Defold I created the most simple map in 8x8 resolution:

image

Having in mind I also want to change the color of each part of the equipment I decided to use Red channel for specifing the part, and G and B channels to map it to normal map, so for each parth I have separate normal maps:

Drawing it is a tedious job, but I will try to figure out a script for this in the future. The colors are similar, because for example each part has a constant Red value, e.g. 243, 160, 90, etc, to distinguish it in the fragment program and if given fragment/pixel has a Red value in the color of 243 I am changing the whole color of the pixel to the color given in uniform “cloak” for cloak color. And analogically for each part.

So I could use such sprite and make a fragment program (attached at the bottom of the post) and modify all colors, separating them by Red value like described above and by Green and Blue channels to specify a normal - all are multiplied by given lighting data. The normals are cheated here a little bit, as you may noticed. This is because the squares are representing already lit object, from the left-bottom side. This is then recalculated in fragment program to ressemble proper lighting. This is not perfect, but good enough to start with. The end effect in the editor looks like this:


Summary

That’s it. This is just a first attempt at doing such lighting in Defold. Making it into production might take some time. I am leaving the link to the repository - take it as an example and learn what you can do! :wink: If you have any thoughts, ideas, tips and hints - I would be very grateful! :heart:

Linked topics:

2d material with a normal map - sprite shading if defold possible? 2D lights and shadows sample
2D lights and shadows sample
2d image materials + lights
Simple 2D Lighting
2d light implemetations

My public repository for this experiment:

24 Likes

That’s really cool!
I always find normal maps to be a bit of a pain so I was looking at alternatives and trying this approach

8 Likes

Oh my, Powerhoof are gods of shaders for Pixel Art! This looks great and (or but) is bound to the style! I admit that normal maps are cumbersome, but that way I could achieve a very dynamic lighting and still leave sprites in limited color palettes.

2 Likes