三维图形处理-如何计算模型视图矩阵

9
我很难理解如何将物体空间转换为视图空间的数学公式。我正在使用硬件进行此操作,下面是我的A转置矩阵:

ATranspose =

         [rightx      upx     lookx    0]
         [righty      upy     looky    0]
         [rightz      upz     lookz    0]
         [-eyeright -eyeup -eyelook    1]

然后我们要找到这个点,需要执行以下步骤:
  [x,y,z,1] = [x',y',z',1]*ATranspose

  xnew = xold*rightx + xold*righty + xold*rightz + xold*(-eyeright)

但我不确定这是否正确。

也可能是

   [x,y,z,1]=atranspose*[x',y',z',1]T

请问有人能帮我解释一下吗?我在网上找到的与此相关的信息都是关于OpenGL代码的,而我只想了解将点从物体坐标转换为视角坐标背后的数学原理。

2个回答

14

这个答案可能比必要的内容更长。如果您已经理解了大部分矩阵数学,可以直接跳到最后两段。

从一维问题开始可能是最容易的。在一维中,我们有一条线上的点。我们可以缩放它们或平移它们。考虑三个点j,k和变换矩阵M

M = [ s t ]
    [ 0 1 ]

i = [1]   j = [-2]   k = [0]
    [1]       [ 1]       [1]

 j     k  i
─┴──┴──┴──┴──┴─
-2 -1  0  1  2

当我们乘以M时,我们得到:

i' = Mi = [ s t ][ 1] = [ s+t ]
          [ 0 1 ][ 1]   [  1  ]

j' = Mj = [ s t ][-2] = [-2s+t]
          [ 0 1 ][ 1]   [  1  ]

k' = Mk = [ s t ][ 0] = [  t  ]
          [ 0 1 ][ 1]   [  1  ]

如果我们给st赋值,那么我们就可以对我们的一维“三角形”进行各种变换。缩放会改变“点”之间的距离,而纯平移则会将它们相对于原点移动,同时保持间距不变:

   s=1 t=0           s=2 t=1           s=1 t=2
 j     k  i        j     k  i        j     k  i   
─┴──┴──┴──┴──┴─   ─┴──┴──┴──┴──┴─   ─┴──┴──┴──┴──┴─
-2 -1  0  1  2    -3 -1  1  3  5     0  1  2  3  4

需要注意的是,变换的顺序至关重要。这些一维变换先进行缩放,然后再进行平移。如果您首先进行平移,则“点”与原点的距离将不同,因此缩放因子会对其产生不同的影响。出于这个原因,变换通常保持在单独的矩阵中,以便清楚地表明顺序。

如果我们升级到二维,则得到矩阵N

   [1 0 tx][ cos(a) sin(a) 0][sx  0 0] [ sx*cos(a) sx*sin(a) tx ]   
N =[0 1 ty][-sin(a) cos(a) 0][ 0 sy 0]=[-sy*sin(a) sy*cos(a) ty ]  
   [0 0 1 ][   0      0    1][ 0  0 1] [    0         0       1 ]

这个矩阵将会1)sx,sy缩放点,2)将点绕原点旋转a度,然后3)将点平移tx,ty。请注意,该矩阵是在假定点被表示为列向量的情况下构建的,并且乘法将以Np进行。正如dantenwolf所说,如果您想使用点的行向量表示,但应用相同的变换,则可以转置所有内容并交换顺序。这是矩阵乘法的一般属性:(AB)^T = (B^T)(A^T)
话虽如此,我们可以根据对象、世界和眼睛坐标来谈论变换。如果眼睛坐落在世界原点上,朝向世界负z轴,+x向右,+y向上,物体是一个立方体,位于-z轴下10个单位(以z轴为中心),在世界x轴上宽度为2,在z轴上深度为3,在世界y轴上高度为4。然后,如果立方体的中心是对象的本地参考系,其本地轴与世界轴方便地对齐。那么盒子在对象坐标中的顶点是[+/-1,+/-2,+/-1.5]^T的变化。从眼睛的角度来看,近端、顶部、右侧的顶点具有对象坐标[1,2,1.5]^T,在世界坐标中,同一顶点为[1,2,-8.5]^T(1.5-10=-8.5)。由于眼睛所处的位置、指向的方向以及我们将眼睛定义为OpenGL相同的方式,该顶点具有与世界坐标相同的眼睛坐标。因此,让我们移动和旋转眼睛,使得眼睛的xright(rt),眼睛的yup,眼睛的-zlook(lk),并且眼睛位于[eyeright(ex) eyeup(ey) eyelook(ez)]^T。因为我们希望将对象坐标转换为眼睛坐标(意味着我们将把眼睛视为原点),我们将取这些变换的逆,并将其应用于物体顶点(在它们被转换为世界坐标之后)。所以我们将有:
ep = [WORLD_TO_EYE]*[OBJECT_TO_WORLD]*wp;

更具体地说,对于我们感兴趣的顶点,我们将会有:
[ rt.x  rt.y  rt.z 0][1 0 0 -ex][1 0 0  0 ][ 1 ]
[ up.x  up.y  up.z 0][0 1 0 -ey][0 1 0  0 ][ 2 ]
[-lk.x -lk.y -lk.z 0][0 0 1 -ez][0 0 1 -10][1.5]
[   0     0     0  1][0 0 0  1 ][0 0 0  1 ][ 1 ]

为了方便起见,我已经将眼睛的旋转和翻译分开进行了翻译。实际上,现在我写了这么多,这可能是混淆的重点。您提供的矩阵将进行旋转,然后进行翻译。我假设眼睛的平移是在世界坐标中进行的。但正如您在问题中所写的那样,它实际上是在眼坐标中执行翻译。我还否定了lk,因为我们已经定义眼睛向下看负z轴,但为了制作标准的旋转矩阵,我们希望使用正值。

无论如何,我可以继续进行下去,但也许这已经回答了您的问题。


接着:

进一步解释上面的内容:将眼睛的变换拆分成两个组件也使其更容易找到逆变换。很容易看出,如果平移tx将眼睛相对于世界中的物体移动到某个位置,通过移动世界中的所有对象-tx并保持眼睛不动,我们可以保持眼睛和世界中点之间的相对位置不变。

同样地,考虑眼睛的方向,由其默认的rightup和look向量定义:

     [1]      [0]      [ 0]
d_rt=[0] d_up=[1] d_lk=[ 0]
     [0]      [0]      [-1]

创建一个旋转矩阵,使得这三个向量指向新方向很容易。 我们只需要把我们的三个新轴 rtuplk 对齐(作为列向量)即可。
[rt.x up.x -lk.x 0]
[rt.y up.y -lk.y 0]
[rt.z up.z -lk.z 0]
[  0    0     0  1]

易见,如果增加d_rt、d_up和d_lk并乘以上述矩阵,则可以分别得到rt、up和lk。因此,我们已经应用了所需的变换。为了成为适当的旋转,这三个向量必须正交。这实际上只是一种基础变换。由于这个事实,我们可以通过取其转置来方便地找到该矩阵的逆。这就是我在上面所做的。如果将该转置矩阵应用于世界坐标中的所有点并保持眼睛静止,则这些点将保持相对于眼睛的同一位置,就像眼睛已经旋转了一样。
例如:
在世界坐标中进行分配:
   [ 0]    [0]    [-1]     [-2]     [1.5]
rt=[ 0] up=[1] lk=[ 0] eye=[ 0] obj=[ 0 ]
   [-1]    [0]    [ 0]     [ 1]     [-3 ]

Simple camera/grid example


好的回答。我从来没有理解过这个世界-眼睛-对象的东西。你能继续讲下去吗? - Micromega
@epitaph 我增加了一些解释。这解释了大部分“modelview”的内容。投影和视口矩阵将点从眼坐标系转换为屏幕坐标系。 - JCooper
这将为我提供xnew = x * rt.x + y * rt.y + z * rt.z - eyeright。 - slimbo
ynew = x * up.x + y * up.y + z * up.z - eyeup - slimbo
显示剩余3条评论

1

如果您在第二个变量中转置 ATranspose,即

[x,y,z,w]^T = ATranspose^T * [x',y',z',w']^T

顺便提一下,^T 表示转置,所以原作者可能是这个意思。

[x,y,z,w] = [x',y',z',w'] * A^T

并重新编写

[x,y,z,w]^T = A^T * [x',y',z',w']^T

那么所有这些表述都是同样正确的。


在你刚刚写的两个方程中,将顶部相乘会得到:x =(rightx * xold)+(upx * xold)+(lookx * xold) 而第二个方程将产生:x =(rightx * xold)+(righty * xold)+(rightz * xold)+(-eyeright * xold) - slimbo
@Steve:第一个是,第二个不是,因为它会给出完全相同的结果。只需编写矩阵乘法方程,并将4维向量视为4×1或1×4矩阵即可。发生的数学运算是,矩阵的列定义了与矩阵相乘的向量的新基向量。如果在右侧乘以向量,则必须是列向量;如果从左侧乘以向量,则必须是行向量。行<->列交换是转置,矩阵也必须进行转置。作为一项练习,请证明A * B = B^T * A^T。 - datenwolf
这种方法 x=(rightxxold)+(upxxold)+(lookx*xold) 的问题在于 eyeright 对新的 x 值没有影响。因此,相机位置会对任何事情产生影响吗? - slimbo
@Steve:这是因为你以转置形式编写了变换矩阵。通常相机位置在最右列。然而,OpenGL使用不同于普通C数组的索引方式,因此人们经常会弄错它们。但是,由于我下面展示的恒等式也对要进行变换的向量进行了转置并交换了乘法顺序,因此可以进行补偿。 - datenwolf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接