写在前面的话:因为英语不好,所以看得慢,所以还不如索性按自己的理解简单粗糙翻译一遍,就当是自己的读书笔记了。不对之处甚多,以后理解深刻了,英语好了再回来修改。相信花在本书上的时间和精力是值得的。
———————————————————————————————
“What if angry vectors veer Round your sleeping head, and form. There’s never need to fear ,Violence of the poor world’s abstract storm.”—Robert Penn Warren
“若疯狂的矢量的运算,纠缠着你昏昏欲睡的大脑,掀起抽象世界的风暴,不要畏惧!干就完了!" —王大伟
变换(Transform)是一种操作,针对的是点,向量或颜色值,以某种方式进行转换。对于计算机图形操作者而言,掌握变换极其重要。利用好这种操作,你可以对物体进行光照,对摄像机进行定位,重塑或者触发动画。你还可以把所有计算转换到同一坐标系下,可以用不同的方式把物体投影到一个平面上。
线性变换满足向量加法和标量乘法:
举个例子,f(x)= 5x,是将一个向量的每个元素都乘以5。为了证明这个是线性操作,4.1和4.2的两个条件都需要满足。4.2被称为是缩放变换(Scaling transform),因为它可以改变一个物体的大小(尺寸)。旋转变换(Rotation transform)也是一个线性变换,旋转变换会将一个向量绕着原点进行旋转。缩放变换和旋转变换,以及所有针对三元素向量的线性变换,都可以用一个3x3的矩阵表示。
然而,3x3通常不够大。例如,f(x) = x + (7,3,2)不是一个线性变换,其中x是一个三元素向量,因为按照公式4.1来算,等号左边比等号右边多加了一个(7,3,2)。将一个固定的向量和另外一个向量相加的操作,称为平移。这是一个很重要的操作。通常我们会组合各种变换,例如,先缩小一个物体的一半,然后移动它到另外一个位置。如果使用3x3的矩阵形式来表达的话,不利于我们组合各种变换。
利于一种仿射变换(Affine transform),通常用4x4的矩阵来存储,可以将线性变换和平移组装到一块。仿射变换通常是先进行线性变换,然后再进行平移变换。用齐次坐标来表示四元素向量,点和方向向量可以用同一种表达式。方向向量可以用表示,点可以用
表示。所有的平移矩阵,旋转矩阵,缩放矩阵,反射矩阵和剪切矩阵都是仿射矩阵。
本章从最基础的基本仿射变换开始。表4.1总结了大部分这些变换、符号、函数和属性。其中,正交矩阵的转置矩阵就是其逆矩阵。
表4.1 本章中讨论的大部分变换的总结
变换可以说是操作几何图形的基本工具。大多数图形API是允许用户设置任意矩阵的,以及一些含有很多矩阵操作的库。然而,还是很值得去理解这些库和API后面的矩阵操作。知道一个API功能的使用只是个开始,了解后面矩阵操作才会使得你走的更远。例如,知道正交矩阵的逆矩阵就是它的转置矩阵,会让你的代码更简单更快速。
4.1 基本变换
4.1.1 平移








4.1.2 旋转

在3D领域中,通常用Rx(φ), Ry(φ), and Rz(φ)来表示旋转矩阵,分别表示沿着x,y,z轴进行旋转。如下公式4.5-4.7:

旋转矩阵的效果见于图4.4。旋转矩阵有两个特征,一是它绕着i轴(x,y,z)旋转,二是i轴上所有点保持不变。注意,旋转矩阵也可以用来表示绕任意轴的旋转矩阵,可以利用上面三个旋转矩阵组合得到绕任意轴的旋转矩阵,这些将在4.2.1和4.2.4中进一步讲到。
所有的旋转矩阵的行列式都为1,并且是正交矩阵。这就使得任意数量的旋转变换都可以级联在一起。可以通过来表示旋转矩阵的逆矩阵,绕着同一个轴进行反方向旋转。
例子:绕点旋转。假设围绕z轴旋转,旋转的中心点为P,如图4.2所示。因为围绕P旋转,所以P不动。变换第一步是平移物体,利用平移矩阵T(-p)将p平移到原点处,然后旋转物体,利用旋转矩阵
进行旋转操作,最后利用平移矩阵T(P)将物体移回到原始位置。最终结果为


4.1.3 缩放
缩放矩阵,将一个物体以Sx,Sy,Sz的比例分别对x轴,y轴,z轴进行缩放。这意味着缩放矩阵可以放大或缩小物体。缩放因为Si,
,越大,对应轴向的缩放比例越大。如果缩放因子为1,那么该方向不会进行缩放。4.10为缩放矩阵:
图4.4 展示了缩放矩阵的作用。如果Sx=Sy=Sz,称为统一变换(uniform),否则称为非统一变换(nonuniform)。有时候也称为各向同性(isotropic)和各向异性(anisotropic)。逆矩阵。
当使用齐次坐标系时,还有一种方式表示统一缩放矩阵,就是利用矩阵位于(3,3)(右下角)的值。这个值会影响齐次坐标系中W值,进而通过W值影响到点的每个轴向的变换,对统一缩放矩阵的方向无效。如下两种方式表示:
和S矩阵相比,S'必须是齐次操作。这可能会比较低效,因为齐次操作中引入了除法,如果右下角的值为1,则不需要进行除法。当然,有些系统是不会去判断是否值为1,因为这样可减少额外的消耗。
如果S中一个元素或者三个元素为负数时,则称为反射矩阵(reflection),或者称为镜像矩阵(mirror)。如果只有两个元素值为-1,则会物体被旋转π。注意,一个旋转矩阵乘以一个反射矩阵,结果仍然是一个反射矩阵。例如:
反射矩阵通常需要进行特殊处理。例如,反射矩阵处理一个逆时针三角形后,会变成一个顺时针三角形。这个顺序的变化可能会导致错误的光照和隐面消除。为了检测出是否为一个反射矩阵,可以先算出左上3x3的行列式。如果行列式为负,则该矩阵是反射矩阵。如4.12所示,0·0 -(-1)·(-1) = -1。
例子:沿着一个特定方向进行缩放。标准缩放矩阵只针对x,y,z轴进行了缩放,如果需要做一个 特定方向上的缩放,需要进行复合变换。假设需要沿着正交坐标系,
和
进行缩放,则第一步是先构建出F矩阵,如下
有了F矩阵,则变换就是先把标准坐标系下的坐标转换到新坐标系下,然后进行缩放变换,最后把坐标转移回来。公式如下
4.1.4 切变
有一种变换叫切变变换,例如,在游戏中利用切变变换可以扭曲整个游戏场景以制造一种迷幻效果,或者扭曲物体的外观。有6个基本切变矩阵,分别为
,
,
,
,
,
。第一个下标表示的是切变矩阵改变的坐标,第二个下标表示的是切变变换使用的坐标。如公式4.15所示,
表示的是切变矩阵,很明显可以从下标中找到参数s,x表示的是第0行,z表示的是第2列,则S位于第0行和第二列的交叉处。
将和点P相乘得到新点P'
。图4.3展示了一个方块切变变换后的效果。
(i 不等于j)的逆矩阵为其反方向的切变矩阵,
。



4.1.5 变换结合
由于矩阵乘法中的不可交换性,矩阵的顺序很重要,在变换串联的时候也是基于顺序的。
假设有两个矩阵,S(2,0.5,1)和Rz(π/6)。S表示将x坐标乘以2,y坐标乘以0.5,z坐标不变,R表示沿着z轴(右手坐标系中,垂直平面指向外面的轴)逆时针旋转 π/6。如图4.4所示,两种顺序进行串联得到的结果不同。
图4.4 矩阵相乘顺序不同的例子。上面表示的是,先进行旋转然后再缩放。下面表示的是
,先进行缩放,然后再进行旋转。可见结果不同,有矩阵的不可交换性
。
串联矩阵的作用在于提高效率,举个例子,一个游戏场景中有几百万个顶点,在场景中的全部物体都需要进行缩放,旋转,然后平移。如果将每个顶点分别和这三个变换矩阵进行变换,不如将三个变换矩阵串联成一个矩阵,然后用来和顶点做变换处理。则串联后的矩阵为。注意,这里的顺序。首先进行的是缩放变换,所以S出现在最右侧。所以有
,其中P表示的是需要变换的顶点。基本上TRS是图形系统中最为常见的顺序。
值得注意的是,因为矩阵是顺序串联在一起的,所以可以对其进行分组计算。例如在进行刚体运算的时候,可以将TR进行结合,所以有TRSp=(TR)(Sp)。可见矩阵串联满足结合定律。
4.1.6 刚体变换
当一个人抓住一个实物,例如桌子上的一支笔,从一个地方移动到另外一个地方,例如衬衫的口袋里,只有实物的方向和位置发生了变化,实物的形状没有任何变化。这样的变换,只是结合了旋转和平移,被称为刚体变换。它的特性是保持了物体本身的长度、角度和对称性。
用T(t)表示平移矩阵,R表示旋转矩阵,X表示刚体矩阵,那么有
X的逆矩阵有。也就是说计算X的逆矩阵,只需要转置R左上的3x3矩阵,平移矩阵T改变符号,然后将两个新矩阵按与原相乘的相反顺序相乘。还有一种计算X的逆矩阵,首先用下面的公式4.18表达X。
其中表示旋转矩阵的第一列,
表示旋转矩阵的第一行。注意
表示的是一个3x1的列向量,每个元素值都为0。所以X的逆可以表达为
例子:摄像机的方向。图形学中一个常见的操作是旋转摄像机的朝向,使之对准一个方向。这里使用函数gluLookupAt()(来自OpenGL Utility库,简称 GLU)。尽管这个函数如今不怎么使用了,但是这个需求还是很常见。假设摄像机位于c,需要摄像机对准I,并给定了世界空间向上的向量u',如图4.5所示。需要计算一个基坐标系{r,u,v}。首先计算出视觉方向,从摄像机指向目标对象的归一化向量。摄像机朝向所对应的朝右方向有
。u'通常不能表示摄像机朝上向量,最终的向上向量还是通过计算
,v和r叉积得到,由于v和r都已归一化,则u也是归一化的,并相互垂直。接下来构造摄像机的变换矩阵M,首先将摄像机的位置归零,然后改变基坐标系,将r对应为(1,0,0),u对应为(0,1,0),v对应为(0,0,1)。
4.1.7 法线变换
4.1.8 求逆运算




4.2 特殊的矩阵变换和操作
4.2.1 欧拉变换
欧拉变换是一种很直观的方式去构建物体的具体朝向。它的名字来自伟大的瑞典数学家莱昂哈德·欧拉(Leonhard Euler)。
首先需要建立一些默认的视图方向。最为常见的是沿着负z轴,并头部沿着y轴向上,如图4.7所示。欧拉变换由三个旋转矩阵相乘得到,图4.7给出了这三个矩阵的名称。用公式E表示如下:
上述矩阵的表达式有24种。这里给出的是最为常用的。因为E是一组旋转矩阵的串联,所以E是正交的,因此它的逆矩阵可以表示为,当然也可以直接使用E的转置矩阵。
图4.7 展示了欧拉变换及如何改变head,pitch和roll。默认的视图方向是,沿着负z轴向前,沿着y轴向上。
欧拉角用h,p,r分别表示旋转顺序及它们各自沿着自己轴线的旋转角度。有时候这些角度都被称为rolls,例如head是y-roll,pitch是x-roll。而head有时候被称为yaw,例如在飞行模拟中。
这种变换是很直观,因为很容易被外行理解。例如,改变head角度和观察者摇头很像,而改变pitch角度就像是点头,改变roll角度就像倾斜头部。所以我们不用x,y,z轴,改而用head,pitch和roll来表示。注意,欧拉变换可以确定任何物体的方向。不仅可以使用世界空间中的全局轴,也可以使用局部坐标系中的轴。
注意一些欧拉角认为z轴是初始向上方向,这只是表达符号上的不同,尽管很容易混淆。在计算机图形学中存在一种分歧:z轴向上或y轴向上。大部分制造领域中,例如3D打印,认为z轴在世界空间中是向上的,而在航空、海洋领域认为-z表示向上。建筑学和地理信息系统(GIS)则认为z轴向上,因为建筑平面图和地图是2D,x轴和y轴。媒体相关的建模系统中认为y轴是世界坐标中向上的方向,这和我们在计算机图形学中一直认为的摄像机的向上方向是y轴的这一说法相吻合。这两种说法的区别仅在于旋转90度(或者反射90度),但是如果不知道容易出问题。在本书中,除非特别说明,使用的是y轴向上。
需要指出的是摄像机的观察空间的向上方向和世界空间的向上方向不同。摄像机Roll的时候,观察视角会发生倾斜,这个时候它在世界空间的向上方向和世界空间定义的向上方向不同。再举个例子,世界空间是y轴向上的,然后用摄像机朝下去拍摄俯视图,这需要摄像机pitch旋转90度,所以这时候摄像机在世界空间中的向上方向为(0,0,-1)。这个方向上摄像机没有y分量,取而代之-z成为了向上方向。但是在观察空间,根据定义,y轴向上仍然是正确的。
虽然在一些小角度上的改变或在观察方向上,欧拉角很有用,但是欧拉角有着严重的限制。很难组合使用两组欧拉角。例如,两组欧拉角间的插值并不是简单对每个角度进行插值。实际上,两组不同欧垃角有可能指向的同一个方向,所以插值根本就没有旋转物体。这也是引出欧拉角的取代方案(例如四元数)的原因。欧拉角会引起一种现象叫万向节死锁,下一节4.2.2将会讲到。
4.2.2 从欧拉角中提取参数
有时候,从一个正交矩阵中提取出欧拉参数(h,p,r)会非常有用。步骤如下:
这里用3x3矩阵替代了4x4矩阵,因为3x3矩阵拥有旋转矩阵所有必要的信息。而4x4矩阵剩下部分除了右下角为1,其他部分全为0。
公式4.22可表示为:
可以看到,pitch可以通过得到。将
除以
可以得到roll,
除以
可以得到head。
因此,欧拉角h(head)、p(pitch)、r(roll)可以通过矩阵E用函数得到:
然而,有一个特殊的情况需要考虑到。如果,那么会出现万向节死锁,roll和head会绕着同一个轴旋转(尽管可能方向不同,这取决于p的旋转角度是
还是
)。(万向节死锁,如果约定旋转的顺序是先y(head or yaw),然后x(pitch),最后z(roll),那么在y旋转后,将x旋转90度,会出现z和y在一条线上)。假设h = 0,则有
因为p不会影响到第一列,如果,则可以用,得到
。
注意arcsin的定义,p的取值区间为,这意味着,如果p的取值在这外,则无法提取参数。h,p,r这三个参数并不是唯一,也就是说多组欧拉角可以得到同一结果。虽然上述方法会导致数据的不稳定,但这可减少性能消耗。
从公式4.26也可以看出万向节死锁,如果,例如
的时候,其中k是一个整数。在p这样的取值情况下,会丢失一个轴向自由度,因为矩阵仅依靠一个角度,
或者
。
尽管规定了欧拉角在模型系统中的顺序为x/y/z,但是还是可以按照其他顺序的。例如,在动画中通常用z/x/y这个顺序。没有完美的顺序可以避免万向节死锁,但是欧拉角还是最为常用的,例如动画师们经常在曲线编辑器中指定角度随着时间变化。
例子:变换的一个限制。假设你有一个虚拟扳手,正在拧螺帽,为了把螺丝拧紧,需要绕着x轴旋转扳手。你的输入设备(例如鼠标,虚拟手套等)会在这时候给出一个旋转矩阵。这个旋转有可能是错误的,因为仅需要绕x轴旋转。为了限制这个输入变换,只需要简单的提取出欧拉角h,p,r。然后创建一个新的矩阵。这样绕x轴旋转就变得很简单了。
4.2.3 矩阵分解
到目前为止,都是建立在假设已知所用变换矩阵,实际上往往并非如此。例如 ,只知道串联而成的变换矩阵,但并不知道各具体变换。从串联矩阵上分解出多个变换,称为矩阵分解。
分解矩阵的原因有很多:
· 提取出物体的缩放因子。
· 找出一些特定系统的变换。(例如一些系统不允许使用任意的4x4矩阵)。
· 确定模型是否只进行了刚体变换。
· 插值动画的关键帧。
· 从旋转矩阵中去掉切变。
已经提出了两种分解,一种是刚体变换中提取出平移矩阵和旋转矩阵,另外一种是从正交矩阵中提取出欧拉角。
可见,分解矩阵很简单,只需要拿到4x4矩阵最后一列的元素即可。还可以通过判断行列式是否为负来检测是否发生了反射。而分解除旋转、缩放、切变稍微要复杂点。
4.2.4 绕任意轴旋转
假设现在需要物体绕一个任意轴旋转。旋转轴为r,已归一化,旋转角度为。
首先需要一个旋转矩阵M把物体转换到一个新的空间,在这个空间,转轴r变成了x轴。然后进行最终的旋转。最后利用逆矩阵变换回原来空间。这个过程如图4.8所示。
图4.8 绕任意轴r旋转。首先利用变换矩阵M找到正交基r,s,t。然后将这个正交基和标准基对齐,也就是r轴和x轴对齐,再进行旋转。最后利用M的逆矩阵转换回原本空间。
为了计算出M,需要找出和r垂直并相互垂直的另外两个轴。只需要找出第二个轴s,第三个轴t就只需通过r和s叉乘得到了,。有一种比较稳妥的方法是找出r的最小分量(绝对值),然后将其设为0 。然后将剩余两个分量的其中一个取反。数学表达式如下:
这样保证了和
是垂直的,(r,s,t)就是正交基。然后利用这三个向量构建出一个旋转矩阵。
这个矩阵将向量r对齐到了x轴,s对齐到了y轴,t对齐到了z轴。那么,绕r旋转度就变成在新坐标系中对x轴旋转。
Goldman提出了另外一种绕任意归一化轴r旋转角度的方法。其中的变换公式如下:
4.3 四元数
尽管四元数( quaternions)在1843年就被Sir William Rowan Hamilton作为复数的扩展而发明出来,但直到1985年Shoemake才将其引入到计算机图形学中。四元数可以用来表示旋转和方向。它在很多方面是优于欧拉角和矩阵的。任何三维方向都可以表示为一个绕着特定轴旋转的单旋转。一旦有了这个特定轴和旋转角,那么转换成四元数或四元数转换成其他都变得很简单,而这对欧拉角无论哪种选择都是一个挑战。 四元数可以用于稳定和常数的方向插值,而这是欧拉角做不到的。
复数分实部和虚部。每个部分都由两个实数表示,第二实数需要乘以。同样的,四元数有四个部分,前三个值和旋转轴紧密相关,旋转角度则对四个部分都有影响。 每个四元数由四个实数表示,每个实数对应一个不同的部分。由于四元数有四个分量,我们可以用向量的形式表示它,但是为了区分,我们加了一个帽子:
。
4.3.1 数学背景
下面先聊下四元数的定义。
定义。四元数可以用下列方式定义,都是等价的。
变量被称为四元数
的实部,而
称为虚部,i,j,k称为虚部单元。
对于虚部,我可以使用所有正常的向量运算,像加,缩放,点乘,叉乘等等。两个四元数和
相乘,如下所示。需要注意,虚部单元的相乘是非交换的。
从上面的等式可以看出,用到了点乘和叉乘。
根据四元数的定义,还可以给出四元数的加法(addition)、共轭复数(conjugate),范数(norm)和恒等式(identity)。
对范数,虚部被消除,只剩下实部。范数有时候也用
表示。通过上面的这些公式可以推导出四元数的逆
。满足
。从范数的定义推导出一个公式如下:
再推导出
四元数和常数的乘法满足交换律:
,
,所以有
。
下面介绍下规则:
单位四元数,也就是
。
也可以写成:
其中三维向量有,因为
当且仅当。单位四元数是非常适合用于创建旋转和方向。
对于复数,一个2维单元向量可以写成。所以四元数可以表示为
单位四元数的log函数和power函数如下:
4.3.2 四元数变换





























矩阵变换
因为经常需要将几个不同的变换组合在一起,并且大部分都是矩阵形式,公式4.34就是其中一种方法。一个四元数可以用公式4.45转换成矩阵
:
其中s是标量,。而对单位四元数可简化为:
一旦四元数构建成功,就不需要三角函数了,这样在实际应用中就有很高的效率。
从正交矩阵转换到成单位四元数就稍微复杂点。从公式4.46可有:
如果知道了,就可以算出四元数剩下的三个元素,也就知道了四元数
。可以算出
的迹(在线代中,一个nxn矩阵A的对角线上的元素和称为矩阵A的迹,用tr(A)表示):
对单位四元数有:
为了保证稳定性,被除数需要避免太小。因此,设,则有:
如果知道,
,
,
谁最大,也就知道了
,
,
,
谁最大。如果
是最大的,公司4.49 就可以用来计算四元数。另外需要注意:
可以用4.51计算出,
,
谁最大。这样就可以避免使用除法计算出四元数。
球面线性插值
球面线性插值是:利用两个单位四元数和
及参数
计算插值。这对动画非常有用。但是对插值摄像机的方向没有用,因为摄像机的“向上”方向是会发生倾斜的,不注意很容易出错。
这种操作的代数表现形式是一个复合四元数,如下:
然后在一些软件内部实现中,用slerp表示球面线性插值:
其中有
。对参数
,slerp计算出的插值四元数,组合在一起构建出了从
到
的四维单位球的最短弧。这个弧位于
和
给出平面和四维单位圆的交点所形成的圆上。如图4.10所示。计算出的旋转四元数以恒定速度绕固定轴旋转。这样一条速度恒定,加速度为0的曲线被称为测地线曲线。如果四元数给出的平面通过圆点,则交点形成的圆是最大圆(great circle),对应的弧为最大弧(great arc)。
图4.10 单位四元数在单位球上的点。slerp用于计算两个四元数的插值,插值路径在球面上形成一个弧。注意,从到
的插值和从
到
再到
的插值不是一回事,虽然最后到达同一个方向。
slerp函数非常适合两个方向进行插值。在固定轴恒速的情况下表现很好,但如果用欧拉角来做会有问题。实际上,直接计算slerp需要用到三角函数,这是很耗性能。Malyshau提出在渲染管线中用四元数进行插值计算,这样可避免万向节死锁,在像素着色中使用,得到的误差可以接受。
如果超过两个方向,有n个方向,并且希望从
插值到
再到
,一直到
。用slerp是一个简单直接的方法。但是当到达
的时候,需要用到
,紧接着到
,又需要用到
,这样会引起抖动,如图4.10所示。这点做线性插值的情况很像。
一种更好的方法是利用样条曲线(spline)。在和
间引入四元数
和
。就可以用
和
做球面三次插值(Spherical cubic interpolation)。
和
的计算如下:
三次球面插值公式如下:





从一个方向旋转到另外一个方向







这种方式生成四元数可以避免s和t几乎指向同一方向的时候计算不稳定,几乎为0。但是当s和t反向的时候还是会有问题。当排除这种特殊情况下,任何垂直于s的旋转轴都可以被用来旋转到t。
有时需要用矩阵来表示从s旋转到t。通过公式4.46可有
其中一些中间计算如下:
可见所有的平方根和三角函数都消掉了。注意,4,57和4.30很像,但是4.57不需要三角函数。
需要注意当s和t平行或者反向的时候,因为这种情况下。如果
,可以返回单位矩阵。然而如果
,可以围绕任意轴旋转
。这个旋转轴可以通过对s和任何与s非平行的向量叉乘得到。
4.4 顶点混合















4.5 变形





















4.6 几何缓存重播
4.7 投影
4.7.1 正交投影











如上式所示,可以是由变换矩阵和串联而成,其中,。矩阵是可逆的,。
在计算机图形学中,投影后通常变成了左手坐标系,视口指向正z轴,向右为x轴,向上为y轴。因为在定义AABB的时候认为far值要比near值小,而正交投影通常都有一个镜像变换(mirroring transform)。AABB的坐标变化有,变为(-1,-1,-1),变为(1, 1, -1)。
可见公式4.64给了一个镜像变换,把右手坐标系变成了左手坐标系。
DirectX定义z值得范围为[0,1],而OpenGL定义为[-1, 1]。可以在正交投影后用一个简单的矩阵解决这个问题。


4.7.2 透视投影







图 4.19 推导透视投影矩阵的表达式。p点投影到z=-d,d>0平面上,对应的点为q。 投影是从摄像机位置的角度进行的,在本例中,位置是原点。
透视投影矩阵如4.69所示,
















点p转换后为点,w分量为
,通常不为0也不等于1。为了拿到正确的投影点p,需要除以w分量
,有
在远平面之外的物体会被裁剪掉,不会被绘制在屏幕上。透视投影可以把远平面当做无穷远,使用公式4.73所示矩阵操作:

综上所述,经过透视投影后,然后进行裁剪和齐次除法(除以w),最后得到NDC(规格化设备坐标, normalized device coordinates)。
为了得到OpenGL中的透视投影,首先需要乘以,原因和正交投影一样,这样会把公式4.71中的第三列的值取反。在镜像操作之后,近平面和远平面的值会变成正数,
。但是在视图的视角方向上,它们仍然表示沿着世界的负z轴的距离。OpenGL的公式如下
还有一个更简单的方法是用FOV(),屏幕宽高比
(w和h是屏幕分辨率),
及
。结果如下
其中,。
在一些APIs中,如DirectX,近平面为z=0(OpenGL为z=-1),远平面为z=1。此外,DirectX使用的是左手坐标系进行投影,这意味着DirectX是沿着z轴的正方向,近平面和远平面的值是正数。DirectX的公式如下:




















