跳转至

3.5 部分线性代数基础

Some Linear Algebra

线性代数是计算机图形学中的一个基本分支,它研究向量、线性变换和矩阵。我们已经在第2.3.8小节中以二维的情境遇到了这些主题。在本节中,我们将更加深入地研究它们,并将讨论扩展到三维。

虽然在这一节涉及的数学细节对你来说并非必要了解,因为它们可以由OpenGL内部处理或由软件库处理。然而,你需要熟悉这些概念和术语。对于现代OpenGL来说尤其如此,因为它把许多细节留给了你的程序。即使你有一个软件库来处理细节,你仍然需要了解足够的知识来使用该库。你可能想略读本节,并在以后作为参考使用。

Linear algebra is a branch of mathematics that is fundamental to computer graphics. It studies vectors, linear transformations, and matrices. We have already encountered these topics in Subsection 2.3.8 in a two-dimensional context. In this section, we look at them more closely and extend the discussion to three dimensions.

It is not essential that you know the mathematical details that are covered in this section, since they can be handled internally in OpenGL or by software libraries. However, you will need to be familiar with the concepts and the terminology. This is especially true for modern OpenGL, which leaves many of the details up to your programs. Even when you have a software library to handle the details, you still need to know enough to use the library. You might want to skim this section and use it later for reference.

3.5.1 向量和向量数学

Vectors and Vector Math

一个向量是具有长度和方向的量。一个向量可以被视为一个箭头,只要你记得重要的是箭头的长度和方向,而其具体位置是无关紧要的。在计算机图形学中,向量经常被用来表示方向,比如从一个物体到光源的方向,或者表面朝向的方向。在这些情况下,我们更关心向量的方向而不是它的长度。

如果我们将一个三维向量V可视化为从原点(0,0,0)开始的箭头,结束于一个点P,那么我们可以在一定程度上将V与P等同起来——至少只要我们记得从任何其他点开始的箭头也可以用来表示V。如果P的坐标是(a,b,c),我们可以使用相同的坐标来表示V。当我们将(a,b,c)视为一个向量时,a的值表示箭头起点到终点之间的x坐标的变化,b是y坐标的变化,c是z坐标的变化。例如,3D点(x,y,z) = (3,4,5)具有与向量(dx,dy,dz) = (3,4,5)相同的坐标。对于点来说,坐标(3,4,5)指定了空间中的一个位置在xyz坐标系中。对于向量来说,坐标(3,4,5)指定了沿着该向量的x、y和z坐标的变化。如果我们用一个从原点(0,0,0)开始的箭头来表示向量,那么箭头的头部将在点(3,4,5)处。但我们也可以将向量视为一个从点(1,1,1)开始的箭头,这种情况下箭头的头部将在点(4,5,6)处。

点与向量之间的区别是微妙的。对于某些目的,可以忽略这种区别;对于其他目的,它是重要的。通常,我们只有一系列数字,我们可以把它们看作是一个向量或一个点的坐标,取决于上下文中哪个更合适。

一个向量的基本性质之一是它的长度。用它的坐标来表达,一个三维向量(x,y,z)的长度由sqrt(x2+y2+z2)给出。(这只是三维空间中的毕达哥拉斯定理。)如果v是一个向量,它的长度用|v|表示。一个向量的长度也称为它的范数。(在这里我们考虑的是三维向量,但是其他维度的概念和公式类似。)

长度为1的向量尤其重要。它们被称为单位向量。如果v = (x,y,z)是任意一个不是(0,0,0)的向量,那么就有一个与v指向相同方向的单位向量。该向量由下式给出:

( x/length, y/length, z/length )

其中 length 是v的长度。将一个向量除以它的长度被称为归一化该向量:结果是一个与原始向量指向相同方向的单位向量。

两个向量可以相加。给定两个向量 v1 = (x1,y1,z1)v2 = (x2,y2,z2),它们的和定义为

v1 + v2  =  ( x1+x2, y1+y2, z1+z2 );

该和有一个几何意义:

123

乘法更加复杂。类似于和的定义,两个向量的乘积的明显定义没有几何意义,并且很少被使用。然而,有三种向量乘法被使用:标量乘积,点积和叉积。

如果 v = (x,y,z) 是一个向量,a是一个数,则a和v的标量乘积定义为

av  =  ( a*x, a*y, a*z );

假设a是正数且v不为零,那么av是一个指向与v相同方向的向量,其长度是v的长度乘以a。如果a是负数,则av指向与v相反的方向,并且其长度是|a|乘以v的长度。这种类型的乘积被称为标量乘积,因为像a这样的数字也被称为“标量”,可能是因为乘以a将v缩放到一个新的长度。

给定两个向量 v1 = (x1,y1,z1)v2 = (x2,y2,z2),v1 和 v2 的点积由 v1·v2 表示,并由以下方式定义:

v1·v2  =  x1*x2 + y1*y2 + z1*z2

注意,点积是一个数,而不是一个向量。点积有几个非常重要的几何意义。首先,注意向量 v 的长度就是 v·v 的平方根。此外,两个非零向量 v1 和 v2 的点积具有以下性质:

cos(角度)  =  v1·v2 / (|v1|*|v2|)

其中角度是 v1 和 v2 之间的角度的度量。特别地,在两个长度为 1 的单位向量的情况下,它们的点积简单地是它们之间的夹角的余弦。此外,由于 90 度角的余弦是零,如果两个非零向量的点积为零,则它们垂直。由于这些性质,点积在光照计算中特别重要,其中光照对表面的影响取决于光线与表面的夹角。

标量乘积和点积在任何维度中都有定义。对于三维向量,还有另一种称为叉积的乘积类型,它也具有重要的几何意义。对于向量 v1 = (x1,y1,z1)v2 = (x2,y2,z2),v1 和 v2 的叉积记为 v1×v2,并由以下向量定义:

v1×v2 = ( y1*z2 - z1*y2, z1*x2 - x1*z2, x1*y2 - y1*x2 )

如果 v1v2 是非零向量,则当且仅当 v1 和 v2 指向相同方向或完全相反方向时,v1×v2 为零。假设 v1×v2 是非零的,那么它同时垂直于 v1 和 v2;此外,向量 v1、v2 和 v1×v2 符合右手法则(在右手坐标系中);也就是说,如果你用右手的手指从 v1 卷曲到 v2,那么你的拇指指向 v1×v2 的方向。如果 v1 和 v2 是垂直单位向量,那么叉积 v1×v2 也是一个单位向量,它同时垂直于 v1 和 v2。

最后,我要指出,给定两个点 P1 = (x1,y1,z1)P2 = (x2,y2,z2),它们的差 P2−P1 可以由以下方式定义:

P2  P1  =  ( x2  x1, y2  y1, z2  z1 )

这个差是一个向量,可以将其视为一条从 P1 开始、指向 P2 结束的箭头。

现在,假设 P1、P2 和 P3 是多边形的顶点。那么向量 P1−P2 和 P3−P2 位于多边形的平面内,因此叉积

(P3P2) × (P1P2)

是一个垂直于多边形的向量。

123

这个向量被称为多边形的法向量。长度为一的法向量称为单位法向量。单位法向量在光照计算中非常重要,因此能够根据多边形的顶点计算出单位法向量将会非常有用。

A vector is a quantity that has a length and a direction. A vector can be visualized as an arrow, as long as you remember that it is the length and direction of the arrow that are relevant, and that its specific location is irrelevant. Vectors are often used in computer graphics to represent directions, such as the direction from an object to a light source or the direction in which a surface faces. In those cases, we are more interested in the direction of a vector than in its length.

If we visualize a 3D vector V as an arrow starting at the origin, (0,0,0), and ending at a point P, then we can, to a certain extent, identify V with P—at least as long as we remember that an arrow starting at any other point could also be used to represent V. If P has coordinates (a,b,c), we can use the same coordinates for V. When we think of (a,b,c) as a vector, the value of a represents the change in the x-coordinate between the starting point of the arrow and its ending point, b is the change in the y-coordinate, and c is the change in the z-coordinate. For example, the 3D point (x,y,z) = (3,4,5) has the same coordinates as the vector (dx,dy,dz) = (3,4,5). For the point, the coordinates (3,4,5) specify a position in space in the xyz coordinate system. For the vector, the coordinates (3,4,5) specify the change in the x, y, and z coordinates along the vector. If we represent the vector with an arrow that starts at the origin (0,0,0), then the head of the arrow will be at (3,4,5). But we could just as well visualize the vector as an arrow that starts at the point (1,1,1), and in that case the head of the arrow would be at the point (4,5,6).

The distinction between a point and a vector is subtle. For some purposes, the distinction can be ignored; for other purposes, it is important. Often, all that we have is a sequence of numbers, which we can treat as the coordinates of either a vector or a point, whichever is more appropriate in the context.

One of the basic properties of a vector is its length. In terms of its coordinates, the length of a 3D vector (x,y,z) is given by sqrt(x2+y2+z2). (This is just the Pythagorean theorem in three dimensions.) If v is a vector, its length is denoted by |v|. The length of a vector is also called its norm. (We are considering 3D vectors here, but concepts and formulas are similar for other dimensions.)

Vectors of length 1 are particularly important. They are called unit vectors. If v = (x,y,z) is any vector other than (0,0,0), then there is exactly one unit vector that points in the same direction as v. That vector is given by

( x/length, y/length, z/length )

where length is the length of v. Dividing a vector by its length is said to normalize the vector: The result is a unit vector that points in the same direction as the original vector.

Two vectors can be added. Given two vectors v1 = (x1,y1,z1) and v2 = (x2,y2,z2), their sum is defined as

v1 + v2  =  ( x1+x2, y1+y2, z1+z2 );

The sum has a geometric meaning:

123

Multiplication is more complicated. The obvious definition of the product of two vectors, similar to the definition of the sum, does not have geometric meaning and is rarely used. However, there are three kinds of vector multiplication that are used: the scalar product, the dot product, and the cross product.

If v = (x,y,z) is a vector and a is a number, then the scalar product of a and v is defined as

av  =  ( a*x, a*y, a*z );

Assuming that a is positive and v is not zero, av is a vector that points in the same direction as v, whose length is a times the length of v. If a is negative, av points in the opposite direction from v, and its length is |a| times the length of v. This type of product is called a scalar product because a number like a is also referred to as a "scalar," perhaps because multiplication by a scales v to a new length.

Given two vectors v1 = (x1,y1,z1) and v2 = (x2,y2,z2), the dot product of v1 and v2 is denoted by v1·v2 and is defined by

v1·v2  =  x1*x2 + y1*y2 + z1*z2

Note that the dot product is a number, not a vector. The dot product has several very important geometric meanings. First of all, note that the length of a vector v is just the square root of v·v. Furthermore, the dot product of two non-zero vectors v1 and v2 has the property that

cos(angle)  =  v1·v2 / (|v1|*|v2|)

where angle is the measure of the angle between v1 and v2. In particular, in the case of two unit vectors, whose lengths are 1, the dot product of two unit vectors is simply the cosine of the angle between them. Furthermore, since the cosine of a 90-degree angle is zero, two non-zero vectors are perpendicular if and only if their dot product is zero. Because of these properties, the dot product is particularly important in lighting calculations, where the effect of light shining on a surface depends on the angle that the light makes with the surface.

The scalar product and dot product are defined in any dimension. For vectors in 3D, there is another type of product called the cross product, which also has an important geometric meaning. For vectors v1 = (x1,y1,z1) and v2 = (x2,y2,z2), the cross product of v1 and v2 is denoted v1×v2 and is the vector defined by

v1×v2 = ( y1*z2 - z1*y2, z1*x2 - x1*z2, x1*y2 - y1*x2 )

If v1 and v2 are non-zero vectors, then v1×v2 is zero if and only if v1 and v2 point in the same direction or in exactly opposite directions. Assuming v1×v2 is non-zero, then it is perpendicular both to v1 and to v2; furthermore, the vectors v1, v2, v1×v2 follow the right-hand rule (in a right-handed coordinate system); that is, if you curl the fingers of your right hand from v1 to v2, then your thumb points in the direction of v1×v2. If v1 and v2 are perpendicular unit vectors, then the cross product v1×v2 is also a unit vector, which is perpendicular both to v1 and to v2.

Finally, I will note that given two points P1 = (x1,y1,z1) and P2 = (x2,y2,z2), the difference P2−P1 is defined by

P2  P1  =  ( x2  x1, y2  y1, z2  z1 )

This difference is a vector that can be visualized as an arrow that starts at P1 and ends at P2.

Now, suppose that P1, P2, and P3 are vertices of a polygon. Then the vectors P1−P2 and P3−P2 lie in the plane of the polygon, and so the cross product

(P3P2) × (P1P2)

is a vector that is perpendicular to the polygon.

123

This vector is said to be a normal vector for the polygon. A normal vector of length one is called a unit normal. Unit normals will be important in lighting calculations, and it will be useful to be able to calculate a unit normal for a polygon from its vertices.

3.5.2 矩阵和变换

Matrices and Transformations

矩阵只是一组数字的二维数组。一个具有 r 行和 c 列的矩阵称为 r 行 c 列的矩阵。如果 A 和 B 是矩阵,并且 A 的列数等于 B 的行数,则可以将 A 和 B 相乘得到矩阵积 AB。如果 A 是一个 n 行 m 列的矩阵,而 B 是一个 m 行 k 列的矩阵,则 AB 是一个 n 行 k 列的矩阵。特别地,两个 n 行 n 列的矩阵可以相乘得到另一个 n 行 n 列的矩阵。

一个 n 维向量可以被视为一个 n 行 1 列的矩阵。如果 A 是一个 n 行 n 列的矩阵,而 v 是一个 n 维向量,被视为一个 n 行 1 列的矩阵,那么乘积 Av 再次是一个 n 维向量。一个 3 行 3 列矩阵 A 和一个 3 维向量 v = (x,y,z) 的乘积通常显示如下:

123

注意,乘积 Av 中的第 i 个坐标简单地是矩阵 A 的第 i 行与向量 v 的点积。

利用向量乘以矩阵的这个定义,矩阵定义了一种变换,可以应用于一个向量以产生另一个向量。用这种方式定义的变换称为线性变换,它们是线性代数中的主要研究对象。线性变换 L 具有以下特性:对于两个向量 v 和 w,L(v+w) = L(v) + L(w),对于一个数 s,L(sv) = sL(v)

旋转和缩放是线性变换,但平移不是线性变换。为了包含平移,我们必须将我们的变换视野扩展到包括仿射变换。粗略地说,仿射变换可以定义为先进行线性变换,然后进行平移。几何上,仿射变换是一种保持平行线的变换;也就是说,如果两条线是平行的,那么它们在仿射变换下的图像也将是平行线。对于计算机图形学,我们对三维仿射变换感兴趣。然而——通过一个乍看起来非常奇怪的技巧——我们可以通过进入第四维度将我们的视野重新聚焦到线性变换上。

首先注意,在三维空间中的仿射变换将一个向量 (x1,y1,z1) 转换为由以下公式给出的向量 (x2,y2,z2):

x2 = a1*x1 + a2*y1 + a3*z1 + t1
y2 = b1*x1 + b2*y1 + b3*z1 + t2
z2 = c1*x1 + c2*y1 + c3*z1 + t3

这些公式表达了一个线性变换,由一个 3 行 3 列的矩阵乘法给出

123

然后在 x 方向平移 t1,y 方向平移 t2,z 方向平移 t3。关键在于用四维向量 (x,y,z,1) 替换每个三维向量 (x,y,z),在作为第四个坐标加入一个 "1"。而不是使用 3 行 3 列的矩阵,我们使用 4 行 4 列的矩阵

123

如果将向量 (x1,y1,z1,1) 乘以这个 4 行 4 列的矩阵,结果正好是向量 (x2,y2,z2,1)。也就是说,不是对 3D 向量 (x1,y1,z1) 应用仿射变换,而是对 4D 向量 (x1,y1,z1,1) 应用线性变换。

这可能对你来说似乎毫无意义,但尽管如此,在 OpenGL 和其他 3D 计算机图形系统中就是这样做的:一个仿射变换被表示为一个 4 行 4 列的矩阵,底部行为 (0,0,0,1),并且通过在最后加入一个 1,将三维向量改为四维向量。结果是,所有在计算机图形中如此重要的仿射变换都可以通过矩阵对向量的乘法来实现。

将向量保持不变的单位变换对应于乘以单位矩阵,单位矩阵在其对角线上有1,其他位置都是0。OpenGL 函数 glLoadIdentity() 将当前矩阵设置为 4 行 4 列的单位矩阵。OpenGL 的变换函数,比如 glTranslatef(tx, ty, tz),其效果是将当前矩阵乘以代表该变换的 4 行 4 列矩阵。乘法是右乘的;也就是说,如果 M 是当前矩阵,T 是代表变换的矩阵,那么当前矩阵将被设置为乘积矩阵 MT。为记录,下图显示了单位矩阵以及与各种 OpenGL 变换函数对应的矩阵:

123

OpenGL 中甚至可以使用任意的变换矩阵,使用函数 glMultMatrixf(T)glMultMatrixd(T)。参数 T 是一个由类型为 floatdouble 的数字数组组成的数组,代表一个变换矩阵。该数组是一个长度为 16 的一维数组。数组中的项是变换矩阵中的数字,按列主序存储,也就是说,首先是第一列的数字,然后是第二列的数字,依此类推。这些函数将当前矩阵右乘以矩阵 T。例如,您可以使用它们来实现一个剪切变换,这不容易表示为一系列的缩放、旋转和平移。

A matrix is just a two-dimensional array of numbers. A matrix with r rows and c columns is said to be an r-by-c matrix. If A and B are matrices, and if the number of columns in A is equal to the number of rows in B, then A and B can be multiplied to give the matrix product AB. If A is an n-by-m matrix and B is an m-by-k matrix, then AB is an n-by-k matrix. In particular, two n-by-n matrices can be multiplied to give another n-by-n matrix.

An n-dimensional vector can be thought of an n-by-1 matrix. If A is an n-by-n matrix and v is a vector in n dimensions, thought of as an n-by-1 matrix, then the product Av is again an n-dimensional vector. The product of a 3-by-3 matrix A and a 3D vector v = (x,y,z) is often displayed like this:

123

Note that the i-th coordinate in the product Av is simply the dot product of the i-th row of the matrix A and the vector v.

Using this definition of the multiplication of a vector by a matrix, a matrix defines a transformation that can be applied to one vector to yield another vector. Transformations that are defined in this way are linear transformations, and they are the main object of study in linear algebra. A linear transformation L has the properties that for two vectors v and w, L(v+w) = L(v) + L(w), and for a number s, L(sv) = sL(v).

Rotation and scaling are linear transformations, but translation is not a linear transformation. To include translations, we have to widen our view of transformation to include affine transformations. An affine transformation can be defined, roughly, as a linear transformation followed by a translation. Geometrically, an affine transformation is a transformation that preserves parallel lines; that is, if two lines are parallel, then their images under an affine transformation will also be parallel lines. For computer graphics, we are interested in affine transformations in three dimensions. However—by what seems at first to be a very odd trick—we can narrow our view back to the linear by moving into the fourth dimension.

Note first of all that an affine transformation in three dimensions transforms a vector (x1,y1,z1) into a vector (x2,y2,z2) given by formulas

x2 = a1*x1 + a2*y1 + a3*z1 + t1
y2 = b1*x1 + b2*y1 + b3*z1 + t2
z2 = c1*x1 + c2*y1 + c3*z1 + t3

These formulas express a linear transformation given by multiplication by the 3-by-3 matrix

123

followed by translation by t1 in the x direction, t2 in the y direction and t3 in the z direction. The trick is to replace each three-dimensional vector (x,y,z) with the four-dimensional vector (x,y,z,1), adding a "1" as the fourth coordinate. And instead of the 3-by-3 matrix, we use the 4-by-4 matrix

123

If the vector (x1,y1,z1,1) is multiplied by this 4-by-4 matrix, the result is precisely the vector (x2,y2,z2,1). That is, instead of applying an affine transformation to the 3D vector (x1,y1,z1), we can apply a linear transformation to the 4D vector (x1,y1,z1,1).

This might seem pointless to you, but nevertheless, that is what is done in OpenGL and other 3D computer graphics systems: An affine transformation is represented as a 4-by-4 matrix in which the bottom row is (0,0,0,1), and a three-dimensional vector is changed into a four dimensional vector by adding a 1 as the final coordinate. The result is that all the affine transformations that are so important in computer graphics can be implemented as multiplication of vectors by matrices.

The identity transformation, which leaves vectors unchanged, corresponds to multiplication by the identity matrix, which has ones along its descending diagonal and zeros elsewhere. The OpenGL function glLoadIdentity() sets the current matrix to be the 4-by-4 identity matrix. An OpenGL transformation function, such as glTranslatef(tx,ty,tz), has the effect of multiplying the current matrix by the 4-by-4 matrix that represents the transformation. Multiplication is on the right; that is, if M is the current matrix and T is the matrix that represents the transformation, then the current matrix will be set to the product matrix MT. For the record, the following illustration shows the identity matrix and the matrices corresponding to various OpenGL transformation functions:

123

It is even possible to use an arbitrary transformation matrix in OpenGL, using the function glMultMatrixf(T) or glMultMatrixd(T). The parameter, T, is an array of numbers of type float or double, representing a transformation matrix. The array is a one-dimensional array of length 16. The items in the array are the numbers from the transformation matrix, stored in column-major order, that is, the numbers in the fist column, followed by the numbers in the second column, and so on. These functions multiply the current matrix by the matrix T, on the right. You could use them, for example, to implement a shear transform, which is not easy to represent as a sequence of scales, rotations, and translations.

3.5.3 齐次坐标

Homogeneous Coordinates

我们在本节中以一点有关变换实现的数学细节结束。在计算机图形学中有一个常见的变换不是仿射变换:在透视投影的情况下,投影变换不是仿射的。在透视投影中,物体看起来会随着离观察者的距离增加而变小,这是任何仿射变换都无法表达的属性,因为仿射变换保持平行线,而在透视投影中,平行线会在远处汇聚。

令人惊讶的是,我们仍然可以将透视投影表示为一个 4 行 4 列的矩阵,只要我们愿意将坐标的使用推广到更远的程度。我们已经用第四个坐标为 1 的四维向量来表示三维向量。现在,我们允许第四个坐标可以是任何值,除了要求四个坐标中至少有一个非零。当第四个坐标 w 非零时,我们将坐标 (x,y,z,w) 视为表示三维向量 (x/w,y/w,z/w)。请注意,这与我们先前的用法是一致的,因为它将 (x,y,z,1) 视为 (x,y,z),与以前一样。当第四个坐标为零时,没有对应的三维向量,但是可以将 (x,y,z,0) 视为表示沿着 (x,y,z) 方向的三维“无穷远点”。

以这种方式使用的坐标 (x,y,z,w) 称为齐次坐标。如果我们使用齐次坐标,那么任何 4 行 4 列的矩阵都可以用于变换三维向量,包括其底部行不是 (0,0,0,1) 的矩阵。可以用这种方式表示的变换之一是透视投影的投影变换。实际上,这正是 OpenGL 内部所做的。它使用齐次坐标来表示所有三维点和向量,并将所有变换表示为 4 行 4 列的矩阵。甚至可以使用齐次坐标指定顶点。例如,命令

glVertex4f(x, y, z, w);

其中 w 的值非零,生成 3D 点 (x/w, y/w, z/w)。幸运的是,你几乎永远不必直接处理齐次坐标。唯一的真正例外是,令人惊讶的是,在配置 OpenGL 照明时使用了齐次坐标,我们将在下一章中看到。

We finish this section with a bit of mathematical detail about the implementation of transformations. There is one common transformation in computer graphics that is not an affine transformation: In the case of a perspective projection, the projection transformation is not affine. In a perspective projection, an object will appear to get smaller as it moves farther away from the viewer, and that is a property that no affine transformation can express, since affine transforms preserve parallel lines and parallel lines will seem to converge in the distance in a perspective projection.

Surprisingly, we can still represent a perspective projection as a 4-by-4 matrix, provided we are willing to stretch our use of coordinates even further than we have already. We have already represented 3D vectors by 4D vectors in which the fourth coordinate is 1. We now allow the fourth coordinate to be anything at all, except for requiring that at least one of the four coordinates is non-zero. When the fourth coordinate, w, is non-zero, we consider the coordinates (x,y,z,w) to represent the three-dimensional vector (x/w,y/w,z/w). Note that this is consistent with our previous usage, since it considers (x,y,z,1) to represent (x,y,z), as before. When the fourth coordinate is zero, there is no corresponding 3D vector, but it is possible to think of (x,y,z,0) as representing a 3D "point at infinity" in the direction of (x,y,z).

Coordinates (x,y,z,w) used in this way are referred to as homogeneous coordinates. If we use homogeneous coordinates, then any 4-by-4 matrix can be used to transform three-dimensional vectors, including matrices whose bottom row is not (0,0,0,1). Among the transformations that can be represented in this way is the projection transformation for a perspective projection. And in fact, this is what OpenGL does internally. It represents all three-dimensional points and vectors using homogeneous coordinates, and it represents all transformations as 4-by-4 matrices. You can even specify vertices using homogeneous coordinates. For example, the command

glVertex4f(x,y,z,w);

with a non-zero value for w, generates the 3D point (x/w,y/w,z/w). Fortunately, you will almost never have to deal with homogeneous coordinates directly. The only real exception to this is that homogeneous coordinates are used, surprisingly, when configuring OpenGL lighting, as we'll see in the next chapter.