推导相机变换矩阵

-潘宏
-2009.12.31

-本人水平有限,疏忽错误在所难免,还请各位数学高手、编程高手不吝赐教

-email: popyy@netease.com

-B站专栏:https://b23.tv/oWsl6PD

 

一些网友写信给我希望能够了解固定流水线中世界空间到相机空间变换矩阵的具体推导过程。其实之前我在《向量几何在游戏编程中的使用6》中已经简单的把相机变换作为一个使用基理论的例子进行了说明,但可能仍然不够具体。这篇文章中,我会尽力阐述相机变换的整个来龙去脉。希望能够对正在学习固定流水线的朋友们有所帮助。这里我们仍然会在推导相机变换之前介绍几个理论知识,目的是为了更好的理解推导过程。我们马上开始!

什么是相机变换?

在流水线中,当物体从模型坐标通过世界矩阵变换到世界空间之后,它将通过相机变换从世界空间变换到相机空间。下图的固定流水线中,蓝色框中的部分就是这个过程。

 

 

 

pipeline.jpg

 

 

 

其实,所谓的相机空间,就是以相机作为坐标原点的一个参考系,所以,从世界空间变换到相机空间,就是把物体从世界坐标系,变换到相机为原点的相机坐标系,如下图所示。

 

 

 

view_space.jpg

 

左半部分是小人在世界空间中的位置,右半部分是小人变换到相机空间后的位置。这样的一个变换可以有很多种方式来实现:欧拉相机系统、UVN系统、Two Points & A Twist等等。这里我们讨论最为广泛的UVN系统构建相机矩阵,如果读者对其他方法感兴趣,可以查找相关的资料。我们仍然讨论OpenGL的相机矩阵的推导,其他API可以类似的推导。

坐标转换公式

我们在《向量几何在游戏编程中的使用6》中提到了正交矩阵,这是在基理论基础上的一个概念(如果对基理论不是很熟悉,请参考《向量几何在游戏编程中的使用6》)。正交矩阵所有列(行)向量构成了一个标准正交基(它的列向量都是互相正交,并且长度为1),因此,可以把正交矩阵看成是对一个坐标系的描述。同时,我们知道:同一个向量,在不同的基下面的坐标是不同的。因此,可以用正交矩阵来代表坐标系(也可以看作基)从而写出在统一的参考系(全局坐标系)下同一个向量在不同基中的坐标。

 

 

qvrv1.jpg

 

 

上面的式子表示,参考系中向量v在基Q中的坐标是v’,在基R中的坐标是v’’(注意这里的环境下基矩阵是用列向量表示的,这样相乘之后的结果表示的是基向量的线性组合)。如下图,黑色基表示的是参考系,红色是基Q,蓝色是基R,v是参考系中的一个向量。

 

ref_qr.jpg

 

为了让大家更清楚,我举一个例子:

 

exp1.jpg

 

上式的意思是:参考系中的向量v,在基Q( 1 0 0 ), ( 0 1 0 ), ( 0 0 1)下的坐标是( 1 2 6 ),在基R( 0 1 0 ), ( 0 0 1 ), ( 1 0 0 )下的坐标是( 2 6 1 )。注意,我们所讨论的所有基和向量的关系都只是线性表示的关系,没有位移关系,因此我们用3D向量表示,而不是4D的齐次表示(如果对齐次坐标不是很熟悉,请参考《深入探索透视投影变换》中的齐次坐标部分)。

这样,已知一个基Q和向量v在它之中的坐标v’,以及另外一个基R,我们可以通过v=Qv’=Rv’’公式来计算v’’。

 

 

 

qvrv2.jpg

上面就是求v’’的公式,注意到右边需要计算基R的逆矩阵R^-1,因为基R是正交矩阵,而正交矩阵的一个重要性质就是逆等于转置。因此,我们可以把它写成

qvrv3.jpg

 

这个公式就是坐标转换公式。特别地,如果Q是和参考系相同的坐标系(3D编程中大多数情况下如此),比如世界坐标系,则Q是一个单位矩阵I,则我们可以把它写成

 

qvrv4.jpg

 

这个坐标转换公式可以解释为:对于世界坐标系中的向量v’,它在坐标系R中的坐标是v’’。我们在后面会用到这个公式。

    除了用正交矩阵来阐述坐标转换,我们还可以使用点积所代表的共线程度(colinear amount)来描述坐标转换(André LaMothe的《Tricks Of The 3D Game Programming Gurus》)。这个理论基于点积的几何意义:一个向量在另一个向量上的共线程度。比如两个向量v和s点积

 

 

colinear.jpg

几何意义就是v在s方向上的投影长和s的长的乘积,或者是s在v方向上的投影长和v的长的乘积(积的符号为:若v和s的角度小于90度,积为正,如果是直角,积为零,否则为负)。

dot.jpg

 

进一步地,如果v是一个单位向量,则这个点积可以解释为s在v方向上的投影长;如果s是一个单位向量,则可以解释为v在s方向上的投影长。现在,我们把点积推广到基的层次上,把一个向量v’和一个基R的三个单位轴向量进行点积,点积得到的三个值则表示这个向量在这个基下的坐标v’’

 

colinear2.jpg

 

数学表达为

 

qvrv5.jpg

 

请注意,为了让v’能够和基的每一个轴向量进行点积,我们必须把基写成转置形式,即行向量乘法,否则就变成了线性组合的形式。这个公式的意义就是世界空间中的向量v’和基R的轴向量进行点积从而得到v’在R下的共线程度——坐标v’’。这个公式和上面我们得到的坐标转换公式一模一样。实际上我们是从两个不同的方向解释同一个公式,希望你能够把两个方向都理解。

UVN系统

UVN系统本身是一个基。如下图所示,三个基向量U,V,N分别指向相机的右方、上方和后方从而构成右手坐标系,相机则处于坐标原点。

 

 

uvn.jpg

使用UVN系统可以非常方便的设置相机朝向。它的构建过程如下如所示

uvn_gen.jpg

 

在参考系下(这里是世界坐标系),我们给定相机的位置——eye,被观察的小人的位置——lookat,以及一个辅助向量——参考系中表示“上方”的向量up,这个向量会影响U和V的生成,因为以后求出的V向量会在up和N向量所决定的平面上(有兴趣可以自己证明一下),所以可以通过这个向量让相机产生不同的偏转。

 

cam_roll.jpg

 

首先我们求出向量N

 

n_gen.jpg

 

很简单,用目标位置减去相机的位置,就是图中的步骤2。第3步,我们求出向量U。这一步需要使用辅助向量up,如果不希望相机产生偏转,一般取(0, 1, 0)

 

u_gen.jpg

 

 

U使用向量的叉乘实现,就是图中的步骤3。最后,使用N和U计算出向量V

 

v_gen.jpg

 

 

最后将计算出的U,V和N进行单位化,就得到了相机的UVN系统。结合上面我们谈到的坐标转换理论,我们可以把UVN系统看作是相机的基,从而可以方便的把一个向量在世界坐标和相机坐标进行转换。

OpenGL的gluLookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz)方法就是使用的上面的步骤进行相机矩阵的设置。它的前三个参数就是相机的位置向量,中间三个参数是所观察的目标位置向量,最后三个参数就是辅助向量up。

相机矩阵的推导

上面我们已经说明了UVN系统,标准流水线中就是使用了UVN系统来描述相机。U, V, N分别对应相机坐标系的三个基向量。

此外,对于一个相机来说,它在开始的时候和世界坐标系是重合的,用户控制相机在世界空间中移动之后,相机的状态可以用两个属性来描述——朝向和位置。也就是说,有了这两个属性,一个相机模型在世界中的状态就确定了。而这两个属性,我们用变换的理论来描述,就是旋转和平移。可以想象,对于世界中的任何一个相机状态,我们都可以把它看成是:相机先围绕自身基原点旋转一定的角度,然后平移到世界空间的某个地方。下图展示了这个过程

 

 

 

cam_xform.jpg

图中,红色是相机的基,而黑色是世界的基,也就是参考系。小人是世界中的一个物体。相机在移动之前,两个基是重合的。当相机在屏幕中定位时,它首先会进行朝向的确定——旋转,然后进行位置的确定——平移。图中的Rotation和Translation两步就是相机定位时所发生的变换。可以看到相机相对于小人的运动。而当进行相机变换的时候,小人应该从世界基变换到相机的基里面。这样,他应该进行一个相机定位的逆定位,先逆平移小人和相机,然后再逆旋转小人和相机,最后相机归位,小人随相机变到了相机空间。这是由Inverse Translation和Inverse Rotation两个步骤完成的,这两个步骤就是相机变换。现在我们推导这个变换。我们把关系写出来,相机本身的变换C包括两个元素

ctr.jpg

 

其中T是平移变换,R是旋转变换。而相机变换是相机本身变换的逆变换

 

ctr_inv.jpg

 

这个C^-1就是我们要求出的相机变换。其中T^-1很容易求出,即

 

t_inv.jpg

 

而R^-1就没有这么容易求出来了。所以,我们不求它,我们用UVN系统。什么意思?请看上面的那张相机变换的图,当相机变换进行完Inverse Translation这一步之后,相机的原点和世界原点就重合了,也就是处理完了关于平移的变换。接下来我们要做的是逆旋转,而其实逆旋转的目的,就是要得到目前世界坐标中经过逆平移的小人在相机坐标系中的坐标。是不是似曾相识?我们的坐标变换理论就派上用场了。我们回忆上面坐标变换的公式

 

vrv.jpg

 

这个坐标转换公式可以解释为:对于世界坐标系中的向量v’,它在坐标系R中的坐标是v’’。那么,我们可以套用在这里:对于世界坐标中的已经经过逆平移的坐标v’,它在相机坐标系R中的坐标是v’’。什么是相机坐标系R?就是我们的相机UVN系统!就是

 

r.jpg

 

则相机变换的完整公式就是

 

vrv2.jpg

 

这里,v是小人在世界空间中的坐标,v’’是小人在相机空间中的坐标。则相机变换矩阵就是

 

c_inv.jpg

至此,我们就完成了相机矩阵的推导。物体经过这个矩阵就从世界空间变换到了相机空间,等待流水线对它进行投影变换。OpenGL就使用了上面推导出的最后的那个矩阵。希望你能够理解这个推导过程,如果你有什么问题或者不同的看法,请一定给我发信J下次见!

 

透视投影变换矩阵推导过程如下: 假设有一个三维点 $(X,Y,Z)$,它在相机坐标系中的坐标为 $(X_c,Y_c,Z_c)$。相机坐标系的原点为相机位置,$Z_c$ 轴指向相机朝向的反方向,$X_c$ 和 $Y_c$ 轴分别与相机的右方向和下方向对齐。 为了把相机坐标系中的点映射到图像平面上,我们需要进行透视投影变换。首先,我们将相机坐标系中的点转换为齐次坐标 $(X_c,Y_c,Z_c,1)$。然后,我们将它乘以一个投影矩阵 $P$,得到一个新的齐次坐标 $(u,v,w,1)$: $$ \begin{bmatrix} u \\ v \\ w \\ 1 \\ \end{bmatrix} = P \cdot \begin{bmatrix} X_c \\ Y_c \\ Z_c \\ 1 \\ \end{bmatrix} $$ 其中,$u$ 和 $v$ 分别表示图像平面上的坐标,$w$ 用来进行透视除法,保证 $u$ 和 $v$ 的值在图像平面上。 投影矩阵 $P$ 可以分解为相机内参矩阵 $K$ 和相机外参矩阵 $[R|t]$ 的乘积: $$ P = K [R|t] $$ 其中,$K$ 是一个 $3 \times 3$ 的矩阵,包含了相机的内部参数,如焦距、主点等。$[R|t]$ 是一个 $3 \times 4$ 的矩阵,包含了相机的外部参数,如相机的旋转和平移。 为了推导 $P$ 的具体形式,我们可以先考虑一个简单的情况:相机坐标系的原点与图像平面重合,且相机的朝向与图像平面平行。这种情况下,投影矩阵可以表示为: $$ P = \begin{bmatrix} f & 0 & 0 & 0 \\ 0 & f & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} $$ 其中,$f$ 是焦距,表示相机到图像平面的距离。 当相机坐标系的原点和图像平面不重合时,我们可以使用相机外参矩阵 $[R|t]$ 来把相机坐标系的原点变换到图像平面上。具体来说,我们可以将相机坐标系的原点变换为 $(X_c',Y_c',Z_c')$,其中 $(X_c',Y_c',0)$ 是图像平面上的点。这个变换可以表示为: $$ \begin{bmatrix} X_c' \\ Y_c' \\ Z_c' \\ 1 \\ \end{bmatrix} = [R|t] \cdot \begin{bmatrix} 0 \\ 0 \\ 0 \\ 1 \\ \end{bmatrix} $$ 然后,我们可以把 $(X,Y,Z)$ 变换为 $(X',Y',Z')$,其中 $(X',Y')$ 是图像平面上的坐标。这个变换可以表示为: $$ \begin{bmatrix} X' \\ Y' \\ Z' \\ 1 \\ \end{bmatrix} = [R|t] \cdot \begin{bmatrix} X \\ Y \\ Z \\ 1 \\ \end{bmatrix} $$ 最后,我们可以将 $(X',Y',Z')$ 投影到图像平面上,得到一个新的齐次坐标 $(u,v,w,1)$。这个投影可以表示为: $$ \begin{bmatrix} u \\ v \\ w \\ 1 \\ \end{bmatrix} = K \cdot \begin{bmatrix} X'/Z' \\ Y'/Z' \\ 1 \\ \end{bmatrix} $$ 将以上三个变换组合起来,我们可以得到透视投影变换矩阵的形式: $$ P = K [R|t] = \begin{bmatrix} f_x & 0 & c_x & 0 \\ 0 & f_y & c_y & 0 \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \begin{bmatrix} r_{11} & r_{12} & r_{13} & t_1 \\ r_{21} & r_{22} & r_{23} & t_2 \\ r_{31} & r_{32} & r_{33} & t_3 \\ \end{bmatrix} $$ 其中,$f_x$ 和 $f_y$ 是 $K$ 矩阵的对角线元素,分别表示 $x$ 和 $y$ 方向上的焦距;$c_x$ 和 $c_y$ 是 $K$ 矩阵的中心点,表示图像平面上的主点;$r_{ij}$ 和 $t_i$ 是 $[R|t]$ 矩阵的元素,表示相机的旋转和平移。
评论 64
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值