物理演算(揺れもの)の仕組みと、扱うときの割り切り

髪・スカート・胸の揺れは、モーションではなく物理演算が毎フレーム作っています。ここを「モーションの一部」と思って扱うと、姿勢の指定と綱引きになったり、シークのたびに髪が暴れたりします。何が計算で決まっていて、何が決まらないのかを整理します。

最終更新: 2026-09-13 · 実モデル(アリシア 剛体79個 / ad_org8 剛体141個・ジョイント236個)のPMXを読んで種別を数えた値。シーク・重力・コンテキストロストの挙動はブラウザで実測

剛体には3つの種別がある

種別意味何が動かすか
0ボーン追従ボーン。 物理は動かさない(当たり判定としてだけ働く)
1物理演算物理。 ボーンは結果を受け取る
2物理演算 + ボーン位置合わせ物理だが、ボーンの位置に引き戻される

実モデルでの内訳です。ボーン追従(0)は体幹や腕といった「モーションが動かす側」に付いていて、揺れものは1か2になっています。

モデルボーン剛体内訳ジョイント
アリシア150本79個0が26 / 2が5353個
ad_org8310本141個0が27 / 1が58 / 2が56236個

ジョイントは剛体と剛体を繋ぐ拘束です。髪の房のように数珠つなぎの構造は、剛体の数だけジョイントが要ります。ad_org8 は剛体141個に対してジョイント236個で、剛体より多いのは1つの剛体が複数の相手に繋がっているためです。

揺れるボーンの姿勢をVMDに書かない

種別0以外の剛体が付いているボーンは、物理が毎フレーム上書きします。そこへVMDでも姿勢を指定すると、両方が同じボーンを引っ張り合います。 結果は「揺れなくなる」か「不自然に硬くなる」で、どちらもモーションの不具合に見えます。

手でポーズを作って書き出すときは、種別0以外の剛体が付いているボーンを除外してください。実測では、アリシアで150本中53本、ad_org8 で310本中114本が除外対象になります(詳しくは手付けモーションの記事)。

シークすると揺れが再現できない

物理演算は現在の状態から時間を進めて積分する方式です。つまり「30秒目の髪の位置」は、0秒から30秒までの経過に依存します。シークバーで30秒目に飛んでも、そこまでの経過が無いので正しい状態を作れません。

実際に起きることは「シーク直後の数フレームだけ髪とスカートが不自然になり、すぐ落ち着く」です。正確に再現するには0秒から目的の時刻まで再シミュレーションする必要があり、30秒なら30秒ぶんの計算が要ります。このサイトはそこまでやっていません(飛んだ直後が少し乱れるのを受け入れています)。

サムネイルを撮る・静止画を書き出すときは、シークの直後に撮らないこと。 このサイトはモーションの40%までシークしてから900ミリ秒描いてから撮るようにしています。すぐ撮ると、髪がねじれた瞬間が保存されます。

重力は変えられる(既定は -9.8 × 10)

重力の既定値は -98(単位はMMDの1ユニット/秒²)です。MMDのスケールは1ユニット ≒ 8cm なので、-9.8 ではなくその10倍がちょうど見た目に合う、という経験的な値です。three.jsのMMDPhysicsの既定値もこれです。

弱めると髪がゆっくり大きく揺れ、強めるとすぐ落ちます。0にすると無重力になり、髪とスカートが浮きます。モーションそのものは変わらないので、見た目の調整として使えます。

体型を変えたときの剛体の扱い

モデルの体型を変えるなら、剛体も一緒に動かさないと当たり判定だけ元の位置に残ります。 位置はボーンの移動に合わせて動かせますが、大きさは素直にいきません。

  • 剛体は自分の回転を持っています。 そのため「X方向に1.5倍」のように軸ごとに拡大すると、回転している剛体では形が合いません(斜めのカプセルが潰れた形になる)。
  • このサイトは体積比から等倍の倍率を1つ決めて大きさに掛けています。極端な値では、髪やスカートの揺れ方が元と変わります。
  • 材質(マテリアル)単位の拡大・縮小では剛体は動きません。 スカートの材質だけ大きくすると、揺れの当たり判定は元の大きさのまま残ります。ボーン経由で体型を変えたときは剛体も追従します。

ブラウザで動かすときに要るもの

物理演算に使う Ammo.js(Bullet物理エンジンのWebAssembly版)は three.js に含まれていません。 別途自分でホストして読み込む必要があります。

  • MMDPhysics はグローバルの window.Ammo を直接参照します。 モジュールとして import するのではなく、<script> タグで読み込んでから Ammo() の解決を待つ形になります。
  • 重力を渡す位置に注意。add() に渡すパラメータの直下に gravity を置きます(入れ子のオブジェクトに入れても読まれず、黙って既定値のままになります)。
  • three.js の型定義は gravity を数値と宣言していますが、実装は Vector3 を前提にしています(型定義側の誤り)。
  • 物理はメッシュのスケール適用より前(MMDのネイティブ単位)で計算されます。 モデルを縮小してもしなくても物理の挙動は変わりません(スケールの記事)。

WebGLのコンテキストは実際に失われます(タブを裏に回す・メモリ不足でOSが破棄する)。復帰したときに経過時間をそのまま足すと、物理が一気に進んで髪が吹き飛びます。復帰時は経過時間を捨てるのが簡単で確実です。