4
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

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

はじめに

この記事は【目次】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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What are the problem?