我目前正在实施一个C ++解决方案来跟踪多个对象的运动。因为我在帧序列中跟踪了这些对象的点,使得每帧中有多个点。结果,我有整个帧序列的那些点的x,y,z坐标。通过研究已经生成的模型,我理解它包括相对于彼此移动的关节系统。每个关节都有一个父母,他们的动作是以四元数格式相对于父母的。因此,我想将相对于同一原点在3D空间中的x,y,z坐标转换为相对于其父级写入的四元数格式。然后我可以使用四元数来动画它们。
我不明白如何计算它所需的角度。你能否提供一个示例代码(用c ++)或任何有用的资源来解决这个问题。
答案 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 ++中未经测试的递归实现。对于每个帧,我们从 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.然后我计算了每个关节的模型中的相对向量。这是递归调用的。