
ff-tracking 是一部通过代码生成的短片,虚拟摄像机跟随 AI 代理在终端中输入文字。类似计算机视觉的叠加层会锁定单个字形,而透视、景深、色彩处理、泛光、扫描线撕裂、颗粒和声音均由代码生成。
项目使用 Rust、fframes、Skia Metal、SkSL、FFmpeg 和 Python 构建。它通过两个协同处理阶段渲染一段六秒视频,视觉效果和音频都由共享的跟踪数据驱动。
项目生成的内容
影片展示了一系列终端消息,例如 thinking…、reading 14 files、tool_call: render_frame() 和 ✓ 0 problems。跟踪器会跟随最新字符,并显示它们的实际像素坐标、目标框、标签以及各框之间的连线。
- 真实坐标:跟踪器标签使用输出帧中每个框的实际位置。
- 移动焦点:景深目标会随着代理输入内容跟随被跟踪的框移动。
- 动态跟踪:连线按阅读顺序连接各个框,并有一个点沿每条连线移动。
- 锁定效果:最新单词会变红两帧,产生扫描线撕裂,并触发空间声像移动的提示音。
- 多种视觉风格:十二个镜头使用 Paper、Thermal、Mono、Phosphor、Amber、Navy 和 Neon 调色板。
- 最终收束:十四个框合并为一个框,随后变成
Done胶囊,再渐隐至黑屏。
了解双阶段处理流程
项目将终端布局与摄像机和镜头效果分离。这样既能让每个字形的位置保持确定,又能让第二阶段将渲染后的终端作为着色器输入处理。
- HUD 数据:
hudcrate 定义场景、跟踪框、摄像机运动和投影数据。 - 平面阶段:
flat二进制程序将终端以 SVG 渲染为 3840×2160,并为每个字形放置一个<text>元素,从而精确获知其位置。 - 镜头阶段:
lens二进制程序将平面视频作为iChannel0采样,并应用虚拟摄像机和 SkSL 效果。 - 音频阶段:
tools/sfx.py读取out/tracks.json,合成与输入、剪切、锁定和最终落点相匹配的声音事件。 - 编码:FFmpeg 将处理后的视频和音频合成为
out/tracking.mp4。
镜头着色器负责透视、景深、调色板映射、色差、泛光、LED 网格、撕裂、颗粒以及摄像机空间中的跟踪器叠加层。景深效果使用 36 个黄金角采样点,高光则采用类似散景的加权方式。
安装前置依赖
渲染需要搭载 Apple silicon 的 Mac,因为项目使用 Metal 上的 Skia。构建前请安装以下工具:
- 支持 2024 edition 的 Rust
- FFmpeg
- Python 3
- Python 软件包
numpy和scipy - 如果要重新构建 README 媒体,还需要 Pillow 和
img2webp
克隆仓库并进入其目录:
git clone https://github.com/mrsarac/ff-tracking
cd ff-tracking
渲染默认影片
主脚本会构建 Rust 工作区、导出跟踪数据、渲染平面阶段、合成声音并渲染镜头阶段:
tools/render.sh
完成后的视频会写入 out/tracking.mp4。在 M3 Pro 上,初次构建完成后,渲染大约需要 40 秒。
生成的数据包括 out/tracks.json,其中描述跟踪事件并驱动默认声音设计。可选的摄像机二进制程序还会写入 out/camera.json,其中包含逐帧缩放、倾斜、旋转、跟踪框位置和屏幕中心信息。
检查帧、音频和着色器阶段
由于每个阶段都是一个 fframes CLI,你可以使用常用命令检查输出:
target/release/lens strip all -n 12
target/release/lens frame 81 -o frames
target/release/lens preview
target/release/lens audio analyze
使用 strip 创建联系表,使用 frame 导出完整尺寸的帧,使用 preview 打开带声音的实时预览。音频分析命令会报告响度和真实峰值信息。
如需在特定阶段后停止着色器,请设置 LENS_STAGE。支持的阶段编号为 0 到 3:
LENS_STAGE=1 target/release/lens frame 81
这对于调试从源图像到最终镜头处理效果的过渡很有帮助。
自定义场景
每个镜头都是 hud/src/lib.rs 中 SCENE_LIST 的一个条目。场景定义其类型、文本、调色板、摄像机、起始位置和字体大小:

SceneDef {
kind: Kind::Typing,
text: "thinking…",
palette: Palette::Thermal,
cam: cam(0.38, -0.22, 0.04, 2.5, 2.8, 50.0, 0.0, 1.2),
hero: (560.0, 540.0),
size: 104.0,
},
更改文本、选择其他调色板或调整摄像机数值,然后再次渲染:
tools/render.sh
跟踪器、焦点目标、标签和锁定提示音会自动采用更新后的场景数据。可用的调色板名称包括 Paper、Thermal、Mono、Phosphor、Amber、Navy 和 Neon。
编辑场景后运行 HUD 测试:
cargo test -p hud
这些测试会检查每一行是否适合显示在屏幕上,以及摄像机是否将所有目标保持在画面内。
使用默认或可选配乐
默认配乐由 tools/sfx.py 生成。它结合了屏幕嗡鸣声、剪切时的比特压缩故障音、按键点击声,以及向被跟踪方框方向声像定位的五声音阶锁定提示音。它还会为收缩过程添加上升音,并在 Done 胶囊出现时加入钟声和低频冲击声。响度使用 FFmpeg 的 ebur128 测量,并设为 -14 LUFS。
仓库还包含一段可选的、速度为 120 BPM 的更暗沉 Slab 配乐。它遵循相同的跟踪和摄像机数据,因此锁定提示音、按键声、铺底音、上升音、故障音、低音滤波和低音撕裂都会响应动画。
使用已提交的配乐渲染文件:
tools/score.sh
若要重新渲染配乐,请提供一个使用 zig build -Doptimize=ReleaseFast 构建的 Slab 检出目录:
SLAB=/path/to/slab tools/score.sh
配乐脚本会写入 score/ff_tracking.slab,渲染后的音频位于 score/score.wav。配乐流程会为了混响尾音延长到画面之外,然后将结果裁剪为六秒,并与视频一起淡出。
请注意,score/score.py 导入了 slabkit,该组件随 Slab 一同以 GPL-3.0 许可证发布。只有重新渲染配乐时才需要 Slab;根据 README,已提交的配乐文件按仓库的 MIT 许可证提供。
重新生成文档媒体
仓库包含一个用于重新生成 README 图片的脚本。安装图像依赖后,使用 Python 运行它:
python3 -I tools/readme_media.py
项目结构
hud/:场景、跟踪方框、摄像机数据和投影逻辑。flat/:分辨率为 3840×2160 的第一阶段终端渲染。lens/:第二阶段渲染,包括lens/shaders/lens.sksl和摄像机空间跟踪器。tools/:主渲染脚本、声音生成器、README 媒体生成器和 Slab 流程。score/:可选的 Slab 配乐、项目文件和渲染后的 WAV 文件。docs/:设计规范、提示词、决策记录和 fframes 贡献。
高级实现说明
两阶段设计十分重要,因为着色器目前无法从同一帧采样 SVG 子树。因此,完整的平面帧会进行同步,并作为图像输入传递给着色器:
.image("iChannel0", &frame.get_synced_video_frame(..)?.into_image())
README 还记录了几个实际限制:
flat是 SkSL 中的保留字,因此应避免将其用作辅助函数名称。- 在
Video层级,应返回根<svg>,而不是Svgr::empty();空根元素可能导致延迟出现渲染错误。 - CPU 投影函数
hud::project会让 HUD 跟踪叠加层与着色器摄像机保持对齐。 - 两个视觉阶段和声音生成器都会读取相同的 HUD 跟踪信息,从而避免焦点和音频事件偏离字形。
结语
ff-tracking 展示了如何完全通过结构化场景数据、SVG、Rust、SkSL、GPU 渲染和合成音频生成一段简短的电影化序列。从 tools/render.sh 开始,检查中间帧,然后编辑 SCENE_LIST,创建自己的终端消息、调色板、摄像机运动和跟踪构图。当你需要一段遵循相同时间安排和摄像机运动的配乐时,可使用可选的 Slab 流程。
