september 8, 2020

JS13k 2020-The search for 04 - Lightsystem

JS13k 2020-The search for 04 - Lightsystem

Lightsystem

I like to play with light effects. Without lights the game would loose a lot of the feeling and ambience.

The final light system is quite silly but effective. First of all in the WebGL-shader I'm using a trick that will make everything in the distance fade out to dark. This gives the illusion of the player has a torch.

Vertex shader:

gl_Position=perspectivematrix * modelViewMatrix * vertexPosition;zDist=gl_Position.z/27.0;
// A lower value the closer the darkness starts

Fragment shader:

vec4 color=vec4(col.rgb-zDist,col.a)
// Substract the distance from the color

But that wasn't enough. I wanted to have light sources and remember some techniques I read about long time ago that I have tested in OpenGL many years ago. It's very silly and very inefficient but for a level this small it works great. It's static lightning so it built once but could be changed in runtime and rebuilt but I never used that.

The basic idea is like this:

A separate arraymap with light values are created using the same grid system as the level. The array is filled with the value for darkness. Then the light building process will start by looping over all tiles in the level. Each tile will check if any neighbour tiles has a brighter light then what it currently has. One the first run of the loop all tiles are dark apart from the light sources which gets the full brightness value added into the light arraymap.

Now when the loop enters the second time it will loop trough all tiles again checking neighbour tiles for the brightness. The ones closest to the light source will detect a neighbour tile being brighter. It will pick the brightest one and reduce the value with a small falloff value. If the tile is a wall it will set darkness to stop the light from spreading trough the wall. The loop will do this for all tiles in the level and will continue looping until not a single tile reports the light has changed. It sounds like this will take a long time but in fact it's done in just a few milliseconds.

Now when we are building the wall, floor and roof mesh each side will query the lightarray for the light value in the direction the mesh is pointing. So for example a left wall would get a light but the right wall would get darkness since lights doesn't shine trough the wall.

Finally in the shader I'm adding this light value to the final color.

varying vec4 li;//Light of the vertex
vec4 color=vec4(col.rgb-zDist,col.a)+(col.rgba*(li*1.2));

This simple system will create good enough lights for a 13k game and without lights the game would have lost a lot of the feeling.

Fog

But I was still not happy with how it looked. I had fade in the distance and I had light sources but looking at the game I have taken inspiration from (Delver) they had fog in the distance and I know fog is quite easy to create in OpenGL and turns out it's off course the same in WebGL. Just a few lines of code in the fragment shader and we are done. I have used the technique described at this page: https://www.geeks3d.com/20100228/fog-in-glsl-webgl/

float z=gl_FragCoord.z/gl_FragCoord.w;
float fogFactor=exp2(-0.15*0.15*z*z*1.4);
fogFactor=clamp(fogFactor,0.0,1.0);
gl_FragColor=mix(vec4(0.05,0.05,0.15,1),c,fogFactor);

The last vec4 (0.05,0.05,0.15,1) is the color of the fog in the distance and it's mixing this together with the final vertex color (c).

The code

The complete code of the shader can be seen here: https://github.com/nicklaslof/searching/blob/3ff998be2c9ff7b0c8875e8c6eca4e82984c2607/src/game.js#L23

The code that generates the lights can be seen here: https://github.com/nicklaslof/searching/blob/3ff998be2c9ff7b0c8875e8c6eca4e82984c2607/src/level/level.js#L131