メインコンテンツへ移動
AIチュートリアル

ff-trackingでコード生成AIターミナル映像を作成する

ff-trackingがターミナルで入力するAIエージェントを描画し、各グリフを追跡しながら、動くカメラとSkSLレンズエフェクトを適用し、同期したサウンドを生成する仕組みを学びます。このチュートリアルでは、セットアップ、2パスのパイプライン、レンダリングコマンド、カスタマイズ、診断、オプションのSlabサウンドトラックについて説明します。

ff-trackingでコード生成AIターミナル映像を作成する

ff-trackingは、仮想カメラがターミナルに入力するAIエージェントを追跡する、コード生成の短編映像です。コンピュータービジョン風のオーバーレイが個々のグリフを捉え、パースペクティブ、被写界深度、カラー処理、ブルーム、走査線の乱れ、グレイン、サウンドをコードで生成します。

このプロジェクトは、Rust、fframes、Skia Metal、SkSL、FFmpeg、Pythonで構築されています。2つの連携したパスで6秒の動画をレンダリングし、映像効果と音声の両方を共有トラッキングデータで制御します。

トラッキングオーバーレイを備えたコード生成のターミナル映像

プロジェクトが生成するもの

映像には、thinking…、reading 14 files、tool_call: render_frame()、✓ 0 problemsなどのターミナルメッセージが順に表示されます。トラッカーは最新の文字を追跡し、実際のピクセル座標、ターゲット枠、ラベル、ボックス間のリンクを表示します。

  • 実座標: トラッカーのラベルには、出力フレーム内の各ボックスの実際の位置が使われます。
  • 移動するフォーカス: 被写界深度のターゲットは、エージェントの入力に合わせて追跡中のボックスを追います。
  • アニメーションするトラッキング: 読み取り順にボックスをリンクし、各リンク上をドットが移動します。
  • ロック効果: 最新の単語が2フレームの間赤くなり、走査線の乱れが加わり、空間的にパンするビープ音を発生させます。
  • 複数のルック: 12ショットで、Paper、Thermal、Mono、Phosphor、Amber、Navy、Neonのパレットを使用します。
  • 最後の収束: 14個のボックスが1つのボックスにまとまり、黒へのフェード前にDoneピルへ変化します。

2パスパイプラインを理解する

このプロジェクトでは、ターミナルのレイアウトをカメラおよびレンズ効果から分離しています。これにより、すべてのグリフ位置を決定論的に保ちながら、2つ目のパスでレンダリング済みターミナルをシェーダー入力として扱えます。

  1. HUDデータ: hudクレートが、シーン、トラッカーボックス、カメラの動き、投影データを定義します。
  2. フラットパス: flatバイナリがターミナルを3840×2160のSVGとしてレンダリングします。位置を正確に把握できるよう、グリフごとに1つの<text>要素を配置します。
  3. レンズパス: lensバイナリがフラット動画をiChannel0としてサンプリングし、仮想カメラとSkSL効果を適用します。
  4. 音声パス: tools/sfx.pyがout/tracks.jsonを読み込み、入力、カット、ロック、最後の着地に対応するサウンドイベントを合成します。
  5. エンコード: FFmpegが処理済みの動画と音声を結合し、out/tracking.mp4を生成します。

レンズシェーダーは、パースペクティブ、被写界深度、パレットマッピング、色収差、ブルーム、LEDグリッド、乱れ、グレイン、カメラスペースのトラッカーオーバーレイを処理します。被写界深度効果にはゴールデンアングルによる36サンプルを使用し、ハイライトにはボケ風の重み付けを適用します。

レンズシェーダー効果の各段階

前提条件をインストールする

このプロジェクトではSkiaをMetal上で使用するため、レンダリングにはAppleシリコン搭載のMacが必要です。ビルド前に以下のツールをインストールしてください。

  • 2024エディションに対応した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として利用できます。スコアのパイプラインはリバーブの余韻のために映像の長さを超えて処理し、その後結果を6秒にカットして映像に合わせてフェードします。

score/score.pyは、GPL-3.0でSlabとともに配布されているslabkitをインポートする点に注意してください。スコアを再レンダリングする場合にのみSlabが必要です。コミット済みのスコアファイルは、READMEに従い、リポジトリのMITライセンスで提供されています。

ドキュメント用メディアを再構築する

リポジトリには、README画像を再生成するスクリプトが含まれています。画像関連の依存関係をインストールした後、Pythonで実行します。

python3 -I tools/readme_media.py

プロジェクト構成

  • hud/:シーン、トラッカーボックス、カメラデータ、投影ロジック。
  • flat/:3840×2160での最初のターミナルレンダリングパス。
  • lens/:lens/shaders/lens.skslとカメラスペーストラッカーを含む2番目のパス。
  • tools/:メインのレンダリングスクリプト、サウンド生成ツール、READMEメディア生成ツール、Slabパイプライン。
  • score/:オプションのSlabスコア、プロジェクト、レンダリング済みWAVファイル。
  • docs/:設計仕様、プロンプト、決定事項、fframesへのコントリビューション。

高度な実装メモ

シェーダーは現在、同じフレームからSVGサブツリーをサンプリングできないため、2パス設計が重要になります。その代わり、完成したフラットフレームを同期し、画像入力としてシェーダーに渡します。

.image("iChannel0", &frame.get_synced_video_frame(..)?.into_image())

READMEには、実装上の実用的な制約もいくつか記載されています。

  • flatはSkSLで予約されているため、ヘルパー名として使わないでください。
  • Videoレベルでは、Svgr::empty()ではなくルートの<svg>を返してください。空のルートは、遅い段階でのレンダリングエラーとして現れることがあります。
  • CPU側の投影関数hud::projectは、トラッカーオーバーレイをシェーダーのカメラと一致させます。
  • 両方のビジュアルパスとサウンド生成ツールは同じHUDトラッキング情報を読み取るため、フォーカスや音声イベントがグリフからずれるのを防ぎます。

まとめ

ff-trackingは、構造化されたシーンデータ、SVG、Rust、SkSL、GPUレンダリング、合成音声だけで短いシネマティックシーケンスを生成できることを示します。まずtools/render.shを実行して中間フレームを確認し、SCENE_LISTを編集して独自のターミナルメッセージ、パレット、カメラ移動、トラッキング構成を作成してください。同じタイミングとカメラモーションに従うスコアが必要な場合は、オプションのSlabパイプラインを使用します。