Rotating Camera

Started by
2 comments, last by YellowMaple 17 years, 11 months ago
Hello, I'm playing around with the concepts of having a camera that will orbit a target (i.e. the gaze direction will always be towards the target, and the camera is rotated by having the eye coordinates confined to a sphere around the target). To implement this I'm using OpenGL; to be more specific, I'm trying to use the gluLookAt function and modifying the eye, 'up', and gaze coordinates to achieve this orbiting effect. There's been a lot of problems so far and I was hoping to get some help here :) What I do is, when there is a mouse movement I attempt to translate this to how many degrees it should rotate around the x, y, and z axis. Then I try to rotate the camera; I translate the target and eye of the camera so that the target is at the origin and then I transform the Cartesian coordinates of the camera 'eye' to spherical coordinates. I then add the desired angle of rotation to theta and phi and translate back to Cartesian coordinates. The relevant code is below: Function to rotate camera:

void rotateCamera(GLfloat x, GLfloat y, GLfloat z)
{
  // Translate eye so that target is at origin
  cam.eye[ 0 ] -= cam.gaze[ 0 ];
  cam.eye[ 1 ] -= cam.gaze[ 1 ];
  cam.eye[ 2 ] -= cam.gaze[ 2 ];
  
  // Spherical Coordinates
  double theta = 0;
  if(cam.eye[ 0 ] != 0)
  {
    theta = atan(cam.eye[ 1 ] / cam.eye[ 0 ]);
  }
  // x-coordinate is zero... we have a problem
  else if(cam.eye[ 1 ] > 0)
  {
    theta = DEG_TO_RAD(90.0);
  }
  else if(cam.eye[ 1 ] < 0)
  {
    theta = DEG_TO_RAD(270.0);
  }

  double phi = acos(cam.eye[ 2 ] / cam.radius);
  
  // Rotate around y-axis
  theta += DEG_TO_RAD(y);
  
  // Rotate around x-axis
  phi += DEG_TO_RAD(x);

  cam.eye[ 0 ] = cam.radius * cos(theta) * sin(phi);
  cam.eye[ 1 ] = cam.radius * sin(theta) * sin(phi);
  cam.eye[ 2 ] = cam.radius * cos(phi);
  
  // Re-calculate 'up' vector for camera
  GLfloat tmpVec[] = {cam.eye[ 0 ], cam.eye[ 1 ], cam.eye[ 2 ]};
  GLfloat yVec[] = { 0.0f, 1.0f, 0.0f};
  GLfloat tmpLen = sqrt(tmpVec[ 0 ] * tmpVec[ 0 ] + tmpVec[ 1 ] * tmpVec[ 1 ] + tmpVec[ 2 ] * tmpVec[ 2 ]);
  
  tmpVec[ 0 ] /= tmpLen;
  tmpVec[ 1 ] /= tmpLen;
  tmpVec[ 2 ] /= tmpLen;
  
  GLfloat xprod1[] = {tmpVec[ 1 ] * yVec[ 2 ] - tmpVec[ 2 ] * yVec[ 1 ],
					  tmpVec[ 2 ] * yVec[ 0 ] - tmpVec[ 0 ] * yVec[ 2 ],
					  tmpVec[ 0 ] * yVec[ 1 ] - tmpVec[ 1 ] * yVec[ 0 ]};

  cam.up[ 0 ] = tmpVec[ 1 ] * xprod1[ 2 ] - tmpVec[ 2 ] * xprod1[ 1 ] * -1.0f;
  cam.up[ 1 ] = tmpVec[ 2 ] * xprod1[ 0 ] - tmpVec[ 0 ] * xprod1[ 2 ] * -1.0f;
  cam.up[ 2 ] = tmpVec[ 0 ] * xprod1[ 1 ] - tmpVec[ 1 ] * xprod1[ 0 ] * -1.0f;
  
  tmpLen = sqrt(cam.up[ 0 ] * cam.up[ 0 ] + cam.up[ 1 ] * cam.up[ 1 ] + cam.up[ 2 ] * cam.up[ 2 ]);
  cam.up[ 0 ] /= tmpLen;
  cam.up[ 1 ] /= tmpLen;
  cam.up[ 2 ] /= tmpLen;

  // Translate eye back
  cam.eye[ 0 ] += cam.gaze[ 0 ];
  cam.eye[ 1 ] += cam.gaze[ 1 ];
  cam.eye[ 2 ] += cam.gaze[ 2 ];
}

and translating mouse movements to rotations:

void mouseMove(int x, int y)
{
  if(mouse.leftButton)
  {
	GLfloat degX = mouse.xPos - (float) x;  // How much we rotate around the y-axis
	GLfloat degY = mouse.yPos - (float) y;  // How much we rotate around the x-axis

    rotateCamera(-1.0f * degX, degY, 0.0f);
	
	mouse.xPos = (x > 0 ? x : 0.0);
	mouse.yPos = (y > 0 ? y : 0.0);
  }
}

At first for the 'up' vector I was always using (0.0f, 1.0f, 0.0f) as I always wanted the camera's up direction to be parallel with the positive y-axis, but I suspected that that might be the source of my problem, so I added in all the code after the comment 'Re-calculate 'up' vector for camera' to try and fix this. Now I'm suspecting that it might be my mouseMove function and how it translates mouse movements to rotation.
Advertisement
YellowMaple,

I'm familiar with what you're trying to do, however I dont usually implement it the way you do.

First I take the difference in the mouse coordinates (cur-prev) and determine the angle the camera has changed along the x and y axis. This is just based on screen size and some magic numbers that I use until moving the mouse from the left to the right side of the screen rotates the camera somewhere between 360 and 720 degrees (use whatever looks best to you).

Movement along the y-axis is considered pitch (up/down movement) and movement along the x-axis is considered yaw (side/side movement.) Then I perform the following calculations to orbit the camera around its target

void Camera::Orbit( float32 yaw, float32 pitch ){    // Create a rotation matrix that rotates about the y-axis    Matrix4 rotationY;    rotationY.BuildRotationY( yaw );    // Get the direction vector    Vector3 dir = m_Target - m_Eye;	    // Normalize the direction vector and cross it with the UP vector in    // order to get the "right" vector, which is just a vector pointing    // out of my right side.  This will be used as our arbitrary axis for    // which to pitch on.  If we dont do this, then when we rotate around our    // object and then try and pitch, our pitch direction will be reversed    Vector3 right = dir.Normalize().Cross( m_Up );    right.NormalizeInPlace();    // Create a rotation matrix about the arbitary axis created above, by the    // angle passed in from the calling function    Matrix4 rotationX;    rotationX.BuildAxisAngle( right, pitch );    // Create a composite transformation matrix    Matrix4 transform = rotationY * rotationX;    // Transform the direction vector into a new direction    dir = transform.Transform3( dir );	    // Subtract the new direction from the target to get the new eye position    m_Eye = m_Target - dir;    // Re-build the camera matrix from the new eye, target, and up vectors    // which represents the view transform    UpdateTransform();}


I hope this code helps you out. Please let me know if you have any specific questions on the implementation of anything above.

Cheers!
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
Chronicles of Elyria (An In-development MMORPG)
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints
Thanks for your help! After some contemplation, I realized that spherical coordinates is actually more complicated than it first sounds. I went with the rotation matrices as you did and it is working much more smoothly now. Thanks!
I'm still having a couple of problems (although there are fewer problems than I had with spherical coordinates). It seems that if the camera eye is near or on the plane defined by (1, 0, -1), (-1, 0, 1), (0, 1, 0) it has trouble rotating for some reason. Also, using the same plane mentioned above, on one side of the plane, moving the mouse up points the camera down, while on the other side, moving the mouse up points the camera up.

Any ideas? I've used the same method jwalsh has mentioned and have used this as a guide to rotating around and arbitrary axis.

I hope that made sense :p Let me know if I need to clarify anything. Thanks!

The function below rotates the point 'point' around the axis 'axis' specified by 'angle' in degrees:
void rotateMatrixArbitrary(GLfloat angle, GLfloat* axis, GLfloat* point){  GLfloat yzProj[ 3 ] = { 0.0f, axis[ 1 ], axis[ 2 ] };  GLfloat xzProj[ 3 ] = { axis[ 0 ], 0.0f, axis[ 2 ] };  GLfloat d = sqrt(yzProj[ 1 ] * yzProj[ 1 ] + yzProj[ 2 ] * yzProj[ 2 ]);    MATRIX4 m1, m2, m3, mi1, mi2, tmp;  initMatrix(&m1); initMatrix(&m2); initMatrix(&m3);  initMatrix(&mi1); initMatrix(&mi2);  initMatrix(&tmp);    if(d != 0.0)  {    m1.matrix[ 1 ][ 1 ] = fabs(yzProj[ 2 ]) / d;    m1.matrix[ 1 ][ 2 ] = -1.0f * fabs(yzProj[ 1 ]) / d;    m1.matrix[ 2 ][ 1 ] = fabs(yzProj[ 1 ]) / d;    m1.matrix[ 2 ][ 2 ] = fabs(yzProj[ 2 ]) / d;      mi1.matrix[ 1 ][ 1 ] = fabs(yzProj[ 2 ]) / d;    mi1.matrix[ 1 ][ 2 ] = fabs(yzProj[ 1 ]) / d;    mi1.matrix[ 2 ][ 1 ] = -1.0f * fabs(yzProj[ 1 ]) / d;    mi1.matrix[ 2 ][ 2 ] = fabs(yzProj[ 2 ]) / d;  }    d = sqrt(xzProj[ 0 ] * xzProj[ 0 ] + xzProj[ 2 ] * xzProj[ 2 ]);    if(d != 0.0)  {    m2.matrix[ 0 ][ 0 ] = fabs(xzProj[ 2 ]) / d;    m2.matrix[ 0 ][ 2 ] = fabs(xzProj[ 0 ]) / d;    m2.matrix[ 2 ][ 0 ] = -1.0f * fabs(xzProj[ 0 ]) / d;    m2.matrix[ 2 ][ 2 ] = fabs(xzProj[ 2 ]) / d;      mi2.matrix[ 0 ][ 0 ] = fabs(xzProj[ 2 ]) / d;    mi2.matrix[ 0 ][ 2 ] = -1.0f * fabs(xzProj[ 0 ]) / d;    mi2.matrix[ 2 ][ 0 ] = fabs(xzProj[ 0 ]) / d;    mi2.matrix[ 2 ][ 2 ] = fabs(xzProj[ 2 ]) / d;  }  rotateMatrixZ(angle, &m3);  // Apply rotations  matrixMult(m1, point);  matrixMult(m2, point);  matrixMult(m3, point);  matrixMult(mi2, point);  matrixMult(mi1, point);}


This is the function which will rotate the camera eye based on mouse movements. It rotates around the point specified by cam.gaze:
void rotateCamera(GLfloat x, GLfloat y){  // Translate eye so that target is at origin  cam.eye[ 0 ] -= cam.gaze[ 0 ];  cam.eye[ 1 ] -= cam.gaze[ 1 ];  cam.eye[ 2 ] -= cam.gaze[ 2 ];    MATRIX4 rotateY;    rotateMatrixY(y, &rotateY);    GLfloat dir[ 3 ] = { cam.eye[ 0 ], cam.eye[ 1 ], cam.eye[ 2 ] };  normalize(dir);  GLfloat rightVec[ 3 ];  xprod(dir, cam.up, rightVec);  normalize(rightVec);    rotateMatrixArbitrary(x, rightVec, cam.eye);  matrixMult(rotateY, cam.eye);  // Translate eye back  cam.eye[ 0 ] += cam.gaze[ 0 ];  cam.eye[ 1 ] += cam.gaze[ 1 ];  cam.eye[ 2 ] += cam.gaze[ 2 ];}

This topic is closed to new replies.

Advertisement