Tower Blocks: Episode 5 - Custom Lighting Shaders

Published on

In Episode 4 we made the game reactive by adding falling blocks and smooth camera motion. This time we tackle one of the most noticeable visual upgrades: custom lighting shaders.

We write our own GLSL shaders to simulate 3D lighting with ambient, diffuse and specular components. This gives every block depth, contrast and a real sense of volume. No more flat cubes.


What We Covered


Design Insights

Why Write Our Own Shaders?

Raylib’s default lighting system is limited. By writing our own shaders we get:

How We Structured It

We use the standard Raylib uniforms (mvp, matModel) so Raylib automatically sends in our matrices. We compute:

This gives us everything we need to do lighting math in world space.


Implementation Highlights

Vertex Shader

uniform mat4 mvp;
uniform mat4 matModel;

in vec3 vertexPosition;
in vec3 vertexNormal;

out vec3 FragPosition;
out vec3 FragNormal;

void main() {
    FragPosition = vec3(matModel * vec4(vertexPosition, 1.0));
    FragNormal = normalize(mat3(transpose(inverse(matModel))) * vertexNormal);
    gl_Position = mvp * vec4(vertexPosition, 1.0);
}

Fragment Shader

uniform vec3 blockColor;
uniform vec3 cameraPosition;

in vec3 FragPosition;
in vec3 FragNormal;

out vec4 FragColor;

void main() {
    // Hardcoded light
    vec3 lightPosition = vec3(-50, 500, -50);

    vec3 lightDir = normalize(FragPosition - lightPosition);
    float diff = max(dot(FragNormal, -lightDir), 0.0);

    vec3 viewDir = normalize(cameraPosition - FragPosition);
    vec3 reflectDir = reflect(lightDir, FragNormal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);

    vec3 ambient = vec3(0.4);
    vec3 diffuse = diff * vec3(0.5);
    vec3 specular = spec * vec3(0.5);

    vec3 lighting = (ambient + diffuse + specular) * blockColor;
    FragColor = vec4(lighting, 1.0);
}

Shader Integration


External Resources


What’s Next?

In Episode 6 we’ll revisit what happens after a game ends. Instead of instantly restarting, we’ll introduce a brand new RESETTING_STATE, where the entire tower gracefully collapses, shrinks or fades away. In other words the stage will have a satisfying restart. This gives the game a natural rhythm and a much more polished reset cycle. Think of it as a reward for losing: a bit of visual closure before you start stacking again.


Project Code

You will find the complete source code here: tower-blocks