Hair
🧪 in progress · rebuilt once on the layer tree’s foundation, the first part to be done that way · the method name and where a person writes it: appendix G · code: appendix B
How an artist paints it
Hair is painted by the lock, not by the strand. Once the local colour is laid: one highlight band per lock (along its direction, tapered at one end), a dark seam between neighbouring locks (also a long thin shape along the direction), and a hard-edged tier of shadow for the large dark areas (back hair against the neck, the fringe pressed onto the forehead). At the tips, a few individual strands, each drawn in one tapered pass with a fine brush. Across the crown, an “angel ring”, a highlight band running across several locks. How hard the edges are depends on the style: in cel shading the highlights and seams are hard-edged shapes, and in painterly work they are softened with an airbrush – but even soft, the direction is lock by lock rather than random. Lines: a lock’s outline is a line, and inside a lock there is hardly any.
What it looks like in the source
Hair from a diffusion model is soft: a strand is a faint highlight streak rather than a line, and both the highlight bands and the seams between locks are airbrush transitions rather than hard cel edges – the bands are not the point, the direction is. In luminance, a lock’s boundary is either a ridge (brighter than either side) or a seam (darker), and top-hat and bottom-hat filters find them.
Our method 🧪
One cylinder per lock, two gradients. That is the whole of this page; everything else is detail.
Two entirely different things happen on a cylinder: what colour it is (yellow at the root, pink at the tip), and how the light falls off from its core to either side. An artist changing the hair colour changes the first and adjusting the light changes the second. So they are written as two layers:
| layer | what it is | blend |
|---|---|---|
Length | a gradient along the lock’s direction, in absolute colour, sampled from the lit colour (the 85th percentile) | normal |
Round | a gradient across the lock, in multiply, equal to 1 at the core and falling off toward the edges | multiply |
The along-axis gradient takes the lit colour rather than the median because multiply cannot brighten: with the median, the across-axis factor would exceed 1, and folding that brightening into the absolute colour instead runs into the 255 ceiling (blonde hair already has R = 250). Taking the lit colour keeps multiply naturally at or below 1, and neither gradient has to strain.
Why not “quantize it into a few bands”. A lock used to have one local colour plus a few hard-edged tiers of shadow. The source’s hair carries a hue shift from yellow at the root to pink at the tip, and quantizing flattened it entirely – only six tenths of the source’s clearly pink pixels survived into the output. With gradients it is back to nine tenths. Bands are also more expensive: each one adds another outline, and cutting the same lock into 4 bands measured three times the numbers with a slightly worse residual.
The other five steps:
- The flow field: harmonic diffusion in double angles. The lineart’s tangents are hard seeds and the structure tensor is a soft one, and it solves for a direction field defined everywhere. Direction is a double angle (theta and theta+pi are the same direction), so the diffusion runs on 2theta.
- Splitting into locks: bright ridges (a top hat with a large normalized blur, and no closing – closing sticks neighbouring ridges into one) are the seeds, an anisotropic Dijkstra along the flow partitions the area (cheap along the direction, expensive across it), and neighbouring locks with similar directions are merged.
grainsets how fine the split is. - The 天使の輪 (angel ring): draw the whole bright band first and then cut it along the seams – the jaggedness is cut into it rather than drawn. A blur mask softens its edge.
- The seams: take the boundary per pair of neighbouring locks (the whole seam network is closed, with no degree-one endpoints, so skeletonizing it as a whole walks no path), dilate with 8-connectivity (with 4-connectivity a 45-degree boundary leaves only isolated points), and walk the longest geodesic with two BFS passes (Zhang-Suen thinning destroys an even-width band at 45 degrees). Width and darkness scale with the contrast.
- Stray hairs and 毛先 (tips): evenly spaced streamlines, dark where it is bright and bright where it is dark, with a hard cap on how many – too many and it is ボサボサ (frizzy).
No strokes exist merely to push the error down. Each group used to carry another three or four hundred error-driven soft strokes. They were deleted: two gradients per lock measure close to the floor any smooth model can reach (blurring the source itself to that degree does barely better), and what is left over is the fine texture of the strands – work for a few dozen strokes, not a few thousand dots.
Hair must not be blurred – 「ぼかしてしまうと毛束の集合体としての質感が壊れる」 (blur it and the texture of a mass of bundles is destroyed). So softening appears in exactly two places: the edge of the angel ring, and the tips. Between one lock and the next it has to be hard, and that hardness is precisely why hair reads as bundles.
Layers
Hair_X/
Shape vector silhouette + flat colour (the backstop: there is always a gap
between one lock's ring and the next)
Locks/ one group per lock
Lock_07/
Length gradient along the axis (absolute colour)
Round gradient across it (multiply, clipped to Length)
X_Halo/ the 天使の輪 (angel ring) + a blur mask
X_Seam the seams between locks (a hard brush, thin)
X_Strand stray hairs / 毛先 (tips)
X_Line the lines
The group has six or seven direct children, so the criterion of at most 10 nodes per group still passes; the dozens of locks are folded into a single Locks folder that an artist can expand into one entry per lock and recolour a single lock in.
How a person writes it in, and its knobs
"Hair_Front": dict(method="hair", bands=40, sigma=4.0, delta=6.0, elong=3.0, sharp=True),
| Knobs | default | meaning |
|---|---|---|
grain | mid | how fine the split is: coarse / mid / fine. Finer is not better – each extra lock adds another outline and two more layers for only a small improvement in the residual |
ns_len ns_rnd | 5 / 9 | how many stops the along-axis and across-axis gradients get. The across-axis one needs a few more to catch the darkness at a lock’s edge (too wide a window smooths the seam away) |
seams seam_max | true / 60 | the seam lines, and their cap |
tips tip_frac tip_mix tip_under | false | tips: one soft stroke over the last fraction of the arc length at the tip, its colour mixed toward the layer below (毛先の透け, see-through tips) |
strands | 0 | how many stray hairs (a hard cap) |
halo halo_q halo_blur | true / 88 / 1.2 | the angel ring: what top percentage of brightness to take, and how soft its edge is |
Front and back hair are the same hair – a person marks them as sharing one flat colour – but may be painted differently (the back has larger dark areas, the front finer locks).
Criteria
The per-group rulers (ch. 7): interior sharpness p90 not exceeding the source’s, the reading at the hard edges, and 1x PSNR; the description count of both hair groups; and hue – the fraction of clearly pink pixels has to come close to the source’s. That last one is a criterion this rebuild forced into existence: with all five criteria passing, four tenths of the pink could still be lost. For a person to look at, one review image per layer (appendix F): the locks, for whether they line up with real ones; the seams, for whether they are thin lines rather than fat worms; and the strands, for the ratio of bright to dark stray hairs (mostly bright).
Where it breaks
- Locks cut too coarsely: one lock spans several real ones, and averaging across it turns the whole thing one colour – the yellow core the source has disappears. This is the main remaining fault.
- Taking the median for the along-axis gradient: the multiply factor exceeds 1, folding it into the absolute colour hits the 255 ceiling, and the residual worsens by three tenths (measured 12.0 -> 15.3).
- Clipping lock groups to each other: clipping inherits the alpha accumulated below inside the same group, so with locks clipped to one another the first lock has nothing beneath it and disappears outright.
- Seams drawn as fat worms: setting the radius from the width of the boundary band drives every one of them to the cap, and at 4x a 22 px brown worm crawls across the fringe. Set it from the contrast.
- Choosing a strand’s colour by coin flip: half of them come out dark. Too many strands: ボサボサ (frizzy), and p90 rises above the source’s.
- A lock’s edge becoming a line: the raster lineart draws a faint highlight band as a line, and the finished painting has more lines inside the hair than the source does (ch. 2).
What has to be decided
How to cut the locks is still the biggest open question: bright ridges are not all found where fine locks are packed together, so should a person draw the dividing lines? And then: how many stray hairs and where they start; whether a lock’s outline belongs to the line layer or to this page; and whether front and back hair need two different grain settings.
On origin
Ch. 18, “The layered route”: both hair groups’ stroke counts, PSNR and interior sharpness; and the table of hair variants in “Three part experiments”.