
さて、できるだけ自動化してシェイプキーをつくっていきます。
1.0~12フレームに13個の表情をautorigのポーズで作成
| フレーム | 発音キー |
|---|---|
| 0 | Basis |
| 1 | sil |
| 2 | PP |
| 3 | kk |
| 4 | CH |
| 5 | SS |
| 6 | DD |
| 7 | nn |
| 8 | aa |
| 9 | E |
| 10 | ih |
| 11 | oh |
| 12 | ou |
chat GPTさんに作ってもらったスクリプトです。
よかったら自己責任でお試しください。
2.顔のオブジェクトを選択してスクリプトを実行
# === JP12 口形ベイク:選択メッシュ → 発音名シェイプキー ===
# 使い方:
# 1) 発音名とフレームを用意(マーカー名=発音名があればそのフレームを優先)
# 2) 対象メッシュ(body / teeth / tongue など)を選択
# 3) OBJECTモードで実行import bpy
# ===== 設定 =====
ORDER = ["sil","PP","kk","CH","SS","DD","nn","aa","E","ih","oh","ou"] # 作るシェイプキー名
BASIS_FRAME = 0 # 参照用。Basis自体は変更しない
USE_TIMELINE_MARKERS_IF_MATCH = True # 同名マーカーがあればそのフレームを優先
START_FRAME = 1 # マーカーが無い場合の開始フレーム
STEP = 1 # 同上:何フレームごとに並べるか(1=連番1..N)
SNAP_BASIS_TO_FRAME = False # TrueにするとBasis形状をBASIS_FRAMEの見た目に置換(要注意)
# =================def build_frame_map(order):
base = {name: START_FRAME + i*STEP for i, name in enumerate(order)}
if USE_TIMELINE_MARKERS_IF_MATCH:
markers = {m.name: m.frame for m in bpy.context.scene.timeline_markers}
for k in base.keys():
if k in markers:
base[k] = markers[k]
return basedef ensure_keys(obj, names):
"""Basisと必要キーを作成・初期化"""
if obj.data.shape_keys is None:
obj.shape_key_add(name="Basis", from_mix=False)
kb = obj.data.shape_keys.key_blocks
if kb[0].name != "Basis":
kb[0].name = "Basis"
obj.data.shape_keys.use_relative = True
# 必要キーを準備
for n in names:
if n not in kb:
obj.shape_key_add(name=n, from_mix=False)
kb[n].value = 0.0
kb[n].mute = False
kb[n].relative_key = kb["Basis"]
return kbdef mesh_coords_from_evaluated(obj):
"""モディファイア/アーマチュア適用後の頂点座標(オブジェクト空間)"""
deps = bpy.context.evaluated_depsgraph_get()
eval_obj = obj.evaluated_get(deps)
tmp = eval_obj.to_mesh()
vcount = len(tmp.vertices)
coords = [c for v in tmp.vertices for c in v.co]
eval_obj.to_mesh_clear()
return coords, vcountdef assign_coords_to_key(obj, key_name, coords, vcount):
kb = obj.data.shape_keys.key_blocks[key_name]
if len(kb.data) != vcount:
raise RuntimeError(
f"{obj.name}: 頂点数不一致(Subdivision/Remesh等でトポロジが変化していませんか?一時OFFに)"
)
kb.value = 1.0
kb.data.foreach_set("co", coords)
kb.value = 0.0def bake_pose_to_shape(obj, key_name, frame):
scn = bpy.context.scene
scn.frame_set(frame)
coords, vcount = mesh_coords_from_evaluated(obj)
assign_coords_to_key(obj, key_name, coords, vcount)def snap_basis_to_frame(obj, frame):
scn = bpy.context.scene
scn.frame_set(frame)
coords, vcount = mesh_coords_from_evaluated(obj)
kb = obj.data.shape_keys.key_blocks["Basis"]
if len(kb.data) != vcount:
raise RuntimeError(f"{obj.name}: Basisに割当不可(頂点数不一致)")
kb.data.foreach_set("co", coords)def main():
targets = [o for o in bpy.context.selected_objects if o.type == 'MESH']
if not targets:
print("メッシュを選択してから実行してください。"); return# 安全:オブジェクトモードに
try:
bpy.ops.object.mode_set(mode='OBJECT')
except:
passscn = bpy.context.scene
current_frame = scn.frame_currentframe_for = build_frame_map(ORDER)
for obj in targets:
try:
kb = ensure_keys(obj, ORDER)# 必要ならBasisを特定フレームの見た目へスナップ
if SNAP_BASIS_TO_FRAME:
snap_basis_to_frame(obj, BASIS_FRAME)# 各発音をベイク
for name in ORDER:
bake_pose_to_shape(obj, name, frame_for[name])# 仕上げ:値は全て0に戻す
for k in obj.data.shape_keys.key_blocks:
k.value = 0.0print(f"[OK] {obj.name}: {len(ORDER)}個の発音キーを作成/上書き")
except Exception as e:
print(f"[NG] {obj.name}: {e}")# 元のフレームへ
scn.frame_set(current_frame)
print("完了:選択メッシュに発音名シェイプキーをベイクしました。")if __name__ == "__main__":
main()
続いて複数オブジェクトにマスターのドライバーをいれて一括に動かすスクリプト
import bpy
# 無いキーを自動追加するか(True: 追加してリンク / False: 無いものはスキップ)
CREATE_MISSING = Truectx = bpy.context
sel = [o for o in ctx.selected_objects if o.type == 'MESH']
if not sel:
raise RuntimeError("メッシュを選択してから実行してください。")master = ctx.view_layer.objects.active
if master not in sel or master.type != 'MESH':
raise RuntimeError("アクティブにマスター(例: body のメッシュ)を置いて実行してください。")if not master.data.shape_keys:
raise RuntimeError("マスターにシェイプキーがありません。")mkeys = master.data.shape_keys.key_blocks
def ensure_key(slave, name):
"""スレーブに name のキーが無ければ作る(必要なときだけ)"""
sk = slave.data.shape_keys
if sk is None:
slave.shape_key_add(name="Basis", from_mix=False)
sk = slave.data.shape_keys
if name in sk.key_blocks:
return sk.key_blocks[name]
if CREATE_MISSING:
return slave.shape_key_add(name=name, from_mix=False)
return Nonedef link_one(slave, name):
sk = slave.data.shape_keys
kb = ensure_key(slave, name)
if kb is None:
return False# 既存ドライバを削除
data_path = f'key_blocks["{name}"].value'
try:
slave.data.shape_keys.driver_remove(data_path)
except Exception:
pass # 無ければOK# 新規ドライバを追加(Key データに対して)
fcu = slave.data.shape_keys.driver_add(data_path)
drv = fcu.driver
drv.type = 'SCRIPTED'
var = drv.variables.new()
var.name = 'src'
# マスター Object を参照し、その中の data.shape_keys の値を参照
var.targets[0].id = master
var.targets[0].data_path = f'data.shape_keys.key_blocks["{name}"].value'
drv.expression = 'src'
return Truecount_ok = 0
count_skip = 0for s in sel:
if s == master:
continue
if not s.data.shape_keys and not CREATE_MISSING:
print(f"[SKIP] {s.name}: シェイプキーが無い(CREATE_MISSING=False)")
count_skip += 1
continue
# Basisは除外して、他のキーをリンク
for name in mkeys.keys():
if name == "Basis":
continue
if link_one(s, name):
count_ok += 1
else:
count_skip += 1print(f"Linked drivers: {count_ok}, skipped: {count_skip}")
これでずいぶん短縮できそうです♪