Sign in to follow this  

LookAt() functionality for FPS (Euler Angle) Camera

This topic is 2849 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

[I had posted this in the Game Programming forum, but thought it might be better here] I have a FPS style camera that accumulates Euler angles, and works well. But now I want to implement a lookAt( Vector ) method which will take in a vector, and then calculate an absolute rotation in Euler Angles to aim the camera at that point in space. This is what I have so far:
    void lookAt( float x1, float y1, float z1 )
    {
        VectorR3 v( x1, y1, z1 );
        v.x = m_position.x - v.x;
        v.y = m_position.y - v.y;
        v.z = m_position.z - v.z;

        float r = v.length();
        float yaw = acos( v.z / r );
        float pitch = atan2( v.y, v.x );

        setRotation( yaw, pitch, 0.0f, true ); // This just wraps the values to ensure they stay within range
    }



Bbuutt... things aren't working. This is Cartesian Coordinate to Spherical coordinate conversion, or at least it's suppose to be. Does anything jump out as majorly wrong here?

Share this post


Link to post
Share on other sites
I'd think you'd want target - position, rather than position - target. Also, it looks like you might have yaw and pitch backwards. That is, it should be:
float pitch = acos( v.z / r );
float yaw = atan2( v.y, v.x );
Also, since acos() can fail if the input falls outside of the range [-1, 1] (e.g. due to numerical error), an (IMO) better way to compute the pitch is:
float length = sqrt(v.x*v.x+v.y*v.y);
float pitch = atan2(v.z, length);

Share this post


Link to post
Share on other sites
Ok so let me see if I get this right:

void lookAt( float x1, float y1, float z1 )
{
VectorR3 v( x1, y1, z1 );
v -= m_position;

float r = sqrt( v.x*v.x + v.y*v.y ); // I'm not suppose to take Z into account?
float yaw = atan2( v.y, v.x );
float pitch = atan2( v.z, r );

setRotation( yaw, pitch, 0.0f, true );
}





I'm not getting proper rotations with this.

Share this post


Link to post
Share on other sites
Quote:
Original post by Wavesonics
Ok so let me see if I get this right:
*** Source Snippet Removed ***

I'm not getting proper rotations with this.
Take another look at my example code (you're computing 'r' differently than what was shown).

But yeah, you don't need to include z there. Think about it this way. Take the vector to the target (v) and project it orthogonally onto the ground plane (call the projected vector v'). v and v' taken together form a right triangle. The length of one of the legs is |v'|, and the length of the other leg is v.z. The lengths of these edges are proportional to the cosine and sine (respectively) of the opposite angle, which means you can submit them directly to atan2(), as shown, to get the angle.

Ok, that wasn't a very good explanation, but maybe it'll be enough to give you the general idea...

Share this post


Link to post
Share on other sites
I had miss-typed and left out the sqrt() but fixed it, is that what you are referring to? Even with the sqrt() i'm still getting incorrect results though.

If there is something else amiss, i don't see it.

Share this post


Link to post
Share on other sites

inline float zeroClamp( float x )
{
return ( std::fabs(x) > std::numeric_limits<float>::epsilon() ? x : 0.0f );
}

inline float wrap( float a )
{
while( a >= RAD360 || a < 0.0f )
{
if(a >= RAD360 )
a -= RAD360;
else if( a < 0.0f )
a += RAD360;
}

return a;
}

void setRotation( float yaw, float pitch, float roll, bool isInRadians = false ) {

m_yaw = 0.0f;
m_pitch = 0.0f;
m_roll = 0.0f;

rotate( yaw, pitch, roll, isInRadians );
}

void rotate( float yaw, float pitch, float roll, bool isInRadians = false ) {

yaw = zeroClamp( yaw );
pitch = zeroClamp( pitch );
roll = zeroClamp( roll );

if( !isInRadians )
{
yaw = Rotation3D::deg2rad( yaw );
pitch = Rotation3D::deg2rad( pitch );
roll = Rotation3D::deg2rad( roll );
}

m_yaw += yaw;
m_yaw = wrap( m_yaw ); // Keep the angle between 0 and 360

m_pitch += pitch;
m_pitch = wrap( m_pitch );

m_roll += roll;
m_roll = wrap( m_roll );
}




[Edited by - Wavesonics on February 26, 2010 2:51:07 PM]

Share this post


Link to post
Share on other sites
Hi Wavesonics,

I tried the following code and it works; It's pretty much what jyk was suggesting, however I still post it. However here I explicitly assume a right-hand setting in which plane XZ is the ground and y-axis is the UP direction, which I think would be the case in a typical OpenGL setting.

The program outputs:

Pitch = -35
Yaw = 135

Which makes sense for the input.

Bye!


#include <stdio.h>
#include <math.h>

int main(int argc, char *argv[])
{
float x1 = -1;
float y1 = -1;
float z1 = 3;

float position_x = 1;
float position_y = 1;
float position_z = 1;

float v_x = x1 - position_x;
float v_y = y1 - position_y;
float v_z = z1 - position_z;

float length = sqrt(v_x * v_x + v_z * v_z);
float pitch = atan2(v_y, length);
float yaw = atan2(v_z, v_x);

printf("Pitch = %d\n", (int)(180 * (pitch / 3.1415)));
printf("Yaw = %d\n", (int)(180 * (yaw / 3.1415)));

return 0;
}

Share this post


Link to post
Share on other sites
I don't see anything wrong with your code, after triple checking things. Are you sure the problem does not lie outside of this function?

For instance, you are using the yaw and pitch to rotate a vector (1, 0, 0) by first applying the pitch (rotation around Y), then the yaw (rotation around Z)? This vector should of course be added to your viewpoint to get your target point back.

And you are certain your Z axis is your up axis? If this would be Y, then you need to swap some coordinates and possibly place a - sign somewhere.

Share this post


Link to post
Share on other sites
Ah no my Z axis is not up, it's standard OpenGL coordinate system so -Z should be forward if I'm not mistaken, and +y is up.

Also how I'm applying the rotation is by generating a quaternion from the Euler Angles, then extracting a matrix from that (Just for convince at the moment).

Share this post


Link to post
Share on other sites
Quote:
Original post by Wavesonics
Ah no my Z axis is not up, it's standard OpenGL coordinate system so -Z should be forward if I'm not mistaken, and +y is up.

Also how I'm applying the rotation is by generating a quaternion from the Euler Angles, then extracting a matrix from that (Just for convince at the moment).
Your Cartesian-to-spherical conversion considers +z to be up, so if +y is up in your simulation you'll need to adjust the code accordingly.

Share this post


Link to post
Share on other sites
Cool, it looks like you are close to solving the problem; just out of curiosity I want to post this code, which is a version that uses a length computed with 3 components. This is very close to your initial guess, the differences are (1) in the vector subtraction, as noted by jyk; (2) in the fact that it uses asin in place of acos; (3) in the fact that considers the OpenGL reference frame, which is right handed with y-axis being up.

Bye!


#include <stdio.h>
#include <math.h>

int main(int argc, char *argv[])
{
float x1 = -1;
float y1 = -1;
float z1 = 3;

float position_x = 1;
float position_y = 1;
float position_z = 1;

float v_x = x1 - position_x;
float v_y = y1 - position_y;
float v_z = z1 - position_z;

float length = sqrt(v_x * v_x + v_y * v_y + v_z * v_z);
float pitch = asin(v_y / length);
float yaw = atan2(v_z, v_x);

printf("Pitch = %d\n", (int)(180 * (pitch / 3.1415)));
printf("Yaw = %d\n", (int)(180 * (yaw / 3.1415)));

return 0;
}

Share this post


Link to post
Share on other sites
Hhhhmmm... Ok, I tweaked a few things and it seems to work. But maybe not 100%, i'm not sure:

void lookAt( float x, float y, float z )
{
VectorR3 v( x, y, z );
v -= m_position;

float r = sqrt( v.x*v.x + v.y*v.y );
float yaw = atan2( v.x, v.z );
float pitch = atan2( v.y*-1.0f, r );

setRotation( yaw, pitch, 0.0f, true );
}



Notice I reverse the values in yaw's atan2(), and I inverted the y value being passed into pitch's atan2()

It's the best it's ever been, but when i'm far away or at extreme angles, it doesn't seem to center the point exactly in the center of the screen.... Hhhhmmm...

[edit]
One thought I had here was when adding translation to the camera, i subtract it from the position, so the "position" is already inverted for translation.

Share this post


Link to post
Share on other sites

This topic is 2849 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.

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