[Next.js #08]VRM / BVH / PMX を Next.js(R3F)で扱うための互換性と実装整理

はじめに

昨日、Unity用にVROID STUIOで作成したオリジナルアバターを、R3Fでも使おうと実装を試みたのですが、表示はできても、アニメーション処理がうまくいかず、思いのほかハマったので、その備忘録メモです。

ネット上にも殆ど実装例がないようで、実装前の段階でも嫌な予感はしていて、Nextjsとreactの旧バージョンにダウンロードして安全な実装を試みたにもかかわらず、うまくいかなかったです。(表示するだけなら簡単)

厳密には、アニメーションファイルとVRMのボーンデータの名前の一致?が問題だったようで、その辺を修正するコードも書いたのですがうまくいかず、またバージョンの差異で仕様が変わるなど、中々厄介な事も分かりました。

前回の記事:

プロローグ

この記事では、Next.js(App Router)+ React Three Fiber(R3F)で VRM / BVH / PMX を扱う際に必ず踏む互換性問題を整理する。

昨日の実装で発生した現象は、すべて “仕様とバージョンの不一致” に起因していた。

発生した問題の一覧:

  • three-vrm の 0.x / 1.x / 2.x が混在し、どの VRM がどの three.js に対応しているか不明瞭
  • R3F の useLoader が VRM 0.x / 1.0 を正しく処理できず、読み込み段階で破綻
  • BVHLoader が three.js のバージョン依存で正常に動作しない
  • PMX(MMDモデル)が VRM / glTF とは根本的に異なる体系で統合できない
  • その結果、正しい入口(正規ルート)を踏まずに実装を進めた影響で、全工程が破壊
  • 1日かけて構築した VRM / BVH の実装が完全ロスト

本記事ではこれらの原因を分解し、 Next.js + R3F でキャラクターモデルを扱う際の正しい実装ルート を明確にする。

1. VRM / BVH / PMX を R3F に入れるなら「正しい入口」が必要

Next.js(App Router)+ React Three Fiber(R3F)で VRM / BVH / PMX を扱う場合、最初に選ぶライブラリと three.js バージョンが実装成否を左右する。

以下は、昨日の実装で明らかになった互換性の差分である。

  • VRM 0.x / 1.x / 2.x は互換が異なる モーション、スケルトン、GLTF Extension 仕様が大きく違う。

  • three-vrm の対応バージョンは three.js の特定バージョンで固定 例:three-vrm 1.0 は three.js v0.149 での動作を前提としている。

  • R3F の useLoader は VRM 0.x を想定していない glTF Extension が古く、パイプラインで正しく処理されない。

  • PMX(MMD)は VRM/glTF とは別体系であり統合不可 ボーン構造・剛体・ジョイント・物理処理が完全に異なる。

  • BVH は R3F が自動処理しないため、AnimationMixer を手動で構築する必要がある ローダー結果 Skeleton Mixer useFrame の更新を自前で行う。

総括: 実装が破綻した理由は、 「互換性の異なるモデル形式を同一レイヤーで扱おうとしたこと」。 本記事では、その“正しい入口”を明確にする。

2. VRM の地獄:three-vrm の 0.x / 1.x / 2.x の違い

VRM は 0.x / 1.x / 2.x で仕様が大きく異なる。 three-vrm も three.js も、それぞれのバージョンに固定した互換関係を持つため、 R3F で扱う場合は「どの VRM を使うか」を最初に決定する必要がある。

以下は各バージョンの挙動と、昨日の検証で分かったポイント。


VRM 0.x 系

  • three.js の 古いバージョン に依存
  • R3F の useLoader が想定していない形式
  • glTF Extension が旧仕様のまま
  • 互換パッチなしではロード時にエラーが発生する
  • rotateVRM() が壊れる主原因(骨格構造が現行VRMと異なる)

総評: R3F でそのまま扱うのは非推奨。 Next.js + R3F 環境では実質 “対象外”。


VRM 1.x 系(昨日使用したバージョン)

  • three.js v0.149 が想定動作バージョン
  • glTF Extension が 0.x より整理されて一定の安定性
  • ただし “移行期” の仕様で、関数・API が揺れている
  • VRMUtils.rotateVRM 周りの挙動がまだ不安定
  • R3F の useLoader と組み合わせても動作は可能(条件付き)

総評: 現時点では最も扱いやすいが、 “three.js v0.149 とセットで使用する” 前提が必須。


VRM 2.x 系

  • glTF Extension が大幅に刷新されている
  • ボーン情報・BlendShape が整理され、将来性は最も高い
  • ただし three-vrm 2.x はまだ開発中要素が多い
  • R3F は 2.x にまだ正式対応していない
  • API 変更が急で、開発者向けの情報が少ない

総評: 将来的には最適解だが、 Next.js + R3F の本番運用では様子見が必要。


結論:この記事で “どれを使うべきか” を明示する

  • 安定運用:VRM 1.x + three.js v0.149
  • 旧VRMを使う案件:0.x は R3F 非推奨(Vanilla Three.js で運用)
  • 実験用途:VRM 2.x(R3Fとの併用は非推奨)

R3F で扱う際の“正しい入り口”は VRM 1.x 前提で環境を整えること。

3. BVHLoader が動かない理由:three.js のバージョン依存

BVH(Biovision Hierarchy)は、古いモーションデータ形式であり、 three.js のバージョンによって内部処理が大きく変化する。 そのため、Next.js + R3F 環境では“そのままロードして動く”ことはほぼない。

以下は、検証によって判明した要点である。


three.js r145〜r152 の間で BVHLoader の挙動が変わる

  • skeleton 構造の生成処理がバージョンごとに異なる
  • TransformHierarchy 周りの実装差分が破綻の原因
  • AnimationClip の扱いも旧仕様から移行期にある

このため、 “特定バージョン以外では動かない BVH が存在する” という状況が発生する。


AnimationMixer が glTF と BVH を統合できない

glTF(特にVRM)と BVH のスケルトン構造は互換性がない。

  • glTF humanoid に最適化された構造
  • BVH 任意階層の骨ツリー

よって、 VRM に BVH を直適用することは不可能。

BVH を使う場合は、 BVH のスケルトン専用の AnimationMixer を別に作成する 必要がある。


R3F はアニメーション制御を自動化しない

R3F は three.js の描画ループを React に統合するだけであり、 AnimationMixer の update() は自動実行されない。

そのため、BVH を R3F で動かすには次の工程が必須になる。

  • Loader で読み込む
  • Skeleton / Bone 構造を取り出す
  • AnimationMixer を生成
  • mixer.update(delta) を useFrame 内で手動更新

Vanilla three.js で処理してから R3F に渡すのが正解

以下は BVHLoader を単体で動かす際の基本構造。

const loader = new BVHLoader();
const result = loader.parse(bvhText);

const clip = result.clip;
const skeletonHelper = new SkeletonHelper(result.skeleton.bones[0]);
scene.add(skeletonHelper);

const mixer = new AnimationMixer(result.skeleton.bones[0]);
mixer.clipAction(clip).play();

R3F では この構造を useFrame に取り込む必要がある。


結論:R3F に BVH をそのまま入れるのは非現実的

  • R3F の Loader 任せでは動かない
  • AnimationMixer を手動で構築・更新する必要がある
  • glTF(VRM) と BVH は構造が違いすぎて直接結合不可
  • three.js のバージョン差分を把握しておくことが必須

4. PMX(MMD) の衝撃:VRM/gltf と完全に別文化

PMX(MikuMikuDance モデル)は、VRM/glTF の標準仕様とは全く異なる設計思想で構築されている。 そのため、Next.js + R3F のワークフローに直接統合することは現実的ではない。

以下は、PMX が他の 3D モデル形式と“根本的に違う”理由である。


1. 物理処理(剛体・ジョイント)が完全独自仕様

PMX は以下のような独自の物理体系を採用しており、glTF/VRM とは互換がない。

  • ボーン IK(逆運動学)とは別に PMX固有の仕組み
  • 剛体(Rigid Body)設定
  • ジョイント(Joint)設定
  • MMD 専用の物理エンジンパラメータ

three.js の MMDAnimationHelper がこれを内部で処理しているが、 構造全体が VRM/gltf の HumanBone とは全く異なる。


2. MMDAnimationHelper は R3F のライフサイクルと非互換

  • 自前の時間管理
  • 自前の update loop
  • R3F の useFrame と統合されていない
  • State オブジェクトへのアタッチ前提で作られていない

つまり、 MMDAnimationHelper は three.js 単体で使うために設計されており、 React の状態管理に乗らない。

R3F に入れ込むと挙動が崩れる原因はこれ。


3. モデルの座標系・比率・ボーン構造が glTF/VRM と共通化されていない

glTF / VRM は以下のポリシーで統一されている:

  • Y-up
  • HumanBone(標準化された人型ボーン)
  • glTF Extension による一元管理
  • Scale / Rotation の標準的扱い

一方 PMX は:

  • 軸向きが違う
  • 回転の基準が違う
  • ボーンの階層構造や数が非標準
  • モデルごとに仕様がバラつく
  • 単位もファイルによって異なる

→ そのため PMX と VRM を同じ Skeleton 系統で扱うことは不可。


4. 結論:VRM と PMX を同一レイヤーで扱うのは設計上不可能

よくある要求:

「VRM の隣に PMX を置いて、モデルだけ切り替えたい」

これは原則不可能で、

  • パイプラインが違う
  • 物理エンジンが違う
  • ボーン体系が違う
  • 座標系が違う
  • 更新処理の仕組みも違う

→ 完全に“別文化”として扱う必要がある。


正しい扱い方

  • PMX three.js + MMDAnimationHelper 専用パイプライン(R3Fに寄せない)
  • VRM three-vrm + glTF pipeline(R3F ベースで構築)

この2つは統合せず、 別 Scene / 別 Canvas / 別パイプライン として管理するのが正しい。

5. 「正しい入口」— 結局どう構築するのが正解か

VRM / BVH / PMX は、それぞれ内部仕様・ボーン体系・ローダーの仕組みが異なるため、 Next.js(App Router)+ R3F で同一のパイプラインに統合することはできない。

実装を安定させるためには、以下の “正しい入口” を選択する必要がある。


VRM(1.x を前提に構築)

  • VRM 1.x + three.js v0.149 が最も安定 three-vrm の想定バージョンと合致する。

  • R3F 内では useFrame で AnimationMixer を手動管理 VRM のアニメーション制御は自動化されないため、 mixer.update(delta) を R3F の render loop に統合する。

  • WebXR(VRモード)は別 Canvas で運用する R3F の XR サポートは VRM の挙動と混線しやすいため、 “通常 Canvas” と “XR Canvas” を分けるのが安全。


BVH(汎用モーションデータ)

  • Raw loader(BVHLoader)で読み込み、AnimationMixer を自前で構築 glTF/VRM 用のシステムとは互換性がないため、 result.skeleton を使って専用の Mixer を作成する。

  • R3F に処理を任せるのではなく、自前 update が必須 useFrame(() => mixer.update(delta)) の形で明示的に更新する。

BVH は three.js のバージョン差分の影響が大きいため、 Vanilla three.js 的な扱いが前提 となる。


PMX(MMD モデル)

  • three.js の MMDAnimationHelper をそのまま使用する 物理処理・剛体計算・IK 制御が Helper 内に完結しているため、 R3F の構造に合わせると挙動が破綻する。

  • R3F に寄せない(コンポーネント化しない) R3F の state や useFrame のライフサイクルとは非互換。

  • three.js の“別シーン”として扱うのが現実解 Canvas を分ける、あるいは別の Scene として並行管理する方式が安定。


まとめ

  • VRM three-vrm 1.x + R3F(AnimationMixer は手動)
  • BVH Vanilla three.js 的に手動管理(R3Fへ自作統合)
  • PMX three.js 専用パイプライン(R3F には載せない)

3つの形式を“同じ入り口から扱う”のではなく、 “形式ごとに最適化された入口を使い分ける” のが正しい構築方法。

6. 昨日の “1日消えたログ” を構造化して書く

この章では、実際の検証過程で発生した問題を時系列ではなく 「技術的な原因ごとに分類」して整理する。 読者が同じ誤りを避けるための記録としてまとめる。


起きた問題(分類整理)

1. VRM ロード関連の問題

  • VRM 1.0 が R3F + useLoader で正常にロードされない three-vrm 1.x と R3F のローダーの設計思想が一致していない。
  • VRMUtils.rotateVRM の挙動が壊れる VRM 1.x / 0.x の仕様差分が原因。 骨格構造の差異が想定外の回転を発生させる。

2. アニメーション関連の問題(BVH)

  • BVH が読み込めない/動かない three.js のバージョン依存により Loader が非互換。
  • AnimationMixer が glTF(VRM)と BVH を統合できない スケルトン構造が根本的に違い、共有不可。

3. three.js のバージョン差分による障害

  • three.js のバージョンだけでローダーやアニメーションが破綻 r145〜r152 の間で BVHLoader/Skeleton の内部実装が変化。 three-vrm の対応バージョンと一致していないと動作不可。

4. R3F に任せると破綻する領域

  • R3F の useLoader に VRM 1.x / BVH を任せると事故が発生 VRM/BVH はロジック層が重いため、R3F の抽象化に乗らない。 アニメーション制御は手動(AnimationMixer)で行う必要がある。

5. モデル仕様の根本差異(PMX)

  • PMX は VRM/glTF と文化が違いすぎて統合不能 物理・IK・剛体・ボーン・座標系すべて別体系。 three.js の MMDAnimationHelper 専用パイプラインが必要。

最後に気づいた本質

「正しい入口じゃない方向から実装を始めたため、互換性の壁に同時に衝突した。」

異なるモデル形式(VRM / BVH / PMX)を “同じ R3F の入口から扱おうとした誤り” が破綻の原因であり、 形式ごとに最適なパイプラインを選択する必要がある。

この記事の中心となるメッセージはここにある。

7. まとめ:R3Fで “キャラ資産” を扱うには道順がある

Next.js + React Three Fiber で VRM / BVH / PMX といったキャラクターモデル資産を扱う場合、 すべてを同じパイプラインで処理することはできない。

形式ごとに異なる仕様と制約が存在するため、 最初に「正しい入口」を選ぶことが、実装の安定性を大きく左右する。


VRM — three-vrm のバージョンを固定して扱う

  • 推奨:VRM 1.x + three.js v0.149
  • R3F の useFrame で AnimationMixer を手動管理
  • WebXR は別 Canvas に分離すると安定

BVH — Vanilla three.js + AnimationMixer が基本

  • BVHLoader は三.js のバージョン差分の影響が大きい
  • glTF(VRM) との統合は不可
  • Mixer の update() は R3F の useFrame に手動で統合する

PMX — three.js 専用パイプラインとして独立運用

  • MMDAnimationHelper が R3F とライフサイクル非互換
  • 物理処理・IK・座標系が VRM/gltf と完全に異なる
  • 専用 Scene / 専用 Canvas として扱うのが最も安定

結論

React Three Fiber は three.js の UI ラッパーであり、 全形式のキャラクターモデルを一元管理するための統合レイヤーではない。

  • VRM
  • BVH
  • PMX

それぞれのモデル形式は別々の文化・仕様・制約を持つため、 形式ごとに最適な入口(パイプライン)を選択することが R3F でのキャラ実装の最重要ポイントである。