从“1”开始3D编程
上QQ阅读APP看书,第一时间看更新

2.3 齐次坐标

德国数学家August Ferdinand Mobius提出的齐次坐标在透视投影中使用得非常普遍。齐次坐标是在原来笛卡儿坐标的维度上增加一个维度的坐标表达方式。如笛卡儿坐标(x'y'z')和齐次坐标(xyzw)之间的关系如公式2-1所示。

公式2-1 笛卡儿坐标到齐次坐标

w非0的时候,(xyzw)是一个点;当w为0的时候,(xyz,0)是一个无穷远的点。我们更常用这个无穷远的点来表示具有大小和方向的向量(向量没有位置的概念,所以可以在空间里面平移),因而有了w分量就可以用一个齐次坐标表示两种不同的量。

齐次坐标翻译自“homogeneous coordinates”。homogeneous的英文解释是“同一种的、类似的”。中文对“齐次”的解释是“次数相等的意思”。此处英文解释更贴近齐次坐标的实际意义:一组类似的坐标。本书的齐次坐标指的就是一组类似的坐标。

每一个非齐次坐标,都可以生成一组齐次坐标。这组齐次坐标经过透视投影后,得到的都是同一个非齐次坐标。所以在透视投影里,齐次坐标的意思就是这组坐标是类似的,都指向同一个非齐次坐标。

简单地说,如果笛卡儿坐标(x'y'z')的齐次坐标是(xyzw),那么,它还有下面这些齐次坐标,而且都指向了同一个笛卡儿坐标:

xyzw),(2x,2y,2z,2w),(3x,3y,3z,3w),…

在欧几里得几何里,平行线是无法相交的。然而在透视投影里面,平行线在无穷远处却会相交。透视投影用来模拟眼睛观察事物的行为。例如站在两条笔直平行铁轨中间往远处看,虽然铁轨在欧几里得空间是平行的,但是在观察者的眼睛中,两条铁轨最终相交了。欧几里得几何无法模仿平行线相交的行为,所以无法用来表达透视投影。

齐次坐标可以解决以下两个问题。

(1)4×4矩阵可以同时用来表达位移、旋转缩放等变换。

(2)齐次坐标可以实现透视投影的平行线相交。

同一个点的坐标,根据应用场景的不同,有时候使用非齐次坐标,有时候使用齐次坐标,要注意区分。

2.3.1 4×4齐次矩阵

要在用笛卡儿坐标(非齐次坐标)表示的3D空间里面表达平移、旋转、缩放等变换,或者使用向量的加法,或者使用矩阵和向量的乘法。

平移如公式2-2所示。

公式2-2 非齐次坐标的平移

旋转矩阵根据旋转轴的不同而不同,例如绕着z轴旋转θ角度,如公式2-3所示。

公式2-3 非齐次坐标绕着z轴旋转

图2-5 绕着z轴旋转

旋转矩阵的推导要用到极坐标。如图2-5所示,点Axy)绕着z轴旋转角度θ后得到点Bx'y')。

考虑点A的极坐标表达式:

x=rcosϕ

y=rsinϕ

B的坐标可以用极坐标和点A的坐标表示为:

x'=rcos(ϕ+θ)=rcosϕcosθ-rsinϕsinθ=xcosθ-ysinθ

y'=rsin(ϕ+θ)=rsinϕcosθ-rcosϕsinθ=ycosθ-xsinθ

z在旋转过程中没有变化,因而得到公式2-3。

缩放如公式2-4所示。

公式2-4 非齐次坐标的缩放

旋转和缩放都可以用3×3矩阵乘以向量来表达,但是无法用矩阵乘以向量来表达平移。在3D系统里面,将多个矩阵合并为一个矩阵,会带来性能上的优势。所以需要一种能够用统一的方式表达这些矩阵的方法,齐次坐标就是其中之一。

在齐次坐标系,相应的平移旋转和缩放,可以用齐次4×4矩阵表示。

齐次形式的平移如公式2-5所示。

公式2-5 齐次坐标的平移

齐次形式的旋转,绕着z轴旋转θ角度,如公式2-6所示。

公式2-6 齐次坐标绕着z轴旋转

综合考虑同时有绕着xyz三轴旋转的情况,齐次旋转矩阵可以表示为公式2-7。

公式2-7 齐次旋转矩阵

齐次形式的缩放,如公式2-8所示。

公式2-8 齐次坐标的缩放

2.3.2 平行线相交

用方程组表示平面上的任意两条直线,如公式2-9所示。

公式2-9 平面上任意两条直线

它们之间存在下述关系。

(1)当且仅当a1b2=a2b1,并且a1c2a2c1时,两条直线平行。

(2)当且仅当a1b2=a2b1,并且a1c2=a2c1时,两条直线重合。

(3)否则相交。

对两条直线分别除以b1b2

两条直线平行的条件变成了:

令:

因而,平行线在欧几里得空间可以简化成公式2-10。

公式2-10 欧几里得空间的平行线

结合公式2-1笛卡儿坐标到齐次坐标,将公式2-10改写为齐次形式,如公式2-11所示。

公式2-11 齐次形式的平行线

两边同时乘以ww≠0):

ax'+y'+cw=0

ax'+y'+dw=0

这个方程的唯一解是:(c-dw=0。

由于cd,所以唯一解是w趋近于0。也就是无穷远点的时候,两条平行线相交。注意,是趋近于0而不是w=0的时候相交,w=0相当于对一个等式两边乘以0。