Results 1 to 8 of 8

Thread: Quaternion based camera

  1. #1

    Quaternion based camera

    While working on my PGD competition entry I ran into a silly problem...

    My nice quaternion based camera class works great in the first person shooter mode of my game, but if I remove the clamping and try to use it as a 6DOF camera it doesn't work properly. It rotates alright, but it always rotates about the world axis, not itself.

    Ie. if I pitch (x-axis) the camera +90 degrees and then yaw (y-axis) +90 degrees it rotates about the world axis and it looks like the camera is actually rolling... How can I do proper cumulative rotation?

    This is how I do it now:
    Code:
    q := Q_FromEuler(Roll, Pitch, Yaw);
    Orientation := Q_Mult(Orientation, q);
    I tried to look for answer and found some nice formulas etc. but I can't understand them. Oh yes, I suck at math.
    If you develop an idiot proof system, the nature develops better idiots.

  2. #2

    Quaternion based camera

    You cant rotate vector by simple quaternion multiplication. To get proper results you have to use the following formula:

    v' = qvq<sup>-1</sup>

    where q is quaternion containig rotation, v is a vector to be rotated represented as a quaternion with zero real (scalar) part, and q<sup>-1</sup> is the inverse of q. If q is normalised then its inverse is equal to conjugate of q.

    This is my function for rotating vectors using quaternions.
    Code:
    function quatRotateVector&#40;v &#58; TVector; q &#58; TQuatermion&#41; &#58; TVector;
    var
     q_1,qv,qt &#58; TQuatermion;
    begin
     q &#58;= quatNormalize&#40;q&#41;;
     q_1 &#58;= quatConjugate&#40;q&#41;; 
     qv &#58;= Quatermion&#40;v,0.0&#41;;
     qt &#58;= quatMul&#40;q,qv&#41;;
     qt &#58;= quatMul&#40;qt,q_1&#41;;
     Result.x &#58;= qt.x;
     Result.y &#58;= qt.y;
     Result.z &#58;= qt.z;
    end;
    I hope it helps you.

    P. S. My code is not tested since I don't use quatrnion based camera.

  3. #3

    Quaternion based camera

    Hmm... Maybe I have misunderstood something about how the cameras work?

    My camera class has only the position as a vector and orientation as a quaternion. I multiply the orientation with given pitch/yaw/roll degrees, then convert the resulting quaternion to a matrix which I then feed to OpenGL to update the view. I also translate the position vector with the direction orientation information of the quaternion when I move the camera.

    All this works fine if I don't try to turn the camera upside down at that point everything goes to hell.
    If you develop an idiot proof system, the nature develops better idiots.

  4. #4

    Quaternion based camera

    I've made a false assumption that you store camera orientation as a vector.

    But if you store orientation as a quaternion, isn't it enough to create orientation from Euler angles, convert to matrix and send to OpenGL, without the multiplication?

  5. #5

    Quaternion based camera

    If I don't reset the pitch/yaw/roll I can do that, but I still got the same problem: the camera rotates about the world axis, not about it's own axis.
    If I pitch the camera by 90 degrees and then yaw it, the camera seems to roll, because it's still rotating about the world y-axis, not the local y-axis.

    Maybe I should make a little test program to demonstrate the problem I have.

    EDIT: Here's some screenshots from a test program.

    Here's the camera's default orientation, facing along the Z-axis:
    Camera1

    Now I rotate it 90 degrees about the X-axis (pitch):
    Camera2

    Next I rotate it 90 degrees about the Y-axis (yaw), but this is wrong. I'd expect the camera to face along the X-axis:
    Camera3

    This is how I'd want it to look like after the X - Y rotations:
    Camera4

    How can I achieve this?
    If you develop an idiot proof system, the nature develops better idiots.

  6. #6

    Quaternion based camera

    I think the problem is that your Euler angles are specified in world coordinate system but they have to be specified in camera coordinate system. You need to store in camera class ist coordinate system as a three vectors:
    :arrow: look - camera z axis
    :arrow: up - camera y axis
    :arrow: right - camera x axis
    So, rotating along y axis means rotating along camera up vector. Each time camera rotates along one of its vectors you must update the other two. Then you create proper viewing matrix from look, up and right vectors and send it to OpenGL.

    Here is an example of rotating along camera Y axis
    Code:
    procedure TCamera.RotateY&#40;angle &#58; single&#41;;
    var
     rot &#58; TQuaternion;
    begin
     //create rotation quaternion from angle and camera up &#40;Y&#41; axis
     rot &#58;= quatFromAxisAngle&#40;fUp,angle&#41;;
     //rotate look and right vectors
     look &#58;= rotateVector&#40;rot,look&#41;;
     right &#58;= rotateVector&#40;rot,right&#41;; //or right &#58;= CrossProduct&#40;look,up&#41;;
     normalize&#40;look&#41;;
     normalize&#40;right&#41;;
    end;
    The right, up and look vectors are columns of a rotation matrix.
    Here is my code for setting such matrix for OpenGL (including translation)
    My code is based on this sample
    Code:
    procedure TCamera.SetView;
    var
     viewMat &#58; TMatrix44;
     _rp,_lp,_up &#58; single;
    begin
     _rp &#58;= vecDot&#40;fRight,fPos&#41;;
     _up &#58;= vecDot&#40;fUp,fPos&#41;;
     _lp &#58;= vecDot&#40;fLook,fPos&#41;;
     viewMat&#91;0&#93; &#58;= fRight.x; viewMat&#91;1&#93; &#58;= fUp.x; viewMat&#91;2&#93; &#58;= -fLook.x;
     viewMat&#91;4&#93; &#58;= fRight.y; viewMat&#91;5&#93; &#58;= fUp.y; viewMat&#91;6&#93; &#58;= -fLook.y;
     viewMat&#91;8&#93; &#58;= fRight.z; viewMat&#91;9&#93; &#58;= fUp.z; viewMat&#91;10&#93; &#58;= -fLook.z;
     viewMat&#91;12&#93; &#58;= -_rp; viewMat&#91;13&#93; &#58;= -_up; viewMat&#91;14&#93; &#58;= _lp; viewMat&#91;15&#93; &#58;= 1.0;
    
     glMultMatrixf&#40;@viewMat&#41;;
    end;
    Two more things:
    1. You may want to get rid of minus signs in the code above.
    2. Such camera may not be good for your FPS mode. In this case instead of rotating along up vector you should always rotate along (0,1,0) vector.
    (This is how I did my FPS camera)

  7. #7

    Quaternion based camera

    I tried something similar, but I couldn't get it to work. Now I tried your code and it didn't work either, after a few rotations the view matrix contained garbage, turned out that my vector normalisation function was broken! :shock:

    I wrote a new one and now it works perfectly, now I can pitch, yaw and roll like there's no tomorrow! Thanks a lot for your help!
    If you develop an idiot proof system, the nature develops better idiots.

  8. #8

    Quaternion based camera

    This is one of the things that's been bothering me, it just felt silly to have those Right/Up/Look vectors to help with rotating and I think I've finally figured out a way to rotate without them.

    This is code from my camera test class:
    [pascal]
    if (Pitch <> 0) or (Yaw <> 0) or (Roll <> 0) then
    begin
    q := QuaternionRotateObjectToInertial(DegToRad(Pitch), DegToRad(Yaw), DegToRad(Roll));
    Orientation := QuaternionCrossProduct(Orientation, q);
    Matrix := MatrixCreateOpenGLTranslation(Orientation, Position);
    Pitch := 0;
    Yaw := 0;
    Roll := 0;
    end;
    [/pascal]

    Here are the quaternion functions:
    [pascal]
    function MatrixCreateOpenGLTranslation(const Orientation: TQuaternion; const Position: TVector3f): TMatrix4x4f;
    begin
    Result[0,0] := 1 - 2*Orientation.y*Orientation.y - 2*Orientation.z*Orientation.z;
    Result[1,0] := 2*Orientation.x*Orientation.y - 2*Orientation.w*Orientation.z;
    Result[2,0] := 2*Orientation.x*Orientation.z + 2*Orientation.w*Orientation.y;
    Result[3,0] := 0;

    Result[0,1] := 2*Orientation.x*Orientation.y + 2*Orientation.w*Orientation.z;
    Result[1,1] := 1 - 2*Orientation.x*Orientation.x - 2*Orientation.z*Orientation.z;
    Result[2,1] := 2*Orientation.y*Orientation.z - 2*Orientation.w*Orientation.x;
    Result[3,1] := 0;

    Result[0,2] := 2*Orientation.x*Orientation.z - 2*Orientation.w*Orientation.y;
    Result[1,2] := 2*Orientation.y*Orientation.z + 2*Orientation.w*Orientation.x;
    Result[2,2] := 1 - 2*Orientation.x*Orientation.x - 2*Orientation.y*Orientation.y;
    Result[3,2] := 0;

    Result[0,3] := Position.x;
    Result[1,3] := Position.y;
    Result[2,3] := Position.z;
    Result[3,3] := 1;
    end;

    function QuaternionRotateObjectToInertial(const Pitch, Yaw, Roll: TFloat): TQuaternion;
    var
    sp, sb, sh: TFloat;
    cp, cb, ch: TFloat;
    begin
    // Compute sine and cosine of the half angles
    sp := Sin(Pitch * 0.5);
    cp := Cos(Pitch * 0.5);
    sb := Sin(Roll * 0.5);
    cb := Cos(Roll * 0.5);
    sh := Sin(Yaw * 0.5);
    ch := Cos(Yaw * 0.5);
    // Compute values
    Result.w := ch*cp*cb + sh*sp*sb;
    Result.x := ch*sp*cb + sh*cp*sb;
    Result.y := -ch*sp*sb + sh*cp*cb;
    Result.z := -sh*sp*cb + ch*cp*sb;
    end;

    function QuaternionCrossProduct(const q1, q2: TQuaternion): TQuaternion;
    begin
    Result.w := q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z;
    Result.x := q1.w*q2.x + q1.x*q2.w + q1.z*q2.y - q1.y*q2.z;
    Result.y := q1.w*q2.y + q1.y*q2.w + q1.x*q2.z - q1.z*q2.x;
    Result.z := q1.w*q2.z + q1.z*q2.w + q1.y*q2.x - q1.x*q2.y;
    end;
    [/pascal]

    This seems to work, but I still need to do some more testing. The quaternion used for orientation should probably be normalized every now and then.

    Does anyone else use this scheme for object/camera rotation, any comments on this?
    If you develop an idiot proof system, the nature develops better idiots.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •