“为何DirectX里表示三维坐标要建一个4*4的矩阵?”

0x00 前言

首先要说明的是,本文的标题事实上来自于知乎上的一个同名问题:为何directX里表示三维坐标要建一个4*4的矩阵? - 编程 。所以,正如Milo Yip大神所说的这个标题事实上是存在问题的:矩阵是用于表示变换而不是坐标的。再了解了矩阵的做用以后,咱们就要继续思考为何变换要使用一个4×4的矩阵而不是3×3的矩阵呢?是否是画蛇添足呢?下面咱们就来聊聊这个话题。编程

0x01 怎么平移一个三维空间中的点?

咱们应该怎么平移一个三维空间中的点呢?答案很简单,咱们只须要对这个点的坐标中的每一个份量(x,y,z)和对应轴上的平移距离相加便可。数组

例如,点p1(x1,y1,z1)在X轴Y轴以及Z轴上分别平移Δx,Δy,Δz到新的点p2(x2,y2,z2),那么咱们只需在坐标对应的份量上加上这些增量就能够肯定点p2的坐标了。函数

此处输入图片的描述

> x2 = x1 + Δx
> 
> y2 = y1 + Δy
> 
> z2 = z1 + Δz

很简单是吗?那么接下来再让咱们来看一看另外一种变换:旋转。spa

0x02 再来旋转一个点

旋转相比较平移来讲,会略显复杂一些。由于咱们须要指明如下几个方面来描述一个旋转:code

  1. 旋转轴blog

  2. 旋转方向游戏

  3. 旋转角度图片

在这里,咱们假设点p须要绕Z轴顺时针旋转β度。ip

此处输入图片的描述

如这个很难看的图所示,咱们的点P1(x1,y1,z1)以Z轴位轴顺时针旋转β度以后来到了点P2(x2,y2,z2)。接下来,让咱们假设原点到P1的距离位L,且P1和Y轴之间的夹角位α,那么根据三角函数公式咱们就能够计算出P1点的具体坐标了:get

> x1 = L·sinα
> 
> y1 = L·cosα

因为是绕Z轴旋转,所以z坐标不变,所以此处不考虑。

一样根据三角函数公式,咱们能够继续计算出P2的具体坐标:

> x2 = L·sin(α + β)
> 
> y2 = L·cos(α + β)

再让咱们回忆一下中学时代的几何数学的内容,对青春的回忆又把咱们带回了课堂上老师声嘶力竭向咱们传授的内容——两角和与差:

> cos(α+β)=cosα·cosβ-sinα·sinβ 
> cos(α-β)=cosα·cosβ+sinα·sinβ
> sin(α+β)=sinα·cosβ+cosα·sinβ 
> sin(α-β)=sinα·cosβ-cosα·sinβ

回忆起老师传授给咱们的知识以后,接下来结合P1的坐标表示形式咱们就能够把P2的坐标换一个表示形式了。

> x2 = L·(sinα·cosβ+cosα·sinβ) = cosβ·x1 + sinβ·y1
> 
> y2 = L·(cosα·cosβ-sinα·sinβ) = cosβ·y1 - sinβ·x1
> 
> z2 = z1

所以,在已知P1点坐标以及旋转角度β的状况下,咱们就能够根据以上公式分别求出P2点坐标的各个份量。若是再结合上一小节中平移一个点的公式来看,咱们能够发现彷佛并不须要矩阵,而仅仅经过两组表达式就能实现坐标的变换。可是……

0x03 带来便捷的矩阵

固然,从理论上讲咱们的确能够只经过数学公式就能实现变换,但实际的状况倒是在变换十分复杂时,直接使用数学表达式来进行运算也是至关繁复的。所以,在现实中经常使用矩阵(由m × n个标量组成的长方形数组)来表示诸如平移、旋转以及缩放等线性变换。而另外一个更有趣的事实是,当两个变换矩阵A和B的积为P=AB时,则变换矩阵P至关于A和B所表明的变换。举一个例子,若A为旋转矩阵,B为平移矩阵,则矩阵P就可以实现旋转和平移变换。不过须要注意的是,矩阵乘法不符合交换律,所以AB和BA并不相等。

说了这么多,咱们彷佛仍是没有回答为何三维空间须要一个4×4矩阵来实现变换呢?下面咱们就带着这个问题,分别看看3×3矩阵和4×4矩阵的使用吧。

3×3矩阵和旋转

首先,咱们先来看一个矩阵乘以一个三维矢量的算式:

能够看到该矩阵为一个3×3的矩阵,矩阵的右侧是点P1的坐标,而矩阵的左侧则是点P2的坐标。根据这个表达式,咱们能够求出x二、y二、z2的值:

> x2 = a·x1 + b·y1 + c·z1
> 
> y2 = d·x1 + e·y1 + f·z1
> 
> z2 = g·x1 + h·y1 + i·z1

为了将矩阵等式和以前小节的数学表达式联系起来,下面咱们就将旋转表达式和该矩阵等式作一个对比。

> x2 = a·x1 + b·y1 + c·z1
> x2 = cosβ·x1 + sinβ·y1
> 
> y2 = d·x1 + e·y1 + f·z1
> y2 = cosβ·y1 - sinβ·x1
> 
> z2 = g·x1 + h·y1 + i·z1
> z2 = z1

经过对比 x2,咱们能够发现 $$a=cosβ,b=sinβ,c=0;$$
对比 y2,也能够发现 $$d=-sinβ,e=cosβ,f=0;$$
最后对比 z2,能够肯定 $$g=0,h=0,i=1;$$

将这个结果带入到以前的矩阵中,咱们的等式就能够变成下面这个样子:

也就是说,经过这个3×3的变换矩阵,咱们就已经实现了三维空间的旋转变换。那么为何还须要使用4×4矩阵呢?

搞不定的平移,4×4矩阵来救场

咱们已经经过一个3×3矩阵搞定了旋转变换,显然若是这个3×3矩阵真的是完美的解决变换的方案的话,那么它显然也必需要适合于其余的变换,例如平移。可是它到底可否知足平移的需求呢?下面咱们仍是经过对比矩阵等式和数学表达式的方式,来寻找答案。

> x2 = a·x1 + b·y1 + c·z1
> x2 = x1 + Δx
> 
> y2 = d·x1 + e·y1 + f·z1
> y2 = y1 + Δy
> 
> z2 = g·x1 + h·y1 + i·z1
> z2 = z1 + Δz

经过对比,咱们发现平移和旋转之间颇有趣的一个区别,那就是平移的表达式中带有常量Δx,而不管是旋转的表达式仍是矩阵等式中都不存在这样一个常量可以与之对应。那么问题就来,咱们没有办法使用3×3的矩阵来表示平移。这个问题该如何解决呢?答案其实很简单,那就是使用4×4矩阵来实现。可是随之而来的一个问题就是如何让一个三维坐标和一个4×4的矩阵相乘呢?

齐次坐标

为了解决三维矢量和4×4矩阵相乘的问题,咱们机智的为三维矢量添加了第四个份量,这样以前的三维矢量(x,y,z)就变成了四维的(x,y,z,w),这样由4个份量组成的矢量便被称为齐次坐标。须要说明的是,齐次坐标(x,y,z,w)等价于三维坐标(x/w,y/w,z/w),所以只要w份量的值是1,那么这个齐次坐标就能够被看成三维坐标来使用,并且所表示的坐标就是以x,y,z这3个值为坐标值的点。

所以,为了和4×4矩阵相乘,咱们的P1点坐标就变成了(x1,y1,z1,1)。而矩阵等式也变成了下面这个样子:

咱们再将这个新的矩阵等式和平移的数学表达式作一番对比:

> x2 = a·x1 + b·y1 + c·z1 + d
> x2 = x1 + Δx
> 
> y2 = e·x1 + f·y1 + g·z1 + h
> y2 = y1 + Δy
> 
> z2 = i·x1 + j·y1 + k·z1 + l
> z2 = z1 + Δz
> 
> 1 = m·x1 + n·y1 + o·z1 + p

经过对比x2,咱们能够发现 $$a=1,b=0,c=0,d=Δx;$$
对比y2,也能够发现 $$e=0,f=1,g=0,h=Δy;$$
再对比z2,能够肯定 $$i=0,j=0,k=1,l=Δz;$$
最后还能够根据表达式求出 $$m=0,n=0,o=0,p=1;$$

这样,咱们就求出了咱们的4×4的平移矩阵:

0x04 总结

写到这里,不知各位是否还记得以前在介绍矩阵乘法的时候我有提到过两个变换矩阵A和B的积P=AB,至关于A和B所表明的变换。事实上在游戏编程中,经常须要把一连串的变换预先经过计算成为单一矩阵,因此就不能即存在3×3的矩阵又存在4×4的矩阵。而将3×3矩阵拓展成4×4矩阵仍是相对更加容易的。这样,就经过一个4×4矩阵整合了平移矩阵、旋转矩阵。