Rotate, project, anchor
The skull stands. Now it turns, and every feature has to go along – foreshortening and occlusion included. The whole trick is that no feature function knows anything about it.
Between the head shape and the drawn line
lie three steps: rotate, project, anchor. The first two are four lines
of arithmetic. The third is the Feld – and it is the
reason there is not a single sine of a head rotation in
gesicht.ts.
Rotate and project
Rotation happens in a fixed order: first yaw about the Y axis, then pitch about X, then roll about Z. That is not a matter of taste – the same three angles in a different order are a different rotation.
It exists in two forms, because it is needed at two orders of
magnitude. The point cloud rotates tens of thousands of points per
frame: there the drehMatrix is set up once and applied to
every point. An anchor, a nose tip, the end of a braid are single
points: there drehUm(p, pose) is more convenient.
Both are the same computation:
drehUm builds the matrix and applies it. There were
once two computation paths side by side here – the matrix multiplied
out, drehUm as a chain of three rotations – which had to keep the
same order without anything forcing them to. Had they drifted apart,
the contour of a turned head (from the cloud) would sit next to its
face (from the anchors) – and nobody would have known which of the
two was the wrong one.
Then the projection. It is a weak perspective: a
scalar s = F/(F−z) with the focal length
F = BRENNWEITE = 4.2. Near things get larger, far things
smaller, and the foreshortening of a turned face follows from that by
itself – nobody computes it.
Two things sit inside those three lines. First the
homothety: because F scales with size and
all radii do too, size cancels out of s – the cache on the
next page builds on that. Second the sign of
y: the projection flips it, because the picture axis
points downwards. Miss that and you draw pouts (see below).
The field
A Feld is an anchor on the head's surface, resolved for
exactly one view. macheFeld(u, v, sicht) samples the
surface at three closely spaced places and reads out of them
everything a feature needs at that location:
| Field | Meaning |
|---|---|
x y z | the picture position of the anchor |
ex ey | the local axes in the picture, unit vectors |
fx fy | the squash across and along, measured against refU/refV |
nrm | the normal as a screen direction |
nz | its z component: positive means facing the viewer |
to(lx, ly, lift) | local coordinate → picture point |
refU and refV come from
macheSicht: the lengths of the same step size on the
unrotated surface at the bridge of the nose. This makes
fx = 1 mean not "one unit measure" but "as wide as
frontally at the bridge of the nose" – a scale every anchor shares.
platz() in the chapter Zones
computes back with exactly these two factors.
Feature functions draw exclusively in −1…1 and know
nothing about the rotation. An eye is a circle at
(0, 0) with radius 0.7. That it is narrow,
tilted and occluded when the head is turned is handled by the field.
The third parameter of to is the lift: it
raises a point off the skin along the normal. That is how an ear
stands beside the head instead of on it, and how a headphone sits
above the temple.
At the free anchor you can see the essential thing: push it outwards
and the transverse arm of the axis cross gets shorter and shorter
until it disappears – fx goes towards zero. Not because
the feature gets smaller, but because the surface it lies on turns
away.
An ear is drawn not beside the head but into the cheek, and the far side suddenly passes every visibility test.
Cause: the normal was flipped towards the viewer so that it would
"always point forward". Then nz is never negative, the
test nz < 0 never fires, and the lift
carries features on the back side inwards with a mirrored direction.
Fix: the normal points outwards from the head's
centre. macheFeld checks this with a dot
product against the position vector and only flips when the cross
product pointed inwards. After that nz < 0 honestly
means: turned away from the camera.
Every smile is a pout. And so convincingly so that one searches the curve for the error for a long time instead of the sign.
Cause: the y axis of an anchor points up – it
follows latitude towards the crown. The y axis of the canvas points
down. Write a mouth curve in canvas direction and send it unchanged
through to, and you get it back mirrored.
Fix: every shape function in gesicht.ts computes in
canvas direction and flips the sign in its own
to – f.to(lx * r, -ly * r). Not in the
field: there it would be wrong for the normal and the axes.
When the anchor tips over the edge
Under strong rotation an eye sitting far out travels past the silhouette. Geometrically that is correct: the surface there is turned away, so the eye does not belong in the picture. As a drawing it is a mistake – a face with only one eye does not read as perspective but as a mishap.
sichtbarerAnker pulls the longitude towards the centre in
steps of 10 % until the field faces the camera again. The eye then
sits narrow at the edge – and that is exactly right. Note the
threshold: min = 0.42, not 0. An anchor that is only just
still facing forward has an fx near zero, and an eye of
zero width is no solution either.
The other direction of the same question is asked by
sichtGrenzen: where does a round line – a
hairline, a hat brim – meet the silhouette? The search runs from the
most strongly facing spot outwards to both sides, and the edge itself
is found by bisection. Analytically that would work only for a smooth
ellipsoid; kegel, scheitelHoch,
beule and kastig turn the ellipsoid into
something for which there is no closed form any more.
How much rotation a style tolerates
The DNA stores the full desired rotation, the style limits it.
begrenzePose does that deterministically, without
randomness – two calls with the same pose give the same pose.
What is interesting is what the file's header comment records about
the scribble style of the first press. That one
worked with a window instead of an upper bound:
yawMin ruled out the full frontal view. The reason was
not taste but geometry – both are degenerate special cases. Frontally
the normal points at the camera everywhere, the head has no volume
left. In profile the local x axis collapses, fx goes
towards zero, and only the lift still carries. Rule both
out and you do not have to catch either.
The clear style of this edition does not need the window: its heads
look at the pointer, and a head that is looking at somebody simply
stands almost frontal. Its limits are
{ yaw: 0.4, pitch: 0.26, roll: 0.14 } – about 23 degrees
to the side, no yawMin.
The pose limits are the only place where a style contradicts the geometry. Everything else – foreshortening, occlusion, order – follows. The limits only say which part of the possible this style can draw without it showing.
Glasses as the counter-example
Almost everything in the face is a field feature. Glasses are not – and that is the only place where the style deliberately refuses the anchor.
A lens drawn through f.to hugs the curvature and
foreshortens like skin. It then looks painted on, because a
spectacle lens is a rigid disc: it does not lie on the face, it stands
in front of it. That is why the glasses here are a real figure in head
space – two circles in the frontal plane, a bridge, two temples to the
ear – rotated and projected like the skull itself. A turned head then
foreshortens a lens only by the cosine of the rotation.
At small rotations the difference is barely visible and from about half a radian on it is impossible to miss: on the left the far lens wraps around the cheek and becomes a line, on the right it stays a disc that gets narrower in perspective. This was learned in the first press, and it is the kind of detail nobody can name but everybody sees.