Edited at

PyQt5でクォータニオンの回転

More than 1 year has passed since last update.


はじめに

この記事は【目次】MMDモーショントレース自動化への挑戦 の一環です。

導入方法や他の技術解説等は、上記目次から各記事を参照してください。

ほぼ自分用作業メモ。


Github

VMD-3d-pose-baseline-multi


元ネタ

このソースコードは、VMD-Lifting を改変しています。

3D関節データからVMDフォーマットへの変換処理は、VMD-Lifting がやっていること - Qiita を参照してください。


課題

生成したVMDファイルをMMDで読み込むと、やや前傾した姿勢になること。


アプローチ



  • VMD-Lifting の出力結果と比較したところ、同じ動作でもモーションがかなり異なる事が判明



  • VMD-Lifting は前傾していない事から、miu200521358/3d-pose-baseline-vmd で生成された3D関節データが既に前傾しているものと想定

  • 野球の投球動画などから、モーションを生成したところ、ローカルX軸(上半身等のX軸方向の傾き)ではく、グローバルX軸(MMD上で表示される座標X軸)方向に17度ほど傾いている事が判明


解決策


1. 補正用クォータニオンの生成

グローバルX軸方向に指定角度(デフォルトで17度)傾けた補正用クォータニオンをまず生成する。

飛行機軸回転2.png

飛行機イラスト) GATAG

MMDにおいては、


  • ピッチ = グローバルX軸

  • ヨー = グローバルY軸

  • ロール = グローバルZ軸

# 補正角度のクォータニオン

# 3次元の角度指定による回転から、クォータニオンを求める
correctqq = QQuaternion.fromEulerAngles(QVector3D(xangle, 0, 0))


QQuaternion QQuaternion::fromEulerAngles(float pitch, float yaw, float roll)

Creates a quaternion that corresponds to a rotation of roll degrees around the z axis, pitch degrees around the x axis, and yaw degrees around the y axis (in that order).


引用元)QQuaternion Class | Qt GUI 5.10


2. 角度の結合

# 上半身

direction = pos[8] - pos[7]
up = QVector3D.crossProduct(direction, (pos[14] - pos[11])).normalized()
upper_body_orientation = QQuaternion.fromDirection(direction, up)
initial = QQuaternion.fromDirection(QVector3D(0, 1, 0), QVector3D(0, 0, 1))
# 補正をかけて回転する
bf.rotation = correctqq * upper_body_orientation * initial.inverted()

upper_body_orientation … 上半身角度クォータニオン

initial … 初期状態からの捻り情報クォータニオン


  1. まず、グローバルX軸に補正角度分回転させる

  2. 補正角度分回した後、上半身の角度分回転させる

  3. 初期状態からの捻り角度分回転させる

順番を間違えると正しく補正されませんでした。


2. 角度の結合(末端の場合)

腕(←上半身)、膝(←下半身)等、親ボーンの回転が影響する場合、自分自身の回転にまず角度補正を行い、その後、親ボーンの回転を差し引く。

# 左腕

direction = pos[12] - pos[11]
up = QVector3D.crossProduct((pos[12] - pos[11]), (pos[13] - pos[12]))
orientation = QQuaternion.fromDirection(direction, up)
initial_orientation = QQuaternion.fromDirection(QVector3D(1.73, -1, 0), QVector3D(1, 1.73, 0))
rotation = correctqq * orientation * initial_orientation.inverted()
# 左腕ポーンの回転から親ボーンの回転を差し引いてbf.rotationに格納する。
# upper_body_rotation * bf.rotation = rotation なので、
bf.rotation = upper_body_rotation.inverted() * rotation


結果

MMDモーショントレース自動化への挑戦【ver0.02】


  • 前傾は概ね直った

  • デフォルト値で概ね修正可能だが、動画によっては微調整した方がいいものもある

  • シーンによっては後傾しちゃってる箇所も…


次回予定

とりあえずフレームの間引き。

同一円周上の三点の移動(関節の回転)が、一定角度差以内であれば、間引く…予定。

#これをクォータニオンでどう表現するのかが分かりませんw