3Dカメラキャプション
After Effectsのムーブ(3Dカメラ、被写界深度、テキストパス、ポスタライズタイム15、モーションブラー)を、HyperFrames用の決定的なフレーム単位ペインターとして再構築したものです。1つのクロックがプレート、マット、すべてのグリフを描画します。CSSアニメーションは一切使用しません。
使用すべき場合 / 使用すべきでない場合
- 使用すべき場合: 静止カメラのトーキングヘッドテイク(アバターまたは実写)、ランドスケープ、ミディアムショット以上、1ピース5~20秒。単語は話者の周りを立体的に動く必要があります。
- 使用すべきでない場合: 単純な字幕、動くカメラやハンドヘルドカメラ(マットとプレートのトランスフォームは固定ショットを前提としています)、極端なクローズアップ(頭の後ろや周りにスペースがない)、人物マッティングモデルで分離できない被写体。
要件と副作用(完全リスト)
- ツール:
- Node 22以上、およびテスト済みバージョンに固定されたHyperFrames CLI、
npx [email protected](registry.npmjs.orgからダウンロード)。以下のすべてのコマンドにこの正確なバージョンを使用します。 PATHにffmpegが必要です。- Python 3に
fonttools、brotli、numpy、pillowが必要です。
- Node 22以上、およびテスト済みバージョンに固定されたHyperFrames CLI、
- マット:
npx [email protected] remove-backgroundをローカルで実行します。初回実行時に人物セグメンテーションモデル(約170 MB)を~/.cache/hyperframes/にダウンロードします。 - レンダリング時間: コンポジションは、プレビュー/レンダリング時に
cdn.jsdelivr.netからGSAP 3.14.2をロードします。 - ワードクロック: ユーザーが既にローカルで実行している任意の単語レベル転写ツール。発音タイミングの精度が重要です。通常のWhisperは発話開始が遅れます。
- フォント: ユーザーが提供するファイル。埋め込みを許可するライセンスを確認してください。
- 手描きスタイル(オプション):
p5-paint-animationスキルがインストールおよびセットアップされている必要があります(そのセットアップで、固定版のpuppeteer、Chrome for Testing、p5、p5.brushがダウンロードされます)。build-sprites.pyがそれをヘッドレスで実行します。フレームはそのスキルのout/に書き込まれ、その後削除されます。 - その他は一切不要: 認証情報、有料APIコール、アップロードは不要です。ファイルはプロジェクト内(
assets/、renders/)にのみ書き込まれます。
準備(テイクごとに1回)
- 動画の切り出しとプレート。
bash scripts/prep-take.sh <take.mp4> <start_s> <dur_s> <project>/assetsにより以下を生成します:portion.mp4: オーディオソース。plate-tall.mp4: 各辺240px、上部480px、下部240pxをリフレクトパディングしたもの。これにより、ウィップ、トラック、プルバックで端が表示されることがありません。person.webm: アルファマット、同じフレーム数。- スクリプトは、プレートとマットを動画(ポートション)に対して(サイズ、フレームレート、フレーム数)チェックし、不一致があれば失敗します。
- ワードクロック。 ポートションの単語タイミング(秒単位、その開始時点から)。
- フォント。 2~3の役割(キャプション/ヒーロー/リング)を選択し、次のコマンドを実行:
python3 scripts/font-metrics.py assets/metrics.js key=font.woff2[@wght=…,opsz=…] …- 可変フォントは、その軸をここで渡し、かつCSSで固定する必要があります(
font-variation-settings、font-optical-sizing: none)。そうしないと、ブラウザのopszがサイズに追従し、単語が重なります。
- 可変フォントは、その軸をここで渡し、かつCSSで固定する必要があります(
- シルエット。 何かを配置する前に、
person.webmのアルファからヘッドボックスとショルダーラインを読み取ります。
ビルド
assets/kit/{cam3d.js,tables.js,finish.js}とmetrics.jsをプロジェクトのassets/にコピーします。references/worked-film.htmlは完全なリファレンスフィルムです。2つのショット、ボディワイプ、両方のリングタイプ、フライスルー、フィナーレ、グレーディングが含まれていますが、そのメディアは含まれていません。references/recipes.mdには、奥行き優先グループ、ラップトリング、階段状配置、手描き単語が記載されています。C3.init(metrics, {W, H}):- レンズはチュートリアルの垂直画角(F = 1326.1·H/1080)を維持するため、
tables.jsは任意の幅でスケールされずに適用されます。 - 1440より広いフレームでは、
wipe.xに(W − 1440) / 2を追加します。
- レンズはチュートリアルの垂直画角(F = 1326.1·H/1080)を維持するため、
ショット文法
4~6のビートを選びます。各話句を独自のグループに分割します。
| ビート | ビルド | ルール |
|---|---|---|
| ウィップインパンチイン | camAテーブル(502 px低い位置から19フレーム)の後、linearクリープ | プレートS0 1.1~1.3; フレームは決して着地しない |
| 奥行きグループ | 句ごとに2~3のキャプショングループをD 1250 / 1500 / 2200に配置 | 近く=大きく、影も大きく; クリープとトラックで視差を明らかにする |
| ヒーロー背後 | ヒーローワードをプレートの奥行きの背後レイヤーに、マストヘッドスタイルで頭の向こう側に配置 | 頭が中央の文字を隠す; 60%以上が判読可能であること; アセンダーがフレーム内にあること |
| パララックストラック | swingセグメント、tx 420 | 古いレイアウトは3ステップでフェード; 次のレイアウトは視差で到着 |
| ボディワイプ | 話者自身のマット、brightness(0) blur()、先行エッジはwipe.xから | ショットAはバンドの左側をクリップ、ショットBは右側; 両側でプルアウト |
| ラップトリング | 話者の前の水平円(中心Z 2000)、上方11~16°、回転 | 単語は話されたときに側面から小さく入り、正面でスイングし、落ち着く; 下の弧は読めるように反転する |
| フライスルー | フレーズをD 1500 / 2000 / 2500に配置、カメラはそれらの間をスイング | フォーカスはフレーズに追従 |
| 階段状フィナーレ | プッシュセグメント; キャプションの各単語は対角線上に、より深く、より小さくなる | プッシュにより近くの単語が離れる |
ルール
- モーション:
- テキストは15fpsにポスタライズされます(自身のアニメーションと、それが見るカメラの両方)。プレートとマットは毎フレーム移動します。
- モーションブラーは、15fpsステップの半分にわたる7つのゴーストです。回転するリングには
shutter: 0.5を使用します。 - キャプションは声を0.2秒リードし、15fpsのグリッドにスナップされます:
fr(t) = 2·round((t − 0.2)·15)。 - エントリーは
ENTRYだけ上昇します: キャプション30px、ヒーロー110~260px。リング単語は、パスに沿った最後の30~44pxをイーズします。隣接する単語がまだ着座していない場合にのみRING_ENTRYのオーバーシュートを使用します。
- 奥行き:
- すべてのグループは独自のDを持ちます。プレート3000、キャプション1250~2400、ラップトリング約1100~2700。
- 奥行きシャドウ
[6, 16, 0.5, 1500]は1500/(D − cz)としてスケーリングされます。 - DOF σ = 6.7·|D − focus|/D px(高さ1080の場合)。フラットなキャプションは1.2pxに制限。リンググリフは完全なブラーを受けます。
- サイズ:
size/riseは着地カメラ(tland)での画面上のpxです。スプライトは画面上のサイズでレンダリングされます。 - 間隔:
- リングテキストは常に
ringLineを通過します(画面上の進行; タイプ: トラック1.10、ギャップ26~30; 手描き: トラック1.0、ギャップ28)。 - 回転するリングは、ポーズごとに再レイアウトされます。
- リングテキストは常に
- オクルージョン:
- リングは話者の前に描画されます。
- 単語が話者の後ろを通ることを意図している場合にのみ、単語を後ろ/前のインスタンス(
side、zSplit)に分割します。
- 判読性:
- キャプションはフレームの暗い側に配置します。
- 肌や明るい色の服にかかるテキストには、暗いハローを付けます。
- 画面上に末尾のピリオドは表示しません。
- グレーディング(オプション):
finish.jsがカーブ、彩度1.12(肌用)、およびフレームごとに再ロールされるグレインを提供します。不透明なシーン(プレート + 背後テキスト + マット)をフィルターラッパーに入れます。前面テキストにも同じグレーディングを適用し、その上にグレインを合成します。これにより、背後/前面に分割された単語が1つの色を維持します(references/worked-film.htmlを参照)。
スタイル
- エディトリアル: コンデンスドディスプレイセリフ、ヒーローはイタリック; キャプションはテキストセリフ; クリーム色に1つのアクセントカラー。
- ボールド: ヘビーグロテスクヒーロー、ライトキャプション、1つの彩度の高いアクセント(作例フィルム)。
- 手描き:
scripts/hand/build-sprites.pyからのp5.brush書き込みスプライト:- 各単語は、そのオンセットから自身を書き込みます。
- リングは単語を全体として配置し、接線に回転し、奥行きでスケーリングし、より大きく平らなリング上に配置します。
- レシピは
references/recipes.mdにあります。
検証
npx [email protected] check . --no-contrast。ゴーストレイヤーがWCAGのコントラストチェックに引っかかります。ウィップとワイプの継ぎ目は設計上重なっています。- プレビューページで、
scripts/frame-bounds.jsをペーストし、__frameBounds(0, dur)を実行します。- 戻り値は
[]である必要があります: 表示中に5+ステップ以上フレーム外にあるグループがないこと。
- 戻り値は
scripts/glyph-gaps.jsをペーストし、静止時に__glyphGaps(t)を実行します。- 単語間の接触がないこと。
- フラットな単語は1つのボックスとして計測されます(ディセンダーは誤検出を引き起こします)。イタリックのfのはみ出しは負の値として読み取られます。ワイプバンドの反対側にある単語は両方表示されます。
- 単語間のギャップ: 単語間のレンダリングされたギャップが、スペース1つ分の進行とほぼ等しいこと。
- 各ビートのスナップショットを取得し、
npx [email protected] render . --crf 12でレンダリングし、ビートごとにフル解像度フレームを確認します。
制限事項
- 細かい髪の毛の部分でマットのエッジがちらつきます。ミラーパッドはウィップ中に短時間表示されます。
- フレームいっぱいに話者がいる場合、背後にスペースがほとんどありません。マストヘッドの高さにするか、肩の後ろに隠します。
snapshot --atは1つのページを共有するため、グレインはキャプチャ間で再ロールされません。グレインはレンダリングで判断してください。- グレーディングと多数のゴーストレイヤーを含むレンダリングは、通常のレンダリングの約2倍のコストがかかります。