「がんばれない」けど「がんばりたい」

ITエンジニアの仕事のこと。AI、機械学習、ディープラーニング。地頭力。車のこと。

回転 Quaternion その4 |3Dプログラミング整理

つづきです。

今回はofQuaternionのmakeRotateメソッドを使って、 回転してみます。

個人的に今抱えている問題を解決するには、 こっちの方を使う事になる事が、この記事を書いているうちに分かってきました。

Kinect(使った事ないけど…)とか、 LeapMotionなど、まぁその他の簡単なセンサーの入力によって、 画面に表示しているオブジェクトを回転させるという事は良くある事。

センサーの値=>何度回転させる というふうに回転角度をロジックの方で決めるのであれば、 あとは、回転軸に沿って回転させてやればOK。 ※回転軸もロジックの中で決まるという条件です

だけども上記のようにロジック内で回転角や回転軸が 決まらない・決められない場合があります。

具体的な課題例として、 LeapMotionのなんかだと、 掌(Hand)オブジェクトが持っているpalmNormal()という法線ベクトルを リアルタイムに取得出来るのですが、

掌…というか手首を回転すると、もちろん掌の向きも変化するので、 法線ベクトルが変化します。

この時、何度回転しかか?というデータは、 LeapMotionのHandオブジェクトからは取得できない(※1)ので、 この場合の掌の変化における回転を画面のオブジェクトに対して、 作用させるのであれば、法線ベクトルを利用するしかありません。

※1|LeapMotionのHandオブジェクトには過去の掌オブジェクトの参照を得られて、 しかも何度回転したか?と、その回転軸を得られるようなメソッドがあるようですが、 100fpsを超えるスピードでデータを取得している最中に過去の掌と現在の掌が、 必ずしも同じものという保証は出来ないと思っています。 もし、このメソッドを採用するのであれば別途、現在の掌と過去の掌のオブジェクトが、 同じかどうかの判定が必要

というように、ベクトルから回転角度を取得するという問題を、 今回は考察します。

問題を単純化する為に、 あるプリミティブなベクトルが、 何かしらの影響で回転が起こってしまったというのを仮定し、 その時の回転角度を求めてみます。

この角度が求められれば、 上述した、他のオブジェクトに対して、その角度を適用させれば良いよねという、 考えです。

今回のイメージは以下。

fig01.001

1)状態や姿勢を示してくれているベクトルが   ある要因で回転されて2)の状態になった。   ※法線ベクトルなどをイメージ

2)このとき1)から、どのくらいの角度回転したか?を求めて、それを他のオブジェクト(あればですが…)にも作用させる。

というイメージになります。 おっと、ちなみに1)から2)の回転軸は、z軸になります。 これは、この後コード検証しているところで、quaternionを作成する時に、 何気に回転軸を(0,0,1)としているところが理解出来ない人が居るといけないので、 ここで、一応説明しておきます。 回転軸がz軸になるのは、想像できますよね。 出来ない方は、この記事よりも昔の記事を見てください。

どのくらいの角度回転したか?については、 特に問題なく求められます。 ベクトルの長さは100pxとなっているし、 回転後のベクトルも得られている。

そうすると三角関数で、 アークタンジェントを使用すれば求められます。

fig02

回転角度α = arctan( 50 / 86.6025 ) * RAD_TO_DEG; ちなみに、この結果は30度となります。

ここで、最初の状態(100,0,0)というベクトルがありましたが、 結局のところ、回転角度αを求めるのに使用している値は、 変化後の値のみしか使っていません。

これは3D座標系の起点が決まっているという前提があるので、 特に最初の状態が必要ではないという推測です。

そうでないケースもあると思いますが、 今回は、問題を単純化したいので全てのオブジェクトの起点を スクリーン座標の起点である左上(0,0,0)にしています。

1)の状態から、 ある要因によって2)のベクトルが得られたとして、

そして回転角度αをarctanで求め、 本当に1)のベクトルに対して求めた角度αだけ回転させたら、 2)のベクトルが再現されるか?というのをofQuaternionを用いた場合の 検証コードを示します。

次回は、1)のベクトルが(100,0,0)でなかった場合の考察をしてみたいと思いますが、 意味あるのかな… 一旦カメラの整理に入るかも。。

#include "testApp.h"


//  1)のベクトル (100, 0, 0)
ofVec3f from;

//  2)のベクトル (86.6025, 50, 0)
ofVec3f to;


//--------------------------------------------------------------
void testApp::setup(){
    
    ofSetFrameRate(60);
    ofSetLineWidth(1);
    
    from = ofVec3f(100, 0, 0);
    to = ofVec3f(86.6025, 50, 0);
}


//--------------------------------------------------------------
void testApp::update(){
}


//--------------------------------------------------------------
void testApp::draw(){
    
    ofBackground(0, 0, 0);
 
    ofSetColor(255);
   
    //  1)のベクトルを確認の為にlineで表示しています
    ofPushMatrix();
    {
        ofTranslate(ofGetWidth()/2, ofGetHeight()/2, 0.0);
        ofLine(0.0, 0.0, 0.0, from.x, from.y, from.z);        
    }
    ofPopMatrix();
    

    //  スクリーン座標をワールド座標とし、
    //  左上の(0,0)を起点とし、2)の回転後のベクトルの回転角度を求める
    float angle = (float)atan(to.y/to.x) * RAD_TO_DEG;
    
    
    //  求めた角度を元にQuaternionオブジェクトを作成する
    //  回転軸はz軸なので、makeRotateの回転軸は(0,0,1)になっています。
    ofQuaternion q;
    q.makeRotate(angle, 0, 0, 1);

    
    //  2)のベクトルを求めたquaternionでtoオブジェクトとは別のオブジェクトfinalとして作成し、
    //  2)と同じベクトルと同じものになるかを検証する
    ofPushMatrix();
    {
        ofTranslate(ofGetWidth()/2, ofGetHeight()/2, 0);
        
        ofVec3f final = q * from;
        ofSetColor(255, 255, 0);
        ofLine(0, 0, 0, final.x, final.y, final.z);
    }
    ofPopMatrix();    
}