To define a perspective camera for CG related tasks, we need some basic information such as camera position (* camPos*), look at position (

*), field of view (*

**lookAt***), distance to near (*

**fov***) and far (*

**dNear***) clipping planes, and finally ratio.*

**dFar**Camera position and * lookAt *are needed to define camera basis vectors, which are used to define camera matrix (

*). We transform objects from world to camera space using inverse of camera matrix (*

**C***) or from camera space to world space (using camera matrix). Objects (meshes) should be in camera space for the tasks such as backface culling, clipping and projection.*

**invC*** fov*, distance to near (

*) and far (*

**dNear***) clipping planes, and ratio are used for perspective projection and frustum culling.*

**dFar**I used right-handed coordinate system for Enginator5000. So, I will explain everything in right-handed coordinate system.

**Generate Camera Basis Vectors**

Remember, in right handed coordinate system, if your origin is screen of the computer, **+z** is towards you, **+y **is towards ceiling, and **+x** is towards your right. To define camera basis vectors, we need to find three basis vectors. After we find these vectors, we will put them into a 4×4 matrix, and get camera matrix * C*.

Generally, people name camera basis vectors as * u, v, w*. I prefer to name them as

*. You can use whatever you want. Basis vectors are located in the*

**camX, camY, and camZ***matrix as below:*

**C**
1 2 3 4 |
C = Ux, Vx, Wx, camPosx Uy, Vy, Wy, camPosy Uz, Vz, Wz, camPosz 0, 0, 0, 1 |

And you can calculate these vectors as below (C++ and GLM):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Get +Z direction (W vector) vec3 camZ = mCamPos - mLookAt; camZ = glm::normalize(camZ); // Get +X direction (U vector) // U = cross(cameraUpVector, W) vec3 upV = glm::normalize(camUp); // For being sure vec3 camX = glm::cross(upV, camZ); // Get +Y direction (V vector) // V = cross(W, U) vec3 camY = glm::cross(camZ, camX); // Ideally it is normalized // Set camera matrix - GLM USES COLUMN MAJOR ORDER: // C = [u, v, w, e; 0 0 0 1] -> C = [camX, camY, camZ, camPos; 0, 0, 0, 1] C[0] = glm::vec4(camX, 0); // Set first column [Ux; Uy; Uz; 0] C[1] = glm::vec4(camY, 0); // Set second column [Vx; Vy; Vz; 0] C[2] = glm::vec4(camZ, 0); // Set third column [Wx; Wy; Wz; 0] C[3] = glm::vec4(mCamPos, 1); // Set 4th column |

**Defining Perspective Projection Matrix**

Perspective projection gives us realistic rendering results; however, it is not the only kinds of projections used in CG and video games. You can check this website to get an idea.

We define perspective projection matrix as below:

1 2 3 4 5 |
// Perspective projection matrix: 2n/(r-l), 0, (r+l)/(r-l), 0 0, 2n /(t-b), (t+b)/(t-b), 0 0, 0, -(f+n)/(f-n), -2fn/(f-n) 0, 0, -1, 0 |

Where, * n* is distance to near plane (

*),*

**dNear***is distance to far plane (*

**f***),*

**dFar***is right point on the near plane,*

**r***is left point on the near plane,*

**l***is top point on the near plane, and*

**t***is bottom on the near plane.*

**b**For more details, and derivation, you can check Sogho.ca, ScratchaPixel, OGLdev or Schabby’s blog.

We calculate the values (* r, l, t, b*) as below (C++):

1 2 3 4 5 6 7 8 9 10 11 |
// tan() function takes radian. So, we should convert our fov float tangent = (float)tan((fov * 0.5) * 3.14159265 / 180.0); // Height and width values of near plane float height = dNear * tangent; float width = height * Ratio; float top = height; // Calculate t float bottom = -height; // Calculate b float right = width; // Calculate r float left = -width; // t: left = -top * ratio = bottom * ratio |