MMDをブラウザで動かす仕組み(three.js + MMDLoader)

ブラウザでMMDを再生するには three.js の MMDLoader と物理演算の Ammo.js が要ります。作ってみて初めて分かった注意点を残しておきます。

最終更新: 2026-09-01 · このサイト自体がこの構成で動いています(three r171 + Ammo.js)

three.js は r171 が最後

MMDLoader・MMDAnimationHelper・MMDPhysics・MMDExporter は three r172 で addons からまるごと削除されました。 MMDを扱うなら r171 に固定するか、公式が案内する移行先(three-mmd-loader)へ移る必要があります。「最新にしておこう」で上げるとビルドが壊れます。

物理演算の Ammo.js は three に含まれない

髪やスカートの揺れには Ammo.js が必要です。MMDPhysics はグローバルの window.Ammo を直接見るので、モジュールとして読み込むのではなく <script> タグで読み込んでから初期化の完了を待ちます。

モデルは等倍で扱う

MMDのモデルは「1単位 ≒ 8cm」の慣習で作られています。小さく感じるからとモデル側を縮小すると、カメラVMDの位置トラックはモデルの子ではないためスケールに追従せず、アングルが大きく崩れます。モデルは等倍のまま扱い、カメラ距離・光源の位置・影のフラスタムといったシーン側の距離定数を調整するのが正解です。

輪郭線の太さはスケール非依存なので、この調整の対象に含めてはいけません。クリップ空間で pos.w を掛けて計算される画面上の値なので、12.5倍すると輪郭が激太りします(実際にやりました)。

読み込み完了のコールバックは「テクスチャの完了」ではない

MMDLoader.load() のコールバックは、メッシュとマテリアルが揃った時点で呼ばれます。テクスチャの取得はその後も非同期で続きます。 サムネイルをこの時点で撮ると、髪が破綻した絵が保存されます(テクスチャ22件のモデルで、取得完了まで実測7秒かかりました)。LoadingManageritemStart/itemEnd を数えて、本当に終わったかを判定してください。

スクリーンショットを撮るなら preserveDrawingBuffer

WebGLRendererpreserveDrawingBuffer: true で作らないと、レンダリングループの外(ボタンを押したとき)で canvas.toDataURL() を呼んだ結果が真っ黒になることがあります。

モバイルではWebGLコンテキストが実際に失われる

タブを裏に回す、メモリが逼迫する、といった状況でOSがWebGLコンテキストを破棄します。対応していないと、戻ってきたときに黒いまま二度と描画されません。 復帰イベントで表示設定と影の設定を入れ直し、失っていた間の経過時間を捨てる(飛びを防ぐ)処理が要ります。

再生中は Screen Wake Lock を取る

4分のモーションを再生している間は無操作なので、取らないと画面が消えます。タブを裏に回すとOSに解除されるので、visibilitychange で取り直してください。