Right-handed vs left-handed, line by line
Fix x pointing right and y pointing up.
The only freedom left is where z goes — and the
cross product
decides it for us. By definition the cross product of the first two basis vectors gives the
third, and the right-hand rule fixes its direction.
Step 1 — curl from x to y with your right hand. Point your right
fingers along +x, curl them toward +y;
your thumb points out of the screen, toward you. The cross product agrees:
\hat{x} \times \hat{y} = +\hat{z} \qquad (\text{right-handed: } +z \text{ comes toward you}).
Step 2 — do it again with your left hand. Same curl from
+x to +y, but the
left thumb points into the screen, away from you. To keep the cross-product
rule, the whole frame flips the sign of z:
\hat{x} \times \hat{y} = -\hat{z} \qquad (\text{left-handed: } +z \text{ goes into the screen}).
Step 3 — read off the consequence. The two frames differ by a single sign
flip on one axis, a mirror reflection. There is no rotation that turns one into the
other — a left hand is not a rotated right hand. So every quantity built from the cross
product (a surface normal, a torque, "which way is out") points the
opposite way between the two conventions.
(\vec{a} \times \vec{b})_{\text{left}} = -\,(\vec{a} \times \vec{b})_{\text{right}}.
Step 4 — and the engines really do disagree. This is not a textbook
nicety. OpenGL (and Maya, and the usual maths convention) is
right-handed; DirectX and Unity are left-handed. Port a
mesh between them without flipping a z and it comes out mirrored.
Two more conventions that bite
Handedness is the famous one, but two cousins cause just as many late nights.
The "up" axis. Even among right-handed frames, engines disagree on which
axis points up. Many graphics engines are y-up
(y is the sky); many modelling tools and CAD systems are
z-up (z is the sky,
y goes into the distance). Import a z-up model into a y-up engine
and it lies on its back.
Row vs column vectors. Is a vector a tall column the matrix multiplies on
the right (M\vec{v}, the OpenGL/maths convention), or a wide row it
multiplies on the left (\vec{v}\,M, the DirectX convention)? The
two store the transpose of each other's matrices, and chain transforms in the
opposite order. Mix them up and your rotations compose backwards.
A 3-D coordinate frame is three axes
\hat{x}, \hat{y}, \hat{z} through an origin. Its conventions are
not universal:
-
Handedness — set by the cross product: a frame is
right-handed when \hat{x} \times \hat{y} = +\hat{z}
(the right-hand rule; +z toward you) and
left-handed when \hat{x} \times \hat{y} = -\hat{z}
(+z into the screen).
-
A mirror, not a rotation — the two frames differ by a sign flip on one
axis, so no rotation maps one to the other and every cross product reverses between them.
-
Engines differ — OpenGL is right-handed, DirectX/Unity are left-handed.
-
Up-axis & vector conventions vary too — y-up vs z-up, and row-vector
(\vec{v}M) vs column-vector (M\vec{v})
matrix layouts (transposes of one another).
When a handedness mismatch slips through, the symptoms are weirdly specific. Models come
out mirrored — text on a sign reads backwards, a character's holster jumps
to the wrong hip. Triangle winding order reverses, so the engine thinks
every front face is a back face: surfaces turn inside-out, and either the
whole model vanishes (back faces culled) or its insides render while its outsides don't.
Lighting goes haywire too, because the surface normal — a cross product — now
points into the wall: lit faces go dark and shadowed faces glow. The usual fix is
a single reflection — negate the z column of the conversion
matrix and reverse the winding — applied once, consistently, at the import boundary. Apply
it twice and you are back where you started, which is its own special kind of bug.