Wednesday, June 20, 2012

OpenGL Camera Class Tutorial Part 2: Manipulating the Camera

Introduction

This tutorial covers sliding and arbitrarily rotating the camera relative to its local coordinate system.

Sliding the Camera

Sliding the camera is just moving the camera some distance from its current location. It would be easy to use a formula such as: Position = Position + SlidingVector. Unfortunately this only moves the camera in world coordinates. What I want to do is slidethe camera around using a vector relative to the camera's local coordinates. This can give the effect of strafing, moving forward and backward, moving up and down, or in a straight line in any direction from the camera's origin.

To slide relative to the camera's local coordinate system, I'll use the U, V, and N vectors defined in the previous tutorial.  The math is simple:
  1. Take in input vector, i, and current position, p.
  2. p.x = p.x(original) + i.x * u.x + i.y * v.x + i.z * n.x
  3. p.y = p.y(original) + i.x * u.y + i.y * v.y + i.z * n.y
  4. p.z = p.z(original) + i.x * u.z + i.y * v.z + i.z * n.z
This can be written as a slide method in the FreeCamera class:
public void Slide(Vector3 vector)
{
  Position += new Vector3(
    vector.X * U.X + vector.Y * V.X + vector.Z * N.X,
    vector.X * U.Y + vector.Y * V.Y + vector.Z * N.Y,
    vector.X * U.Z + vector.Y * V.Z + vector.Z * N.Z
    );
}

Rotating: Pitch, Yaw, and Roll

I'm not going to define Pitch, Yaw, and Roll, but here is a good, quick reference of flight dynamics. To accomplish rotating, the U, V, and N vectors will be transformed by an angle in degrees. I'm not going to describe the math for rotations, as it is out of the scope of this tutorial. Getting familiar with rotations will help in understanding the following methods.

Pitch is rotating around the local X-axis (or U-vector). This means that U will not be transformed, but the V and N vectors will be rotated around U. It is really just a bit of trigonometry; here is the method:
public void Pitch(float angle)
{
  float cosVal = (float)Math.Cos(MathHelper.DegreesToRadians(angle));
  float sinVal = (float)Math.Sin(MathHelper.DegreesToRadians(angle));

  Vector3 tempN = new Vector3(N);
  N = new Vector3(
    cosVal * tempN.X + sinVal * V.X,
    cosVal * tempN.Y + sinVal * V.Y,
    cosVal * tempN.Z + sinVal * V.Z
    );
  V = new Vector3(
    cosVal * V.X - sinVal * tempN.X,
    cosVal * V.Y - sinVal * tempN.Y,
    cosVal * V.Z - sinVal * tempN.Z
    );
}
Yaw is rotating around the local Y-axis (or V-vector). This means that V will not be transformed, but the U and N vectors will be rotated around V. The method is similar to Pitch:
public void Yaw(float angle)
{
  float cosVal = (float)Math.Cos(MathHelper.DegreesToRadians(angle));
  float sinVal = (float)Math.Sin(MathHelper.DegreesToRadians(angle));

  Vector3 tempU = new Vector3(U);
  U = new Vector3(
    cosVal * tempU.X + sinVal * N.X,
    cosVal * tempU.Y + sinVal * N.Y,
    cosVal * tempU.Z + sinVal * N.Z
    );
  N = new Vector3(
    cosVal * N.X - sinVal * tempU.X,
    cosVal * N.Y - sinVal * tempU.Y,
    cosVal * N.Z - sinVal * tempU.Z
    );
}
Roll is rotating around the local Z-axis (or N-vector). This means that N will not be transformed, but the V and U vectors will be rotated around N. The method is similar to Pitch and Yaw:
public void Roll(float angle)
{
  float cosVal = (float)Math.Cos(MathHelper.DegreesToRadians(angle));
  float sinVal = (float)Math.Sin(MathHelper.DegreesToRadians(angle));

  Vector3 tempV = new Vector3(V);
  V = new Vector3(
    cosVal * tempV.X + sinVal * U.X,
    cosVal * tempV.Y + sinVal * U.Y,
    cosVal * tempV.Z + sinVal * U.Z
    );
  U = new Vector3(
    cosVal * U.X - sinVal * tempV.X,
    cosVal * U.Y - sinVal * tempV.Y,
    cosVal * U.Z - sinVal * tempV.Z
    );
}

Rotating: An Arbitrary Axis

Rotating the camera about an arbitrary axis is not a simple task. I'm not going to go into detail about how this method works (I haven't even found a good use for it other than saying, "Hey, check this out. This is cool!"). This method transforms the U, V, and N, vectors by an arbitrary axis defined as a vector (think of it as a ray from the camera's local origin).
public void Rotate(Vector3 axis, float angle)
{
  float cosVal = (float)Math.Cos(MathHelper.DegreesToRadians(angle));
  float sinVal = (float)Math.Sin(MathHelper.DegreesToRadians(angle));

  float a = 1 + (1 - cosVal) * (axis.X * axis.X - 1);
  float b = (1 - cosVal) * axis.X * axis.Y - axis.Z * sinVal;
  float c = (1 - cosVal) * axis.X * axis.Z + axis.Y * sinVal;
  float e = (1 - cosVal) * axis.X * axis.Y + axis.Z * sinVal;
  float f = 1 + (1 - cosVal) * (axis.Y * axis.Y - 1);
  float g = (1 - cosVal) * axis.Y * axis.Z - axis.X * sinVal;
  float i = (1 - cosVal) * axis.X * axis.Z - axis.Y * sinVal;
  float j = (1 - cosVal) * axis.Y * axis.Z + axis.X * sinVal;
  float k = 1 + (1 - cosVal) * (axis.Z * axis.Z - 1);

  Vector3 tu = new Vector3(U);
  Vector3 tv = new Vector3(V);
  Vector3 tn = new Vector3(N);

  U = new Vector3(
    tu.X * a + tv.X * b + tn.X * c,
    tu.Y * a + tv.Y * b + tn.Y * c,
    tu.Z * a + tv.Z * b + tn.Z * c
    );
  V = new Vector3(
    tu.X * e + tv.X * f + tn.X * g,
    tu.Y * e + tv.Y * f + tn.Y * g,
    tu.Z * e + tv.Z * f + tn.Z * g
    );
  N = new Vector3(
    tu.X * i + tv.X * j + tn.X * k,
    tu.Y * i + tv.Y * j + tn.Y * k,
    tu.Z * i + tv.Z * j + tn.Z * k
    );
}

Conclusion

With these methods, an instance of the FreeCamera can now describe a camera's position and orientation, and manipulate the position and orientation in a pretty intuitive way. Keep in mind that every operation performed on the camera (slide, pitch, yaw, roll, rotate) is done from the perspective of the camera. Sliding the camera to the right slides the camera to the right of the camera . In the next tutorial I'll cover how to model the viewport and view volume: Part 3.

2 comments:

  1. Hello, could you name or mention how you derived the computation for the slide?

    Thanks, these articles really helped :)

    ReplyDelete
    Replies
    1. I actually did this as part of an assignment for an undergraduate college course. The Pitch and Yaw methods were given to us as examples and we were required to come up with the Roll and arbitrary rotation Rotate methods. I spent one night using Wikipedia's article on rotation matrices figuring them out. I've refined them a bit since then, but they are probably pretty crude for today's standards in 3D graphic engines.
      See: Rotation Matrices

      Delete