Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Computer Graphics (rasterisation, ray tracing, WebGPU)  Module 7 – Light, shading and transparency

Phong and Blinn-Phong reflection models

Compute the ambient, diffuse and specular terms of the Phong reflection model, swap its reflection vector for the half vector of Blinn-Phong, and compare.

  • Intermediate
  • 25 minutes
  • Examples run with Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free core, Pro depth: 9 of 25 minutes free
Free: Phong adds a highlight where the reflected light meets the eye · Blinn-Phong compares the half vector with the normal · Practise: both models in code. With a pass: 3 in-depth parts and 3 interview answers, about 16 minutes.

What you will learn

  • Compute the ambient, diffuse and specular terms of the Phong reflection model
  • Replace the reflection vector with the half vector of Blinn-Phong and compare the highlights
  • Match a Blinn-Phong exponent to a Phong exponent and predict where the match fails
  • Implement toon shading and explain why neither classic model conserves energy

Before you start

What you’ll learn · Before you start

What you will learn

  • Compute the ambient, diffuse and specular terms of the Phong reflection model
  • Replace the reflection vector with the half vector of Blinn-Phong and compare the highlights
  • Match a Blinn-Phong exponent to a Phong exponent and predict where the match fails
  • Implement toon shading and explain why neither classic model conserves energy

Before you start

On this page

Lambert’s law alone makes every surface look like chalk: a polished ball under one lamp needs a bright spot where the lamp’s reflection meets your eye. The Phong and Blinn-Phong reflection models add that highlight as a third term, Phong as (r · v)^α and Blinn-Phong as (n · h)^β, from the half vector, with an exponent about four times larger for a spot of the same size.

Both are still in daily use; three.js’s MeshPhongMaterial, for one, computes its highlights with Blinn-Phong. The sections below build both on one sphere and compare them pixel for pixel.

Phong adds a highlight where the reflected light meets the eye

A mirror sends a lamp’s light in one direction only: r, the vector l mirrored about the normal, r = 2 (n · l) n − l. A polished surface behaves like a blurred mirror, brightest where r points at the viewer and dimmer as v turns away from r. Bui Tuong Phong’s model of 1975 adds three terms for each light:

I=ka+kdmax⁡(0, 𝐧⋅𝐥)+ksmax⁡(0, 𝐫⋅𝐯)αI = k_a + k_d \max(0,\ \mathbf{n} \cdot \mathbf{l}) + k_s \max(0,\ \mathbf{r} \cdot \mathbf{v})^{\alpha}

In words: a constant ambient term: A constant added to every pixel by a classic reflection model, standing in for light bounced around the scene, so that the side facing away from every light is not black. ka for light bounced around the room, Lambert’s diffuse term, and a specular reflection: Reflection gathered around the mirror direction, seen as a highlight that moves when the viewer moves; the Phong and Blinn-Phong models give it as a power of a cosine. term, ks times a factor that peaks at 1 when r points straight at the viewer. The exponent α, the shininess: The exponent of a Phong or Blinn-Phong specular term: the larger it is, the faster the highlight falls away from its peak and the smaller it looks., decides how fast the highlight falls away: 0.9731 to the power 32 is 0.418. Phong chose his exponents from 1 to 10 and wrote that such values “are empirically adjusted for the picture”. The figure shows the vectors at one point:

At one surface point: the normal n, l towards the light, its mirror image r, v towards the viewer and h halfway between l and v.The vectors at one surface pointhere the angle from n to h is half the angle from r to vsurfacenlrvh30°15°nsurface normalltowards the lightrl mirrored about nvtowards the viewerhhalfway between l and v

The five vectors of the Phong and Blinn-Phong models

Text description of the diagram

A flat surface is drawn as a horizontal line, with one point marked on it. Five arrows start at the point. The normal n points straight up. The vector l, towards the light, leans 50° to the left of n. Its mirror image about n, the reflection vector r, is dashed and leans 50° to the right. The vector v, towards the viewer, leans 20° to the right, so the angle between r and v, marked with an arc, is 30°. The half vector h, halfway between l and v, leans 15° to the left of n, and the arc between n and h is marked 15°: half of the 30° between r and v, as it always is when all the vectors lie in one plane. A legend under the drawing names the five vectors.

Step through one pixel of the sphere below, from its three vectors to its value:

Interactive · stage by stage One pixel through the Phong model Step through the program's pixel, from its three vectors to the sum of the three terms, then take the Blinn-Phong branch.
One pixel through the Phong model, step 6 of 6. Showing Unit vectors n (n, l, v of length 1), Diffuse: kd max(0, n · l) (0.6 × 0.829 = 0.497), Mirror l about n (r = (0.163, −0.163, 0.973)), Specular: ks (r · v)^α (0.4 × 0.418 = 0.167), Add the three terms (0.764). In focus: blinn, Specular: ks (r · v)^α.Blinn: n · hUnit vectors nn, l, v of length 1Diffuse: kd max(0, n · l)0.6 × 0.829 = 0.497Mirror l about nr = (0.163, −0.163, 0.973)Specular: ks (r · v)^α0.4 × 0.418 = 0.167Add the three terms0.764n · h

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. The vectors. At the program's pixel the normal is n = (−0.250, 0.250, 0.935). The light is along l = (−0.577, 0.577, 0.577) and the viewer along v = (0, 0, 1), both far away.
  2. Diffuse. n · l = 0.829, so the diffuse term is kd × 0.829 = 0.6 × 0.829 = 0.497: Lambert's law, unchanged.
  3. Mirror. Mirroring l about n gives r = 2 (n · l) n − l = (0.163, −0.163, 0.973), the direction a mirror at this point would send the light.
  4. Specular. r · v = 0.9731: the reflection points almost at the viewer. Raised to the power α = 32 it shrinks to 0.418, and ks × 0.418 = 0.4 × 0.418 = 0.167.
  5. The sum. Ambient, diffuse and specular add up: 0.1 + 0.497 + 0.167 = 0.764, the value the program prints for Phong with α = 32.
  6. The Blinn-Phong branch. Blinn-Phong skips the mirror. It compares n with h = (−0.325, 0.325, 0.888): n · h = 0.993. With β = 32 that gives 0.805 and a total of 0.919; with β = 128, 0.420 and 0.765, almost Phong's value.

Checkpoint

Check: The three terms of the Phong model

  1. Question 1 of 2 A Phong material has ka = 0.1, kd = 0.6, ks = 0.4 and α = 8. At a pixel, n · l = 0.8 and r · v = 0.9. What is its value, to three decimals?

    Type a number.

    A hint

    Add the three terms: ka, kd × n · l and ks × (r · v)^α.

    Show the answer: Question 1 of 2

    Answer: 0.752 (anything from 0.751 to 0.753 counts)

    0.1 + 0.6 × 0.8 + 0.4 × 0.9⁸ = 0.1 + 0.48 + 0.4 × 0.4305 = 0.1 + 0.48 + 0.1722 = 0.752. The exponent shrinks 0.9 to 0.43, so even a reflection 26° from the viewer gives less than half the highlight's peak.

  2. Question 2 of 2 The normal is n = (0, 1, 0) and the direction to the light is l = (0.6, 0.8, 0). What is the reflection vector r = 2 (n · l) n − l?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: (−0.6, 0.8, 0)

    r keeps l's component along the normal, 0.8, and flips the component across it, 0.6: (−0.6, 0.8, 0). Like l, it points away from the surface.

Blinn-Phong compares the half vector with the normal

James Blinn’s model of 1977 asks another question: how far is the normal from the one a perfect mirror would need to send the light to the viewer? That normal is the half vector: h = normalise(l + v), the direction halfway between the light and the viewer: the normal a perfect mirror would need to reflect the light into the eye. h, halfway between l and v, and the highlight becomes (n · h)^β:

𝐡=𝐥+𝐯‖𝐥+𝐯‖,I=ka+kdmax⁡(0, 𝐧⋅𝐥)+ksmax⁡(0, 𝐧⋅𝐡)β\mathbf{h} = \frac{\mathbf{l} + \mathbf{v}}{\lVert \mathbf{l} + \mathbf{v} \rVert}, \qquad I = k_a + k_d \max(0,\ \mathbf{n} \cdot \mathbf{l}) + k_s \max(0,\ \mathbf{n} \cdot \mathbf{h})^{\beta}

In words: the same three terms, with n · h in place of r · v; at the pixel above n · h is 0.993 where r · v is 0.973. The angle between n and h is about half the angle between r and v, so β must be about four times α for a highlight of the same size. With the light and the viewer both far away, h is the same at every pixel and is computed once, where Phong needs a reflection per pixel; OpenGL 2.1’s fixed-function lighting used h this way. The program counts the samples where each highlight is above half its peak:

Phong and Blinn-Phong on one sphere JavaScript · models/phong_blinn.mjs
// The Phong and Blinn-Phong reflection models on one sphere: one directional light, a viewer far along +z.
// I = ka + kd max(0, n.l) + ks spec, where spec = max(0, r.v)^a for Phong and max(0, n.h)^b for Blinn-Phong.
// Both are 0 when the light is behind the surface (n.l <= 0).
const len = (v) => Math.hypot(v[0], v[1], v[2]);
const normalize = (v) => v.map((c) => c / len(v));
const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
const fmt = (v) => `(${v.map((c) => c.toFixed(3)).join(', ')})`;

const l = normalize([-1, 1, 1]); // towards the light
const v = [0, 0, 1]; // towards the viewer, the same at every pixel
const h = normalize(l.map((c, k) => c + v[k])); // the half vector: also the same at every pixel
const KA = 0.1;
const KD = 0.6;
const KS = 0.4;

const reflect = (n) => n.map((c, k) => 2 * dot(n, l) * c - l[k]); // r = 2 (n.l) n - l
const phong = (n, a) => (dot(n, l) > 0 ? Math.max(0, dot(reflect(n), v)) ** a : 0);
const blinn = (n, b) => (dot(n, l) > 0 ? Math.max(0, dot(n, h)) ** b : 0);
const total = (n, spec) => KA + KD * Math.max(0, dot(n, l)) + KS * spec;

// One pixel, step by step.
const n = normalize([-0.25, 0.25, Math.sqrt(0.875)]);
const r = reflect(n);
console.log(`n = ${fmt(n)}, n.l = ${dot(n, l).toFixed(3)}`);
console.log(`r = ${fmt(r)}, r.v = ${dot(r, v).toFixed(3)}`);
console.log(`h = ${fmt(h)}, n.h = ${dot(n, h).toFixed(3)}`);
for (const [name, spec] of [['Phong a=32', phong(n, 32)], ['Blinn b=32', blinn(n, 32)], ['Blinn b=128', blinn(n, 128)]]) {
  console.log(`${name.padEnd(11)} ${KA} + ${KD} x ${dot(n, l).toFixed(3)} + ${KS} x ${spec.toFixed(3)} = ${total(n, spec).toFixed(3)}`);
}

// The whole sphere, 400 x 400 samples: how large is each highlight?
console.log('\nsamples where spec is above 0.5:');
for (const [name, spec] of [['Phong a=32', (m) => phong(m, 32)], ['Blinn b=32', (m) => blinn(m, 32)], ['Blinn b=128', (m) => blinn(m, 128)]]) {
  let count = 0;
  for (let j = 0; j < 400; j++) {
    for (let i = 0; i < 400; i++) {
      const x = ((i + 0.5) / 400) * 2 - 1;
      const y = 1 - ((j + 0.5) / 400) * 2;
      if (x * x + y * y <= 1 && spec([x, y, Math.sqrt(1 - x * x - y * y)]) > 0.5) count++;
    }
  }
  console.log(`  ${name.padEnd(11)} ${String(count).padStart(5)}`);
}

// The sphere under Blinn-Phong, b = 128, in tenths; # is 1.0 or more.
console.log('\nBlinn-Phong, b = 128:');
for (let j = 0; j < 16; j++) {
  let row = '';
  for (let i = 0; i < 32; i++) {
    const x = ((i + 0.5) / 32) * 2.2 - 1.1;
    const y = 1.1 - ((j + 0.5) / 16) * 2.2;
    if (x * x + y * y > 1) {
      row += '.';
      continue;
    }
    const m = [x, y, Math.sqrt(1 - x * x - y * y)];
    const value = total(m, blinn(m, 128));
    row += value >= 1 ? '#' : String(Math.floor(value * 10));
  }
  console.log(row);
}

Output

n = (-0.250, 0.250, 0.935), n.l = 0.829
r = (0.163, -0.163, 0.973), r.v = 0.973
h = (-0.325, 0.325, 0.888), n.h = 0.993
Phong a=32  0.1 + 0.6 x 0.829 + 0.4 x 0.418 = 0.764
Blinn b=32  0.1 + 0.6 x 0.829 + 0.4 x 0.805 = 0.919
Blinn b=128 0.1 + 0.6 x 0.829 + 0.4 x 0.420 = 0.765

samples where spec is above 0.5:
  Phong a=32   1342
  Blinn b=32   4729
  Blinn b=128  1204

Blinn-Phong, b = 128:
................................
.........56666555544432.........
......66666666665554443321......
.....6666666666555544433221.....
...66666667766655554443322111...
..566666679#865555444333221111..
..6666666677655544443332211111..
.455666655555554444333222111111.
.455555555554444433322211111111.
..4455554444444333322211111111..
..3444444443333322221111111111..
...23333333332222111111111111...
.....2222222221111111111111.....
......11111111111111111111......
.........11111111111111.........
................................

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node phong_blinn.mjs

At β = 32 the Blinn-Phong highlight covers 4,729 samples, 3.5 times Phong’s 1,342; at β = 128 it covers 1,204, and the pixel’s value is 0.765 against Phong’s 0.764.

Divide Phong's exponent instead

Press Edit and change Phong’s exponent in the counting loop from 32 to 8. Its highlight grows to 5,217 samples, close to Blinn-Phong’s 4,729 at 32: the rule of four again, from the other side.

The table puts the two models side by side:

Phong Blinn-Phong
Compares r with v n with h
Distant light and viewer r at every pixel h once per frame
Exponent for a similar spot α about 4α
Light near grazing r · v can turn negative inside the lit area, cutting the highlight off n · h stays positive while l and v are above the surface

The Phong and Blinn-Phong reflection models both add a highlight to Lambert’s law, and four more things matter once either is in a renderer. At grazing angles the two highlights take different shapes. Three common shader bugs move or remove the highlight. toon shading: A non-photorealistic shading style, also called cel shading, that cuts the lighting into a few flat bands of tone so that a rendered surface looks drawn rather than lit. cuts the same terms into flat bands. Neither model keeps the light it reflects below the light it receives.

Checkpoint

Check: The half vector of Blinn-Phong

  1. Question 1 of 2 With n = (0, 0, 1), the light along l = (0, 0.6, 0.8) and the viewer along v = (0, 0, 1), what is n · h, to three decimals?

    Type a number.

    A hint

    Add l and v, divide the sum by its length, then take the dot product with n.

    Show the answer: Question 1 of 2

    Answer: 0.949 (anything from 0.948 to 0.95 counts)

    l + v = (0, 0.6, 1.8), whose length is 1.897, so h = (0, 0.316, 0.949) and n · h = 0.949. The light is 36.9° from the normal and the viewer on the normal, so h is halfway, 18.4° from it, and cos 18.4° = 0.949.

  2. Question 2 of 2 An artist's material uses Phong with α = 20. The engine moves to Blinn-Phong. Which exponent keeps the highlight about the same size?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: About 80

    The angle from n to h is about half the angle from r to v, and a highlight of the same size then needs about four times the exponent: 80 for α = 20, as Blinn-Phong at 128 gives 1,204 samples above half its peak against Phong's 1,342 at 32 on the lesson's sphere.

Practise: both models in code

The exercise writes both models. Its sample tests use the pixel above, seen from straight ahead and from one side, a viewer at the mirror direction and a light behind the surface.

Exercise · Medium · JavaScript

Write the Phong and Blinn-Phong reflection models

Write phong(n, l, v, m) and blinnPhong(n, l, v, m) in models.mjs. n, l and v are unit vectors: the surface normal, the direction towards the light and the direction towards the viewer. m holds ka, kd, ks and shininess. Both return one number:

  • Phong: ka + kd × max(0, n · l) + ks × max(0, r · v)^shininess, with r = 2 (n · l) n − l;
  • Blinn-Phong: ka + kd × max(0, n · l) + ks × max(0, n · h)^shininess, with h = normalise(l + v);
  • in both, the specular term is 0 when n · l ≤ 0, so a light behind the surface leaves only the ambient term.

The starter's phong has no highlight at all, and its blinnPhong forgets to normalise h and lights the dark side.

Starter code · models.mjs

const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
const normalize = (a) => {
  const d = Math.hypot(a[0], a[1], a[2]);
  return [a[0] / d, a[1] / d, a[2] / d];
};

/** Phong: ka + kd max(0, n.l) + ks max(0, r.v)^shininess, r = 2 (n.l) n - l; no specular when n.l <= 0. */
export function phong(n, l, v, m) {
  // Only ambient and diffuse so far: the highlight is missing.
  return m.ka + m.kd * Math.max(0, dot(n, l));
}

/** Blinn-Phong: the same, with max(0, n.h)^shininess and h = normalize(l + v). */
export function blinnPhong(n, l, v, m) {
  // The half vector is not normalised, and nothing stops the highlight on the dark side.
  const h = [l[0] + v[0], l[1] + v[1], l[2] + v[2]];
  return m.ka + m.kd * Math.max(0, dot(n, l)) + m.ks * Math.max(0, dot(n, h)) ** m.shininess;
}
The sample tests · models.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { phong, blinnPhong } from './models.mjs';

const near = (got, want, what) => assert.ok(Math.abs(got - want) <= 1e-9 * Math.max(1, Math.abs(want)), `${what}: expected ${want}, got ${got}`);
const unit = (a) => {
  const d = Math.hypot(a[0], a[1], a[2]);
  return [a[0] / d, a[1] / d, a[2] / d];
};
const M = { ka: 0.1, kd: 0.6, ks: 0.4, shininess: 32 };

test("the lesson's pixel under both models", () => {
  const n = unit([-0.25, 0.25, Math.sqrt(0.875)]);
  const l = unit([-1, 1, 1]);
  const v = [0, 0, 1];
  near(phong(n, l, v, M), 0.7642488106803615, 'Phong, shininess 32');
  near(blinnPhong(n, l, v, M), 0.9192567013305534, 'Blinn-Phong, shininess 32');
  near(blinnPhong(n, l, v, { ...M, shininess: 128 }), 0.7652471130101057, 'Blinn-Phong, shininess 128');
  const aside = unit([0.2, -0.3, 0.9]);
  near(phong(n, l, aside, M), 0.8632785277264826, 'Phong, a viewer off the z axis');
  near(blinnPhong(n, l, aside, M), 0.9546921902737963, 'Blinn-Phong, the same viewer');
});

test('a viewer at the mirror direction sees the full highlight under both', () => {
  const n = [0, 0, 1];
  const l = [0.6, 0, 0.8];
  const v = [-0.6, 0, 0.8];
  near(phong(n, l, v, M), 0.98, 'Phong: ka + kd x 0.8 + ks');
  near(blinnPhong(n, l, v, M), 0.98, 'Blinn-Phong: h is n itself');
  const off = unit([-0.6, 0.3, 0.8]);
  near(phong(n, l, off, M), 0.6807479050690338, 'Phong, viewer off the plane of incidence');
  near(blinnPhong(n, l, off, M), 0.8145718870334656, 'Blinn-Phong, the same viewer');
});

test('no highlight and no diffuse light from a light behind the surface', () => {
  const n = [0, 0, 1];
  const behind = [0.6, 0, -0.8];
  const v = [-0.6, 0, 0.8];
  near(phong(n, behind, v, M), 0.1, 'Phong keeps only the ambient term');
  near(blinnPhong(n, behind, v, M), 0.1, 'Blinn-Phong keeps only the ambient term');
});
A hint

Compute n · l once and use it three times: for the diffuse term, inside r = 2 (n · l) n − l, and to decide whether there is a specular term at all. For Blinn-Phong, add l and v, divide the sum by its length, and only then take its dot product with n.

The sample tests run on this device, in your browser (QuickJS): nothing is sent to mysmartcopilot.com. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Practice that adapts to you

Practise this lesson

6 questions about 7 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 Under the program's Phong model, with ka = 0.1, what value does a pixel on the dark side of the sphere get?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: 0.1, the ambient term alone

    The ambient term stands in for light bounced around the room, so it lights every pixel the same: 0.1 on the dark side of the sphere.

  2. Question 2 of 6 At the program's pixel, r · v = 0.973 and the specular factor at α = 32 is 0.418. What is the factor at α = 64, to three decimals?

    Type a number.

    A hint

    Raise r · v to the new exponent, or square the old factor.

    Show the answer: Question 2 of 6

    Answer: 0.174 (anything from 0.172 to 0.176 counts)

    0.973⁶⁴ = (0.973³²)² = 0.418² = 0.174 (0.1743 with the program's unrounded r · v). Doubling the exponent squares the factor, so the highlight shrinks around the same peak.

  3. Question 3 of 6 An artist wants a smaller highlight whose centre stays exactly as bright. Which change does it, under Phong?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: Raise the shininess α

    The specular term is ks × (r · v)^α. At the centre of the highlight r · v = 1, so only ks sets its brightness and only α its size.

  4. Question 4 of 6 Why can a shader compute the half vector once per frame for a directional light and a far-away viewer?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: Both l and v are then the same at every pixel, so their normalised sum is too

    h = normalise(l + v) uses no per-pixel value. Phong's r = 2 (n · l) n − l uses the normal, so it changes at every pixel.

  5. Question 5 of 6 The light is 80° from the normal and the viewer 20° from it, on the same side as the light, all in one plane. What do the two specular dot products give?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: r · v = −0.174, so Phong gives no highlight, while n · h = 0.643, so Blinn-Phong gives a faint one

    This is the cut-off of the comparison table: inside the lit area Phong's r · v can turn negative while Blinn-Phong's n · h stays positive, so the two disagree near grazing light.

  6. Question 6 of 6 A three.js MeshPhongMaterial has its default shininess of 30. What does that mean for an artist used to Phong's exponent?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: three.js computes it with Blinn-Phong, so 30 gives roughly the highlight of a Phong exponent of 7.5

    three.js's documentation says MeshPhongMaterial uses a non-physically based Blinn-Phong model, with a shininess of 30 by default; divided by four, that is about Phong's 7.5.

Matching the two highlights, with the exponents measured Pro: opens with a Pro, Premium or Ultimate pass

The rule of four comes from geometry in one plane, and outside that plane it breaks. This part derives it, measures the exponent that matches each Phong exponent, and renders a floor on which the matched highlights take different shapes.

In this part:

  • Why the angle to h is half the angle to r, and what that makes the exponent
  • A floor at a grazing angle: a wide blob against a tall streak

2 runnable examples (JavaScript) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Three shader bugs that move or remove the highlight, reproduced Pro: opens with a Pro, Premium or Ultimate pass

Each of these bugs compiles, runs and gives a picture that looks almost right. This part reproduces all three on the lesson’s sphere and gives the one-line fix for each.

In this part:

  • A missing clamp lights the dark side
  • reflect() wants the vector towards the surface
  • A fractional power of a negative cosine is not a number

1 runnable example (JavaScript) · about 3 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Toon bands and the energy the classic models lose, with numbers Pro: opens with a Pro, Premium or Ultimate pass

The same terms can be cut into flat bands for a drawn look, or corrected for a physical one. This part builds a toon shader with outlines, then measures how much light each model sends back in total.

In this part:

  • Toon shading: the cosine in bands, outlines from depth jumps
  • Cut lighting into toon bands and find the outlines
    • Results of the sample tests
  • Neither model conserves energy, measured over the hemisphere

2 runnable examples (JavaScript) · 1 exercise with sample tests · about 6 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Interview questions

Start with the warm-up, answered here in full. Three harder questions follow, on highlights that appear, vanish or shrink when they should not; each row leads to its opening move on the module’s question page:

  1. Fresher to mid · Warm-up

    What is the difference between the Phong and the Blinn-Phong reflection models?

    Both add three terms for each light: a constant ambient term, Lambert's diffuse term kd max(0, n · l), and a specular highlight. Phong mirrors the light direction about the normal, r = 2 (n · l) n − l, and measures how close r comes to the viewer: ks max(0, r · v)^α. Blinn-Phong uses the half vector h = normalise(l + v), the normal a mirror would need to reflect the light into the eye, and measures it against the surface normal: ks max(0, n · h)^β. The angle from n to h is about half the angle from r to v, so β must be about four times α for the same highlight. With a distant light and viewer, h is the same at every pixel and computed once, where r is needed per pixel.

  2. Mid to senior

    Testers report a faint second highlight on the shadowed side of every shiny prop, opposite the real one. The fragment shader computes the specular term as pow(dot(r, v), 32.0) after r = reflect(-l, n). Explain the bug with numbers, fix it, and say what a fractional shininess would have shown instead.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    An engine switches its shiny materials from Phong to Blinn-Phong, and every highlight suddenly looks about three times bigger. The art team asks for one conversion factor for the shininess values they tuned. What do you give them, and where will it still look different?

    Model answer, Pro. First step and follow-ups

  4. Senior to staff

    A lighting artist raises a Phong material's shininess from 8 to 128 to make a car look polished. The highlight shrinks but gets no brighter, and the car looks duller overall. Explain this with an energy argument and numbers, and fix it without leaving the classic model.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Phong adds ka, Lambert’s diffuse term and ks max(0, r · v)^α, where r is l mirrored about the normal.
  • Blinn-Phong compares the half vector h = normalise(l + v) with the normal, computed once per frame when the light and the viewer are far away.
  • The angle from n to h is about half the angle from r to v, so a Blinn-Phong highlight needs an exponent about four times larger: 128 matches Phong’s 32 on the lesson’s sphere.

Terms in this lesson

ambient term
A constant added to every pixel by a classic reflection model, standing in for light bounced around the scene, so that the side facing away from every light is not black.
half vector
h = normalise(l + v), the direction halfway between the light and the viewer: the normal a perfect mirror would need to reflect the light into the eye.
shininess
The exponent of a Phong or Blinn-Phong specular term: the larger it is, the faster the highlight falls away from its peak and the smaller it looks.
specular reflection
Reflection gathered around the mirror direction, seen as a highlight that moves when the viewer moves; the Phong and Blinn-Phong models give it as a power of a cosine.
toon shading
A non-photorealistic shading style, also called cel shading, that cuts the lighting into a few flat bands of tone so that a rendered surface looks drawn rather than lit.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.