VMDの補間曲線64バイトの読み方|Z移動と回転のx1は1行目に無い

VMDのボーンのキーフレームには、4項目(X移動・Y移動・Z移動・回転)の補間曲線が64バイトで入っています。「先頭16バイトだけ読めばよい」と説明されることが多いのですが、実物を数えると、1行目のうち2バイトは本当の値ではありませんでした。そこを読むと、回転の緩急が変わります。

最終更新: 2026-10-07 · kphpリポジトリ同梱の実VMD 30本(ボーンのキー255,380件)の64バイトを全件数え、three.js r171 の MMDLoader のソースで読み方を確認。ずれの大きさは、隣り合うキーの組ごとに補間の進み具合の差 × キー間の回転角で見積もった

このサイトの再生と書き出しは、2026-10-07からこの読み方に直しています。 それまでは three.js と同じく1行目を読んでいて、自前の書き出しもZ移動と回転の x1 を0で書いていました。

64バイトの並び

1つの曲線は3次ベジェの制御点2つ (x1, y1)・(x2, y2) で、値は0〜127です(127が1.0)。4項目ぶんで16個の値になり、1行目にはこう並びます。

1行目の位置中身
[0]〜[3]x1(X移動・Y移動・Z移動・回転の順)
[4]〜[7]y1(同じ順)
[8]〜[11]x2(同じ順)
[12]〜[15]y2(同じ順)

2〜4行目は、1行目を1バイトずつ左へずらした写しです。実物で確かめると、写しが1行目と食い違う場所は1行目の [2]・[3] の写しにあたる5か所([17]・[18]・[32]・[33]・[48])だけで、ほかは全件一致していました。

1行目の [2]・[3] は全部0

実物30本・255,380件のキーで、1行目の 2と 3は1件残らず0でした。一方、2行目の写しにあたる [17] は143,385件、[18] は134,552件で、0以外の値を持っています。

どちらが本当の値かは、MMDが新しいキーに入れる「直線」を見ると分かります。実物でいちばん多かった64バイト(68,138件)は次のとおりです。

20, 20,  0,  0, 20, 20, 20, 20, 107,107,107,107,107,107,107,107
20, 20, 20, 20, 20, 20, 20,107, 107,107,107,107,107,107,107,  0
20, 20, 20, 20, 20, 20,107,107, 107,107,107,107,107,107,  0,  0
20, 20, 20, 20, 20,107,107,107, 107,107,107,107,107,  0,  0,  0

X移動とY移動は (20, 20)-(107, 107) で、対角線上の直線です。1行目の0を信じると、Z移動と回転だけ (0, 20)-(107, 107) という直線でない曲線になります。写しの20を読めば、4項目とも同じ直線になります。Z移動と回転の x1 は、写しの [17]・[18] から読むのが正しいと判断しました(MMDが1行目の2バイトを何に使っているかは、ここでは確かめていません)。

  • 残りの写しも同じ値を持っています([32]=[17]、[33]=[48]=[18])
  • 64バイトがすべて0のキーも37,189件ありました。制御点が (0, 0)-(0, 0) の曲線は直線になるので、読み方で結果は変わりません

three.jsのMMDLoaderは0の方を読む

three.js(r171)の MMDLoader は、項目 i の x1 を interpolation[i] から読みます。X移動・Y移動はそれで正しいのですが、Z移動と回転は常に x1 = 0 として補間されます。

どれくらい変わるかを、実物30本で見積もりました。同じボーンの隣り合うキーの組は250,950あり、1フレームしか離れていない組を除いて、回転の曲線が食い違う組が124,560ありました。補間の進み具合の差の最大値に、2つのキーの間の回転角を掛けた値で見ると次のとおりです。

回転のずれ(見積もり)値
中央値1.29°
上位10%10.0°以上
上位1%44.2°以上
最大83.2°
5°を超える組25,544

MMDの既定の直線でも、x1 を0で読むと進み具合が最大0.070(区間の22%あたり)ずれます。作者が回転の緩急を付けたキーほど差が大きくなり、ダンスの止め・溜めの位置がずれて見えます。Z移動の位置のずれは、ほとんどが0で、最大3.56ユニットでした。

直し方

  • 読むとき: Z移動と回転の x1 だけ [17]・[18] から読む。ほかの14個は1行目のままでよい
  • three.jsに渡すとき: VMDのバイト列を写し、各キーの [2]・[3] へ [17]・[18] の値を入れてから MMDLoader に渡す。three.js のコードには手を入れずに済みます
  • 書くとき: 上のMMDの64バイトをそのまま使う。1行目をずらして写すと、1行目の0が写しにも入り、MMDで読み直したときにZ移動と回転が直線でなくなります

カメラのキーの補間曲線(24バイト)はずらし書きをしていない別の並びなので、この話は当てはまりません(カメラVMDの中身)。