Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Interpolating between directions

Started by Jiia Nov 4, 2004 at 1:04 PM 9 replies 6.1k views
Original Post
Jiia
Jiia
I'm going to use this for my character's look at point. He has a TryLookDir (direction to something he wants to look at), and a CurLookDir (the direction he is already looking). I want to smoothly interpolate between these. The directions can never look backwards, so there is always only one route to take when interpolating a full 180 degrees. Interpolating directions is bad, mkay? Actually, it seems to work really well until the angle is more than 90 degrees. This makes perfect sense, but I'm not sure how I can manifest a direction which is up to 90 degrees difference from one direction to another. Do I have to convert this to a quaternion? I'm really trying to avoid doing that [smile] If anyone has any ideas, I would greatly appreciate it. edit: Is it complicated to perform an axis rotation on a unit vector? Where the axis is the cross between the two directions I want to rotate through?
nmi
nmi
A quaternion is just another representation for a direction and an amount of rotation. So you might as well chose another representation.
In your case it would be easy to create two unit sized vectors defining the plane you two look vectors are. Also make them perpendicular. Next calculate the projection of your two original vectors into the plane to get the angles. Then you can use a simple interpolation to get the angles in between. Transform back and your done.
SpaceDude
SpaceDude
Quote:
Original post by Jiia
edit: Is it complicated to perform an axis rotation on a unit vector? Where the axis is the cross between the two directions I want to rotate through?


Yeh thats the best way to do it... Get cross product the two vectors, normalise it... then use it as the rotation axis. The total angle that you need to rotate by to reach the final position is the inverse cosine of the dot product of the two vectors...

Its sufficiently complicated that i would not be able to give you the maths off the top of my head, but if you look it up its not so hard. You basically need to create a matrix transform. If you are using Direct 3D there are built in functions to handle this for you.
ajas95
ajas95
What happens at 90 degrees? I assume you're just linearly interpolating the direction vector then normalizing it, right? There's no reason past 90 should be a problem. At least, this is exactly what I do, and it works perfectly well.

The only problem I had was that if the camera is tilted down and you do this interpolation, the y-coord gets changed during normalization (which makes the lookat swoop further downward between the cur and try lookats), so I just fixed it to normalize the vector in such a way that the y-coord is preserved from the interpolated vector and that was it.
Jiia
Jiia
Hmm. I looked it up before I posted, but I seem to have a disability with comprehending math notation, heheh. The math doesn't bother me, but the symbols I cannot understand.

I don't expect anyone to translate it for me. But I have another question.

When a rotation is performed on a matrix, there are three directions being rotated. Where rotating the vector itself would only be a single direction. Wouldn't using a matrix be overkill? I know the maths to rotate a single vector on a world axis, but not on an arbitrary axis.

In other words, rotating y on a vector..
new_x = original_x * cosf(angle) + original_z * sinf(angle);
new_z = original_z * cosf(angle) - original_x * sinf(angle);
.. is much simpler than doing so on a matrix then transforming the vector with the matrix. Is the same true with an axis rotation? If not, I will happily use a matrix [smile]

Quote:
Original post by ajas95
What happens at 90 degrees? I assume you're just linearly interpolating the direction vector then normalizing it, right? There's no reason past 90 should be a problem. At least, this is exactly what I do, and it works perfectly well.

The only problem I had was that if the camera is tilted down and you do this interpolation, the y-coord gets changed during normalization (which makes the lookat swoop further downward between the cur and try lookats), so I just fixed it to normalize the vector in such a way that the y-coord is preserved from the interpolated vector and that was it.

I'm not dealing with a rotation on a camera, exactly. I'm using [CurLookDir dot GetRight()] as a factor to blend in a look-to-the-right animation for a 3rd person character. Also, [CurLookDir dot GetUp()] as the factor for blending up into the states. The idea is the same, though. Actually, I'm not having a problem beyound 90 degrees exactly, I just assumed there would be problems. It seems as though blending between left to right would add some up/down/forward/back. Renormalizing a roll from left to right is going to boost up one of those axes, but which one? Is there a way to control it?

Thanks for the help!
Dmytry
Dmytry
It should be done *exactly* like SLERP with quaternions, except that with vectors. Exactly same formules.
If you have two unit vectors A and B , you want to interpolate 'em with t , i think something like that will work:

C=B-A;
lc=Length(C);
alpha=asin(0.5*lc);// // alpha is angle between vectors.(Yet Another Formule :-)
w=sin(alpha*(t-0.5))/lc + 0.5;
Result=A+w*C;
Normalize(Result);// warning:bad things will happen if points is at 180 from eachother.

It should work equally with vectors and with quaternions, with anything with dimension>=2 .It's just spherical interpolation.
Jiia
Jiia
Quote:
Original post by Dmytry
C=B-A;
lc=Length(C);
alpha=asin(0.5*lc);// // alpha is angle between vectors.(Yet Another Formule :-)
w=sin(alpha*(t-0.5))/lc + 0.5;
Result=A+w*C;
Normalize(Result);// warning:bad things will happen if points is at 180 from eachother.

On the line, lc=Length(C), do you mean to get the length of the vector, or the angle length?

It seems to be setting Current to Try instantly. Here is the code converted to my setup. See anything horribly wrong?
#define LOOK_WEIGHTFACTOR 0.996f	// Weight factor for looking aroundFLOAT factor = powf(LOOK_WEIGHTFACTOR, FrameLaps); // FrameLaps is milliseconds passed this frameVEC diff = TryLookDir - CurLookDir;FLOAT diff_len = diff.Length();FLOAT alpha = asinf( 0.5f * diff_len );FLOAT w = sinf( alpha * (factor - 0.5f) ) / diff_len + 0.5f;CurLookDir += w * diff;CurLookDir.Normalize();

What does w represent? And is alpha just your every day angle between Try and Current? Also, is the diff_len + 0.5f; supposed to be (diff_len + 0.5f) so that the division occurs after the addition? Sorry for all of the questions.

Thanks Dmytry

edit: This is my old version, so that you can understand how I'm using factor:
FLOAT factor = powf(LOOK_WEIGHTFACTOR, FrameLaps);CurLookDir = (CurLookDir * factor) + (TryLookDir * (1.0f - factor));CurLookDir.Normalize();
Dmytry
Dmytry
w is like factor:
A+w*(B-A)=A+w*B-w*A=(1-w)*A+w*B // note the last!

As w grows from 0 to 1, result goes from A to B.

Same with t, as t grows from 0 to 1 , w goes from 0 to 1 with correction, and result goes from A to B.

Code is not very right. To fix it you need to use
FLOAT factor = 1.0f-powf(LOOK_WEIGHTFACTOR, FrameLaps);
'cause you need factor to be small to make result to be close to CurLookDir, and without 1.0-, you get relatively big factor and get result very close to TryLookDir.

i think no other fixes needed. (and addition must be really after division, code is correct there)

Also you need to check if diff_len is very small and in that case don't do anything, because of division by zero problem.(and floats have bad precision, too)

edit: spotted ze bug in my code, it should work anyway but not good, at all.
You should use
(tan(alpha*(t-0.5))*0.5*Length(A+B)/lc) + 0.5;
Eelco
Eelco
actualy, i would advice against using a 'mathematical' approach here, but rather use a physical approach.

doing an interpolation in a mathematical correct way can give some really funky results. for example, consider the head tilted slightly up.

then a new direction is assigned somewhat behind the character, also slightly elevated.

ill leave it to yourself to figure out what that will look like using any of the suggested methods above, but i guarantee you even the look of it will want to make you scream out in pain [grin].


what i think is a better approach is taking into account how humans are built, and how they behave. our head basicly has two degree of freedom: rotation around our spine and tilting.

what i would do is exctract the heads current and target orientation in terms of these degrees of freedom, which, as the clever reader will realize, are simple polar coordinates, and interpolate these values. you can just use a linear interpolation for them, and it will look great, or you can incorporate some acceleration, but i doubt thats really significant.

if you dont know how to convert to and from polar coordinates: google will help you.

this really cant go wrong, no matter the input, and is guaranteed to look more natural. besides its really not that hard.
ajas95
ajas95
Rotating about an axis perpendicular to the Try and Cur lookDirs is going to distort the y-coordinate also. It will take the "Great Circle Route"... just like an airplane flying from San Francisco to Japan (both at the same latitude) will fly far north near alaska. It's the shortest route. But that's not what you want.

So, when I correct my linearly-interpolated look-at vector, this is the code I use (where 'z' is the interpolated vector, and m_ornt.face(z) calculates the x and y axes from the given z):

	const float D = 1.0f - z.y * z.y;	const float L = z.x * z.x + z.z * z.z;	const float mult = sqrtf(D / L);	z.x *= mult;	z.z *= mult;	m_ornt.face(z);


[edit] I just read Eelco's post, about the 2 opposite directions, slightly elevated, the code that I posted solves exactly that problem. And I should note that it is used -instead- of normalizing z [/edit]
Jiia
Jiia
Thanks much for the help. The transitions between where he looks is very smooth and looks very realistic. I can plot a spot on the ground and have him walking around and it looks exactly like he lossed something [wink]

Unfortunately there is an accuracy problem. I don't think it has anything to do with the interpolation though. Either the dot product with the character's right/up is not getting the correct values to use as factors, or my animations just need tweaked.

It is super cool nonetheless. Making the characters look around at stuff as they do other things adds 90% more realism to them.

Thanks again [grin]

Topic Locked

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

Sign in to reply to this topic.