Opus 5.5 当导演,Sonnet 5.5 当画师:纯 JS 画出一部蜡笔水彩风《奥德赛》短片

文章来源声明: 原文作者:wieimmer; 来源站点:掘金; 原文链接:https://juejin.cn/post/7690573326390116379; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

强模型定方向、快模型做执行的可复制范本,适合想用多模型协作产出长内容或程序化视觉的团队参考,成本与稳定性兼顾。

![cover](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6f73ef5ee02f4f98a7af6fef2330e6bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgd2llaW1tZXI=:q75.awebp?rk3s=f64ab15b&x-expires=1791278045&x-signature=9GhIBVA6NDIuXLRJxhu%2BjQAoEZk%3D)

这次想试一件事:让大模型分工拍一部短片。

需求就一句话:

生成一个关于奥德修斯的故事的短片,风格使用手绘蜡笔水彩风格,先由你确定创意构思,再使用 sonnet 完成落地。

分工很明确:

  • Claude Opus 5.5(主代理):导演 + 美术指导。负责创意、写设计规格、拆任务、审稿、给修改意见。它自己一行业务代码都不写。
  • Claude Sonnet 5.5(子代理):执行团队。拿着规格写代码、出图、跑测试,遇到规格外的审美问题不许自己拍板,只能在报告里提出来。

最后的成片:2 分 40 秒、16 个镜头、1080p,所有画面都是 JavaScript 程序画出来的,旁白、配乐也是代码合成的。

先看成品(每个镜头抽一帧):

16 个镜头


一、任务:一个关于"回家"的故事

Opus 拿到需求后,先定了创意,而不是马上动手:

  • 主题不是打怪,是回家。 十年战争、十年漂泊,独眼巨人、塞壬、斯库拉都只是回家路上的阻碍。情绪落点放在老狗认主和橄榄树床相认。
  • 贯穿符号:一条红围巾。 佩涅洛佩在织布机上织的红线,就是奥德修斯脖子上的红围巾。全片只有这条围巾是高饱和的红色,大海再大,那一点红就是"家"的牵挂。结尾围巾挂在自家窗台上随风飘——回到了。
  • 首尾呼应。 从织布机开始,到织布机窗口结束。
  • 16 个镜头:片头 → 织机 → 木马 → 独眼巨人 → 波塞冬 → 风袋 → 喀耳刻 → 塞壬 → 斯库拉与卡律布狄斯 → 孤舟夜海 → 卡吕普索 → 老狗阿尔戈斯 → 拉弓穿斧 → 橄榄树床 → 黎明 → 片尾。

这些全部写进了两份文件:brief.md(创意说明)和 shots.json(每个镜头的旁白、画面描述、运镜参数)。后面所有 Sonnet 都以这两份文件为唯一依据。

二、画面路线:用 JS 把它们画出来

这次的画面全部用 JavaScript(Node + @napi-rs/canvas)程序化绘制。

难度不低:程序画"蜡笔水彩",质感全靠算法;人物、怪物、船、海全要用代码构造。但这也恰好是验证"Opus 设计 + Sonnet 执行"这套分工最好的场景——设计含量极高,执行量也极大。

三、分工方式:Opus 写规格,Sonnet 照规格干活

Opus 先写了一份约 400 行的绘制规格 js_art_spec.md,这是整个项目的核心产物。它不是"画一个好看的海"这种模糊描述,而是可以直接执行的设计:

规格章节写清楚了什么
色板30 个颜色的 hex 与用途;`scarfRed` 只允许红围巾使用
纸纸齿、斑驳两张噪声场怎么生成,最终怎么乘到画面上
水彩叠层变形算法的每一步参数:递归深度、方差衰减、层数、alpha、边缘加深、颗粒沉积
蜡笔线条怎么重采样、抖动、压力、两端收尖、每个采样点戳几个小矩形
分层合成每个"平面"= 遮盖层 + 水彩层(multiply) + 蜡笔层(纸齿蒙版),从后往前合成
人物骨架以身高 h 为单位的比例(头半径 0.09h……),关节角约定,14 种姿态的动作含义
分镜构图15 个场景每个元素的坐标(2560×1440 基准),并保证关键人物在运镜起止窗口内
审查流程先出半尺寸预览 → 自查技术问题 → 全尺寸出图;**审美问题只报告,不自改**

然后按依赖关系拆成 4 个阶段,能并行的都并行:

阶段 1  笔刷核心库(1 个 Sonnet)
<span>          │  Opus 审测试页 → 调参意见 → 通过
阶段 2  人物骨架与角色(1 个 Sonnet) ∥ 环境道具库(1 个 Sonnet) ∥ 渲染管线改造(1 个 Sonnet)
          │  Opus 审角色页/道具页 → 各 1 轮修改 → 通过
阶段 3  场景 S00–S04 ∥ S05–S09 ∥ S10–S14(3 个 Sonnet 并行)
          │  Opus 逐张审 15 个场景 → 8 个场景返修 1 轮 → 通过
阶段 4  最终渲染与验证(1 个 Sonnet)
</span>

几个让并行不打架的约定:

  • 文件所有权:每个 Sonnet 只能写自己名下的文件,场景代理禁止修改 lib/,私有辅助函数放各自的 _helpers_A/B/C.js。
  • 接口先行:阶段 1 结束时让 Sonnet 在报告里写清 API 签名,阶段 2/3 的任务说明直接引用。
  • 遇到规格没覆盖的情况停下来问,而不是自己发挥。

四、笔刷:程序怎么画出"蜡笔 + 水彩"

笔刷测试页

水彩用的是 Tyler Hobbs 的叠层变形法:一个多边形反复做"边中点沿法线随机偏移",派生出几十个略有不同的形状,每层只填 4.5% 的不透明度,叠起来就有了水彩那种不规则边缘和内部浓淡:

<span>// 每条边取中点,沿法线偏移 gauss(0, variance*v*边长);递归 depth 次</span>
<span>function</span> <span>deform</span>(<span>verts, depth, variance, rng</span>) {
  <span>let</span> cur = verts;
  <span>for</span> (<span>let</span> d = <span>0</span>; d < depth; d++) {
    <span>const</span> out = [];
    <span>for</span> (<span>let</span> i = <span>0</span>; i < cur.<span>length</span>; i++) {
      <span>const</span> a = cur[i], b = cur[(i + <span>1</span>) % cur.<span>length</span>];
      out.<span>push</span>(a);
      <span>const</span> dx = b.<span>x</span> - a.<span>x</span>, dy = b.<span>y</span> - a.<span>y</span>, len = <span>Math</span>.<span>hypot</span>(dx, dy);
      <span>const</span> vv = (a.<span>v</span> + b.<span>v</span>) * <span>0.5</span>;
      <span>const</span> off = rng.<span>gauss</span>(<span>0</span>, variance * vv * len);
      out.<span>push</span>({ <span>x</span>: (a.<span>x</span> + b.<span>x</span>) / <span>2</span> - dy / len * off, <span>y</span>: (a.<span>y</span> + b.<span>y</span>) / <span>2</span> + dx / len * off, <span>v</span>: vv });
    }
    <span>for</span> (<span>const</span> q <span>of</span> out) q.<span>v</span> *= <span>0.65</span>;   <span>// 越细的层级偏移越小</span>
    cur = out;
  }
  <span>return</span> cur;
}

蜡笔是在路径的每个采样点上"戳"一把 1~2.4px 的小矩形,位置在法线方向三角分布(中间密两边稀),再叠加低频抖动、压力变化、两端收尖。

真正让它"像蜡笔"的是纸齿蒙版:蜡笔层合成前逐像素乘上 smoothstep(0.22, 0.62, tooth),蜡只挂在纸的凸起上,笔触自然断续、有颗粒:

<span>// 纸齿蒙版:a *= smoothstep(0.22,0.62,tooth)(蜡笔只挂在纸的凸起上)</span>
<span>for</span> (<span>let</span> i = <span>0</span>, p = <span>3</span>, N = <span>Wp</span> * <span>Hp</span>; i < N; i++, p += <span>4</span>) {
  <span>const</span> a = d[p];
  <span>if</span> (a) d[p] = (a * mask[i] + <span>127</span>) / <span>255</span>;
}

第一版测试页 Opus 并不满意,给 Sonnet 的意见是这样的(节选):

wash 默认值:spread 0.9→0.55(边缘要像湿水彩边,不是撕纸);granulate 0.35→0.15;tone 0.45→0.22,且浓淡噪声尺度至少是形状尺寸的 2 倍(色块内部是宽缓的明暗过渡,不是云团斑块);edge 0.6→1.1(边缘有可见的深色水线,这是水彩最重要的特征)。

gradientWash 重写(横带不能再被看出来):……

意见里是具体的参数和判断标准,不是"再好看一点"。 这是 Sonnet 能一次改到位的关键。

五、人物与道具

人物是一个简单的 2D 骨架:以身高 h 为单位定比例(绘本式大头,头半径 0.09h),关节角驱动四肢,服装、头发、胡子、红围巾都是参数化部件。14 种姿态覆盖了所有镜头的动作:拉弓、被绑在桅杆上、背影坐在石头上、单膝跪地伸手、相拥……

人物姿态表

道具库包括希腊船(高翘船头、船眼、方帆、一排桨,船身拆成前后两部分,好让水手"站在船里")、双刃斧、木马、城墙、织布机、羊、猪、老狗、橄榄树……

道具页

这一轮 Opus 的意见同样很具体,比如:

斧头改成古希腊双刃斧(labrys),不能再像雨伞或镐头;皮袋改成软皮囊,不能再像陶罐;红围巾现在像一串锁链,改成连续的布带:先整条铺 scarfRed 水彩,再叠垂直于布带方向的密排线,两侧用同色相更深的红描边……

六、场景:15 张画,三个 Sonnet 并行

场景阶段三个 Sonnet 同时开工,每人 5 个场景。每个代理都会用脚本把运镜的起止窗口框画到预览图上,确认主角全程在镜头里。

独眼巨人是返修最多的一张。第一版鼻子画成了猪鼻子、四肢像散落的香肠、洞壁像蜂巢。Opus 的返修意见把每个部位的形状、朝向、连接关系都写了出来,第二版就对了:

S03 独眼巨人

塞壬:奥德修斯被绑在桅杆上,围巾飘向歌声;水手耳朵里塞着蜡:

S07 塞壬

老狗阿尔戈斯:二十年后,只有它认出了扮成乞丐的主人。第一版里红围巾掉在地上像一条红腿、狗又小又和草堆一个颜色,返修后变成了现在这样:

S11 老狗

橄榄树床:床是围着一棵活着的橄榄树做的,这是只有他们两人知道的秘密:

S13 橄榄树床

七、让画"动"起来:线条抖动

每个镜头用不同的蜡笔随机种子渲染 3 个变体,只有蜡笔线条不同,水彩与构图完全一致(这要求代码里构图随机数和线条随机数严格分成两条流,混用就是 bug)。视频里每 3 帧切换一次变体,就得到了手绘动画那种"线条在呼吸"的效果:

线条抖动

再配合缓慢的推拉摇(Ken Burns,亚像素插值避免抖动)、1 秒叠化、纸纹和暗角,整片就有了绘本翻页的感觉。

声音部分:旁白用 edge-tts(zh-CN-YunxiNeural),配乐是 numpy 合成的 Karplus-Strong 拨弦里拉琴(D 多利亚调式)+ 海浪噪声,旁白出现时音乐自动压低,最终响度归一到 -16 LUFS。字幕用 PIL 逐帧绘制(本机 ffmpeg 没有 drawtext)。

S14 黎明

八、数据

项数值
成片2 分 40 秒,1920×1080,24fps,16 个镜头
绘图核心库(笔刷/骨架/道具/环境)约 3700 行 JS
15 个场景约 3450 行 JS
音频与合成管线约 650 行 Python
插画15 个场景 × 3 个抖动变体 = 45 张 2560×1440
Sonnet 子代理10 个任务(最多 3 个同时跑)
Opus 审查笔刷 1 轮、角色 1 轮、道具 1 轮、场景 8 张返修 1 轮
最终渲染约 4 分钟

九、几点体会

1. 规格写到"可执行",Sonnet 的产出就很稳。 坐标、比例、参数、验收标准都写死之后,Sonnet 几乎不需要猜,返工基本只来自"规格本身没想到"的地方。

2. 审美决策必须收回到 Opus。 明确要求 Sonnet"审美问题只报告不自改"之后,它的报告里会列出"需要主代理判断的点"(比如"S12 求婚者被斧子挡得比较碎,是否接受"),由 Opus 统一拍板,风格才不会在 15 个场景里各自漂移。

3. 审查意见要像规格一样具体。 "猪鼻子改成蒜头鼻:圆润的水滴形,比脸略深的肤色,鼻尖有阴影,鼻孔只是底边两道短弧"这样的意见,Sonnet 一轮就能改对;"再好看一点"只会换来随机的变化。

4. 并行靠边界清晰。 文件所有权、接口先行、随机数分流、"不许改 lib"——这些工程约定比模型能力更决定并行能不能顺利进行。

5. 自查要可验证,而不是"我看过了"。 每个任务都写明了用脚本自证的方法:把运镜起止窗口框画到预览图上、用 numpy 对比三个抖动变体只有蜡笔区域不同、把四条边各裁 60px 拼起来检查有没有漏出纸白。Sonnet 交上来的是证据,Opus 的审查就能集中在审美上。


如果你也在用多模型协作,不妨试试这种"强模型定方向和标准、快模型大量执行"的分工:贵的模型花在判断上,便宜的模型花在体力上。