日期:2026年8月6日标签:Computer Graphics

回看 LookAt 矩阵 #

矩阵变换文章中专门解释过 lookAt 矩阵的原理。可以先看一下前面的文章了解下 LookAt 矩阵。

这里专门详细说明下,为什么 LookAt 矩阵的形式如下:

image-20210526213813353

其中 (Rx,Ry,Rz)(R_x, R_y, R_z) 表示摄像机的 XX 轴方向向量,(Ux,Uy,Uz)(U_x, U_y, U_z) 表示摄像机坐标系的Y轴方向,(Dx,Dy,Dz)(D_x, D_y, D_z) 表示摄像机的视线方向及 ZZ 轴。 (Px,Py,Pz)(P_x, P_y, P_z) 表示摄像机位置的世界坐标。

这张图里的 LookAt 矩阵,本质上不是“把摄像机变换到世界中”的矩阵,而是它的逆变换

把一个世界坐标点,转换成以摄像机为原点、以摄像机三个方向为坐标轴的观察空间坐标。

文章也是按照这个思路定义 LookAt 的:世界坐标经过观察矩阵后,变成相对于摄像机位置和方向的观察坐标。(Pengfei XC)

下面假设使用 OpenGL/GLSL 常见的列向量右乘形式:

Pcamera=ViewPworldP_{camera}=View\cdot P_{world}

1. 摄像机实际上定义了一个新的坐标系 #

假设摄像机具有:

  • 世界坐标位置:(\mathbf P)
  • 摄像机 X 轴:(\mathbf R),Right
  • 摄像机 Y 轴:(\mathbf U),Up
  • 摄像机 Z 轴:(\mathbf D)

其中:

R=[RxRyRz],U=[UxUyUz],D=[DxDyDz]R= \begin{bmatrix} R_x\\R_y\\R_z \end{bmatrix}, \quad U= \begin{bmatrix} U_x\\U_y\\U_z \end{bmatrix}, \quad D= \begin{bmatrix} D_x\\D_y\\D_z \end{bmatrix}

可以把摄像机坐标系想象成:

                    U:摄像机上方
                    ↑
                    |
                    P --------→ R:摄像机右方
                   /
                  /
                 D:摄像机 Z 轴

这些向量都是用世界坐标系描述的。

所以摄像机坐标系的完整信息是:

原点:P
X 轴:R
Y 轴:U
Z 轴:D

LookAt 要解决的问题是:

已知点在世界坐标系中的坐标,求它在这个摄像机坐标系中的坐标。


2. 第一步:让摄像机位于原点 #

假设世界中有一点:

X=[xyz]X= \begin{bmatrix} x\\y\\z \end{bmatrix}

摄像机位于:

P=[PxPyPz]P= \begin{bmatrix} P_x\\P_y\\P_z \end{bmatrix}

从摄像机观察这个点,首先需要求出“点相对于摄像机的位置”:

XPX - P

因此需要把整个世界平移 P-P

T(P)=[100Px010Py001Pz0001]T(-P)= \begin{bmatrix} 1&0&0&-P_x\\ 0&1&0&-P_y\\ 0&0&1&-P_z\\ 0&0&0&1 \end{bmatrix}

作用到世界坐标点上:

T(P)[xyz1]=[xPxyPyzPz1]T(-\mathbf P) \begin{bmatrix} x\\y\\z\\1 \end{bmatrix} = \begin{bmatrix} x-P_x\\ y-P_y\\ z-P_z\\ 1 \end{bmatrix}

这一步很好理解:

世界点的位置 - 摄像机的位置
= 世界点相对于摄像机的位置

例如:

摄像机位置 P = (3, 0, 0)
世界点位置 X = (5, 1, 0)

那么从摄像机看来:

XP=(2,1,0)X-P=(2,1,0)

所以矩阵中必须是 -Px、-Py、-Pz

文章里所说的“移动的不是摄像机,而是整个场景,将场景向相反方向移动”,指的就是这个过程。(Pengfei XC)


3. 第二步:用摄像机的三个轴描述这个相对位置 #

经过平移以后,得到相对向量:

Q=XPQ=X - P

但这个向量当前仍然是用世界坐标轴描述的。

我们希望知道:

Q 在摄像机右方向上有多少?
Q 在摄像机上方向上有多少?
Q 在摄像机 Z 方向上有多少?

这正好通过点积计算:

xcamera=RQycamera=UQzcamera=DQx_{camera}=R \cdot Q \\ y_{camera}= U \cdot Q \\ z_{camera}= D \cdot Q \\

展开:

xcamera=RxQx+RyQy+RzQzycamera=UxQx+UyQy+UzQzzcamera=DxQx+DyQy+DzQzx_{camera} = R_xQ_x+R_yQ_y+R_zQ_z \\ y_{camera} = U_xQ_x+U_yQ_y+U_zQ_z \\ z_{camera} = D_xQ_x+D_yQ_y+D_zQ_z

把三个点积堆叠成矩阵:

[xcameraycamerazcamera]=[RxRyRzUxUyUzDxDyDz][QxQyQz ]\begin{bmatrix} x_{camera}\\ y_{camera}\\ z_{camera} \end{bmatrix} = \begin{bmatrix} R_x&R_y&R_z\\ U_x&U_y&U_z\\ D_x&D_y&D_z \end{bmatrix} \begin{bmatrix} Q_x\\ Q_y\\ Q_z\ \end{bmatrix}

这就是图片中左侧矩阵的来源:

[RxRyRz0UxUyUz0DxDyDz00001]\begin{bmatrix} R_x&R_y&R_z&0\\ U_x&U_y&U_z&0\\ D_x&D_y&D_z&0\\ 0&0&0&1 \end{bmatrix}

注意这里三个轴放在了行上,因为每一行都需要和世界坐标向量做一次点积:

第一行 · Q = 摄像机 x 坐标
第二行 · Q = 摄像机 y 坐标
第三行 · Q = 摄像机 z 坐标

4. 两个步骤组合起来 #

我们刚才得到两个步骤。

第一步,减去摄像机位置:

Q=XPQ= X - P

第二步,分别投影到摄像机三个轴:

[xcyczc]=[RTUTDT](XP)\begin{bmatrix} x_c\\y_c\\z_c \end{bmatrix} = \begin{bmatrix} R^T\\ U^T\\ D^T\\ \end{bmatrix} (X - P)

所以:

LookAt=[RxRyRz0UxUyUz0DxDyDz00001][100Px010Py001Pz0001]LookAt= \begin{bmatrix} R_x&R_y&R_z&0\\ U_x&U_y&U_z&0\\ D_x&D_y&D_z&0\\ 0&0&0&1 \end{bmatrix} \begin{bmatrix} 1&0&0&-P_x\\ 0&1&0&-P_y\\ 0&0&1&-P_z\\ 0&0&0&1 \end{bmatrix}

这正是图片中的形式。

因为使用列向量,所以矩阵从右向左作用:

世界坐标
   ↓
先乘 T(-P):让摄像机移动到原点
   ↓
再乘摄像机轴矩阵:换成摄像机坐标轴描述
   ↓
观察空间坐标

也就是:

Pcamera=RviewT(P)PworldP_{camera} = R_{view}\cdot T(-P)\cdot P_{world}

(完)

目录