5

現在、複数のオブジェクトの動きを追跡する C++ ソリューションを実装しています。その中で、各フレームに複数のポイントがあるように、フレームシーケンスでこれらのオブジェクトのポイントを追跡しました。その結果、フレーム シーケンス全体のこれらの点の x、y、z 座標が得られます。すでに生成されたモデルを研究することで、それが互いに相対的に動く関節システムで構成されていることがわかりました。すべてのジョイントには親があり、その動きはその親を基準にしてクォータニオン形式で書き込まれます。したがって、同じ原点を基準とした 3D 空間にある x、y、z 座標を、親に対して相対的に記述された四元数形式に変換したいと考えています。その後、クォータニオンを使用してそれらをアニメーション化できます。

必要な角度を計算する方法がわかりません。この問題を解決するためのサンプル コード (C++) または有用なリソースを提供してください。

4

2 に答える 2

4

したがって、接続されたジョイントのシステムがあり、あるフレームから別のフレームへのジョイントの相対的なデルタ回転を見つけたいと考えています。「相対回転」自体では、それが何に対して相対的であるかがわからないため、相対回転をローカル回転と呼びます。(それは、宇宙の中心などに対して、オブジェクトに対して相対的ですか?)

仮定

各ジョイントには親が 1 つだけあり、ルート ジョイントが 1 つだけ (親なし) になるように、ジョイントのツリー構造を想定します。ジョイントごとに複数の親がある場合、同じソリューションを使用できるはずですが、各ジョイントは親ごとに 1 つの相対回転を行うため、親ごとに計算を 1 回行う必要があります。親のないジョイントが複数ある場合、それぞれが、接続されたジョイントで構成される独自のツリーのルートと考えることができます。

次のことができる四元数数学ライブラリがあると仮定します。軸と角度から作成し、単位元、逆数、四元数の累積に設定します。そうでない場合は、ウィキペディアまたは Google からそれらを実装するために必要なすべての情報を見つける必要があります。

回転の計算

以下のコードは、最初にフレームの開始と終了のジョイントのローカル回転を計算します。2 つのベクトルを使用してこの計算を行います。親からのベクトルと祖父母から親へのベクトル。次に、デルタ回転を計算するために、反転された開始回転を使用して、逆回転を適用することによって終了回転から開始回転を「削除」します。そのため、そのフレームのローカル デルタ ローテーションで終了します。

ジョイント階層の最初の 2 つのレベルには、直接解決できる特殊なケースがあります。

疑似コード

out パラメータは、 resultという名前の多次元配列です。
注意: startPosition、endPosition、parentStartPosition、parentEndPosition、grandParentStartPosition、grandParentStartPosition はすべて、ループの反復ごとに更新する必要があります。問題の核心に焦点を当てるために、その更新は表示されません。

for each frame {
  for each joint {

    if no parent {
      // no parent means no local rotation
      result[joint,frame] = identityQuaternion
    } 
    else {
      startLink = startPosition - parentStartPosition
      endLink = endPosition - parentEndPosition

      if no grandParent {
        // no grand parent - we can calculate the local rotation directly
        result[joint,frame] = QuaternionFromVectors( startLink, endLink )
      } 
      else {
        parentStartLink = parentStartPosition - grandParentStartPosition
        parentEndLink = parentEndPosition - grandParentEndPosition

        // calculate the local rotations 
        // = the difference in rotation between parent link and child link
        startRotation = QuaternionFromVectors( parentStartLink, startLink )
        endRotation = QuaternionFromVectors( parentEndLink, endLink )

        // calculate the delta local rotation
        // = the difference between start and end local rotations
        invertedStartRotation = Inverse( startRotation )
        deltaRotation = invertedStartRotation.Rotate( endRotation )
        result[joint,frame] = deltaRotation 
      }
    }
  }
}

QuaternionFromVectors( fromVector, toVector ) 
{
  axis = Normalize( fromVector.Cross( toVector ) )
  angle = Acos( fromVector.Dot( toVector ) )
  return Quaternion( axis, angle )
}

C++ 実装

以下は、テストされていない C++ での再帰的な実装です。フレームごとに、JointDataツリーのルートから開始し、 JointData::calculateRotations()関数を再帰的に呼び出してツリーをトラバースします。

コードを読みやすくするために、ジョイント ツリー ノードJointDataから FrameData へのアクセサーがあります。おそらく、実装でそのような直接的な依存関係を持ちたくないでしょう。

// Frame data holds the individual frame data for a joint
struct FrameData
{
    Vector3 m_positionStart;
    Vector3 m_positionEnd;

    // this is our unknown
    Quaternion m_localDeltaRotation;
}

class JointData
{
public:
    ...
    JointData *getChild( int index );
    int getNumberOfChildren();

    FrameData *getFrame( int frameIndex );

    void calculateDeltaRotation( int frameIndex, JointData *parent = NULL, 
                                 Vector3& parentV1 = Vector3(0), 
                                 Vector3& parentV2 = Vector3(0) );
    ...
}

void JointData::calculateDeltaRotation( int frameIndex, JointData *parent, 
                                        Vector3& parentV1, Vector3& parentV2 )
{
    FrameData *frameData = getFrame( frameIndex );

    if( !parent ) 
    {
        // this is the root, it has no local rotation
        frameData->m_localDeltaRotation.setIdentity();
        return;
    }

    FrameData *parentFrameData = parent->getFrame( frameIndex );

    // calculate the vector from our parent
    // for the start (v1) and the end (v2) of the frame
    Vector3 v1 = frameData->m_positionStart - parentFrameData->m_positionStart;
    Vector3 v2 = frameData->m_positionEnd - parentFrameData->m_positionEnd;

    if( !getParent()->getParent() )
    {
        // child of the root is a special case, 
        // we can calculate it's rotation directly          
        frameData->m_localDeltaRotation = calculateQuaternion( v1, v2 );
    }
    else
    {
        // calculate start and end rotations
        // apply inverse start rotation to end rotation 
        Quaternion startRotation = calculateQuaternion( parentV1, v1 );
        Quaternion endRotation = calculateQuaternion( parentV2, v2 );       
        Quaternion invStartRot = startRotation.inverse();

        frameData->m_localDeltaRotation = invStartRot.rotate( endRotation );
    }

    for( int i = 0; i < getNumberOfChildren(); ++i )
    {
        getChild( i )->calculateRotations( frameIndex, this, v1, v2 );
    }
}

// helper function to calulate a quaternion from two vector
Quaternion calculateQuaternion( Vector3& fromVector, Vector3& toVector )
{
    float angle = acos( fromVector.dot( toVector ) );
    Vector3 axis = fromVector.cross( toVector );
    axis.normalize();
    return Quaternion( axis, angle );   
}   

コードは読みやすくするために書かれており、最適ではありません。

于 2012-08-17T22:24:42.033 に答える
0
Point3d Controller::calRelativeToParent(int parentID,Point3d point,int frameID){
if(parentID == 0){
    QUATERNION temp = calChangeAxis(-1,parentID,frameID);
    return getVect(multiplyTwoQuats(multiplyTwoQuats(temp,getQuat(point)),getConj(temp)));
}else{
    Point3d ref = calRelativeToParent(originalRelativePointMap[parentID].parentID,point,frameID);
    QUATERNION temp = calChangeAxis(originalRelativePointMap[parentID].parentID,parentID,frameID);  
    return getVect(multiplyTwoQuats(multiplyTwoQuats(temp,getQuat(ref)),getConj(temp)));
}}
QUATERNION Controller::calChangeAxis(int parentID,int qtcId,int frameID){ //currentid = id of the position of the orientation to be changed
if(parentID == -1){
    QUATERNION out = multiplyTwoQuats(quatOrigin.toChange,originalRelativePointMap[qtcId].orientation);
    return out;
}
else{
    //QUATERNION temp = calChangeAxis(originalRelativePointMap[parentID].parentID,qtcId,frameID);
    //return multiplyTwoQuats(finalQuatMap[frameID][parentID].toChange,temp);
    return multiplyTwoQuats(finalQuatMap[frameID][parentID].toChange,originalRelativePointMap[qtcId].orientation);  
}}

これは私が使用したアルゴリズムです。最初に、各フレームの親に対する相対ベクトルを計算しました。ここで、ルートの親 ID は 0 です。次に、各関節のモデル内の相対ベクトルを計算しました。これは再帰的に呼び出されます。

于 2012-08-29T16:54:06.873 に答える