Point Light
This tutorial extends directional lighting with two new ideas: the light has a position, and it gets weaker with distance. The key lines are:
Let's break each part down.
Directional light vs. point light
A directional light has only a direction — every pixel on the canvas receives light from the exact same angle, like sunlight.
A point light has a position. Each pixel computes a different direction toward the light source, and pixels farther away receive less light — like a candle that's bright up close and fades into darkness.
Light direction per pixel
normalize shrinks the vector to length 1, keeping only its direction. Every pixel gets a different l — that's the fundamental difference from a directional light.
Distance attenuation
The farther the pixel is from the light, the dimmer it should appear:
dot(v, v) gives the squared length of a vector without a costly sqrt. The formula 1 / (1 + 3d²) starts at 1 when distance is 0 (100% bright) and drops toward 0 as distance grows. The coefficient 3.0 controls how quickly the light fades — larger means faster falloff.
Combining diffuse and attenuation
0.1 is the ambient floor — it keeps the back of the sphere from going completely black. diff * 2.0 amplifies the diffuse contribution.
Try changing it
| Change | Effect |
|---|---|
Move lightPos x/y to -0.3, 0.2 | Light shifts left, lit face changes |
Change the 3.0 coefficient to 10.0 | Faster falloff, darker overall |
Change the 3.0 coefficient to 0.5 | Slower falloff, wider bright area |
Change 0.1 to 0.4 | Stronger ambient, back face brightens |
Exercise
The exercise code already computes the light direction l, but attenuation is missing. Fill in the att calculation and multiply it into diff to make the sphere darken with distance from the light.
Answer Breakdown
The starting code has diff = max(dot(n, l), 0.0) with no attenuation — the light acts as if it has infinite reach, so brightness depends only on the surface angle, not the distance.
Adding att brings in falloff: pixels close to the light source get an att near 1 (nearly full brightness), while distant pixels get an att near 0 (almost no contribution). The sphere gains a natural gradient from bright center to dark edges.
Using dot(lightPos-pos, lightPos-pos) instead of length() avoids a square root, which is faster on the GPU.
Try changing the light's z from 0.6 to 0.1 to bring it closer to the sphere surface and watch how the falloff shape changes.