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

More than 1 year has passed since last update.

posted at

updated at

TouchDesignerでAzure Kinectを使ってBlenderで作成した自作モデルを動かす

Azure KinectのBody Trackingを使ってアバターを動かす(IKではなくFK)的なことを検索するとUnityの情報ばっかり出てきて、TouchDesignerでどうやんねんって情報が見つからなかったのでやってみました。

今回はこんな感じで、Blenderで作った簡単なモデルにボーンを入れて、ちゃんと動きに追従しますよというところまでです。

前提

前回までやっていたような、トラッキングされた各関節の位置に何かを表示するという場合は、モデルには親子階層構造を持たせずに、ワールド位置とワールド回転(?)を反映させるのが簡単です。

が、今回やろうとしているような、3Dモデル(アバター)を動かそうという場合はそれではいけません。関節間がモデルの体型を無視して演者のサイズに合わせて伸び縮みしてしまいます。

ですので、関節間の距離は無視して回転だけを適用するという手法がよく使われます。ルートノードから順に計算していく手法であるため、Forward Kinematics(FK)と呼ばれます。

ちなみに、末端の位置または向きを先に決め、そこからルートに向かって角度計算を伝播させていく手法をInverse Kinematics(IK)といいます。

今回はFKをやります。

やることを整理すると、

  • Blenderでモデルを作って
  • TouchDesignerにインポートして
  • Azure KinectのBodyTrackingから取れるデータを使用して
  • モデルを動かす

です。

Blenderでモデル作り

「僕、Blender触ったことないんですよね・・・」
マジです。Blender起動したことくらいはありますが、「え?オブジェクト選択できないんだけど?え?動かせないんだけど?え?え?え?」ってなって何もしてませんでした。

ということで入門してみました。
Blenderのバージョンは2.83.4です。
キーボードでサクサク操作できるのすんごい良いですね。

今回作るものはこういう、円錐にボーンが入ってるやつです。これを腕にします。
スクリーンショット 2020-08-20 12.10.29.png

Blenderの話を飛ばしてTDの話を読みたい方はこちら

はじめてのモデル作成始めます。

まず最初に配置されてるキューブを消します。
選択してx

メッシュを作る

後から分かったんですが、はじめに知っておいた方がよかったことを書いておきます。

  • Blender座標はZが上
    • 変更は書き出し時に可能
  • Blenderはメートル法
    • このあとコーンを長さ4mとかで作ってしまう。50cmとかで作ればよかった
    • 書き出し時にスケーリング可能
  • BlenderのボーンはY方向を向いている
    • 変更は書き出し時に可能

Shift+AでMesh(Cone)追加
クリックで選択
rで回転
yで回転軸(y)固定
90と打って角度を指定
sx2と打ってx方向2倍
gx2でx方向移動(コーンの底面が原点になる)
動かした時にどの軸で回転しているのかがわかるようにsy0.5でY方向に潰しておく
Shift+Dで複製
gx4でx方向移動(旧コーンの頂点に新コーンの底面)
Shift+D, gx4でもう一つ複製しておく

いまこんな感じ
スクリーンショット 2020-08-20 11.49.44.png

ボーンを入れる

Shift+Sで3Dカーソルを移動するメニューを開く
Cursor To Originを選んで原点(コーンの底面中心)へ移動させる
Shift+AでArmatuerがカーソル位置に作成される
こんな感じで作られる
スクリーンショット 2020-08-20 11.59.08.png

ry90でコーンと同じ方向を向くように回転させる
TabでEditモードへ
Boneのtip(先っちょ)を選択してgx3でコーン先端へ移動させる
ex4で次のコーン用のボーンを作成
再度ex4
スクリーンショット 2020-08-20 12.10.29.png

ボーンの向きを設定する

公式ドキュメントにある各関節の向きに合うようにボーンを回転させる
※軸のXYZは合ってなくていいから、矢印が同じ形になるように編集していく
ちなみにこれは左腕のつもり

スクリーンショット 2020-08-20 22.34.05.png

メッシュとボーンの関連付け

aで全選択
Object->Parent->Armature Deform With Automatic Weightsすると、メッシュがArmatureの子になる
(Ctrl+Pでparentメニューへショートカット)
スクリーンショット 2020-08-20 12.13.50.png
スクリーンショット 2020-08-20 12.13.56.png

(動作確認)
Ctrl+TabでPoseモード
ボーンを選択してrで動かしてみる
スクリーンショット 2020-08-20 12.19.45.png

FBXで書き出し

  • Scaleを0.01にする
    • BlenderもTDもメートルスケールのはずだが、こうしないとダメだった。多分どこかに設定がある
  • ForwardをZに、UpをYに
    • TD側の座標に合わせるため
  • BoneAxisをPrimaryをXに、Secondaryを-Zに
    • ボーンのローカル回転座標を公式ドキュメントに合わせるため

スクリーンショット 2020-08-20 22.19.26.png

FBXをTouchDesignerで読む

FBXはTDのウィンドウにD&Dすれば読めます。
Blenderで作ったメッシュとボーンの構造がそのまま表れていますね。
スクリーンショット 2020-08-21 9.10.36.png

今回気が回らなかったのですが、Blender側で最初のボーンを回転させて倒して作ってしまったために、TD側でもBone(180,0,0)の回転が入ってしまっています。
このボーンはKinectからの値で動かしたいので、急場しのぎですがArmatureにその分の回転を押し付けました。((90,0,180) => (-90,0,180)
本来は、回転を操作しないルートノード的なボーンを一つ置いておけば良いのだと思います。

Azure Kinectから情報を取得

Azure Kinect TOPを置いて、Azure Kinect CHOPに食わせればスケルトンの情報が取れます。
今回は前作ったレコーダーで書き出したデータをMacで再生しています。

ここでDerivativeに感謝したいんですけど、Azure Kinectのドキュメントによると、Azure KinectのSDK自体は、回転の情報はワールドでのQuaternionしか渡してくれません。しかもその基準になる各関節の座標軸はドキュメントにある「独自の関節座標系」とのこと。

しかしThanks to Derivative, Azure Kinect CHOP が面倒な計算をやってくれて、我々ユーザーはTDでそのまま使えるオイラー角での回転として使えます。

さらに、Kinect SDKが返す位置情報はミリメートル単位ですが、TDではメートルであるのもちゃんと考慮してくれているようです。
どこまでが親切でどこからがお節介なのかは決められませんが、分かって使う分にはお手軽で良い仕様だと思います。
今回は手探りだったので、Kinectのドキュメントに書いてあることとTDで実際に取れる値が違って戸惑ったこともありました。。

ともあれ、Azure Kinect CHOPからは[プレイヤーインデックス]/[関節名]:[パラメータ名]という名前で情報が取得できます。
パラメータ名は以下です。

  • 位置: tx,ty,tz
  • 回転(親からの相対): rx,ry,rz
  • 回転(カメラ座標での絶対値): [関節名]_abs:rx,ry,rz

モデルへ適用

望みの関節の回転の値を対応するボーンに設定してやればOKです。

ボーンの数が多くなってくると手動でスクリプトを書くのが辛くなってくるので、こんな感じのTable DATを用意して、Exportスイッチ(右下の緑色のスイッチ)をオンにすると、指定したジョイントの姿勢をボーンに反映するのが比較的楽にできます。
スクリーンショット 2020-08-21 9.25.46.png

path    parameter   value   enable
Armature    tx  op('current_frame')['shoulder_l:tx']    1
Armature    ty  op('current_frame')['shoulder_l:ty']    1
Armature    tz  op('current_frame')['shoulder_l:tz']    1
Bone    rx  op('current_frame')['shoulder_l:rx']    1
Bone    ry  op('current_frame')['shoulder_l:ry']    1
Bone    rz  op('current_frame')['shoulder_l:rz']    1
Bone_001    rx  op('current_frame')['elbow_l:rx']   1
Bone_001    ry  op('current_frame')['elbow_l:ry']   1
Bone_001    rz  op('current_frame')['elbow_l:rz']   1
Bone_002    rx  op('current_frame')['wrist_l:rx']   1
Bone_002    ry  op('current_frame')['wrist_l:ry']   1
Bone_002    rz  op('current_frame')['wrist_l:rz']   1

これで左腕ができました。
あとは同じことを体全体でやればアバターが動かせるはずです。たぶん。

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?