如何将连接关节的位置转换为相对三角形旋转

时间:2012-08-14 15:45:09

标签: c++ quaternions

我目前正在实施一个C ++解决方案来跟踪多个对象的运动。因为我在帧序列中跟踪了这些对象的点,使得每帧中有多个点。结果,我有整个帧序列的那些点的x,y,z坐标。通过研究已经生成的模型,我理解它包括相对于彼此移动的关节系统。每个关节都有一个父母,他们的动作是以四元数格式相对于父母的。因此,我想将相对于同一原点在3D空间中的x,y,z坐标转换为相对于其父级写入的四元数格式。然后我可以使用四元数来动画它们。

我不明白如何计算它所需的角度。你能否提供一个示例代码(用c ++)或任何有用的资源来解决这个问题。

2 个答案:

答案 0 :(得分:4)

所以我们有一个连接关节系统,我们想要找出从一个框架到另一个框架的关节的相对三角形旋转。我将相对旋转称为局部旋转,因为"相对旋转"它自己没有告诉我们它的相对之处。 (它是相对于一个物体,相对于宇宙的中心等等吗?)

假设

我将假设一个关节的树结构,这样每个关节只有一个父关节,我们只有一个关节(没有父母)。如果每个关节有几个父母,你应该可以使用相同的解决方案,但是你会在每个关节中为每个父母进行一次相对轮换,你需要为每个父母做一次计算。如果你有几个没有父母的关节,那么每个关节都可以被认为是它自己由连接关节组成的树的根。

我假设你有一个四元数学库,可以;从轴和角度创建,设置为标识,反转和累积四元数。如果您没有,您应该找到从维基百科或Google实施这些信息所需的所有信息。

计算旋转

下面的代码首先计算帧的开始和结束的关节的局部旋转。它使用两个向量进行计算;来自它父母的向量和从祖父母到父母的向量。然后,为了计算三角形旋转,它使用反向开始旋转来“去除”#34;通过应用它的反向旋转从结束旋转开始旋转。因此,我们最终得到该帧的局部delta旋转。

对于联合层次结构的前两个级别,我们有特殊情况可以直接解决。

伪代码

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 );   
}   

编写代码是为了提高可读性而不是最佳。

答案 1 :(得分: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);  
}}

这是我使用过的算法。我首先计算了每个帧的相对向量wrt到它的父级。这里root的父ID是0.然后我计算了每个关节的模型中的相对向量。这是递归调用的。