Blenderアニメーション — Python駆動の3Dワーク
Blenderをbpyで、バックグラウンドモードで、再実行可能なスクリプトから操作します。.blendファイルはスクリプトのビルド成果物として扱います。つまり、スクリプトが信頼できるソースであり、ファイルが納品物です。
アーキテクチャと判断基準についてはreferences/BLENDER-PLAYBOOK.mdを、コピー可能なコードパターンについてはreferences/BLENDER-RECIPES.mdを参照してください。すべてのAPIの詳細は、インストールされているBlenderバージョンのドキュメントで必ず確認してください。Python APIはリリース間で変更されます。
1. 最初に環境を確認する
blender --version
blender --background --factory-startup --python-expr \
"import bpy, sys; print('BLENDER', bpy.app.version_string); print('PY', sys.version.split()[0]); print('ENGINES', [e.bl_idname for e in bpy.types.RenderEngine.__subclasses__()][:5])"
ルール:
bpyはBlenderにバンドルされているPythonに属します。pip install bpyを実行したり、シーンスクリプトをシステムPythonで実行したりしないでください。--python-exit-code 1を渡して、Python例外が発生した場合にサイレント成功ではなくシェルコマンドを失敗させるようにします。- クリーンビルドには
--factory-startupを使用します。ユーザーの開いているプロジェクトを消去するためにこれを使用しないでください。 - すべてのレンダリングの開始時に、Blenderバージョン、エンジン、フレーム範囲、解像度、FPS、出力パスをログに記録します。
2. 標準的な呼び出し方
# スクラッチからシーンを構築し、.blendファイルを保存
blender --background --factory-startup --python-exit-code 1 --python blender/scripts/build_scene.py
# 既存のプロジェクトを変更
blender --background project.blend --python-exit-code 1 --python blender/scripts/update_scene.py
# 保存されたアニメーションをレンダリング
blender --background project.blend --render-anim
# 1フレームをレンダリング
blender --background project.blend --render-frame 120
# 範囲を安全にレンダリング(画像シーケンス)
blender --background project.blend --python-exit-code 1 --python blender/scripts/render_frames.py
長時間のジョブは出力をlogs/にリダイレクトして実行し、再開が必要になる可能性があるものには常に画像シーケンスを優先します。
3. スクリプトアーキテクチャ
小規模なシーンでは1つのファイルから始めます。シーンが約300行を超えるか、複数のショットがある場合は分割します:
blender/
config.py # fps、フレーム範囲、解像度、パレット、パス、シード
build.py # エントリポイント: リセット → セットアップ → ショット構築 → 保存
scene_setup.py # ワールド、ユニット、コレクション、レンダリング設定
geometry.py # プリミティブ、カーブ、パラメトリックメッシュ
materials.py # ノードビルダー、再利用可能なマテリアルファクトリー
animation.py # キーフレーム、ドライバー、プロシージャルモーション
camera.py # リグ、オービット/ドリー、被写界深度、フレーミングチェック
lighting.py # キー/フィル/リム、HDRI、エミッシブアクセント
simulation.py # パーティクル、リジッドボディ、クロス — キャッシュ設定
render.py # エンジン、サンプル数、出力、コンタクトシート、エンコード
shots/ # ショットごとに1モジュール: shot_01.py、shot_02.py
譲れない条件:
- 冪等性。 ビルドを再実行すると、指定したコレクション/オブジェクトのみが更新され、ジオメトリが重複したり、無関係なユーザーコンテンツが消去されることはありません。
- 命名。
Main_Logo、Cam_Orbit、Light_Key、Emitter_Particles。Cube.003のような名前は決して使わないでください。 - 設定済み。 調整可能なすべての値は
config.pyまたはデータクラスに格納されるため、リビジョンは1行の変更で済みます。 - アサーション。 アセット、カメラ、フォント、出力ディレクトリがない場合は、エラーを出して停止します。
- スコープ。 クリーンアップでは、自分が所有する名前(例:
MS_プレフィックス)のコレクションのみを削除します。
4. シーン構築
- コンテキスト依存の
bpy.ops(選択、モード、エリアに依存)よりも、データAPI(bpy.data.objects.new、bpy.data.meshes.new、collection.objects.link)を優先します。 - オペレーターが本当に必要な場合は、そのバージョンに適したコンテキストオーバーライドを提供し、その理由を文書化します。
- マテリアルはノードと小さなファクトリー関数を介して構築します。オブジェクトごとに20ノードのツリーをコピーペーストしないでください。
- 現実世界のスケールを使用し、シーンの単位系を設定します。間違ったスケールは被写界深度、物理、照明を壊します。
- ワールドノードツリーを明示的に(色、強度、HDRI)設定して、レンダリングが別のマシンでも再現可能になるようにします。
- アセットをパックまたは相対パスにします。
//assets/...パスとパッケージフォルダは、誰かのダウンロードフォルダへの絶対パスより優れています。
5. アニメーション
メカニズムを意図的に選択してください:
| ニーズ | 使用法 |
|---|---|
| 自作の位置/回転/スケール/マテリアル変更 | 明示的な補間を使用したキーフレーム |
| 別のプロパティまたは式に従うプロパティ | ドライバー(シンプルで文書化された式) |
| 繰り返し/パラメトリックモーション(オービット、波動、グリッド、スパイラル) | フレーム依存の数学、ステートレスに評価 |
| 数千のインスタンス、パターン、散乱 | ジオメトリノード |
| 物理的に創発するモーション | シミュレーション(キャッシュ、全範囲を確認) |
| 空間を通るカメラパス | カーブ + Follow Path制約、またはキーフレーム化されたリグ |
キーフレームの衛生管理:
obj.location = (0, 0, 0)
obj.keyframe_insert("location", frame=1)
obj.location = (4, 0, 0)
obj.keyframe_insert("location", frame=48)
for fc in obj.animation_data.action.fcurves:
for kp in fc.keyframe_points:
kp.interpolation = 'BEZIER'
kp.easing = 'EASE_IN_OUT'
kp.handle_left_type = kp.handle_right_type = 'AUTO_CLAMPED'
- 補間は意図的に設定します。デフォルトのベジェは有機的な動きに適しています。一定速度や機械的な動きには
LINEARを、ステップ/ピクセルルックにはCONSTANTを使用します。 - ループに意図しないイージングを残さないでください。ループポイントで動きがぎくしゃくします。
- ループの場合: フレーム1とフレームN+1を同一にしてから、
[1, N]をレンダリングします。 - すべてのプロシージャルはフレームの純粋関数である必要があります:
f(frame)。状態を蓄積したり、シードなしのrandomを使用したりしないでください。
6. ジオメトリノードとインスタンス化
スケール、バリエーション、非破壊的な制御が必要な場合にジオメトリノードを使用します:
- 散乱/インスタンス化(木、デブリ、星、群衆ブロック)
- パラメトリック配列、グリッド、放射状パターン、イスラム幾何学タイル
- アニメーション化されたディスプレイスメント、フィールド、インスタンスごとの回転/スケール
- 重い10,000オブジェクトのPythonループを1つのノードツリーに変換
ノードは脆弱なインデックスではなく、nodes.new(...)とlinks.new(...)で名前を指定して構築します。ユーザーが要求するであろうノブ(カウント、半径、シード、速度)をモディファイア入力として公開し、リビジョンにコード変更が不要になるようにします。
7. マテリアル、照明、カメラ
- マテリアル: Principled BSDFベース。明示的な粗さ/メタリック。アクセントにはエミッション(控えめに抑えてください — ブルームでフラットなショットを修正することはできません)。本当に必要な場合のみリアルな透明度を使用し、最終エンジンで確認します。
- 照明: キー → フィル → リム → 環境。形状と奥行きが装飾よりも優先されます。露出とハイライトのクリッピングをチェックします。ビューポートだけではありません。
- カメラ: 被写体に合わせて焦点距離とフレーミングを選択します。オービット、ドリー、クレーン、固定 — それぞれに理由が必要です。フレーム1だけでなく、極端なフレームでのフレーミングを確認します。スムーズなベジェハンドルを優先し、ハードストップではなく微妙な落ち着きを追加します。
- 被写界深度: ストーリーポイントに焦点を合わせます。小さなテキストをぼかさないでください。
8. エンジンとレンダリング設定
| エンジン | 使用目的 | コスト |
|---|---|---|
| EEVEE | モーショングラフィックス、スタイライズドワーク、高速イテレーション、プレビュー | 低 |
| Cycles | フォトリアルな光、反射、GI、ガラス、最終的な美しさ | 高 |
| Workbench | ブロッキング、シルエット、アニマティック | 非常に低 |
両方ともわからない場合はテストします。エンジンの速度はマーケティングではなくシーンに依存します。GPUはビルド、ドライバー、OSに依存します。常にCPUパスを維持します。
明示的に設定してログに記録:
scene.render.engine = 'BLENDER_EEVEE_NEXT' # 4.2+; 古いビルドは'BLENDER_EEVEE'を使用する場合があります
scene.render.resolution_x, scene.render.resolution_y = 1920, 1080
scene.render.resolution_percentage = 100
scene.render.fps, scene.render.fps_base = 30, 1.0
scene.frame_start, scene.frame_end = 1, 300
scene.render.image_settings.file_format = 'PNG'
scene.render.image_settings.color_mode = 'RGBA'
scene.render.filepath = "//renders/shot_01/frame_"
- プレビュー: 25~50%解像度、低サンプル数、EEVEE、またはWorkbench
- 最終: フル解像度、ルックに合わせて選択したエンジン、ノイズ除去が役立つ場合に使用、正しいビュートランスフォーム
- 色: ビュートランスフォーム(AgX/フィルミック/スタンダード)を一度決定し、すべてのパスに適用し、後続のRemotion/FFmpegで一致させます。トランスフォームの不一致は、「コンポジットが色あせて見える」最も一般的な原因です。
- 長時間のレンダリング → PNG(またはHDRが必要な場合はEXR)シーケンス、その後エンコード。直接ビデオ出力はクイックレビュー用です。
9. プレビューラダー
- 代表的なフレームで1枚静止画。
- キーフレーム: 最初、出現、ピーク、各トランジション、最後。
- 最も難しい動きを含む、低サンプル、低解像度のセグメント。
- 縮小スケール、実際のFPSでのフルプレビュー。
- 1~4が正しく見えた後にのみ最終。
コンタクトシートとターゲットフレームで検査:
ffmpeg -i preview.mp4 -vf "fps=2,scale=320:-1,tile=4x4" -frames:v 1 previews/contact.jpg
フリッカー、Zファイティング、ポッピング、交差、テクスチャのスイミング、カメラのクリッピング、露出の変化、判読できないテキストを探します。
10. テキスト、多言語、ロゴ作業
- テキストは、コピーとフォントライセンスを確認した後にのみメッシュに変換します。ソースには編集可能なテキストオブジェクトを保持します。
- アニメーション化する前にグリフカバレッジを確認します。ペルシャ語/アラビア語の場合は、レンダリングされたフレームでシェーピング、結合、RTL順序、ダイアクリティカルマーク、混在スクリプトの句読点を確認します。テキストパスがシェーピングしない場合、正しい文字列でも誤ってレンダリングされる可能性があります。
- ブランド/ロゴ作業の場合は、可能な限りベクターソースを入手します。ラスターの場合は、意図的にトレースし、4Kでエッジを確認します。
- タイプにベベル/ウェイトを付けて、ライトに耐えられるようにします。真っ黒の上に真っ黒、または真っ白の上に真っ白のエッジは避けます。
11. パフォーマンスと信頼性
- 複製ではなくインスタンス化します。サンプル数を減らす前にサブディビジョンを下げます。
- 透明レイヤーとボリューメトリックを制限します。これらは通常のレンダリング時間の killer です。
- シミュレーションをキャッシュし、キャッシュがプロジェクト内にあるか、文書化されていることを確認します。
- 脆弱なジョブの場合はフレームバッチでレンダリングします。フレーム数を確認し、エンコード前に欠落フレームを検出します。
- RAM/VRAM/ディスクを監視します。4K EXRシーケンスは大きいです。長時間のレンダリングの前に空き容量を確認します。
- 新しいBlenderメジャーバージョンで開く/保存する前に、
.blendファイルをバックアップします。新しいファイルは古いビルドで開けない可能性があります。
12. 最小限の実用的な例
# build.py — 次のコマンドで実行:
# blender --background --factory-startup --python-exit-code 1 --python build.py
import bpy, bmesh, math, os
scene = bpy.context.scene
scene.frame_start, scene.frame_end = 1, 120
scene.render.fps = 30
scene.render.resolution_x, scene.render.resolution_y = 1920, 1080
scene.render.engine = 'BLENDER_EEVEE_NEXT'
scene.render.filepath = "//renders/frame_"
# 所有コレクションをクリーン
col = bpy.data.collections.get("MS_Main") or bpy.data.collections.new("MS_Main")
if col.name not in scene.collection.children:
scene.collection.children.link(col)
def add_sphere(name, loc, radius, mat):
"""データAPIスフィア: オペレーターなし、コンテキスト依存なし"""
mesh = bpy.data.meshes.new(name)
bm = bmesh.new()
bmesh.ops.create_uvsphere(bm, u_segments=32, v_segments=16, radius=radius)
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new(name, mesh)
obj.location = loc
col.objects.link(obj)
obj.data.materials.append(mat)
return obj
mat = bpy.data.materials.new("MS_Emission")
mat.use_nodes = True
bsdf = mat.node_tree.nodes["Principled BSDF"]
bsdf.inputs["Base Color"].default_value = (0.05, 0.6, 0.55, 1)
bsdf.inputs["Emission Color"].default_value = (0.1, 0.9, 0.8, 1)
bsdf.inputs["Emission Strength"].default_value = 3.0
bsdf.inputs["Roughness"].default_value = 0.3
core = add_sphere("MS_Core", (0, 0, 1), 0.8, mat)
core.keyframe_insert("scale", frame=1)
core.scale = (1.15, 1.15, 1.15)
core.keyframe_insert("scale", frame=40)
core.scale = (1.0, 1.0, 1.0)
core.keyframe_insert("scale", frame=90)
# カメラ
cam_data = bpy.data.cameras.new("MS_Cam")
cam = bpy.data.objects.new("MS_Cam", cam_data)
col.objects.link(cam)
scene.camera = cam
cam.location = (6, -6, 3)
cam.rotation_euler = (math.radians(75), 0, math.radians(45))
# 保存
os.makedirs(bpy.path.abspath("//renders"), exist_ok=True)
bpy.ops.wm.save_as_mainfile(filepath=bpy.path.abspath("//scene.blend"))
print("BUILD OK", len(col.objects), "objects")
詳細はreferences/BLENDER-RECIPES.mdを参照: オービットリグ、イージング、ジオメトリノード散乱、パーティクル、テキスト、アルファパス、レンダーファームスクリプト、エンコードコマンド。
13. Blender MCP(オプションの便利機能)
バックグラウンドモードのスクリプトは、再現可能、差分可能、デバッグ可能、CIフレンドリーな本番パスです。MCPは、対話的なシーン検査と、既に開いているセッションでのライブ調整に役立ちます。これはサードパーティの統合であり、Blenderコンポーネントではありません。
ユーザーが希望する場合: リポジトリとライセンスを確認し、インストールされているBlenderとの互換性をチェックし、Blender内で任意のPython実行を許可することを理解し、ループバックにバインドし、信頼できないネットワークに公開しないでください。現在のパッケージ名はmcp-for-blenderです。古いblender-mcpのスニペットではなく、リポジトリの現在のセットアップ手順に従ってください。納品するプロジェクトの依存関係にしないでください。
14. 成果物
Blenderジョブは、以下の項目が揃った時点で完了です:
output/final.mp4(または要求されたフォーマット)、ffprobeで検証済み。blender/scripts/*.py— ビルドソース。scene.blend— 編集可能なプロジェクト、アセットはパックまたは相対パス。renders/画像シーケンス(または文書化されたマスター)、有用な場合は保持。README.md— 正確なBlenderバージョン、インストール、ビルド、プレビュー、最終レンダリング、リビジョンコマンド。- 短いレポート: ツール、解像度、FPS、期間、フレーム数、視覚的に確認した内容。
ユーザーが変更したい可能性のあるアニメーションを依頼した場合、MP4のみを納品しないでください。