17 规划器总论
✅ 已验证(软化边缘那一条是留下的账) · 代码 code/strokes.py · 调用 trace_prepare.py 第 2 步
起点不是黑画布
规划器要回答「在哪里、用什么颜色、多大的笔、画一笔」。它的起点是色阶铺完之后的画面(第 6 章的 preview_cel.png),不是空白。它只补差额:哪里和源图差得够多,才在哪里起笔。于是笔数花在看得见的地方,而不是花在重画一遍已经对了的色块上。
模拟画布
strokes.Sim 在 1x 上模拟每一笔落下去的样子:圆头软笔核(hardness=0.42),沿折线按 0.35·R 的间隔盖章,alpha = kernel × opacity × mask,alpha-over 合成。它只需要够准到能判断「还差多少」,不是渲染器——真正的落笔是 Krita 的 brush engine 干的,形状不会完全一样。
叠加符合 1−(1−a)^N(probe_glaze.py 实测 b) Airbrush Soft opacity 0.35 叠 1/2/3/5/8 笔:83/139/177/219/244,理论 89/147/184/225/246),所以模拟和 Krita 的差别主要在笔的形状,不在叠色。
坐标一律 1x
规划器工作在 1x(1024×1536),产出的 pts 和 R(半径)都是 1x 单位;job_trace.py 统一乘 SCALE=8。R=1 的笔在 8x 上是直径 16px,与参考画 1:1 下的发丝宽度同量级。
活动范围是色阶格,不是部件
trace_prepare.py 把 (部件, 色阶) 编成联合标签 cells = pid × 100 + tier(cells.png)。每种笔在一个格子里规划:生长时不出格(_grow() 的 region 约束),落笔时 alpha 乘格子的 mask。
这和第 12 章的「每色阶一组」是同一件事的两端:Krita 那边 clip 到色阶形状,规划器这边不跨格取色、不跨格落笔。只做一边不够——只在 Krita 裁,笔的颜色仍然是跨格取的(见下一节)。
两个软化边缘的坑
跨区域取色。 规划器给笔上色用的是「模糊过的参考图」。不加 mask 的高斯模糊会把区域外的颜色渗进来:一支落在阴影区里的笔,颜色混进了亮部;每条色阶边两侧都被各自往对方拉,边就软了。修法是归一化卷积 _blur(a, s, mask):gaussian(a·m) / gaussian(m),颜色只在 mask 内取。
无差别铺一层。 给部件的大色阶区也做了一遍平涂(wash,opacity 0.30),结果归一后的梯度 p99 从 48.4 掉到 32.3、色数 2303 → 1206。平涂是不看误差地铺满,把色阶边一起盖软。平涂只用在背景那种纯渐变上。
留下的账
纯矢量阶段归一到 2508px 的梯度 p99 是 78.9,比源图(41.6)还锐;落完笔是 36.9。笔画在软化边。上面两个坑修掉之后还剩这么多,主要是 soft(airbrush,约 3.9 万笔)和 detail(贪心细节笔,约 3.9 万笔)叠在色阶边上。参考画落在 59.4–74.6,正好在两者之间——我们软过头了。
下一步的方向在第 20 章:不同风格给这两种笔不同的预算。
笔画的顺序
每个部件、每个色阶,按这个顺序规划(后画的盖前画的):
soft(大) → soft(小) → hatch → detail(粗→细) → strand
背景单独:wash × 3 个角度 → soft。线稿最后,落在 06_Line,不 clip。