使用代码制作3D视频(长视频和短视频)
每一帧都是时间的纯函数,编写为HTML/CSS/three.js代码。同一个页面既是实时编辑器(即时播放,无需保存)又是渲染源(Chromium逐帧捕获,ffmpeg合并)。用户是导演,你负责执行。在用户看过实时版本之前,不进行任何渲染。
工作流程包含七个步骤。请按顺序执行,不要跳过步骤5。
| # | 步骤 | 产出 |
|---|---|---|
| 1 | 设置 | 从 template/ 复制项目文件夹,启动服务器 |
| 2 | 脚本 | script.md,然后制定镜头计划:一句描述 = 一个镜头 |
| 3 | 语音 | voiceover.mp3 + 单词时间戳,或如果没有语音则使用预估时长 |
| 4 | 风格 | 用数字在 STYLE.md 中定义视觉风格,并构建到3D世界中 |
| 5 | 修复 | 在实时编辑器中审查,应用笔记,重复的错误添加到 references/lessons.md |
| 6 | 渲染 | 分块渲染,然后与音频合并 |
| 7 | 上传 | 标题选项、描述、章节标记、缩略图提示词 |
1. 设置
- 工具位于
~/.youtube-video-editor/venv(Playwright Chromium, imageio-ffmpeg, pillow, faster-whisper)。 如果缺少,运行~/.claude/skills/youtube-video-editor/scripts/setup.sh。始终使用~/.youtube-video-editor/venv/bin/python -I调用。用户的Mac上没有系统级的ffmpeg或node。 - 将
template/复制到用户可查看的文件夹内的<项目名>/中(他们通常在~/Downloads中工作)。 16:9 视频 →index.html+scenes.js。竖屏短视频 →short.html+scenes-short.js(舞台尺寸 1080×1920)。 - 启动服务:在项目文件夹中使用
python3 -m http.server 8766 --bind 127.0.0.1(使用nohup … &运行), 然后在浏览器面板中打开http://localhost:8766/index.html。在该面板中file://页面会显示为静态快照, 而preview_start配合 launch.json 在这台Mac上无法启动服务器,因此请从Bash启动。
2. 脚本
- 脚本决定了视频内容。用用户的语气编写:短句口语化,用词朴素,不要用教科书口吻。 他们的观众是非技术初学者(Low Code Studio)。语速约为每分钟150词;一个短视频需要60–110词。
- 然后在制作动画之前规划镜头:一个句子 = 一个场景,并为每个场景写一行描述摄像机看到的内容。 修改计划是免费的,但修改动画则不是。
- 受到其他视频启发?保留结构和要点,但替换所有示例、视觉画面和台词。绝不要重复使用他人的视频素材、措辞、缩略图或角色。
3. 语音
- 用户自己录音(这是品牌的一部分)并在之后加上头像。因此第一次迭代没有音频:每个场景的长度根据其
vo字数估算(engine.js中的WPS/PAD)。 - 当
voiceover.mp3存在时:转换为16kHz单声道wav,运行~/.claude/skills/youtube-video-editor/scripts/transcribe.py获取单词时间,将每个场景的dur设置为该场景第一个单词到下一个场景第一个单词之间的间隔,并调整data-in值使其与关键词对齐。 场景内的时间线会自动随场景长度拉伸(创作节奏 → 实际节奏),因此大多数场景只需要设置dur。渲染时传递--audio voiceover.mp3。 - 在这台i3 Mac上,当机器繁忙时whisper
small模型可能会运行缓慢。使用base、beam_size=1、不带单词时间戳的速度大约快5倍,适合阅读参考;仅在最终同步时使用small+ 单词时间戳。
4. 风格:用数字,不用形容词
"电影感"意味着什么并不明确;每个场景的猜测都会不同。在项目中编写 STYLE.md,写入可以检查的规则,然后一次性构建到3D世界中。从 references/style-rules.md(室内3D外观:摄像机角度、镜头、光照值、调色板、运动、字幕)开始。如果用户提供参考图片,读取它们并将你所看到的转化为同样的数字。这是他们自己的参考,而不是复制其他创作者的视频。
2D动态图形消耗的token少得多,而且比3D更稳定。在需要考虑空间、尺度或旅程的镜头中使用3D(钩子、"世界"镜头),在提示、列表、对比和章节卡片中使用2D卡片。一个好的混合比例大约是每三到四个场景中有一个3D镜头。短视频可以全程使用3D,因为它只有几个镜头。
5. 修复:实时编辑器(绝不跳过)
- 为用户打开编辑器并停在那里。他们明确要求这样做:在任何完整渲染之前在实时编辑器中进行审查。侧面板显示每个场景的画外音,允许用户调整其长度(±0.5秒)并写下笔记;复制我的审查笔记将所有内容放到剪贴板上,让用户能粘贴回给你。
- 在交给用户之前,先检查自己的工作:
render.py --sheet sheet.jpg会生成每个场景的一帧画面。查看它(将一张长图分割成3个裁剪部分以便保持可读性),修复错误,重复操作。典型的第一轮错误在references/lessons.md中。在构建之前先阅读该文件。 - 当相同的笔记出现在多个场景中时,修复视频并向
references/lessons.md添加一行,以便下一个视频开始时就已经解决了这个问题。这是该技能的全部意义所在。
6. 渲染
- 仅在用户批准实时版本之后进行。
render.py --chunks out/chunks --chunk-len 60然后render.py --join out/chunks final.mp4 [--audio voiceover.mp3]。对于短视频添加--size 1080x1920并使用short.html?renderURL。如果一个场景出错 → 删除对应的chunk_NNN.mp4并重新运行;只会重新渲染这一个块。 - 在i3上的速度(实测):全屏3D场景在1080p下每帧约1秒,因为无头WebGL在软件模式下运行。一个10分钟的视频,30fps就是18000帧,所以需要计划3–5小时,并在开始前告知用户。如果用户接受,可以使用
--fps 24,使用nohup在后台运行,保持Mac唤醒(request_keep_awake),并且最多同时运行两个块处理进程(使用--start/--end将范围分割到不同的文件夹)。 - 无头Chromium需要
--use-angle=swiftshader --enable-unsafe-swiftshader才能使用WebGL(已在 render.py 中实现)。 - 无需重新构建即可重新剪辑:场景是一个数组,因此重新排序或删除场景就是编辑列表。如果开场太慢,将最强的镜头移到索引0。
7. 上传
你了解脚本内容,因此编写5个标题选项(推荐一个)、描述、基于场景开始时间的章节标记和标签。遵循 ~/.claude/skills/youtube-video-editor/references/youtube-metadata.md。缩略图是图像模型能提供帮助的地方:渲染一个强有力的静态帧(render.py --stills),编写提示词,然后让用户生成或合成它。缩略图上的每个声明都必须与视频内容一致。
场景格式(engine.js)
SCENES.push({
id: 'belt', chapter: '2 · 脚本', title: '盒子在传送带上', // 在编辑器中显示
vo: `你的盒子在传送带上移动。`, // 口语行;设置长度,直到存在音频
dur: 4, // 可选:固定长度(秒)
cls: 'z', // 可选:plain | grad | paper | z | z1(缩放)
html: `<div class="three abs" style="inset:0"></div>
<div class="h1" data-in="0.4" data-a="pop">你好</div>`,
tick: (t, root) => K.draw3d(root.querySelector('.three'), 1920, 1080, t, 'belt'),
});
- 声明式:
data-in/data-out(秒),data-a(up down left right pop fade drop zoom),data-d,data-type(打字机效果),data-count="0,2400000",data-w="0,80"(宽度百分比),data-draw(SVG描边)。 tick(t)用于任何自定义内容。它必须仅依赖于t:不要使用Date.now(),不要使用Math.random()(使用E.rnd(i)),不要使用CSS动画或过渡。否则渲染结果将与预览不匹配。- 工具包:
K.chat()Claude风格的提示框,K.track()步骤跟踪器,K.chapter()全出血章节卡片,K.wave()+K.waveTick(),K.draw3d(holder, w, h, t, mode)用于一个3D视图,K.snap(canvas, t, mode)用于 在同一场景中显示多个摄像机视图(一个共享的WebGL渲染器;不要为每个场景创建渲染器)。 - 3D世界在kit.js中通过
K.world()构建:构建一次,update(t, mode)移动物体并放置摄像机。 对于新影片,在那里替换物体和摄像机模式;保持模式不变。examples/workflow-video/是一个完整的10分钟视频(45个场景),使用此技能制作。在编写新场景之前,先阅读其中的模式。
两种外观:玩具立体模型和故事世界
kit.js中的K.world()是简单的平底着色立体模型(构建快速,适合解说和背景)。template/worlds.js是更丰富的故事外观,用户在评价立体模型"不够真实"后更喜欢这种:平滑的玩具般角色(person()带有可动画化的头、手臂、腿,加上walk()),渐变天空穹顶,ACES电影色调映射,一个低角度暖色太阳加上暖色实用灯光,用于灯具的附加辉光精灵,蒸汽和光束,画布纹理窗户,用于深度的雾效。其中包含三个完整的6秒世界场景(黎明茶摊、夜间送货骑士、灯塔看守人);examples/story-samples/展示了如何将它们放在页面上。在kit.js之后加载worlds.js,并使用K.drawStory(holder, w, h, t, name)进行绘制。- 代码生成的3D无法达到照片级真实感。如果用户要求"超现实",明确告知他们并提供选择:AI视频(消耗积分,且视频不能再声称"纯代码")或故事外观。然后在构建整部影片之前提供2-3个简短的样本片段供选择。
- 为每个故事镜头赋予一个角色,该角色在镜头内有一个开始和结束的动作(走进来、停下、挥手;提起灯笼)、一个名称标签和一个移动光源。这正是观众会觉得"这是一个故事"的关键。
短视频检查清单
1080×1920,20–45秒,一个想法。在2秒内看到并听到钩子,以动态画面开始。字幕为2–5个词,放在深色药丸形状背景上(明亮3D天空上的文字难以阅读),位于安全区域内:顶部220像素和底部420像素内不要放置重要内容。使用更紧凑的摄像机模式(belt,follow,hero),因为宽镜头在竖屏中会变得很小。以高潮加上一句请求关注的话语结束;剪掉任何片尾。
捆绑文件
template/:index.html, short.html, engine.js, kit.js, worlds.js, style.css, render.py, 起始场景, lib/three.min.jsexamples/story-samples/:三个角色样本片段页面examples/workflow-video/:完整的"我如何用Claude Code制作视频"构建文件(scenes-1/2/3.js + index.html); 将这些复制到模板文件旁边即可运行(其0:21处的缩略图墙未被捆绑)references/style-rules.md:以数字形式呈现的3D室内外观规则references/lessons.md:已经付出代价的错误(在构建前阅读,在每次审查后追加)