Half-Lambert

2 / 17
Learn core lighting terms (diffuse/specular/rim) on a simple sphere.

Keeping dark areas from going fully black comes down to this one line:

Let's break it down.


The problem with standard Lambert

Standard Lambert uses max(dot(n, l), 0.0), which clamps the back-facing side to zero — pure black. In games and stylized rendering, that looks dead: back-lit surfaces lose all their detail.


What Half-Lambert does differently

It remaps dot(n, l) from [-1, 1] down to [0, 1]:

Result:

  • Facing the light (dot = 1.0) → 1.0 × 0.5 + 0.5 = 1.0 (100%, brightest)
  • Perpendicular (dot = 0.0) → 0.0 × 0.5 + 0.5 = 0.5 (50%, mid)
  • Facing away (dot = -1.0) → -1.0 × 0.5 + 0.5 = 0.0 (0%, darkest)

Compared to standard Lambert, the shadow side retains some brightness and visible surface detail. The technique was popularized by Valve's art team on Half-Life, hence the name.


Try changing it

ChangeEffect
* 0.5 + 0.5 to * 0.4 + 0.6Shadow side gets brighter, lower contrast
* 0.5 + 0.5 to * 0.8 + 0.2Closer to standard Lambert, only a small base brightness in shadow
Wrap with pow(diff, 2.0)Increases contrast — only surfaces facing the light stay bright

Exercise

The sphere in the exercise is fully black because diff = 0.0. Fix the TODO line to compute the Half-Lambert diffuse value.

Answer Breakdown

Starting state: diff is hardcoded to 0.0, making the sphere black.

The fix: drop the max clamp and use dot(n, l) * 0.5 + 0.5 instead — this maps the full [-1, 1] range into [0, 1], so even back-facing pixels receive some brightness.

Try changing the coefficients from * 0.5 + 0.5 to * 0.6 + 0.4 and observe how the shadow side responds.

GLSL Code Editor

Correct Code Preview

Current Code Preview