CS 381 Fall 2012  >  Lecture Notes for Thursday, September 27, 2012

CS 381 Fall 2012
Lecture Notes for Thursday, September 27, 2012

Moving the Camera

More on Coordinate Systems & Matrices

So far, we have always considered the camera to be stationary. What if we want to move it, as in a flight simulator? Then it becomes useful to think of some of the transformations we put into model/view to be moving objects, while others move the camera. The former form the object transformation, while the latter form the camera transformation.

As before, we begin in object coordinates (the coordinates you give to a glVertex* command). The object transformation puts the object into the world; after applying it, we are in world coordinates. Then the camera transformation puts the world into the camera’s point of view: camera coordinates.

In OpenGL, the object and camera transformations are combined in the model/view matrix. Thus: model (object) and view (camera).

Model/View Transformation  
Camera
Transformation
Object
Transformation
 
 \( \Bigl(\text{matrix1}\Bigr) \Bigl(\text{matrix2}\Bigr) \Bigl(\text{matrix3}\Bigr) \)  \( \Bigl(\text{matrix4}\Bigr) \Bigl(\text{matrix5}\Bigr) \Bigl(\text{matrix6}\Bigr) \)  \(\Bigl(\text{vertex}\Bigr)\)
\(\uparrow\)
camera
coordinates
\(\uparrow\)
world
coordinates
\(\uparrow\)
object
coordinates

When we want to move an object in the world, we still multiply new transformations on the left side of the object transformation matrix. But now we also want to be able to move the camera in its own coordinate system. To do this, we multiply new transformations on the left side of the camera transformation matrix. But remember that the camera transformation actually the world goes into the camera’s coordinate system. Thus, moving the camera is backwards from moving an object.

Flying

We need something more interesting to fly around. We can use our function to draw a cube, from manip.cpp, to make a “city”. This idea of reusing a drawing function, with different transformations applied outside the function, is a powerful one; we will revisit it when we cover hierarchical objects.

Let us store the camera transformation in a variable cammat. We do DICE, as usual. When we initialize, we put the camera wherever we want.

glLoadIdentity();
glRotated(-90., 1.,0.,0.);
glTranslated(0., 12., -0.3);
glGetDoublev(GL_MODELVIEW_MATRIX, cammat);

Remember that camera transformations are backwards; the above puts the camera at the point \((0, -12, 0.3)\). Camera transformations are also in reverse order; thus our rotation comes before the translation.

A nice interface involves turning toward the mouse position. If the mouse is in the center of the window, then we do not turn. Otherwise, we turn in the direction of the vector from the window center to the mouse.

What axis do we rotate about? If \((\mathrm{mx}, \mathrm{my})\) is the mouse position, relative to the center of the window, then we rotate about the line through the origin and the vector \(\langle -\mathrm{my}, \mathrm{mx}, 0 \rangle\). The amount we rotate should be proportional to the distance from the center of the window to the mouse, that is, to \(\sqrt{\mathrm{mx}^2 + \mathrm{my}^2}\).

glLoadIdentity();
double turnamt = sqrt(mx*mx+my*my) * angspeed * elapsedtime;
glRotated(turnamt, -my, mx, 0.);
glMultMatrixd(cammat);
glGetDoublev(GL_MODELVIEW_MATRIX, cammat);
glutPostRedisplay();

The variable angspeed above is a constant whose value is found by experimenting to see what makes the turning proceed at a reasonable rate.

Trying out this interface, we find that we have good control over pitch and yaw, but little over roll. Adding that makes for a decent, albeit very simple, flight simulator.

See flyer.cpp for a simple flight simulator.

Where is the Camera?

Camera Position

Suppose we have moved the camera in the world, and we want to know where in the world it is now. Recall that the camera transformation moves from world coordinates to camera coordinates. So if we multiply the camera transformation matrix by the camera’s position in the world, then we get the camera’s position in its own coordinate system. And in its own coordinate system, the camera lies at the origin, which is \(\langle 0,0,0,1 \rangle\) in homogeneous coordinates.

Letting \(M\) denote the camera matrix, we have the following.

\[ M \begin{pmatrix}\text{camera pos}\\ \text{in world coords}\end{pmatrix} = \begin{pmatrix}\text{camera pos}\\ \text{in camera coords}\end{pmatrix} = \begin{pmatrix} 0\\ 0\\ 0\\ 1 \end{pmatrix}. \]

Now multiply both sides by \(M^{-1}\), the inverse of \(M\). (Note: the matrix \(M\) will almost always have an inverse.) The \(M\) on the left cancels, and we are left with the following.

\[ \begin{pmatrix}\text{camera pos}\\ \text{in world coords}\end{pmatrix} = M^{-1} \begin{pmatrix} 0\\ 0\\ 0\\ 1 \end{pmatrix}. \]

Try doing that last matrix-vector product with an actual matrix. It is not hard to see that, when a matrix is multiplied by \(\langle 0,0,0,1 \rangle\), the result is the rightmost column of the matrix. For example,

\[ \begin{pmatrix} 1&2&3&4\\ 5&6&7&8\\ 9&10&11&12\\ 13&14&15&16 \end{pmatrix} \begin{pmatrix} 0\\ 0\\ 0\\ 1 \end{pmatrix} = \begin{pmatrix} 4\\ 8\\ 12\\ 16 \end{pmatrix}. \]

And so we conclude the following.

See function whereAmI in whereami.h for code that computes the camera position. See flyer.cpp for code that calls function whereAmI.

Finding the Inverse

Of course, we have not dealt with how the above inverse is found. There are techniques for finding the inverse of any matrix that has an inverse. But things get a bit simpler when the matrix is composed entirely of translations and rotations (as the camera matrix generally is).

Write

\[ M=TR, \]

where \(T\) is a translation matrix, and \(R\) is a rotation matrix. Then the inverse of our camera matrix is

\[ M^{-1} = R^{-1}T^{-1}. \]

We can find \(T\) by taking the right-hand column of \(M\) and replacing the rest by the values from an identity matrix. The inverse of \(T\) is obtained by reversing the translation: multiply the first three values in the right-hand column by \(-1\).

We can find \(R\) by taking the upper-left \(3\times 3\) submatrix of \(M\) and replacing the rest by the values from an identity matrix. The inverse of \(R\) is the transpose of this: swap rows and columns. (Note: this is because \(R\) is an orthogonal matrix.)

Lastly, we find \(M^{-1}\) by matrix multiplication.


CS 381 Fall 2012: Lecture Notes for Thursday, September 27, 2012 / Updated: 27 Sep 2012 / Glenn G. Chappell / ggchappell@alaska.edu