Sign in to follow this  
Bemba

Camera with Angular Velocity or Angular Momentum

Recommended Posts

Hi All, I'm trying to add Angular Velocity (or possibly Angular Momentum) with a clamped Angular Acceleration to my camera system and have begun to bang my head against a brick wall. So I thought I might shout for help from those of you with a better understanding than myself. The reason I only say possibly Angular Momentum is because the Moment of Inertia would be uniform and constant. I have a camera system that currently stores its orientation (and position in a matrix. Each Step I calculate an ideal "lookat" matrix. Rather than Slerp between the two what I want to do maintain an Angular Velocity for the camera, and accelerate the camera (using a maximum acceleration) towards the ideal orientation. My thinking was: 1. Get the Angular Displacement between the Ideal and Current orientations (this would be the Angular Velocity we would need to have to make change the orientation to the ideal orientation in one time step.) 2. Get the direction of this angular velocity. 3. Apply an Angular Acceleration to the Angular Velocity in this direction, but limiting the Angular Acceleration to some maximum constant. 4. Apply this new Angular Velocity to the Current orientation to get the new orientation. The part I'm having the biggest problem with is the direction of the Angular Acceleration, but the whole solution would be great. This calculation is done in a fixed timestep Update function, and the Current Orientation is stored as 4x4 Matrix. The Angular Velocity can be stored as whatever, and the maximum Angular Acceleration would be a constant float. I have conversions to and from Euler/Quaternions at my disposable. And I have tried the Quaternion equations based around dQ/dT = 0.5WQ(t) etc on my journey so far. This needs to be fairly quick as well. This is all to create the effect of the camera having inertia, so that when the target suddenly changes direction, the camera will overshoot a bit, before catching back up to the target. So if there is a better way to achieve this effect, I'm all ears. Would appreciate your thoughts, Thanks.

Share this post


Link to post
Share on other sites
No takers? Starting to think this is actually quite a difficult thing to work out, considering how quiet it is in this thread :)

Share this post


Link to post
Share on other sites
Quote:
No takers? Starting to think this is actually quite a difficult thing to work out, considering how quiet it is in this thread :)
I read the post, but didn't understand everything in it. Perhaps you could describe the problem in a little more detail. What do you mean by the camera 'overshooting'? What kind of camera is it exactly? Does the camera always remain 'upright', or can it be arbitrarily oriented? Can you tell us anything more about the context?

Share this post


Link to post
Share on other sites
If all you are trying to get is a non-uniform blend from the start location to the finish location, try using a non-uniform blending parameter for your slerp. In effect, apply the acceleration to the blending parameter that you send into the slerp function.





You can think of f(t) as a kind of blend velocity.

If you want to start slow, and end fast, you can use a quadratic function



If you want to start fast and end slow, you can use this function

Share this post


Link to post
Share on other sites
Quote:
Original post by jyk
Quote:
No takers? Starting to think this is actually quite a difficult thing to work out, considering how quiet it is in this thread :)
I read the post, but didn't understand everything in it. Perhaps you could describe the problem in a little more detail. What do you mean by the camera 'overshooting'? What kind of camera is it exactly? Does the camera always remain 'upright', or can it be arbitrarily oriented? Can you tell us anything more about the context?


Thanks for the response.

What I mean by overshooting is that lets say the camera was initially pointing straight down the X axis, but the ideal orientation had it pointing directly down the Y axis. So to begin with the camera would gain velocity starting to turn and gather rotational velocity towards the Y axis. As the camera approaches the Y axis, lets now suddenly change the ideal orientation back to pointing down the X axis. The camera will of course accelerate back towards pointing down X axis, but as its got Angular Velocity in the direction of the Y axis, it will actually get closer to the Y axis before the acceleration towards the X axis overcomes the built up velocity towards the Y axis. With a Slerp the camera would immediately have moved towards the Y axis.

Hope that made sense, its that "appears to have inertia" effect I want to capture.

Camera can be arbitrarily orientated.

This is for a camera system for a game, which I want to make look as realistic as possible.

Many thanks.

Share this post


Link to post
Share on other sites
I've seen a few games (fps) where the gun acts in the same way as you would like your camera, when you move the mouse to look around you can notice the guns slightly trailing behind (and overshooting if you whip back in the other direction).

I don't have much useful to say on this subject unfortunatly but if you treat this as a linear problem then you have a position, current velocity and acceleration (acceleration being found from current position and desired position).

Translating this into rotational and you have current position being the current orientation, velocity being angular velocity and it just leaves the problem of how to deal with acceleration :/.

Eric Brown's idea is nice, changing how the time increases when you use slerp but I don't think it will give you the overshoot effect you desire (as it no longer uses previous velocity).

I have my doubts that this will work but:
if I have 3 quaternions,
current - represents no rotation at present
angularVelocity - this is a 90 degree rotation about an axis
angularAccel - this is a -45 degree rotation about the same axis

then if I do:
angularVelocity = angularVelocity * angularAccel;
current = current*angularVelocity;

current now represents a 45 degree rotation about the same axis.

In effect we've increased the current velocity by an acceleration and then increased the current position by that velocity.

Some problems (appart from being maths pretty much plucked randomly out the air and maybe making no real mathematical sense :P) time isn't considered, if the update occurs every 0.5 seconds instead of every second. You could maybe convert acceleration back to axis angle form, create a new temp acceleration where you scale the angle by time (perfect oppotunity to cap angular acceleration) and then create the temp acceleration from that. Apply it to the velocity and then do the same for velocity to scale it by time (and cap as needs be) Finally apply it to the orientation of the camera.

I haven't tested this (as I can't) and I probably have my quaternion multiplication order the wrong way around etc (this is using physX quaternions) but hopefully it should demonsttrate my idea(as bad as it may be :P)

Quaternion m_CurrentOrientation;
Quaternion m_CurrentAngVelocity; // per second

void ScaleByTime(Quaternion q, float seconds)
{
float angle;
Vector axis;
q.getAngleAxis(angle, axis);
angle *= seconds;
q.fromAngleAxis(angle, axis);
}

void ClampAngle(Quaternion &q, float max)
{
float angle;
Vector axis;
q.getAngleAxis(angle, axis);
if(angle > max)
{
angle = max;
}
q.fromAngleAxis(angle, axis);
}

void Update(Quaternion desired, float seconds)
{
// Work out a quaternion which rotates m_CurrentOrientation to desired
// I can't recall how to do this off the top of my head but its needed for slerp right?
Quaternion acceleration;
// Scale it by time
ScaleByTime(acceleration, seconds);
// make sure we're not accelerating too rapidly
ClampAngle(acceleration, MAX_ACCEL_ANGLE_PER_SECOND * seconds);
// Work out new velocity
m_CurrentAngVelocity = m_CurrentAngVelocity*acceleration;
// make sure velocity isn't too large, keep it as a per second value
ClampAngle(m_CurrentAngVelocity, MAX_VELOCITY_ANGLE_PER_SECOND);
// Now we need to scale velocity by time before applying it
Quaternion velocity = m_CurrentAngVelocity;
ScaleByTime(velocity, seconds);
// clamp it
ClampAngle(velocity, MAX_VELOCITY_ANGLE_PER_SECOND * seconds);
// apply it
m_CurrentOrientation = m_CurrentOrientation*velocity;

}


[Edited by - Nanoha on April 7, 2010 7:50:18 AM]

Share this post


Link to post
Share on other sites
Thanks very much for the response, seems like you put alot of effort into that, much appreciated.

I am trying out some code based on what you said, and whilst its not working, its not pointing the camera at random locations! It broadly seems to be doing something right. Any other suggestions please let me know, but I will post how I get on...

Also your right about Eric Brown's suggestion, I meant to thank him. I have the camera Slerping at the moment, and in fact will probably put both methods as options into the final code. I'm planning to try out Eric's suggestion for the Slerp option, so thanks Eric.

Share this post


Link to post
Share on other sites
Here is a solution that might work for you. I haven't tried this, but it seems to work on paper. Sorry, it's kind of long :(

If you were dealing with positions, instead of rotations, you may try a damped spring approach, where each frame you calculate the spring force between your current location and your desired location. You would also calculate some damping that is opposed to your velocity so that you don't wildly oscillate.

Here is the rotational version of a damped spring force using quaternions.

You need to store a "position" (q) and "velocity" (v) quaternion to represent your rotational state. You are going to calculate a rotational acceleration (a) that is analogous to a damped spring. I'll discuss later how to calculate a, but once we have it, we will step the state forward like this:




If your time step is small you should replace the expensive Slerps with NLerps. If your time step is equal to 1 then you can remove the slerps altogether and the equations simplify to




There are two components to a. One applies a spring force to push you toward your desired rotation. The other dampens the motion so that you don't start wildly oscillating.

The linear spring force is proportional to the difference between your current and desired positions. For rotations, this difference is the rotation that rotates your current "look at" vector into your desired "look at" vector. We will call this rotation Δq. Here is a function based on the half angle method that will quickly calculate this quaternion, without employing any trig or inverse trig functions.


Quat getRotationQuat(const Vector& from, const Vector& to)
{
Quat result;
Vector H = VecAdd(from, to);
H = VecNormalize(H);

result.w = VecDot(from, H);
result.x = from.y*H.z - from.z*H.y;
result.y = from.z*H.x - from.x*H.z;
result.z = from.x*H.y - from.y*H.x;
return result;
}



Once you have calculated Δq, you need to "scale" it by a constant that represents the springs stiffness. The analogue of scaling in quaternion land is slerping between 1 and the given quaternion by the scale parameter. The acceleration due to the spring is given by



The acceleration due to damping is proportional and opposed to the current velocity. Again, you just need to "scale" the velocity quat using a slerp between 1 and the velocity.



Since quaternion multiplication does not commute, it is not clear if the final acceleration quat should be composed with the spring acceleration first, or with the damping acceleration first. My guess is that you should just pick one, and then tweak the k and μ parameters until it behaves correctly. At any rate, the total acceleration can be given by the product of these two acceleration components



As before, if your k and μ paramters are small enough, you may want to consider replacing the expensive slerps with normalized lerps. You may just want to do this anyway - I would!

If you decide to try this, let me know how well it works. I've often contemplated if it was possible to do rotational "dynamics" in this way.

Share this post


Link to post
Share on other sites
Angular momentum and angular momentum acceleration can be described by 3 element vectors.

Integrating the angular momentum with angular momentum acceleration is just a mater of simple linear integration (ex. using euler Am = Am + Ama * dt). You want to dampen the acceleration somehow (Am = Am * e^(-dt*k), or while integrating Am = Am + (Ama - Am *k) * dt).

Integrating the rotation using the angular momentum are a little bit more difficult:

A simple way would be to create a rotation matrix using an angle and an axis ( Rn+1 = Rn * matrixFromAngleAxis(length(Am) * dt, normalized(Am)).

Another way of calculating next rotation would be to look at how the rotation matrix axes changes during rotation. Integrating the rotation axis using euler would become Xn+1 = Xn + cross(Xn, Am * dt), Yn+1 = Yn + cross(Xn, Am * st), ...

You can also convert these things to quaternions. This is probably the best way of doing things, but I cant remember it right now.

You must always renormalize/ortogonalize your matrix/quaternion for making sure it does not get skewed or resized.

The hardest thing is to find the angular momentum acceleration. You need to find the axis of rotation between the two rotations, and then scale it by the angular difference and some k. I cant remember how to do this effectively, but it can be achieved by examining the quaternion representation of the rotations. There is probably some direct way of getting an angle/axis from a pair of matrices.

For limiting the rotation velocity and the rotation acceleration velocity you only need to clamp the lengths of the angular momentum and angular momentum acceleration vectors.

Hope that helps.

Share this post


Link to post
Share on other sites
Hi Eric,

Sorry I intended to report back fully once I had tweaked and played around until I had something I was completely happy with, it's been a hectic week so far, however an interim report...

First I tried Nanoha's ideas which produced something broadly correct, but suffered from instability and the camera would sometimes just go mad. However I'm not suggesting that there was anything wrong with his advice, there may well be a bug in my stuff.

Next I tried implementing your ideas, pretty much word for word which I have to say worked exactly as you and I had hoped. I'm amazed at quite how smooth it is.

Quote:
I've often contemplated if it was possible to do rotational "dynamics" in this way.


So you can stop wondering whether it was possible, it works beautifully.

Since I got it working yesterday, the issue I'm having is tweaking the constants and seeing whether its possible to get numbers I'm happy with. It damps very well, and it doesn't oscillate much around the target, but if the camera has to accelerate quite a lot to catch the car up, it does then tend to over shoot too far!. Sometimes it produces brilliant shots and sometimes not. I suppose what I'm struggling with is that whilst this technique does do exactly what I was looking for, it's not always exactly what a human camera operator would do, but I think alot of it depends on what the target (a car) is doing. Whereas in real life the camera man might overshoot a bit, but he generally does have a fairly good idea of where the car is going to go, so he catches the overshoot earlier.

So I decided today to leave the code as it was and implement the other effects I want to add, which is camera shake and imperfections (to give a cinematic feel) and once they are in, then go back to tweaking. I started today looking into adding some kind noise into the target's position to give it that human error quality. I want to add two types of noise:

1. General (I guess low frequency) noise to simulate human error and
2. Vibration caused by the car driving very close to camera (I guess high frequency)

I'm still investigating that at the moment.

I also spent some time on your blogs which are fascinating, and I'm now getting the glimer of understanding quaternions :) I find I am interested in some of the same things as you, but I don't quite have the maths head you do! I did study AstroPhysics for a while though :) Your quaternion tricks section, particularly #3 is very interesting.

So all in all I can't thank you enough for your help, and will be working on this over the next few weeks. I will post updates and final code once it's finished.

This should all appear in a big brand car based game scheduled for release at Christmas, so hopefully you'll actually be able to see it action! I don't think I can say what the game is yet :(

Many thanks

Matt.

P.S. Thanks to mzeo77 for your reply but I have not had chance to look at your ideas yet, but I will.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this