Jump to content
  • Advertisement
Sign in to follow this  
BrianDavis

Find yaw/pitch of one vector relative to another

This topic is 4899 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

This is a 3D problem that I am trying to solve. I have a scenario where I need to be able to travel in a direction (v) while maintaining a line of sight with a point in 3D space (u). However, one restriction is that the line of sight must be confined to a fixed viewing area. For instance, the camera traveling in the v direction is restricted so that it can only tilt up/down 45 degrees and turn left/right 50 degrees. If the point in 3D space is too far behind the camera, then the camera will no longer be able to point in the precise direction of the point, but it would point in the general direction of the point. I've been tackling this problem for some time now, and conceptually, it is very easy to understand. The most confusing aspect to me is how to accurately calculate the angles for the yaw and pitch. What I've done so far.... To determine the values for the camera's direction vector, I am taking the known yaw and pitch angles (my current heading) and calculating the x, y, z coordinates onto a unit sphere. v.x = cos(alpha) * cos(beta); v.y = sin(beta); v.z = sin(alpha) * cos(beta); This produces my direction vector in an already normalized form. I then take the point in space that I'm looking at and subtract my current position to create the vector, u, to the point in 3D space. I am not normalizing this vector. Once I've created these two vectors, I attempt to get the yaw and pitch angles between them. To do this, I create a new vector, w, by subtracting u from v (so w = v - u). I then normalize that vector. I know that the yaw angle will be the asin of the y component of the w vector. yaw = asin(w.y) That yaw value can then be plugged in to my sphere equation to give pitch. Of course, I'll have to account for situations where cos(yaw) equals 0. pitch = acos(w.x / cos(yaw)) However, the results that I'm seeing don't match what I am expecting to see. As an example... in some cases, a yaw angle of 82 might be calculated when I'm actually expecting 98. I know that this is due to the fact that my C compiler returns asin values in the range -PI/2 to PI/2, however I don't know how to deal with it. Incidentally, I'm not as concerned with verifying whether or not the camera can actually position itself to view the point. That will be relatively easy. I included that in the description to help indicate *why* I need to know the exact values of the pitch and yaw. Also, the roll angle at this point is 0, but I can't guarantee that it will always be so. Having done some research on this problem, I am unsure as to whether I should be doing this using quaternions or Euclidean math. Quaternions are totally foreign to me, whereas Euclidean math has the drawback of gimbal lock. Does anyone have any experience with this type of problem? If so, any help would be greatly appreciated. Thanks, Brian

Share this post


Link to post
Share on other sites
Advertisement
I didn't quite follow what you were doing there. Whether that's because it's wrong or just because I didn't understand it, I'm not sure. So I'll just tell you how I would try to solve this problem.

If I understand correctly, we have a camera which for now rotates with yaw and pitch only. We also have a direction vector, v, derived from the yaw and pitch values.

There is a point, which I'll call p, that we would like the camera to look at. We are interested in finding the yaw and pitch deltas that, if applied to our current yaw and pitch, would result in v pointing directly at p. Of course the deltas can be applied incrementally over time rather than all at once.

Let d = p - camera.position. We can project d and v into the xz plane by dropping the y components. We can now use the following code to find the signed angle between the two vectors in 2d:

Vector2 v1(v.x, v.z);
Vector2 v2(d.x, d.z);
deltaYaw = atan2(v1.PerpDot(v2), v1.Dot(v2));

Note that the input need not be normalized. Also, you may have to negate the result, depending on your conventions.

If p is directly above the camera, this will fail, as it should. In that case no deltaYaw is required. If the camera is pointing straight up it will also fail. This wouldn't be the result that I would want, since if the camera is pointing straight up it should still be able to yaw 'towards' p. So instead of using v in the above code, I would just use the vector [cos(yaw), sin(yaw)]. That way the camera will yaw in the appropriate direction even when pointing straight up.

For pitch, we can find the signed angle from d to the xz plane as targetPitch = atan2(d.y, v2.Length()). Then, deltaPitch = targetPitch - pitch.

Note that if p is at or very near the camera position, both calculations will fail, as they should. So you'll only want to proceed if |d| > epsilon.

Well, it's quite late and I could have easily made some mistakes, or just be wrong altogether. In any case, let me know if any of the above is not clear.

Share this post


Link to post
Share on other sites
Quote:
Original post by BrianDavis
Having done some research on this problem, I am unsure as to whether I should be doing this using quaternions or Euclidean math. Quaternions are totally foreign to me, whereas Euclidean math has the drawback of gimbal lock.
Euler angles have the drawback of gymbal lock. The Axis-angle method does not.

Do you want the camera to point as close as possible towards some point, subject to constraints on the amounts of pitch and yaw?
Or do you want some object to move around on the screen but not go outside of what the camera can see, whilst holding the camera still?
Can you sum up what you are wanting to do in one sentence please, if it doesn't match either of these.

Share this post


Link to post
Share on other sites
Quote:
Original post by iMalc
Do you want the camera to point as close as possible towards some point, subject to constraints on the amounts of pitch and yaw?

Yes. That would be a more concise way of putting it.

Basically, I have a vehicle with a camera positioned on its nose which has a 100 degree (left/right) by 90 degree (up/down) range of motion; so if the object I'm wanting to look at falls outside that range, then the camera would point in the most likely direction of the object.

Share this post


Link to post
Share on other sites
Quote:
Original post by jyk
Vector2 v1(v.x, v.z);
Vector2 v2(d.x, d.z);
deltaYaw = atan2(v1.PerpDot(v2), v1.Dot(v2));


How are you calculating v1.PerpDot(v2)? Is it the dot product of v1 with the cross product of v1 and v2?

Quote:
Original post by jyk
Also, you may have to negate the result, depending on your conventions.


How do I determine this?

Share this post


Link to post
Share on other sites
Quote:
How are you calculating v1.PerpDot(v2)? Is it the dot product of v1 with the cross product of v1 and v2?
Remember that v1 and v2 are 2d vectors, derived from projecting v and d into the xz plane, so the cross product will not be involved. For two vectors [x1 y1] and [x2 y2], the perp-dot (or at least one formulation of it) is x1y2-x2y1.
Quote:
How do I determine this?
If the result is wrong, negate it :) I'm pretty sure the result will be correct, though, unless you're mixing right- and left-handed coordinate systems and rotations.

I'll try to put everything together. The following code should take your current yaw and pitch and a point 'p', and produce appropriate deltas that will align the camera with p.

Vector3 d = p - camera.position;
float s = sin(yaw);
float c = cos(yaw);
deltaYaw = atan2(c*d.z-s*d.x, c*d.x+s*d.z);
deltaPitch = atan2(d.y, sqrt(d.x*d.x+d.z*d.z)) - pitch;

Again, no guarantees here - there are a lot of places I could have messed up. Also, in the above example I didn't check for p being directly above or at the camera position.

This general approach should be perfectly adequate as long as only yaw and pitch are involved. But as has been suggested elsewhere in the thread, once you add roll, you may need to switch to matrices or quaternions.

Share this post


Link to post
Share on other sites
Quote:
Original post by jyk
Vector3 d = p - camera.position;
float s = sin(yaw);
float c = cos(yaw);
deltaYaw = atan2(c*d.z-s*d.x, c*d.x+s*d.z);
deltaPitch = atan2(d.y, sqrt(d.x*d.x+d.z*d.z)) - pitch;

Again, no guarantees here - there are a lot of places I could have messed up. Also, in the above example I didn't check for p being directly above or at the camera position.


Thanks for the code snippet. It's kind of working, but I'm relatively certain that my problem lies in the way my coordinate system has been set up for me (not by me.) How are your axes set up? I have Z up, X in and Y right.

Thanks for your help.
Brian

Share this post


Link to post
Share on other sites
Quote:
Thanks for the code snippet. It's kind of working, but I'm relatively certain that my problem lies in the way my coordinate system has been set up for me (not by me.) How are your axes set up? I have Z up, X in and Y right.
Oops, I inferred y-up from your original post. The code I posted will definitely not work if z is up. I'm wondering, though...here is the code you posted for finding v:

v.x = cos(alpha) * cos(beta);
v.y = sin(beta);
v.z = sin(alpha) * cos(beta);

This looks like y-up to me. Are you sure this code works correctly? Or perhaps I'm missing something...

In any case, I can certainly post a revision with z up. But to make sure I get it right this time: when yaw is zero, where is your camera pointing? (i.e. +/-world x or +/- world y.)

I think the solution is correct; it's just a matter of getting the coordinate system issues worked out.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Perhaps another (general) solution:

1. Create a classic lookAt() matrix using vector arithmetic.
2. Extract Euler angles in the appropriate order.
3. Clamp Euler angles as desired.
4. Create new matrix from clamped Euler angles.


// Extracts yaw, pitch, roll ordering (from <a href="http://vered.rose.utoronto.ca/people/david_dir/GEMS/GEMS.html">Shoemake's code</a>).
inline Vec3 Mat3::getEulerYXZ(void) const {
Vec3 euler;
euler.x = rAsin(-z.y);
if (euler.x < R_PI/2.f) {
if (euler.x > -R_PI/2.f) {
euler.y = rAtan2(z.x,z.z);
euler.z = rAtan2(x.y,y.y);
} else { // Not a unique solution
euler.y = -rAtan2(-y.x,x.x);
euler.z = 0.f;
} // if
} else { // Not a unique solution
euler.y = rAtan2(-y.x,x.x);
euler.z = 0.f;
} // if
return euler;
} // Mat3::getEulerYXZ

inline Mat3 & Mat3::setEulerYXZ(Vec3 & euler) {
RSinCos ex(euler.x),ey(euler.y),ez(euler.z);
x.x = ey.cosine*ez.cosine + ex.sine*ey.sine*ez.sine;
x.y = ex.cosine*ez.sine;
x.z = -ez.cosine*ey.sine + ey.cosine*ex.sine*ez.sine;
y.x = ez.cosine*ex.sine*ey.sine - ey.cosine*ez.sine;
y.y = ex.cosine*ez.cosine;
y.z = ey.cosine*ez.cosine*ex.sine + ey.sine*ez.sine;
z.x = ex.cosine*ey.sine;
z.y = -ex.sine;
z.z = ex.cosine*ey.cosine;
return *this;
} // Mat3::setEulerYXZ

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!