# 工作流：一个始终是它自己的角色

> 「我想拍一部关于这个特定角色的电影。」角色只建**一次**，做成一个模块；只审批**一次**，以设定图（model sheet）的形式；从那以后，每一张静帧、每个镜头、每部影片、每种画风都只是给它**摆姿势**。再也没有任何东西重新画它。现成的示例：Mira（`engine/src/canvas-core/characters/mira/`）。

**为什么。** `storybook.ts` 和 `print.ts` 都画 Bit，用的是两套各自独立的控制点。改了其中一个，另一个就成了另一个机器人。逐个镜头画的角色会逐个镜头走样：眼镜变大变小，外套换了一种黄，手长出第六根手指。解决办法是结构上的，而不是靠勤勉：一个唯一的事实来源，加上一道在某个镜头偏离它时会报出来的闸门。

## 模块（每个角色一个文件，外加它的姿势和配色）

| 导出项 | 是什么 |
|---|---|
| 比例标准 + 规格（canon + spec） | 比例标准表里的一**行**（`human/canon.ts`）、身高、身体和头部的规格 |
| 骨骼绑定（rig） | 比例标准所隐含的 3D 骨架，带关节限位（`human/skeleton.ts`） |
| 手绘视图（drawn views） | 体积描述不了的部件（刘海、翘起的一撮头发），在 3 个视角里各画成 2D 形状，各视角之间有对应点，按视角角度混合（`view25d.ts`，Rivers et al. 2010） |
| 颜色**角色**（palette roles） | `coat`、`skin`、`hair` …… 每个都有一个色相族；每种手笔用自己的媒介去**映射**这些颜色角色（`palettes.ts`） |
| 辨识特征（distinguishing features） | 眼镜、刘海、翘发、雀斑、雨衣、挎包、雨靴：部件 id，加上它们必须出现的视角 |
| `pose(p)` | 校验一个姿势；任何关节超出范围就抛出异常 |
| `toScene(pose, camera)` | 把角色转成一个 Scene：按绘制顺序排列的部件，每个部件是若干多边形的并集再加上笔触，用颜色角色的颜色着色 |

各种手笔用自己的笔触渲染任意一个 Scene：`storybook`、`marker`、`riso`、`woodcut`、`scratch`、`sumi`、`pencil`（在 `character/render/` 里），每一种都建立在它所属样张的工具包上。对同一个姿势，每种手笔拿到的几何完全相同（闸门会断言这一点）。

**一致性与画风无关。** 模块是一份规格（特征、比例、颜色角色、标志性形状），而不是一种外观。线条类手笔直接画 Scene；**网格**类手笔（`render/grid.ts` → `pixel.ts`、`brick.ts`）用它们自己的单位重新表达它：每个格子取最上层的部件，把小圆环（眼镜）夸大到一个最小半径，丢掉精灵图容纳不下的东西（眼睑、眉毛、镜腿）。然后在格子上而不是在 Scene 上检查身份（`character/identity.ts`），再加上与更细采样之间的剪影 IoU。

**绘制层（插画师优先，进行中）。** 骨架、比例标准、限位和平衡是看不见的检查器和姿势来源。看得见的体块是设计出来的扫掠体（`character/design.ts`）：一条穿过各关节的动势曲线，一张沿曲线编写的截面表，垂直于曲线取出的剪影，在关节处切开并留出重叠，这样就看不到接缝。Mira 的外套、袖子、腿和靴子，以及面包师（`characters/theo/baker.ts`）都用了它；Scene 带有 `gesture` 曲线，供起稿那一遍使用。还没做完的：每个视角手工放置的轮廓控制点、所有衣物从接触点出发的垂坠、作为绘制层上轮廓断点的标志点。较早的 Theo 人物模型仍然照字面渲染体块。

## 步骤

1. **从描述出发**：用一段话写出身份（年龄、比例标准、一个孩子凭记忆会画出来的 5-8 个特征）。选比例标准表里的一行；如果造型需要更大的头，就**声明**一个风格化的行，身体保持写实。**从用户的图片出发**：列出特征，把颜色取样归入颜色角色，对照以头长为单位的网格读出比例，先画这张图本身的那个视角；图里没有出现的视角是编出来的，并在设定图上标明是编的。
2. **搭建模块**：规格、颜色角色、特征、做成膨胀身体团块的衣物、头发用的 2.5D 部件、一个姿势库，由落脚位置、求解出的腿、平衡和够取目标构建出来。
3. **设定图**（`miraSheet.ts`）：在头长网格上画转面图（正面、3/4、侧面、背面），六种表情、六个姿势、六只手、一张身高比例对照。以原尺寸对照 [references/anatomy.md](../anatomy.md) 的「看一眼清单」去看它。✋ **唯一的一次审批。** 它锁定版本。
4. **验证各种手笔**：把同一个姿势、同一个镜头放进你要用的每一种手笔里（`miraTriptych.ts`，以及九种手笔的 `miraEveryHand.ts`，每一格都是 `miraTile.ts` 出的原尺寸图）。如果她看起来像不同的女孩，修的是手笔的颜色角色映射，不是角色本身。
5. **使用它**：镜头导入模块并给它摆姿势。影片逐帧计算一个姿势（走路是一个个脚步，不是关键帧），再交给 `toScene`。
6. **修改它**：对模块的任何改动都要升版本号，并重新渲染设定图。

## 闸门检查的不变量（`node tools/character-check.mjs`）

- 比例标准的比值和各段长度；每个姿势里骨骼长度不变；左右对称；
- 姿势库里的每个姿势都在关节限位之内；站姿保持平衡；脚踩在地面上，而不是穿过地面；
- 五根手指长度顺序正确，指关节成弧，捏的动作里拇指碰到食指；
- 每个辨识特征都出现在它该出现的每一个视角里（十个视角，两侧都查）；
- 每种手笔里每个颜色角色都在它的色相族之内（riso 按它实际印出的叠印色来测量）；
- Scene 几何是确定性的；影片里踩实的脚不打滑。

- 全手笔对照图里的每一种手笔：特征清单（线条类手笔查 Scene 特征，像素和积木查格子特征）、颜色角色在色相族之内、像素和积木的网格 IoU > 0.8。

每一项都有一个必须失败的反例孪生。闸门看不到的是：她**看起来**像不像她自己。在线条类手笔之间，剪影重叠在构造上就完全相同，所以在那里它什么也证明不了；设定图和全手笔对照图，由人去看，才是身份测试。
