如何将连接关节的位置转换为相对增量旋转 [英] How to convert the positions of connected joints to relative delta rotations

查看:78
本文介绍了如何将连接关节的位置转换为相对增量旋转的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm currently implementing a C++ solution to track motion of multiple objects. In that I have tracked points of those objects in a frame sequences such that multiple points in each frame. As a result of that I have x, y, z coordinates of those points of the entire frame sequence. By studying an already generated model I understood it consists of a joints system which move relative to each other. Every joint has a parent and their movements are written relative to its parent in Quaternion format. Therefore, I want to convert my x,y,z coordinates, which are in 3D space relative to same origin, to quaternion format which are written as relative to its parent. I can then use the quaternions to animate them.

我不明白如何计算它需要的角度.能否请您给我提供一个示例代码(在 C++ 中)或任何有用的资源来解决这个问题.

I don't understand how to calculate the angle that it requires. Can you please provide me a sample code (in c++) or any useful resources to overcome this problem.

推荐答案

所以我们有一个连接关节系统,我们想找出关节从一帧到另一帧的相对增量旋转.我将相对旋转称为局部旋转,因为它本身的相对旋转"并没有告诉我们它相对于什么.(它与物体、宇宙中心等有关吗?)

So we have a system of connected joints and we want to find out the the relative delta rotation of the joints from one frame to another. I'll call the relative rotation the local rotation since the "relative rotation" on it's own doesn't tell us what it's relative to. (Is it relative to an object, to the center of the universe, etc?)

我将假设一个关节树结构,这样每个关节只有一个父关节,而我们只有一个根关节(没有父关节).如果每个关节有多个父级,您应该能够使用相同的解决方案,但是每个关节将每个父级进行一次相对旋转,并且您需要为每个父级进行一次计算.如果你有几个没有父节点的关节,那么每个关节都可以被认为是它自己的树中的一个根,由连接的关节组成.

I'm going to assume a tree structure of joints so that each joint only has one parent and we only have one root joint (without parents). If you have several parents per joint you should be able to use the same solution, but you'll then each joint will one relative rotation per parent and you need to do the calcualtion once for each parent. If you have several joints without parents then each one can be thought of as a root in it's own tree made up of the connected joints.

我假设你有一个四元数数学库,可以;从轴和角度创建,设置为恒等、逆和累积四元数.如果没有,您应该从 Wikipedia 或 Google 找到实现它们所需的所有信息.

I'll assume you have a quaternion math library that can; create from an axis and an angle, set to identity, inverse, and accumulate quaternions. If you don't you should find all the info you need to implement them from Wikipedia or Google.

下面的代码首先计算帧开始和结束时关节的局部旋转.它使用两个向量进行计算;来自它的父级的向量和从祖父级到父级的向量.然后为了计算增量旋转,它使用反向开始旋转通过应用它的反向旋转从结束旋转中移除"开始旋转.所以我们最终得到了该帧的局部增量旋转.

The code below first calculates the local rotations of the joint for the start and the end of the frame. It does this calculation using two vector; the vector from it's parent and the vector from the grandparent to the parent. Then in order to calculate the delta rotation it uses the inverted start rotation to "remove" the start rotation from the end rotation by applying it's inverse rotation. So we end up with the local delta rotation for that frame.

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

For the first two levels of the joint hierarchy we have special cases which we can solve directly.

out 参数是一个名为 result 的多维数组.
注意:startPosition、endPosition、parentStartPosition、parentEndPosition、grandParentStartPosition、grandParentStartPosition 都必须针对循环的每次迭代进行更新.该更新显示是为了关注问题的核心.

The out parameter is a multidimensional array named result.
NB: startPosition, endPosition, parentStartPosition, parentEndPosition, grandParentStartPosition, grandParentStartPosition all have to be updated for each iteration of the loops. That update is not shown in order to focus on the core of the problem.

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() 函数遍历树.

C++ implementation

Below is an untested recursive implementation in C++. For each frame we start at the root of our JointData tree and then traverse the tree by recursivly calling the JointData::calculateRotations() function.

为了使代码更易于阅读,我有一个从联合树节点 JointDataFrameData 的访问器.您可能不希望在您的实现中有这样的直接依赖.

In order to make the code easier to read I have an accessor from the joint tree nodes JointData to the FrameData. You probably don't want to have such a direct dependency in your implementation.

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

代码是为了可读性而不是最佳而编写的.

The code is written for readability and not to be optimal.

这篇关于如何将连接关节的位置转换为相对增量旋转的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆