PDA

View Full Version : CCD IK is Jerky and sometimes spazzy


bearsomg
08-27-2012, 09:50 PM
Hello. I am writing an IK solver for a 3D rendering engine that I am designing. My solver works correctly for the most part, but sometimes it will make the animation look jerky or completely spazz out (this usually happens when the joints have to bend over each other more).

This is my solver function:

D3DXMATRIX effTrans, destTrans, curBoneTrans;
D3DXVECTOR3 effPos, destPos, curBonePos;

D3DXVECTOR3 curBoneToEff, curBoneToDest;
D3DXVECTOR3 cross, refAxis;

D3DXVECTOR4 boneToEff, boneToDest;

D3DXQUATERNION rot, origEffRot, tmpRotQ;

D3DXMATRIX tmpRot, curBoneTransInv;

destTrans = _destBone->getCombinedTrans();
destPos = D3DXVECTOR3(destTrans(3,0), destTrans(3,1), destTrans(3,2));

origEffRot = _effBone->getRot();

double dot, angle, distance2;
float x;

for (int i=0;i<_numBone;i++)
{
_boneList[i]->UpdateIK()
}
_effBone->UpdateIK();

for (int i=0;i<_iteration;i++)
{
for (int b=0;b<_numBone;b++)
{
//get end effector position
effTrans = _effBone->getCombinedTrans();
effPos = D3DXVECTOR3(effTrans(3,0), effTrans(3,1), effTrans(3,2));

//get current bone position
curBoneTrans = _boneList[b]->getCombinedTrans();
curBonePos = D3DXVECTOR3(curBoneTrans(3,0), curBoneTrans(3,1), curBoneTrans(3,2));

//create inverse of bone position
D3DXMatrixInverse(&curBoneTransInv, NULL, &curBoneTrans);


//create point-to-point vector from bone inverse matrix
D3DXVec3Transform(&boneToEff, &effPos, &curBoneTransInv);
D3DXVec3Transform(&boneToDest, &destPos, &curBoneTransInv);

//transfer the vectors over
curBoneToEff = (D3DXVECTOR3)boneToEff;
curBoneToDest = (D3DXVECTOR3)boneToDest;


//get distance between direction vectors
distance2 = D3DXVec3Length(&(curBoneToDest-curBoneToEff));
distance2 *= distance2;
if (distance2 < IK_MIN_DIST)
{
//stop if end effector is close enough
i = _iteration;
break;
}

D3DXVec3Normalize(&curBoneToEff, &curBoneToEff);
D3DXVec3Normalize(&curBoneToDest, &curBoneToDest);

//get dot product and angle between the 2 vectors
dot = D3DXVec3Dot(&curBoneToDest, &curBoneToEff);
if (dot > 1.0f)
{
continue;
}
angle = acos(dot);
if (fabs(angle) < IK_MIN_ANGLE)
{
continue;
}
if (angle < -_angleConstraint)
{
angle = -_angleConstraint;
}
else if (angle > _angleConstraint)
{
angle = _angleConstraint;
}

//get the cross product (rotation axis)
D3DXVec3Cross(&cross, &curBoneToEff, &curBoneToDest);

//get length of axis
distance2 = D3DXVec3Length(&cross);
distance2 *= distance2;
if (distance2 < IK_MIN_AXIS && i > 0)
{
//axis is too small (direction dest to target is too close) skip to next bone
continue;
}

D3DXVec3Normalize(&cross, &cross);
//build rotation from axis and angle
D3DXQuaternionRotationAxis(&rot, &cross, (float)angle);

//check is current bone is limited to x
if (_boneList[b]->getIsLimitX())
{
quatToEuler(rot, NULL, NULL, &x);

if (x < -PI)
{
x = -PI;
}
if (-IK_MIN_ROT_SUM < x)
{
x = -IK_MIN_ROT_SUM;
}

D3DXMatrixRotationX(&tmpRot, x);
D3DXQuaternionRotationMatrix(&rot, &tmpRot);
}

D3DXQuaternionNormalize(&rot, &rot);

//apply rotation to bone
//combine with previous rotation for multiple iterations
tmpRotQ = _boneList[b]->getRot();
tmpRotQ *= rot;
D3DXQuaternionNormalize(&rot, &rot);
_boneList[b]->setRot(tmpRotQ);

for (int j=b;j>=0;j--)
{
_boneList[j]->UpdateFromIK();
}
_effBone->UpdateFromIK();
}
}
_effBone->setRot(origEffRot);
_effBone->UpdateFromIK();

The UpdateIK() function uses this algorithm to update the bone's combined transform:

D3DXMatrixRotationQuaternion(&combTrans, &combRot);
combTrans(3,0) = translate(3,0) + offset.x;
combTrans(3,1) = translate(3,1) + offset.y;
combTrans(3,2) = translate(3,2) + offset.z;

if (_parent)
combTrans *= _parent->getCombinedTrans();


Here is a video example of how the IK will be jerky or spazz out. Look at the left leg. The video is the model's bone structure.

http://youtu.be/w56-Kv0akNo

ThE_JacO
09-07-2012, 08:13 AM
Honestly, I haven't read the code.
But looking at the video, and assuming you're using the pretty standard law of cosine 3 point IK for 2 joints chains, it simply looks like you have a preferred plane implementation with no directionality.

Given two points, two lengths and a plane for them live on, there's always two solutions to law of cos or practically any other stateless (no temporal dimension) IK solver. The knee might go one way or the other basically.

Make sure a direction is available for the IK to solve the knee consistently, and that direction doesn't become ambiguous (never aligned to the solver's plane).

CGTalk Moderation
09-07-2012, 08:13 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.