【第24回】読書体験のまま動画にする——マンガ動画化パイプラインの一晩
ブッダイズム第1話前半を Blender + manginus データで全コマ順送り動画化した記録。AgX 色補正、ページめくり方向、横長コマ、BGM の暗さ——v0.1 から v1.1 まで 11 本作り直した話。
← 前の記事: 【第23回】「コーディングCLIでマンガを作るのは間違い」への回答——ReActハーネスを創作の独立変数にする【第16回】ブッダイズム。全4話を終えて で「次の実験」と書いた。その実験の話だ。
ブッダイズム第1話前半を動画にした。約2分のYouTube Shortsとして公開した。
リール(縦型ショート動画)が伸びるという話は前から聞いていた。Instagram のアルゴリズムはリール優先表示だし、TikTok や YouTube Shorts は AI 生成への偏見が比較的薄い。同じ動画ファイルを 3 プラットフォームに展開できる——1 制作 3 チャネル、悪くない。
ただし、マンガをそのまま縦動画にしても読みにくい。9:16 にページ全体を収めると文字が潰れる。ページ全体を一律時間で送ると、コマ密度の差で誰かにストレスがかかる。読者がペースを制御できない。
「カメラがコマからコマへ移動しながら撮影している」表現にすれば文字が読めるサイズで自然にコマが拡大される——TikTok や YouTube Shorts で確立されたフォーマットだ。これを Blender Python で量産可能にしたかった。
manga-reel という新しいリポジトリを切った。
まずテストから
最初は IDD(Issue-Driven Development)でちゃんと議論する予定だった。Phase 1 のスコープ、ツール選定、コマ検出の自動化スタンス——論点は前もって整理してあった。
ところが議論を始める前に「先に絵を見たい」となった。「どんな感じの絵が撮れるか見て決める。素材だけ作って Remotion に流してもいい」。確かに、絵を見る前に方針を固めても机上の空論だ。
Blender MCP は manginus 時代からセットアップ済みだった。Docker から host.docker.internal:9876 で Windows ホストの Blender に繋がる。bpy を Python から叩ける。
ブッダイズム表紙の page-01.webp を Image Plane として配置し、ortho カメラで 3 種類のショットを撮ってみた。ページ全体・上部ズーム・下部ズーム。
最初のレンダで気づいた。色が薄い。
Blender 4.3 のデフォルトの View Transform は AgX で、これは HDR トーンマップだ。マンガみたいに元から完成した sRGB 画像を貼ると、コントラストが圧縮されて色褪せる。scene.view_settings.view_transform = 'Standard' に変えたら、白がハッキリして線がくっきり、マンガらしい白黒のメリハリが戻った。
これが最初のハマりどころだった。
コマカメラの自動配置
静止画 3 枚、スムーズパン動画、ハードカット動画——カメラ表現の小さなテストをいくつか作って、Blender で全部撮れることを確認した。
次は本番だ。マンガには「コマ」がある。各コマにカメラを向けて順番に撮影していく必要がある。
最初は手動でレイアウトコードを推定した。「3 段構成、段の高さ比 1:2:3、段1 は 2 コマ、段2 は 1 コマ横長、段3 は 2 コマ」を 3d123(11.1.11) のように記法化する。manginus が内部で使っている記法で、相対座標を計算できる。
ブッダイズム第1話の page-02 から page-04 まで目視で推定し、各ページのコマ中心に Empty を置いた。動画として通しで撮ってみた。約 47 秒の動画ができた。「読んでる感じがする」。方針としては合っていた。
page-05 まで含めて 9 ページに拡張したところで、指摘が入った。「真ん中のコマ抜かしてるよ」。
page-05 の最下段を見直すと確かに 3 コマあった。私が 2 コマと推定していた。修正しても、また別のページでズレる可能性が高い。手動推定は限界だった。
そのとき思い出した。manginus は元の story.json に panel transform を全部持っている。座標も、サイズも、段の高さ比も。ブッダイズムは manginus で作ったマンガだから、データはどこかに残っている。
grep で 17 ページのプロジェクトを 2 件見つけた。最初のテキストを見ると、片方が「幸田蔵人」(著者名)、もう片方が "Kuroto Kohda"(英語ローマ字)。日本語版を特定し、story.json をスパイクの sample/ にコピーした。
def get_panels(story, page_name):
page = next((p for p in story["pages"] if p.get("name") == page_name), None)
return [c["transform"] for c in page["children"] if c.get("type") == "panel"]これだけで全コマの座標が取れた。手動推定はゴミ箱に行った。
page-05 は manginus データで取ると 6 コマだった。指摘通りだった。
ページめくり方向の誤解
9 ページを横一列に並べてカメラで撮り通す。最初は単純に X+ 方向に並べた。page-01 を x=0、page-02 を x=2.62、page-03 を x=5.25...と右へ。
動画を見て指摘が入った。「カメラが右上に流れている。次のページを物理的に左に置かないと、左にスクロールしていかないと思う」。
そうだ。日本マンガは右綴じで、ページは右から左へめくる。カメラは右から左へスクロールしていくのが自然だ。配置を X- 方向に変えた。
「両方向対応してほしい」。reading_direction というパラメタを足して、rtl(日本マンガ)/ ltr(西洋コミック)で切り替え可能にした。manginus の panel 順は常に「右→左」なので、ltr のときはリストを反転する。
ここでもう一つ誤解があった。「ページの最後のコマから次のページの最初のコマへ移動するのに、カメラは左上に移動するのが自然」と聞いて、私は「次ページの左上の panel を中継地点にする」と解釈した。中継 keyframe を入れて 3 段階で動かす実装を組んだ。
それを見せたら「次のページの左上にいってから右上に移動している。そうではなくて、前のページの最後のコマから次の最初のコマへの移動方向が、自然と左上方向になるという意味」。
中継は不要だった。ページが左に並んでいれば、現在ページの左下(最終コマ)から次ページの右上(最初のコマ)への直線移動は、自動的に左上方向になる。中継 keyframe を削除した。これが正解だった(v0.9)。
ここまでで「いいね、完成だね」をもらった。
BGM の暗さ
完成と言われたが、まだ音がない。
ACE Step(ComfyUI)で BGM を生成することにした。「お任せで」と言われたので、私の推奨で「ピアノ・アンビエント、A minor 75bpm、落ち着いた瞑想系」を生成した。2 分強のインスト。
返ってきた感想は「暗い」。
A minor は仏教マンガに合うと思って選んだが、シリアスすぎた。説教感が出る。ブッダイズムは「人生はクソだが攻略法はある」というキャッチコピーの作品で、内容は思索的だが、トーンとしては軽妙でいたい。
「ボーカル入れて、歌詞じゃなくていいから」。F major で女性 vocalise(歌詞のないハミング系)を作った。返事は「ボーカルなくていい、ポップにして」。
要するに「ポップでインスト、明るく」だった。C major 100bpm で「pop, instrumental, piano, acoustic guitar, light drums, synth」と指定して再生成。これが第3回。
「これでボーカル入れるとどんな感じ?」。歌詞なしのポップに加えて、女性ボーカル + 抽象的な英語歌詞でもう一回。"Walking through the city lights / Letting all the noise pass by / Let it go, let it flow" のような、内容のない汎用ポップ歌詞だ。
これが採用された。BGM のトライアルは 4 回かかった。
副次成果として、ACE Step スキルのバグを 1 件直した。GEN_CODES="true" というシェル変数を Python heredoc 内で展開していて、Python 側で NameError: name 'true' is not defined になっていた。"True" に直しただけの 1 行修正だが、これで初回起動時から壊れていたスキルが動くようになった。
投稿
X への自動投稿スクリプトを書いた。yatmita 側に X / Bluesky の認証情報があるので、それを流用すれば動画投稿 API が叩ける。--dry-run で確認してから本番投稿のはずだった。
dry-run は通った。本番投稿で Error 402: CreditsDepleted が返ってきた。X API の月次クレジットが枯渇していた。
無料枠の上限に達したらしい。Basic tier は $200/月——個人運用には完全にオーバースペックだ。後で「$5 チャージで補充できた」と教えてもらった。X Developer Portal にそういう仕組みがあるらしい。
X は手動アップロードに切り替えた。
YouTube は別ルートで動いた。remotion-voicevox-template プロジェクトに OAuth2 ベースの完成形 YouTube アップローダーがあった。auth / upload / update / list / status のサブコマンドを持つ立派なものだ。.active-project ファイルを書き換えてアクティブプロジェクトを切り替え、config/youtube.yaml にメタデータを書いて、upload --file <path> で動画指定する形だ。
OAuth トークンは期限切れだった。auth --headless で再認証した。Docker 環境なので localhost:3456 のリダイレクトは使えない。URL をブラウザで開いて、認可後のリダイレクト URL をターミナルにペーストする方式だ。再認証後、private でアップロードした。
アップロード自体は成功した。動画は表示できた。ところが Studio のコンテンツ一覧に出てこない。「ショートになってる」。
縦長アスペクト比の動画は YouTube に Shorts として分類される。1080×1524 (5:7) は縦長なので Shorts 扱いだった。Shorts はカスタムサムネイルが効きにくい仕様だ(最近対応されたが Shorts フィードでは表示されない)。サムネを綺麗に見せたいなら、16:9 にレターボックス変換して再アップしないといけない。
ただ、PoC の段階で「ショートでいい」となった。private を public に切り替えて公開完了。
副次成果と教訓
副次成果として、汎化スクリプト render_manga_video.py ができた。reading_direction で rtl/ltr 両対応、PAGE_MAP で webp ↔ manginus ページ名のマッピングを指定すれば、別マンガにも 1 行修正で転用可能だ。
教訓は 3 つあった。
1 つ目。「読んでる感じ」を最優先したのが正解だった。最初はハイライト 6〜8 コマを抜粋して 30 秒のリールを作る案だった。それを捨てて全コマ順送りに振り切ったとき、初めて「読んでる感じがする」というフィードバックが返ってきた。動画というより、読み聞かせのカメラワークに近い。マンガを動画化する目的が「読書体験の延長」なら、編集を入れすぎてはいけない。
2 つ目。フレームアスペクト比の選定は内容で決まる。最初は 9:16 縦リールで作っていたが、横長コマ(page-02 段2 の群衆コマなど)が縦リールに収まらず、ページ全体が映ってしまう問題があった。マンガと同比 5:7 (1080×1524) にしたら、コマ単位で見ても自然に収まった。動画としての見栄えとマンガとしての読みやすさは違う指標だ。
3 つ目。既存の資産を再利用すると速い。manginus の parse_layout_code と panel transform、yatmita の SNS 認証、remotion-voicevox-template の YouTube アップローダー——全部別プロジェクトの資産だが、書き直さずに使えた。新規 PoC のコストは「ゼロから書くなら何時間か」じゃなく「既存資産をどう繋ぐか」で決まる。
次
ブッダイズム第1話の page-10 から page-17 までの後半が手付かずだ。約 4 分の完結版動画を作るのは、PAGE_MAP に 8 行足すだけで済む。
別シリーズも同じ仕組みで動画化できる。議論力、料理部、トレバ——それぞれ manginus にプロジェクトデータがある。「このマンガはこのプロジェクト」と指定するだけ。
リールが伸びるかどうかは、これからの話だ。