LookAt() method for camera

Started by
30 comments, last by Zakwayda 14 years, 2 months ago
I'm trying to implement a LookAt() method in my camera. I accumulate Euler angles, but use a Quaternion to generate the finale rotation matrix as to avoid gimbal lock. So for my LookAt() method I figured the following would be sufficient:
  1. Subtract the point at which to look (P) from the camera's position (K) resulting in vector (V)
  2. Take the cross product of V and the vector (Vz(0,0,-1) chosen b/c this is the default for opengl) resulting in vector (V'). V' is now the axis of rotation, to get from the default opengl orientation, to the rotation necessary to look at our point.
  3. Now we need the angle, acos( Vz DOT V ). Now we have the axis of rotation and the angle with which to rotate around it.
  4. Lastly, because my camera accumulates Euler Anglers, I need to turn this Axis Angle rotation back into Euler Angles.

    void lookAt( float x1, float y1, float z1 )
    {
        // Step 1) Setup our vectors
        VectorR3 v1( VectorR3( 0.0f, 0.0f, -1.0f ) );
        VectorR3 v2( m_position );
        v2.x -= x1;
        v2.y -= y1;
        v2.z -= z1;
        v2 = v2.normalized();

        // Step 2) Calculate Rotation Axis
        VectorR3 rotationAxis( v1.cross( v2 ).normalized() );

        // Step 3) Calculate Angle
        float rotationAngle = acos( v1.dot( v2 ) );

        // Step 4) Calculate Euler Angles
        float x = rotationAxis.x;
        float y = rotationAxis.y;
        float z = rotationAxis.z;

        float s = sin( rotationAngle );
        float c = cos( rotationAngle );
        float t = 1.0f-c;

        if ((x*y*t + z*s) > 0.998f) { // north pole singularity detected
            m_yaw = 2.0f*atan2(x*sin(rotationAngle/2.0f),cos(rotationAngle/2.0f));
            m_pitch = PI/2.0f;
            m_roll = 0.0f;
            return;
        }
        if ((x*y*t + z*s) < -0.998f) { // south pole singularity detected
            m_yaw = -2.0f*atan2(x*sin(rotationAngle/2.0f),cos(rotationAngle/2.0f));
            m_pitch = -PI/2.0f;
            m_roll = 0.0f;
            return;
        }

        m_yaw = atan2(y * s- x * z * t , 1.0f - (y*y+ z*z ) * t);
        m_pitch = asin(x * y * t + z * s) ;
        m_roll = atan2(x * s - y * z * t , 1.0f - (x*x + z*z) * t);
    }









But the resulting Euler Angles aren't quite right, especially when there should be more pitch involved. Any idea what I'm doing wrong? [edit] Ah so I clamped the resulting Euler Angles to be within 2PI Radians (rolls back over to 0) and I get better results, but still off in some cases. Another problem is I'm not preserving my up vector, so it rotates how ever it needs to look at the point. How can I incorporate what should be the up vector so my camera doesn't roll while it's looking? Also, when ever it detects a north or south pole singularity, it craps out and produces a completely incorrect rotation :/ Boy... [/edit] [Edited by - Wavesonics on February 17, 2010 7:58:04 PM]
==============================
A Developers Blog | Dark Rock Studios - My Site
Advertisement
Just for clarification, are you trying to orient the angle with respect to the negative z-axis?

If so, I would think you want v2 = lookAtPoint - cameraPos, to get a direction from the camera to the point, not the other way around. camPos - lookAtPoint will point behind the camera, not in front of it.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Hhhmmm, yes I do want to orient FROM the -Z axis to the vector I generate, so I see your point, and even agree with you.

But reversing the two seems to give me worse (possibly inverse) results.

Currently, as long as I don't elevate the camera, and i just move around the look at point on it's plane, it works. It's when I go off plane, that it fails.
==============================
A Developers Blog | Dark Rock Studios - My Site
Quote:Original post by Wavesonics
I accumulate Euler angles, but use a Quaternion to generate the finale rotation matrix as to avoid gimbal lock.
While it is likely entirely tangential to your current issue, this is doing nothing to help you. it would be worth reading up on gymbal lock, or forgetting the term exists at all.

Gymbal lock is (roughly speaking) a case when you lose one axis of rotation, due to the other two axes being in alignment with each other. But more importantly here, it only affects your ability to rotate further.

If your camera becomes gymbal locked (and it isn't that common), then the fact that you convert it into a matrix via quaternion will not in any way affect the fact that the camera will *still* be gymbal locked.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

hhmmm... I was thinking this. Though I thought the order in which you applied Euler Angle rotation would cause gimble lock, while creating a Quaternion in one shot with all three Euler Angles would avoid it.

I've tried accumulating Quaternions: I took Euler Angles in, immediately converted them to a Quaternion, then multiplied the Camera's internal Quaternion by the newly created one, thus adding the new rotation to the current.

But extremely quickly, I get bleed over into another axis, and my camera begins to roll, when it should only be pitching & yawing.
==============================
A Developers Blog | Dark Rock Studios - My Site
Quote:hhmmm... I was thinking this. Though I thought the order in which you applied Euler Angle rotation would cause gimble lock, while creating a Quaternion in one shot with all three Euler Angles would avoid it.
Like swiftcoder said, just throwing a quaternion or two into the mix won't prevent gimbal lock. In fact, you can think of quaternions as being functionally equivalent to matrices as far as rotations are concerned. (Obviously there are differences, or else no one would bother with quaternions; however, the differences have more to do with efficiency and elegance than they do with fundamental behavior.)
Quote:I've tried accumulating Quaternions: I took Euler Angles in, immediately converted them to a Quaternion, then multiplied the Camera's internal Quaternion by the newly created one, thus adding the new rotation to the current.

But extremely quickly, I get bleed over into another axis, and my camera begins to roll, when it should only be pitching & yawing.
It sounds like you were implementing 6DOF motion here, which behaves exactly as you describe; that is, accumulated pitch and yaw will (usually) result in perceived roll.

I think the real question here is what kind of motion you want. If you only want pitch and yaw, then 'from scratch' Euler angles should work fine, and gimbal lock should not be a concern.

As for the 'look at' function, a typical implementation will compute the orientation using vector cross products rather than axis-angle rotations. However, this method will still fail when looking more or less straight along the reference axis. This has to be handled as a special case with any method though, simply because if the target is directly above or below the viewer, there's no unique orientation that will satisfy the constraints.
Right I am trying to implement a 6DOF camera. But I want to be able to rotate it accurately. If I only want to change the pitch I only to change the pitch. But if I do want to be able to use all 6 dof, then I want them there.
==============================
A Developers Blog | Dark Rock Studios - My Site
Quote:Right I am trying to implement a 6DOF camera. But I want to be able to rotate it accurately.
Can you clarify what you mean by 'accurately'? You can rotate an object using incremental, local rotations with complete accuracy, but I get the sense that you mean something else here.

Your mention of 'only changing the pitch' suggests that you want to in some way combine 6DOF motion with absolute, 'from scratch' Euler angles. If so, it's not immediately clear to me how that would be done, as 6DOF motion and 'from scratch' Euler angles are sort of fundamentally at odds with one another.

If you still find yourself stuck on this, maybe you could go into a little more detail about the nature of the simulation and what sort of control scheme(s) you're using. That might make it a little easier to suggest ways you could get the behavior you're looking for.
Ah ok. Well the only reason I was using "from scratch Euler Angles" was because they are easiest to think in when saying "When mouse moves left, add yaw of 10 degrees".

Would starting with predefined Quaternions (10 degrees of yaw expressed as a Quaternion) and just using that to apply incremental rotation be the answer?

My current project only calls for pitch and yaw, but I want a general purpose camera I can use for anything down the road.
==============================
A Developers Blog | Dark Rock Studios - My Site
Nope. Clearly I have a bigger miss-understanding then I understand. Which is sort of a cyclical problem...

What did you mean btw about using cross products to do look at functions?
==============================
A Developers Blog | Dark Rock Studios - My Site

This topic is closed to new replies.

Advertisement