Communitygithub.com

Ameetms/Claude-技能-方块卡通故事视频

根据脚本或旁白,生成 9:16 的第一人称故事叙述短视频,采用粗犷的 3D 方块卡通风格(圆角方块角色、涂画面部、墨水轮廓、始终移动的摄像机、逐字彩色字幕),并完全通过代码渲染。

Claude-技能-方块卡通故事视频 是什么?

该技能能够将脚本或旁白转化为垂直格式(720x1280 或 1080x1920,30 fps)的第一人称故事视频,时长 30-130 秒。视频风格受到“擦鞋匠”式视觉风格的启发,特色在于由圆角方块构成的 3D 角色、平滑持续的动画以及生动的彩色字幕。整个制作过程完全基于代码实现:使用 Three.js 在无头 Chromium 中渲染 3D 场景,通过一个纯时间函数的 `frame(t)` 驱动每一帧,并将截图通过管道传输给 ffmpeg 进行最终合成。 该技能的核心优势在于其严格遵循的视觉风格指南(包括详细的角色设计蓝图和环境调色板),以及确保动画流畅性的精确规则(如使用缓动函数、摄像机永不静止、角色空闲动画、唇形同步和手势动画)。它支持 Claude Code、Cursor 等通过运行命令和生成代码工作的 AI 编程助手平台。工作流程涵盖脚本创作、旁白处理、强制对齐生成单词时间戳、镜头列表规划、角色和场景构建、分帧渲染、音频合成(包括背景嗡鸣声和音量标准化处理)以及最终的质量控制检查(电影接触页、运动差异分析和响度测量)。所有生成的 PNG、JS 和 MP4 文件均通过 `SendUserFile` 发送给用户。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Ameetms/Claude-skill-block-toon-story-video/tree/HEAD

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

方块卡通故事视频("擦鞋匠"风格)

当用户想要一个垂直(720x1280 或 1080x1920,30 fps)的第一人称故事视频,时长 30-130 秒,旁白讲述,带有粗犷的圆角方块 3D 角色、平滑连续的动画和醒目的彩色字幕时使用此技能。整个过程基于代码:使用 Three.js 在无头 Chromium 中渲染,通过一个纯时间函数 frame(t) 驱动,将截图通过管道传输给 ffmpeg。

用户的优先考虑是角色设计的忠实度和动画的平滑度。不要跳过第 5 步中的角色检查。

1. 参考视频的外观(实测数据)

  • 720x1280,30 fps,约 122 秒,AAC 立体声,响度约 -14 LUFS,人声下方有安静的持续背景音。
  • 约 47 个硬切(单帧切换,无交叉淡入淡出),平均镜头约 2.6 秒(范围 1.2-5 秒)。在一个镜头内,摄像机始终在移动:缓动推近、拉远、吊臂/高角度漂移、低角度推近、向上急摇至天花板或窗户。少于 1% 的帧是静态的。
  • 字幕位于画面下方三分之一处(顶部约占画面高度的 64-68%),每组 2-3 个小写单词,粗体圆润几何无衬线字体(Poppins ExtraBold),白色基线,关键词着色(金色 #f5c32c、青色 #48c9f0、珊瑚色 #ff5b4d、粉色 #ff7ac0、绿色 #2ecc8f、橙色 #ffa21f),带有粗黑色描边,每组出现时有轻微的缩放弹出效果。
  • 环境:暖色调奶油色/棕褐色内饰(墙壁 #dcbf96、地板 #c7aa7d、石头 #d9c39b、护墙板 #b99a6f、天空 #c7dcec),搭配点缀色栗色 #570d0e、海军蓝 #1c2a49、金色 #e0990d、蓝绿色 #1f8f8c、红色 #a3160f。
  • 光照:柔和、明亮、略带暖色;平滑渐变着色和柔和的投影。无硬边赛璐珞色块。

2. 角色设计圣经(严格遵守)

轮廓和比例

  • 每个身体部位都是一个圆角方块。倒角半径约为该部位最小尺寸的 15-18%。切勿使用球体或胶囊体作为身体部位。
  • 头部:一个略宽于高的长方体(0.44 x 0.41 x 0.39 单位),在特写镜头中约占角色身高的 1/4 到 1/3,因此在全景镜头中角色约为 4 头高。短脖子方块。躯干 0.46 x 0.56 x 0.26。手臂 0.27 + 0.26(上臂 + 前臂)。腿 0.30 + 0.27。总高度约 1.7 单位。
  • 手是粗短的连指手套方块:一个手掌方块,带有四根短的两段式手指和一根分开的拇指。它们可以从张开到握拳和张开。手绝对不能看起来像蜘蛛腿。
  • 每个手腕处有白色袖口方块。黑色鞋子,一个扁平的圆头方块。
  • 没有可见的关节断裂。 在两个圆角方块在弯曲关节(臀部、膝盖、肩膀、肘部、手腕、颈部/躯干、臀部/躯干)处相遇时,倒角会形成一个间隙或缺口,看起来像断裂处。用与服装颜色相同的布料层隐藏每个关节:一个以关节枢轴为中心的小球体(ball()),大小约为肢体半宽 + ~0.002,并沿肢体方向拉伸(sc [1,1.2-1.4,1]);一个连接两个臀部的骨盆方块;一个覆盖颈部/躯干接缝的领子方块;一个袖口下方的肤色腕部小方块。不要使球体过大(肢体半宽 + 0.015 导致了肿块状的膝盖和肘部)。用弯曲姿势的应力测试进行验证(手臂举起、中步行走、腿抬起),放大查看。

面部(平面绘制在头部前方,每帧在画布纹理上重新绘制)

  • 大眼睛:白色巩膜、绿色或棕色虹膜、黑色瞳孔、白色高光。粗黑色上眼线。眼睛可以环顾四周(lookX/lookY)、眨眼和眯眼。
  • 粗的锥形眉毛可以倾斜(生气/担忧)和抬高。黑色圆框眼镜比眼睛大得多(圆环半径约为眼睛半径的 2 倍)。眼镜必须是一个连在一起的框架:两个圆环由一条弯曲的鼻梁弧线连接,每个圆环都有一条太阳穴线延伸至面部边缘。两个浮动的圆环看起来像坏了一样(用户在第一个角色表上就该问题提出了反馈)。
  • 无鼻子。嘴巴是一条细小的线/曲线,或者说话时是一个充满颜色的张开嘴,带有粉色舌头。微笑值范围 -1..1。
  • 可选柔和的腮红。

头发、帽子、配饰(堆叠的粗犷方块)

  • 主角:棕色蓬巴杜发型,带一个尖顶,侧边部分和一个后脑勺方块。其他样式:短发,或长发(侧边刘海 + 后脑勺方块)。黑色刘海是一个薄的倾斜方块。
  • 警察帽:海军蓝帽冠方块 + 黑色帽檐 + 金色徽章。平顶帽:棕色方块 + 短帽檐 + 浅色纽扣。
  • 年长男性:白色方块胡子 + 白色鬓角。
  • 服装是色块化的且简洁:西装(栗色夹克、白衬衫、深色领带、翻领、金色挂绳 V 形 + 带有蓝色条纹的白色 ID 卡)、制服(海军蓝、金色徽章、黑色腰带 + 金色搭扣)、围裙(白衬衫、带有纽扣点的棕色围裙),以及用来做配角的灰色/绿色/棕褐色/海军蓝/蓝绿色变体。
  • 肤色:桃色 #efaa71、棕褐色 #d89a62、棕色 #8a5230、深色 #5a3320、浅色 #f4c49a。头发:棕色 #59321a、黑色 #14110f。

渲染

  • 每个部件上都有细的深色墨水轮廓(#1c0f06),通过反向外壳着色器实现,宽度在屏幕空间内恒定。宽度是相机距离的一个分数:w = px / ((H/2) / tan(fov/2)),其中 px 是期望的屏幕像素线条粗细(对于 720x1280 的视频镜头约为 0.003,约 1.5-2 像素)。对于远距离多角色表(1600x900),使用 K.setOutlineWidth(.0009),对于半身像瓦片约用 .0016;默认值对于远距离相机来说太粗了。面部和极小的装饰部件跳过轮廓。
  • Lambert 材质,半球光(约 2.9)+ 一个柔和阴影太阳(约 1.5)。软 PCF 阴影。
  • 背景人群使用 lod: 1(无手指,更少的头发方块)。永远不要为每个群演设计独特的设计;改变肤色、头发、衣服颜色和发型。

多样性规则: 每个有名角色必须能仅凭轮廓识别(帽子、头发方块形状、眼镜、胡子、肤色、服装颜色)。对所有角色使用相同的比例。

参考表(当用户请求角色设计或环境时,在制作任何视频之前执行这些操作)

在 scene.html 旁边(使用相同的工具包、相同的 render.cjs)构建一个 designsheet.html 和一个 envsheet.html,运行 node render.cjs designsheet.html out 0,1,2 --w 1600 --h 900。frame(t) 使用 Math.round(t) 选择一个模式;标签是一个 DOM 覆盖层(Poppins 字体),通过使用 new THREE.Vector3(x,y,z).project(camera) 投影 3D 点来定位;页面标题和色板也是 DOM 元素。

  • Cast 文件 cast.js:一个 DEF 表(名称、角色、4 个色板 [皮肤、头发、服装、点缀色])、一个 BUILD 表,其中包含每个角色的一个构建器(makeChar + 一些额外的 box() 配件)、makeOne(name)、makeCast() 和 pose(name, C),该函数每帧重置面部和姿势,并应用一个干净的角色表姿势。通过不同的 scale/headScale、发型、帽子、眼镜、胡子方块、肤色和一种点缀色,使每个角色通过轮廓独一无二。
  • 表 0 队列:所有角色排成一行(相机大约在 [0,1.02,15],fov 16.5),每个下方显示名称 + 角色 + 色板方块。表 1 主角转向动作,偏航角为 0、.75、PI/2、PI(正面、3/4 侧面、侧面、背面)。表 2 表情:六个半身像(中性、快乐、生气、惊讶、担忧、得意),绘制为剪刀瓦片,因为在一个 3D 场景中堆叠六个角色会使行位于地板下并重叠。瓦片模式:renderer.autoClear=false; renderer.setScissorTest(true); 然后对于每个瓦片 setViewport(x, H-y-h, w, h); setScissor(...); renderer.clear(); camera.aspect = w/h; camera.updateProjectionMatrix(); renderer.render(scene, camera); 并在 frame 结束时重置视口、剪刀测试和 autoClear。
  • 眉毛符号约定:face.brow = [height, tilt, arch],tilt > 0 表示内端向下(生气),tilt < 0 表示内端向上(担忧)。在 drawFace 中那就是 ang = -s * f.brow[1] * .55。(第一个版本中这是颠倒的,导致一个生气的警官看起来很难过。)
  • 环境表 0:四个 760x318 的剪刀瓦片(英雄从入口处广角、带有拱形窗户的端墙、关键道具角落、一个高角度吊臂且 hall.ceiling.visible = false),每个都带有标题;将角色布置在场景中以显示比例,并让他们面向相机或彼此(检查偏航:偏航 0 意味着面向 +z 朝向位于 +z 的相机;背对相机的角色是舞台布置错误)。表 1:每个道具单独放在一个遥远的道具舞台(x = 400 + i*20)上,这样它永远不会出现在大厅镜头中,瓦片大小 486x300。表 2:俯视图,up = [-1,0,0](长轴水平方向),相机大约在 [-1,120,-13] 向下看,窄 fov(约 9.3),并从世界点投影标签。始终在标题中包含一个调色板条(10 个色板)。
  • 大厅的天花板和横梁位于一个组中,暴露为 H.ceiling(阴影投射关闭,这样太阳仍然照亮大厅);在吊臂和俯视镜头中隐藏它。标志文字的大小必须与其表面相匹配:一个 1.9 x 0.5 的标志需要 800 56px Poppins,而不是 78px(会被裁剪)。
  • Three.js 陷阱:Object3D.position 和 .rotation 是只读属性;设置组件(m.rotation.x = ...、m.position.set(...)),永远不要使用 Object.assign(mesh, { rotation: ... }),这会抛出错误并停止 __ready。
  • 使用 SendUserFile 交付 PNG 和 .js 文件;在工具包发生任何更改后,重新渲染并重新发送每个表,以便用户永远不会持有过时的表。

3. 动画平滑规则

  1. 动画是时间的纯函数:frame(t) 根据 t 设置所有内容。帧之间不保持状态,因此任何帧都可以按任何顺序或并行渲染。
  2. 为每个移动使用 kf([[t0,v0],[t1,v1]], t, easeFn)。对推进和到达使用 easeOut,对吊臂/急摇移动使用 easeIO,对小动作使用 ease。绝不线性移动摄像机或肢体。
  3. 摄像机从不静止。每个镜头都有缓动位置 + 注视目标 + fov + 几度的滚动,加上始终开启的手持漂移 [sin(.5t)*.03, sin(.37t)*.02]。
  4. 仅在镜头边界处进行硬切,在某个词或节拍上。无交叉淡入淡出。平均镜头时长 2-3 秒。
  5. 每个角色身上持续存在空闲动画层:呼吸起伏、摇摆、头部转动、每约 3-4 秒眨眼(0.13 秒缓动)、微小的眼睛转动。K.idle() 处理此操作。
  6. 嘴巴张开程度来自真实旁白响度(lipsync.py 以 30 fps 写入 env.json)。绝不使用循环的嘴部动画。
  7. 手势:预期动作(在指/拍前轻微后拉)、过冲(easeBack)、停顿、稳定。头部引导身体(先使用 headYaw 再使用 twist)。
  8. 行走循环:双腿交替,每只手臂与同侧腿反向摆动,膝盖仅向后弯曲(Math.max(0, knee)),每个循环起伏 2 次,躯干轻微扭转和倾斜。相位以循环为单位:正常行走约使用 1 循环/秒。
  9. 角色绝不能与家具重叠:使用显式的 x/z 放置它们并检查静止帧。

4. 工作流程

  1. 脚本:第一人称视角,前 3 秒内设置钩子,简短有力的句子,升级冲突,剧情转折,最后一句点题。语速约 2.5 词/秒。
  2. 旁白:用户通常提供 ElevenLabs 文件。如果没有,请询问或合成一个占位音轨。保存为 voice.wav。
  3. 单词时间戳:获取单词级别的时间戳(针对脚本文本进行强制对齐,例如使用 pocketsphinx 或 whisper 的单词时间戳)。保存 words.json 为 [{"w":"my","s":0.20,"e":0.41}, ...],使用脚本的拼写和标点(字幕使用尾部标点来分隔组)。
  4. 唇形同步包络线:python3 lipsync.py voice.wav env.json。
  5. 镜头列表:每行一个镜头:开始/结束时间、观众看到的画面、摄像机移动、谁在表演。目标是每约 2.6 秒一个切换。变化取景:面部特写、双人半身镜头、低角度推近、人群上方的高角度吊臂、急摇、过肩镜头、道具特写(手机、徽章)。将镜头写入 scene.html 中的 SHOTS。
  6. 从 blockkit.js / props.js 构建角色 + 场景(参见代码)。重用 room()、chair()、bench()、crowd();使用上方调色板中的 box() 构建新道具。
  7. 首先检查静止帧(成本低):node render.cjs scene.html outstills 1,4,7,...;检查每个有名角色的特写、一个行走动作、一个手势、一个广角镜头。在渲染视频前修复。
  8. 渲染视频(在 2 核上以 720x1280 分辨率,每帧约 1-1.7 秒,因此 40 秒视频约需 15-25 分钟;并行并在后台运行分块):
export NODE_PATH=$(npm root -g)      # playwright 在沙盒中是全局安装的;否则:npm i playwright && npx playwright install chromium
S=30; for i in 0 1; do (node render.cjs scene.html part$i.mp4 --from $((i*S)) --to $(((i+1)*S)) > r$i.log 2>&1 &); done   # 2 个 30 秒分块;等待两者完成
printf "file 'part0.mp4'\nfile 'part1.mp4'\n" > list.txt && ffmpeg -y -f concat -safe 0 -i list.txt -c copy silent.mp4
  1. 音频 + 混流(人声下方安静的持续背景音,约 -14 LUFS):
D=130   # 大于等于视频长度
ffmpeg -y -f lavfi -i "sine=f=55:d=$D" -f lavfi -i "sine=f=82.4:d=$D" -f lavfi -i "sine=f=110:d=$D" -filter_complex "[0][1][2]amix=inputs=3:normalize=0,lowpass=f=500,tremolo=f=0.12:d=0.35,aformat=channel_layouts=stereo,afade=t=in:d=1[bed]" -map "[bed]" -ar 44100 bed.wav
ffmpeg -y -i silent.mp4 -i voice.wav -i bed.wav -filter_complex "[2:a]volume=0.12[b];[1:a][b]amix=inputs=2:duration=first:normalize=0,loudnorm=I=-14:TP=-1.5:LRA=7,aformat=channel_layouts=stereo[a]" -map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k final.mp4
  1. 交付前的质量控制(QC): (a) 电影接触页,每约 2.6 秒一帧,检查角色/字幕; (b) 运动检查,对缩小后的灰度流计算平均帧差异,期望 0 个静态帧:
python3 - <<'E'
import subprocess, numpy as np
W,H=90,160
a=np.frombuffer(subprocess.run(['ffmpeg','-v','error','-i','final.mp4','-vf',f'scale={W}:{H},format=gray','-f','rawvideo','-'],capture_output=True).stdout,dtype=np.uint8).reshape(-1,H,W).astype(np.float32)
d=np.abs(np.diff(a,axis=0)).mean(axis=(1,2)); print(len(a),'frames; static:',(d<.02).sum(),'; spikes(cuts):',(d>14).sum())
E

(c) 响度:ffmpeg -i final.mp4 -af ebur128 -f null -(约 -14 LUFS), (d) 字幕与语音匹配。 使用 SendUserFile 交付(mp4 + 一个封面静止帧)。

5. 设置

mkdir -p work && cd work && npm init -y >/dev/null && npm i [email protected] @fontsource/poppins
# 在下面这个文件夹中创建这些文件:blockkit.js props.js captions.js render.cjs lipsync.py scene.html
# (或者从这个仓库的 kit/ 文件夹复制准备好的文件,该文件夹也包含标准配置...)

…

[为翻译而截断]

相关技能