Sunday 10 January 2016

Adding a camera (part 15)

So we've looked at our model matrix which positions our object within our 3D space and we've looked at our projection matrix which defines our lens in a manner of speaking. The matrix we've so far left alone is our view matrix.

Our view matrix in essence defines the position and orientation of our camera in our 3D world. Well to be precise, it defines the inverse of that because we actually end up moving the entire 3D world to make our camera the center of this world.


Modifying the view matrix directly


Our first option therefor is to modify the view matrix directly. We could simply apply matrix operations onto this matrix to get the camera to move through space. We thus need to do the opposite of what we intent to do. Move the camera 10 units forward? Apply a translation to our matrix to move the world 10 units back. Rotate the camera 10 degrees to the left? We need to rotate our matrix 10 degrees to the right. Etc.

Align our view matrix with another object


Our second option is a nice one for certain types of games such as racing games. When we are sitting in the drivers seat of a car our camera is basically inside of that car. If we take the model matrix for our racing car, add a translation to move the center where the driver would be, and then inverse that matrix, we have a view matrix looking nicely out of the cars windshield. This is assuming the model of the car is properly oriented so we're not looking through the side window or the floor but that is easy to rectify.

The good old 'lookat' matrix


But the option we'll look at today is using a look-at calculation. This is a function that was available in the standard glu library and I've added to my math3d.h implementation.

The look-at calculation takes the location of your camera (or eye), the location you are looking at, and what you consider to be "up" and applies a proper view matrix.

Our changes are fairly simple. First we add our lookat and position variables to track our camera (for now globals to keep things easy):
// our camera (and view matrix)
mat4          view;
vec3          camera_eye = { 10.0, 20.0, 30.0 };
vec3          camera_lookat =  { 0.0, 0.0, 0.0 };
Next we initialize our view matrix using our lookup function in our engineLoad function:
  // init our view matrix
  mat4Identity(&view);
  mat4LookAt(&view, &camera_eye, &camera_lookat, vec3Set(&upvector, 0.0, 1.0, 0.0));  
And that's it.

I've also removed the code that rotates the box and instead added this code to our engineUpdate function to allow basic interaction with the camera using the WASD keys:
  vec3  avector, bvector, upvector;
  mat4  M;
    
  // handle our keys....
  if (engineKeyPressedCallback(GLFW_KEY_A)) {
    // rotate position left
    
    // get our (reverse) looking direction vector
    vec3Copy(&avector, &camera_eye);
    vec3Sub(&avector, &camera_lookat);
    
    // rotate our looking direction vector around our up vector
    mat4Identity(&M);
    mat4Rotate(&M, 1.0, vec3Set(&bvector, view.m[0][1], view.m[1][1], view.m[2][1]));
    
    // and update our eye position accordingly
    mat4ApplyToVec3(&camera_eye, &avector, &M);
    vec3Add(&camera_eye, &camera_lookat);
  } else if (engineKeyPressedCallback(GLFW_KEY_D)) {
    // rotate position right
    
    // get our (reverse) looking direction vector
    vec3Copy(&avector, &camera_eye);
    vec3Sub(&avector, &camera_lookat);
    
    // rotate our looking direction vector around our up vector
    mat4Identity(&M);
    mat4Rotate(&M, -1.0, vec3Set(&bvector, view.m[0][1], view.m[1][1], view.m[2][1]));
    
    // and update our eye position accordingly
    mat4ApplyToVec3(&camera_eye, &avector, &M);
    vec3Add(&camera_eye, &camera_lookat);
  } else if (engineKeyPressedCallback(GLFW_KEY_W)) {
    // get our (reverse) looking direction vector
    vec3Copy(&avector, &camera_eye);
    vec3Sub(&avector, &camera_lookat);
    
    // rotate our looking direction vector around our right vector
    mat4Identity(&M);
    mat4Rotate(&M, 1.0, vec3Set(&bvector, view.m[0][0], view.m[1][0], view.m[2][0]));
    
    // and update our eye position accordingly
    mat4ApplyToVec3(&camera_eye, &avector, &M);
    vec3Add(&camera_eye, &camera_lookat);    
  } else if (engineKeyPressedCallback(GLFW_KEY_S)) {
    // get our (reverse) looking direction vector
    vec3Copy(&avector, &camera_eye);
    vec3Sub(&avector, &camera_lookat);
    
    // rotate our looking direction vector around our right vector
    mat4Identity(&M);
    mat4Rotate(&M, -1.0, vec3Set(&bvector, view.m[0][0], view.m[1][0], view.m[2][0]));
    
    // and update our eye position accordingly
    mat4ApplyToVec3(&camera_eye, &avector, &M);
    vec3Add(&camera_eye, &camera_lookat);    
  };

  // update our view matrix
  mat4Identity(&view);
  mat4LookAt(&view, &camera_eye, &camera_lookat, vec3Set(&upvector, 0.0, 1.0, 0.0));
Note that I am using my current view matrix to determine what my current up and right are as far as my camera is concerned and rotate around those vectors.

Well that was quick:)

Download the source here

What's next


Now that we have a camera that we can move around, it's time to start loading more complex objects instead of our hard coded cube...

No comments:

Post a Comment