Skip to main content
gamedev.net
🔒 Locked

jittering when interpolating between keyframes

Started by 16bit_port 8 replies 1.5k views
Original Post
16bit_port
16bit_port
Posted this in DirectX section but no one had an answer so I figure I'd try here.

JointLocalParams InterpAnimJoint;JointLocalParams &BeginAnimJoint = Frame.Joints[j];JointLocalParams &EndAnimJoint = NextFrame.Joints[j];//////// interpolation!!!!!	float InterpValue = FrameTimer.ElapsedTime()/FrameTimer.IntervalLength;	if( InterpValue > 1.f )		InterpAnimJoint = EndAnimJoint;	else if( InterpValue == 0.f )		InterpAnimJoint = BeginAnimJoint;	else if( BeginAnimJoint.Rotation == EndAnimJoint.Rotation )	{		InterpAnimJoint.Rotation = BeginAnimJoint.Rotation;		D3DXVec4Lerp( &InterpAnimJoint.Position, &BeginAnimJoint.Position, &EndAnimJoint.Position, InterpValue );		D3DXVec4Lerp( &InterpAnimJoint.Scale, &BeginAnimJoint.Scale, &EndAnimJoint.Scale, InterpValue );	}	else	{		//D3DXQuaternionNormalize( &BeginAnimJoint.Rotation, &BeginAnimJoint.Rotation );		//D3DXQuaternionNormalize( &EndAnimJoint.Rotation, &EndAnimJoint.Rotation );		D3DXQuaternionSlerp( &InterpAnimJoint.Rotation, &BeginAnimJoint.Rotation, &EndAnimJoint.Rotation, InterpValue );		//D3DXQuaternionNormalize( &InterpAnimJoint.Rotation, &InterpAnimJoint.Rotation );		D3DXVec4Lerp( &InterpAnimJoint.Position, &BeginAnimJoint.Position, &EndAnimJoint.Position, InterpValue );		D3DXVec4Lerp( &InterpAnimJoint.Scale, &BeginAnimJoint.Scale, &EndAnimJoint.Scale, InterpValue );	}


FrameTimer.ElapsedTime() is CurrentTimeStamp - PreviousTimeStamp

and

FrameTimer.IntervalLength is the time length we're checking against (in this case, the length of the frame)

Any thoughts?

Works fine if I always set it to BeginAnimJoint or EndAnimJoint (just a little bit choppy - as expected)
Ohforf sake
Ohforf sake
Does InterpValue have sane values during the animation? Is JointLocalParams::Rotation truly a quaternion or could it be a different representation (for example euler angles)?
RabidDog
RabidDog
You probably shouldn't be testing floating point == floating point, since they tend not to be equal even when they look like they should be.
16bit_port
16bit_port
InterpValues stay within [0,1] during the last else block. Definitely a quaternion.
16bit_port
16bit_port
Quote:
Original post by RabidDog
You probably shouldn't be testing floating point == floating point, since they tend not to be equal even when they look like they should be.


I changed it to :
float epsilon = .00001f;...else if( InterpValue < epsilon )	InterpAnimJoint = BeginAnimJoint;else if( ((BeginAnimJoint.Rotation.x - EndAnimJoint.Rotation.x) < epsilon) &&	 ((BeginAnimJoint.Rotation.y - EndAnimJoint.Rotation.y) < epsilon) &&	 ((BeginAnimJoint.Rotation.z - EndAnimJoint.Rotation.z) < epsilon) && 	 ((BeginAnimJoint.Rotation.w - EndAnimJoint.Rotation.w) < epsilon) )...


and I'm still getting the jittering.

RabidDog
RabidDog
If instead of using a timer you just increase InterpValue by 0.1f or something each frame, is it smooth then?
16bit_port
16bit_port
Less jittering but still some (expected because when its time for the next frame, it'll snap to the end/begin joint since incrementing it by .1 won't probably be enough to get to 1), so it's probably the timer.

class Timer{	public :		////////// Constructors, Destructor			Timer();			~Timer();		////////// Methods			bool TimeElapsedExceedsInterval();			void SetBeginningOfInterval();			float ElapsedTime();		////////// Member Variables			float IntervalLength;	private :		////////// Member Variables			float PreviousTimeStamp;			float InverseCountsPerSecond;		////////// Methods			float GetTimeStamp();};Timer::Timer(){	PreviousTimeStamp = 0;	__int64 CountsPerSecond = 0;	QueryPerformanceFrequency( (LARGE_INTEGER*)&CountsPerSecond );	InverseCountsPerSecond = 1.f / float(CountsPerSecond);}void Timer::SetBeginningOfInterval(){	PreviousTimeStamp = this->GetTimeStamp();}float Timer::GetTimeStamp(){	__int64 Counts = 0;	QueryPerformanceCounter( (LARGE_INTEGER*)&Counts );	return Counts * InverseCountsPerSecond;}float Timer::ElapsedTime(){	return this->GetTimeStamp() - PreviousTimeStamp;}bool Timer::TimeElapsedExceedsInterval(){	if( this->ElapsedTime() > IntervalLength )		return true;	return false;}...FrameTimer.IntervalLength = 1.f / _AnimationData.FramesPerSecond();


_AnimationData.FramesPerSecond() returns a float.

[Edited by - 16bit_port on June 11, 2010 4:31:41 PM]
BertS
BertS
You shouldn't use floats for your timer value. Use integers.
Ohforf sake
Ohforf sake
Quote:
You shouldn't use floats for your timer value. Use integers.

Or measure PreviousTimeStamp in ticks (as an integer value), rather than seconds, and do the conversion in Timer::ElapsedTime().
alagtriste
alagtriste
I've looked at D3DXQuaternionSlerp documentation and it doesn't speficy if it's doing a shortest way check. So maybe it isn't doing.

Try to perform an hemisphere check. Something along the lines:
D3DXQuat q0 = BeginAnimJoint.Rotation;D3DXQuat q1 = EndAnimJoint.Rotation;if (Dot(q0, q1) < 0.0){	q0 = -q0;}

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.