OFFICIAL Using Normal Maps to Light Your 2D Game

rmanthorp

GameMaker Staff
Admin
GameMaker Dev.
Check out our latest guest tech blog!

Using Normal Maps to Light Your 2D Game

PolyCrunch Games are the developers of Pyramid Plunge, a cartoony rogue-lite, action-platformer with handcrafted pixel-art. They join us on the blog to share the way they use normal maps to bring awesome lighting their upcoming game.
INTRODUCTION
Using 3D shading techniques in 2D is an interesting one to create immersive environments that react to dynamic light sources. I'm using it in my game Pyramid Plunge for lighting the environment. However, I'm not using it on the characters, as I wanted to keep them looking cartoony and thus flat shaded.
HOW IT WORKS
You will need a simple shader that calculates shading similar to how its done in 3D, but instead of reading normal information from the 3D object or normal maps applied to 3D objects, we supply the normal maps as sprites. At the end of the article you'll find a sample project to play with.

undefined


READ: https://opr.as/xp7n
 

Roldy

Member
Nice. I was messing with this the other day with good results. I'll check the blog out and see if they have some more insight or tricks.
 

kburkhart84

Firehammer Games
I've done some of this in the past, though I was using TMCLux off the marketplace. The method that asset uses is slightly different than what this tutorial is doing. The tutorial is going to surfaces and such, while the asset I'm using does it straight up in a single shader that receives both texture and normal map at the same time and handles it.

And the way I've been generating normals is also as mentioned in the tutorial, using Blender. I make a model that I render as a sprite with no lighting, only color, and then I render a normal map bake. The way my setup is, I can easily do animations as well with no difficulty.
 
I was able to allow for unlimited light sources by using differed rendering to drawing the results of all lights to one application surface sized surface. Then use this and the application surface itself to calculate the final scene in one pass.
 
I was able to allow for unlimited light sources by using differed rendering to drawing the results of all lights to one application surface sized surface. Then use this and the application surface itself to calculate the final scene in one pass.
any example to see with your changes ?
 

Roldy

Member
any example to see with your changes ?
Im sure someone has a decent mock up but in general just google 'deferred rendering/shading' and it should be fairly self explanatory. Typically you just render your scene as two maps; normal/depth, diffuse/spec (or something comparable) and then use those for a full screen lighting pass. The choice you have to make with GMS is to limit platform support and do MRT or support all platforms and do multiple render passes.

I mocked something similar up for the Neon Jam to get a crap ton of neon line lights rendering... didn't complete it but it was fairly straightforward.
 
Last edited:
Im sure someone has a decent mock up but in general just google 'deferred rendering/shading' and it should be fairly self explanatory. Typically you just render your scene as two maps; normal/depth, diffuse/spec (or something comparable) and then use those for a full screen lighting pass. The choice you have to make with GMS is to limit platform support and do MRT or support all platforms and do multiple render passes.
Yeah, this is about it. I used MRT to draw the base and normal textures to their own surfaces without having to set up additional render batches. Then when drawing a light source it samples the base and normal surface at a given pixel and outputs the lighting data to the application surface.

If you change the blend mode to add, you can just iterate through the light objects and the lighting results will be compiled to the final game frame. My optimization allowed multiple light shadows to share one surface so that one draw call would display several lights instead of only one. Every surface creates another texture page, so I created one surface the size of a default texture page and drew the shadow vertices in separate sections. You can multiply the lights you render by 4 if you store the shadow mask on one color channel (argb) of this surface instead of all of them. When you sample the shadow mask texture, just get the magnitude of the color vector multiplied by the color of the channel that light's data is stored with.

You can also optionally choose to set an ambient light intensity so that you can still see even without any light. You just draw the base surface to the application surface in a shade of gray.
 
Top