Lambertian lighting shader implementation.

Antoine Fortin
4 min readJul 27, 2020

--

Been a while since I wrote an article on lighting and ray-marching, so I decided to write one on how to set-up a basic Lambertian light algorithms. All the code and the final shader are available on my ShaderToy page, anyways, let’s dive in. We will use Ray-Marching using 2 triangles, and a distance field for the ease of use.

Let’s start all black

I started by only using a simple sky and black out every color on my scene. Sad, sad, sad…

The goal of lambertian lighting is quite simple, let’s throw the equation first…

I love to break every part of an equation, this way it makes it easier to understand and implement. What we want to know, or compute is “What is the color of the pixel”, and for now, we have a hit point in our scene. Well for now we do not see it, because I forced all the hitpoint to be in black. I will take a sphere as an example, because we love spheres :)

I made the sphere in white, then we need to shade it. Let’s walk every terms of the equation:

L dot N

Is the first part we need to look at.

L: Is the light position in worldspace. Normally defined by a vec3 in GLSL or float3 in HLSL.

N: is the normal in worldspace from the point we want to shade.

The dot part:

Most shading language have a dot operator, if not, the idea is simply to mesure how two vectors are oriented towards each others. In 2D we could express the dot product between vector a and b as follow.

The dot gives you the ability to track information on how the point N and the light pos L are oriented one vs the other.

Therefore we will use the power of GLSL to deal with the dot product. Let’s code this simple idea before moving to color and intensity.

vec3 lightPos = vec3(1.0, 1.0, -12.);
vec3 L = normalize(lightPos — pos);
vec3 N = computeNormal(pos);
col = vec3(dot(N, L));

I plug the dot product result into a vec3 constructor, as the result is a float, and we want to get express as a rgb out color. Synthaxic sugar, but very usefull :)

Giving us:

So at this point, we are almost done. The hardest part is done and everything that we need to warp our head around is simply: what is the color of the light, and what is it’s intensity?

C and I

C: the color of the light

I: the intensity

So we simply need to define a color and an intensity. Let’s say the light will be green.

vec3 ligthColor = vec3(0.01, 1., 0.01);

With an intensity of 1.5, intensity if usually define as a float, but to smash everything into GLSL, simply use the vec3(float A) to assign a float as every member of the vec3 class.

float intensity = 1.5; vec3 Li = vec3(intensity);

vec3 lightPos = vec3(1.0, 1.0, -12.);
vec3 L = normalize(lightPos — pos);
vec3 N = computeNormal(pos);
vec3 C = vec3(0.01, 1.0, 0.05);
col = vec3(dot(N, L)) * C;

Giving us:

For intensity let’s add this way:

vec3 lightPos = vec3(1.0, 1.0, -12.);
vec3 L = normalize(lightPos — pos);
vec3 N = computeNormal(pos);
vec3 C = vec3(0.01, 1.0, 0.05);
float scalarI = 1.5;
vec3 I = vec3(scalarI);
col = vec3(dot(N, L)) * C * I;

Giving us:

Here is the final Pixel shader code :) Everything we implemented is at line 94!

--

--

Antoine Fortin

In between Montreal and London, I love to write, read, learn and explore.