Home | Lehre | Videos | Texte | Vorträge | Software | Person | Impressum, Datenschutzerklärung | Blog RSS

Computing Linear Perspective Projection

Coordinate system of Direct3D: x points to the right, y points upward, z points into the screen; the origin is at the center of the screen. This is a left-handed system.

Situation to derive the projection equations: viewer at origin, image plane perpendicular to z axis at distance d.
To which point on the image plane is the point (x,y,z) projected?
Intercept theorem [Strahlensatz]: It is projected to (xd/z, yd/z,d).

Hence, perspective projection needs a division by z. This cannot be implemented using matrices as usual (i.e. as a linear mapping or an affine mapping)

Homogeneous Coordinates

Using vectors and matrices nearly as usual would be nice. Mathematicians found a clever workaround to implement perspective projection using matrices and vectors: homogeneous coordinates.
  1. Add a fourth dimension w to (x, y, z). If (x, y, z) represents a position, convert it to (x, y, z, 1). If (x, y, z) represents a (genuine) vector (i.e., direction and length), convert it to (x, y, z, 0).
  2. Subject those 4D vectors to 4x4 matrices. The result will be some (x', y', z', w').
  3. To convert a position (x', y', z', w') to regular 3D coordinates, divide by w', what yields (x'/w', y'/w', z'/w').
A large set of transformations can be implemented in this way: rotation, scaling, perspective projection, translation (!). For instance, a translation can be achieved through the matrix
(1 0 0 a)
(0 1 0 b)
(0 0 1 c)
(0 0 0 1).
Note that a (genuine) vector will not be affected by this matrix because its fourth component is zero.

Transformation Matrices

Some technical preparations (to be explained later):
// create the z buffer
presentParams.AutoDepthStencilFormat = DepthFormat.D24X8;
presentParams.EnableAutoDepthStencil = true;
// generate a mesh (introduce Mesh theTeapot as a member variable and include a reference to Direct3DX)
theTeapot = new Mesh.Teapot(device)
// clear frame and depth buffer
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Blue, 1.0f, 0);
// show the mesh
device.RenderState.Lighting = false;
theTeapot.DrawSubset(0);

Typically, we use several coordinate frames [Koordinatensysteme] to describe a 3D world. Direct3D offers four of them:

We do not specify these frames directly, but rather specify matrices to let Direct3D do the conversion: There are some pre-built matrices to do the job:
device.Transform.World = Matrix.RotationZ(0.6f)*Matrix.Translation(0.0f, 0.0f, 1.5f);
device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f, 0.0f, -3.0f), new Vector3(), new Vector3(0.0f, 1.0f, 0.0f));
device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI/3.0f, ClientSize.Width/(float)ClientSize.Height, 0.1f, 50.0f);

We can multiply matrices using *, which is appropriately overloaded in Managed DirectX. (In contrast to C++ and C#, current Java does not allow to overload operators such as + and *.) The order [Reihenfolge] is important most of the times: For matrices, A*B and B*A are different in general. As opposed to usual mathematics, DirectX employs row vectors, not column vectors. Thus, products are formed with the vector on the left hand side: v*A*B*C. This means that in the matrix product A*B*C first A is applied, then B, then C.

The basic functionality of OpenGL concerning matrices is very similar to that of Direct3D. Here are the main differences: