Dev Blog: Adding dynamic lighting, part 1

This week, I started working on dynamic lighting, which is one of the major planned features in the next release. The idea is that explosions, fires, etc. light up their surroundings.

Posted by on

This week, I started working on dynamic lighting, which is one of the major planned features in the next release. The idea is that explosions, fires, etc. light up their surroundings. I also want to take into account light direction, so surfaces that are angled towards the light source are lit up more strongly. Like in real life!

Ignoring directional light for now, the easiest way to do this is to draw light flashes on top of everything else:

But that exposes an additional point: things that are further back should be lit less. The back walls of ships, and especially the sky and clouds, should receive less light. Though Airships is 2D there's an implicit third dimension. Since the light sources are right at the front, things further back are at a greater distance to the light source and need to be lit less.

To make this work, the game has to be selective about the degree to which it applies the lighting. To make this work, I used a separate light map. This is a separate image from what you see on the screen. It's initially black and then has all the lights drawn onto it.

The code for drawing various parts of the screen can then refer to this light map to superimpose it onto what's being drawn to a greater or lesser degree. In practice, this means creating a shader for ship modules that indexes into the light map.

``````uniform sampler2D tex;
uniform sampler2D light;
uniform vec4 tint;
uniform float strength;
uniform vec2 lightSize;
uniform float screenHeight;

void main(void) {
// Extract light intensity value. The light map is upside-down for Reasons.
vec4 light = texture2D(light, vec2(gl_FragCoord.x, screenHeight - gl_FragCoord.y) / lightSize);
// Multiply light intensity with a strength factor and get rid of the alpha.
light = vec4(light.x * strength, light.y * strength, light.z * strength, 0);
// Get the texture color and add the light on top of it.
gl_FragColor = texture2D(tex, floor(gl_TexCoord[0].st) / 1024.0) * tint + light;
}``````

The result is that the modules are lit up strongly and the back walls somewhat less so. Finally, I also draw the whole light map on top of the screen once. This is technically incorrect, but it gives things an amount of "shine". And that's where I am right now:

Particles and modules produce coloured light, which is drawn onto the light map every frame, making the lighting fully dynamic, which is pretty neat! I haven't yet measured the performance hit from this, and I'll probably include the option to turn off lighting for speed purposes.

Next up is going to be directional light, which will involve a bit more cleverness, multiple light maps, and bump maps for the textures...