Course outline
Dot and Cross Product
In one line
The dot product tells you how much two directions agree: 1 when they match, 0 at a right angle, negative when they oppose. The cross product gives a new direction at right angles to both. Lighting, culling and surface directions all use them.
Think of it like this
Stand outside at noon with the sun straight overhead and hold a tray flat. The tray catches the most sun. Tilt it, and it catches less. Turn it edge-on and it catches none. How much light the tray catches depends on how much its facing direction agrees with the direction to the sun. That "how much they agree" is the dot product.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- dot product
- A number that says how much two directions agree: 1 when the same, 0 at a right angle, −1 when opposite (for unit vectors).
- cross product
- A vector at right angles to two others, found by the right-hand rule. Its length is the area of the parallelogram they span.
- normal
- The unit vector that points straight out of a surface. Lighting maths needs it.
- perpendicular
- At a right angle (90°) to something.
Step by step
01Two arrows and their dot product
In the first example the orange arrow is a = (2, 0, 0) and the blue arrow is b = (1, 1.5, 0). a.dot(b) is 2. The angle between them is 56.31°: they lean the same general way (the dot is positive) but are not the same direction.
02The special cases
The second example runs four pairs through dot, cross and angleTo. Parallel: dot 3, cross (0, 0, 0), angle 0. Perpendicular: dot 0, angle 90. Opposite: dot −2, angle 180. And one zero vector: dot 0, cross zero, and angleTo returns 90°, because three.js has no angle to give and picks π/2.
03In front of me?
A camera looks along −z. The post at (0, 0, −5) has dot 1.00 with the camera's forward direction: in front. At (0, 0, 5) it is −1.00: behind. At (5, 0, 0) it is 0.00, to the side: a right angle is neither in front nor behind. The test is dot > 0.
04Light on a floor
The floor's normal N is straight up. A light straight above (0° from vertical) gives N · L = 1.00. At 60° it is 0.50: half as bright. At 90° (skimming along the floor) it is 0.00. At 120° the light is under the floor: −0.50, and max(0, ...) clamps it to 0.00. A surface never gets negative light. Phase 5 builds on this.
Try it yourself
- 1
Make the arrows perpendicular
In the first example, change
btonew THREE.Vector3(0, 2, 0). The console printsa . b = 0,angle between a and b = 90.00 degreesanda x b = (0, 0, 4). At a right angle the dot product is 0, and the cross product is as long as it can be (2 × 2 = 4). - 2
A post that is both ahead and to the side
In the second example, change
V(5, 0, 0)toV(5, 0, -5). The post is now ahead and to the right at 45°: the line printspost to the side: dot 0.71 in front. Between 0° and 90° the dot is positive, so the test says "in front".
Code & diagrams
Orange is a, blue is b, white is a × b, drawn with the origin axes. The numbers are printed from the same vectors.
Console
Run the scene to see its console.Expected console output
a . b = 2
angle between a and b = 56.31 degrees
same thing from the dot product: 56.31
a x b = (0, 0, 3)
b x a = (0, 0, -3) (swap the order and it flips)
a . (a x b) = 0, b . (a x b) = 0The picture shows the 60 degree light: white is the floor's normal N, orange is the direction L towards the light.
Console
Run the scene to see its console.Expected console output
parallel: dot 3, cross (0, 0, 0), angle 0
perpendicular: dot 0, cross (0, 0, 2), angle 90
opposite: dot -2, cross (0, 0, 0), angle 180
one is zero: dot 0, cross (0, 0, 0), angle 90
camera forward = (0, 0, -1)
post ahead: dot 1.00 in front
post behind: dot -1.00 not in front
post to the side: dot 0.00 not in front
light 0 degrees from vertical: N . L = 1.00, brightness 1.00
light 60 degrees from vertical: N . L = 0.50, brightness 0.50
light 90 degrees from vertical: N . L = 0.00, brightness 0.00
light 120 degrees from vertical: N . L = -0.50, brightness 0.00Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Cross changes the first vector
In the first example, change const c = new THREE.Vector3().crossVectors(a, b); to const c = a.cross(b);.
Myth vs fact
Myth
The cross product exists in any number of dimensions.
Fact
The version that gives a new vector is a 3D thing. That is one reason 3D graphics uses it so much: surface edges are in 3D, and one cross product gives the normal.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Lighting maths on the GPU multiplies and adds millions of dot products per frame; dot products map directly onto GPU instructions, which is one reason shading is cheap. The
max(0, N · L)clamp is the Lambert diffuse model from the 1760s (Phase 5 builds on it). - ▸
Back-face culling uses the same test: a triangle's normal dotted with the direction to the camera is negative when the triangle faces away, and those triangles are skipped. Topic 4.3 shows what happens when vertex order flips the normal.
Remember this
- 1
The problem this solves: the GPU must decide how bright a surface is, whether an object is in front of the camera, and which way a surface faces. Each of those is one question about two directions. Two small operations answer all of them.
- 2
Dot product: do they agree? For two unit vectors (length 1)
a.dot(b)is a number from −1 to 1. It is 1 when they point the same way, 0 when they are at a right angle, and −1 when they point opposite ways. For vectors that are not unit length it is scaled by both lengths, so normalise first when you want "how much they agree". - 3
The formula, last. Multiply matching parts and add: a.x·b.x + a.y·b.y + a.z·b.z. For a = (2, 0, 0) and b = (1, 1.5, 0) that is 2·1 + 0·1.5 + 0·0 = 2. The dot product equals |a|·|b|·cos(angle): that is why it gives 1, 0 and −1 for angles of 0°, 90° and 180°. In the first example, the angle comes out as 56.31° both from
angleToand from the dot product. - 4
Cross product: the direction at right angles.
a.cross(b)is a new vector at right angles to both a and b. Its direction follows the right-hand rule: fingers along a, curl towards b, thumb along the result. Its length equals the area of the parallelogram the two arrows make, and it is 0 when they are parallel. It is how a surface's normal is found from two of its edges. - 5
Order matters for cross, not for dot.
a.dot(b)equalsb.dot(a). Butb × ais the opposite ofa × b: swapping the order flips the arrow. In the first example, a × b is (0, 0, 3) and b × a is (0, 0, −3). Like the in-place trap of Topic 2.2,a.cross(b)changesa; usecrossVectors(a, b)to write into a separate vector.
Explain it without notes
A light shines from direction L onto a surface with normal N. How does the brightness depend on them, and why is it clamped at 0?
Why do a.cross(b) and b.cross(a) give different answers, and when is the result the zero vector?
Practice
A triangle has corners P0 = (0, 0, 0), P1 = (1, 0, 0) and P2 = (0, 1, 0). Compute its normal with three.js and say which way it faces.
Write isInFront(camera, point) that returns true when point is in front of a camera that looks along its own forward direction.
Done when you can
Say what a dot product of 1, 0 and −1 means
Use a dot product for "in front?" and for light brightness
Use the cross product to find a perpendicular direction, and say why order matters
Explain why a.cross(b) is risky and what to use instead