Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/AMS-Regular.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >图形学入门(一):坐标变换

图形学入门(一):坐标变换

作者头像
zhiruili
发布于 2021-08-10 03:12:03
发布于 2021-08-10 03:12:03
1.9K0
举报
文章被收录于专栏:简易现代魔法简易现代魔法

概述

将一个物体显示到屏幕上,这个事情似乎非常简单,以至于我们基本上认为它已经天经地义到直接告诉计算机我们要显示什么物体它就会自动显示出来,毕竟我们拍照的时候就是举起相机按下快门就会出现一张图片了。但事实上,相机是基于物理感光元件实现了从三维世界到二维图片的投影,在计算机的程序世界中一切都需要被计算出来,也就是说,我们只有一堆图形的描述信息,我们需要自己将这些图形在二维的平面上绘制的方式告诉操作系统,操作系统才能最终在屏幕上绘制出我们想要的图形。

那么,我们究竟要进行怎样的一些计算呢?我们可以将这个过程和拍照进行类比,物体的位置、角度,相机的位置、角度以及相机本身设置的一些参数都会对拍照的结果产生影响,相机离物体近,物体就显得大一些,相机往左偏,物体在最终相片上的位置就会往右。显然,光有场景中物体本身的模型信息还不足以让我们知道最终呈现在屏幕上的图像的样子,我们还需要考虑上述的种种信息才能最终得出在二维的平面上这个场景最终的形态,这些计算主要分为三部分:

  • 模型空间到世界空间的变换 这个过程将物体的每个顶点坐标从自己模型空间移动到世界空间,也就是将物体移动到世界的对应位置摆放好。
  • 世界空间到观察空间的变换 这个过程将物体的每个顶点坐标从世界空间移动到相机的观察空间,由于位置的移动是相对的,这也就相当于把相机移动到对应位置摆放好。只不过为了计算方便,我们一般假设相机的位置就在原点的位置,看向 z 轴负方向。
  • 观察空间到裁剪空间的变换 这个过程就是将物体的每个顶点坐标从三维空间投影到相机的二维成像平面上,这也就相当于相机拍照时在胶片上记录下当时的画面。

数学基础

为了说明这三种变换在计算机中是如何进行的,这里需要先补充一点相关的基础知识。在计算机中,为了进行快速的计算,采用了矩阵(Matrix)这一数学工具。下面是一个 3×2 的矩阵(即 32 列的矩阵):

\[A = \begin{bmatrix} 1 & 2 \\ 3 & 4 \\ 5 & 6 \end{bmatrix}\]

矩阵有一个操作叫转置(Transpose),矩阵 A 的转置写作 AT,这个过程其实就是将矩阵沿着左上到右下的对角线翻转,即把 A 的每一行写 AT 的列,把 A 的每一列写 AT 的行,对于上面的矩阵 A 来说,我们有:

\[A^\mathrm{T} = \begin{bmatrix} 1 & 3 & 5 \\ 2 & 4 & 6 \end{bmatrix}\]

一个 N 维向量也可以表示为一个矩阵的形式,也就是一个 N×1 的矩阵:

\[\vec{v} = \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}\]

为了减少版面占用,我们在写向量的时候往往写它们的转置形式:

\[\vec{v} = \begin{bmatrix} 1 & 2 & 3 \end{bmatrix}^\mathrm{T}\]

类似地,空间中的点也能用类似这一的表示。这里需要略微说明的是,由于坐标系中的一个点本身可以看作是一个从原点开始指向该点的向量,因此,在许多图形库中也常直接用向量来表示顶点。多数情况下,我们并不需要区分这两个概念,但是,在一些特定的场合(如后文将提到的平移变换)下,我们还是要严格区分点和向量的。

对于一个矩阵 A 而言,我们用 Aij 表示这个矩阵第 i 行第 j 列的值。

矩阵之间可以进行加法和乘法运算,加法运算要求矩阵有相同的行数和列数,然后进行对应位置的相加:

\[\begin{bmatrix} A_{11} & A_{12} & A_{13} \\ A_{21} & A_{22} & A_{23} \end{bmatrix} + \begin{bmatrix} B_{11} & B_{12} & B_{13} \\ B_{21} & B_{22} & B_{23} \end{bmatrix} = \begin{bmatrix} A_{11} + B_{11} & A_{12} + B_{12} & A_{13} + B_{13} \\ A_{21} + B_{21} & A_{22} + B_{22} & A_{23} + B_{23} \end{bmatrix}\]

乘法运算则稍微复杂一点,对于 AB 两个矩阵相乘,我们需要确保 A 的列数等于 B 的行数。假设 A 是一个 3×2 的矩阵,而 B 是一个 2×1 的矩阵,则 AB 的结果就是一个 3×1 的矩阵:

\[\begin{bmatrix} A_{11} & A_{12} \\ A_{21} & A_{22} \\ A_{31} & A_{32} \end{bmatrix} \begin{bmatrix} B_{11} \\ B_{12} \end{bmatrix} = \begin{bmatrix} A_{11} B_{11} + A_{12} B_{12} \\ A_{21} B_{11} + A_{22} B_{12} \\ A_{31} B_{11} + A_{32} B_{12} \end{bmatrix}\]

下面快速给出一组我们会用到的矩阵相关的定义:

  • 一个矩阵 A 的主对角线(Main Diagonal)被定义为所有 Aij, (i=j) 的值。
  • 对角矩阵(Diagonal Matrix)被定义为除了主对角线都为 0 的矩阵。
  • 方阵(Square Matrix)被定义为行列数相同的矩阵,一个 n×n 的方阵被称为 n 阶方阵,n 为方阵的阶。
  • 单位矩阵(Identity Matrix)被定义为主对角线上的元素都为 1 而其他元素都为 0 的方阵,一个 n 阶的单位矩阵记作 In
  • 给定一个 n 阶方阵 A,如果存在一个 n 阶方阵 B 使得 AB=BA=In,则称 BA 的逆矩阵(Inverse Matrix),记作 A1
  • 如果一个方阵 A 的转置矩阵为其逆矩阵,即 AT=A1,则称 A 为正交矩阵(Orthogonal Matrix)。

说完矩阵的一些相关的定义和运算之后,我们来说一下矩阵和我们的坐标变换有什么关系。由于整个坐标变换过程事实上是对模型的顶点应用了一组线性变换,因此它们可以被转变为用矩阵表示,例如:

\[\begin{align} x^\prime &= x + y + z \\ y^\prime &= 3x + 5z \\ z^\prime &= 6x \end{align}\]

可以用矩阵乘法表示为:

\[\begin{bmatrix} x^\prime & y^\prime & z^\prime \end{bmatrix}^\mathrm{T} = \begin{bmatrix} 1 & 1 & 1 \\ 3 & 0 & 5 \\ 6 & 0 & 0 \end{bmatrix} \begin{bmatrix} x & y & z \end{bmatrix}^\mathrm{T}\]

这里不用简单的运算而引入矩阵这一概念,是因为矩阵乘法有一个很好的性质:运算服从结合律。因此,对一个点 p 先应用矩阵 A 再应用矩阵 B 就相当于直接对 p 应用 (AB)。在实际的场景中,我们可能需要处理非常大量的顶点,矩阵乘法的这一特性可以使得我们将变换过程计算一次,生成一个变换矩阵后,再将这个矩阵应用到所有顶点上,显著减少计算量。

图形基本变换

通过前文的介绍,我们已经了解了矩阵以及矩阵的一些基本运算方式。既然我们有了一个可组合的计算工具,现在我们就需要去了解我们可用的一些基础组合子,也就是图形变换的基本形式1,以及这些变换如何用矩阵乘法的形式进行表示。在这里,以二维情况为例,说明图形几种基本的变换所对应的变换矩阵:

二维缩放

所谓缩放,其实就是对图形的每一个顶点的每一个分量都乘上一个缩放因子,例如我们想让一个二维图形在 x 轴方向缩放 2 倍,在 y 轴方向缩放 3 倍,那么,我们只需要对它上面的任何一个顶点 p=(x, y)T 进行如下操作:

\[\begin{align} x^\prime &= 2x \\ y^\prime &= 3y \end{align}\]

将其写成矩阵形式:

\[\begin{bmatrix} x^\prime & y^\prime \end{bmatrix}^\mathrm{T} = \begin{bmatrix} 2 & 0 \\ 0 & 3 \end{bmatrix} \begin{bmatrix} x & y \end{bmatrix}^\mathrm{T}\]

也就是通用情况下,我们有:

\[\begin{bmatrix} s_x & 0 \\ 0 & s_y \end{bmatrix} \begin{bmatrix} x & y \end{bmatrix}^\mathrm{T} = \begin{bmatrix} s_x x & s_y y \end{bmatrix}^\mathrm{T}\]

二维旋转

相比于缩放,旋转要稍微复杂一点。对于一个二维的图形的每一个顶点 p=(x, y)T 我们希望对其应用一个 2×2 的矩阵,使得其绕原点逆时针旋转一个 θ 角后得到

\[\begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x & y \end{bmatrix}^\mathrm{T} = \begin{bmatrix} x^\prime & y^\prime \end{bmatrix}^\mathrm{T}\]

我们可以从两种最简单的情况进行考虑,来求出 分别是什么。

第一种情况是 。此时,矩阵相乘的结果为:

\[\begin{align} \begin{bmatrix} x^\prime & y^\prime \end{bmatrix}^\mathrm{T} &= \begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x & 0 \end{bmatrix}^\mathrm{T} \\ &= \begin{bmatrix} a x + 0 & c x + 0 \end{bmatrix}^\mathrm{T} \\ &= \begin{bmatrix} a x & c x \end{bmatrix}^\mathrm{T} \end{align}\]

我们可以知道,对于这样的点而言,旋转 角后,。将这个结果对应于矩阵,我们就可以知道:

\[\begin{align} a &= \cos{\theta} \\ c &= \sin{\theta} \end{align}\]

类似地,对于 的情况而言。此时,矩阵相乘的结果为:

\[\begin{align} \begin{bmatrix} x^\prime & y^\prime \end{bmatrix}^\mathrm{T} &= \begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} 0 & y \end{bmatrix}^\mathrm{T} \\ &= \begin{bmatrix} 0 + b y & 0 + d y \end{bmatrix}^\mathrm{T} \\ &= \begin{bmatrix} b y & d y \end{bmatrix}^\mathrm{T} \end{align}\]

对于这样的点而言,旋转 角后,。将这个结果对应于矩阵,我们就可以知道:

\[\begin{align} b &= -\sin{\theta} \\ d &= \cos{\theta} \end{align}\]

因此,二维场景下,逆时针旋转的矩阵为:

\[\begin{bmatrix} \cos{\theta} & -\sin{\theta} \\ \sin{\theta} & \cos{\theta} \end{bmatrix}\]

二维平移

所谓平移,其实就是对点的每一个分量都加上一个偏移量,例如我们想让一个图形在 轴方向平移 1 个单位长度,在 轴方向平移 2 个单位长度,那么,我们只需要对其每一个顶点 进行如下操作:

\[\begin{align} x^\prime &= x + 1 \\ y^\prime &= y + 2 \end{align}\]

这看起来比前面的两种变换都要简单,但事实上,我们却无法用上面的 矩阵乘以一个向量的方式表示这样的运算。这是因为对于任意的 矩阵而言,当其应用于一个向量的时候,它的结果总为如下形式:

\[\begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x & y \end{bmatrix}^\mathrm{T} = \begin{bmatrix} a x + b y & c x + d y \end{bmatrix}^\mathrm{T}\]

我们会发现,这里并没有任何地方能放下一个与 都无关的值,因此,我们只能将其写为:

\[\begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x & y \end{bmatrix}^\mathrm{T} + \begin{bmatrix} t_x & t_y \end{bmatrix}^\mathrm{T} = \begin{bmatrix} a x + b y + t_x & c x + d y + t_y \end{bmatrix}^\mathrm{T}\]

这看起来似乎没什么问题,但是它导致了一个巨大的灾难,就是我们无法将若干矩阵变换应用结合律合成一个矩阵了,这是我们不想看到的。因此我们需要引入一个新的数学工具:齐次坐标(Homogeneous Coordinates)。在齐次坐标下,一个点 将被表示为 ,向量 将被表示为:。在引入其次坐标后,我们对点的平移就可以用如下的矩阵乘法实现了:

\[\begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x & y & 1 \end{bmatrix}^\mathrm{T} = \begin{bmatrix} x + t_x & y + t_y & 1 \end{bmatrix}^\mathrm{T}\]

我们可以看到,此时,这个向量的前两个值恰好就是我们要的结果。前面提到,我们大多数情况下并不严格区分点和向量,这里之所以需要严格区分,是因为向量具有平移不变性。当一个向量应用了上述变换后,则变为:

\[\begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x & y & 0 \end{bmatrix}^\mathrm{T} = \begin{bmatrix} x & y & 0 \end{bmatrix}^\mathrm{T}\]

结果还是原来的向量,确保了平移不变性。

在使用了其次坐标后,本章提到的三个基本的变换矩阵可以分别被表示为:

  • 缩放 \[S(s_x,\ s_y) = \begin{bmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & 1 \end{bmatrix}\]
  • 旋转 \[R(\theta) = \begin{bmatrix} \cos{\theta} & -\sin{\theta} & 0 \\ \sin{\theta} & \cos{\theta} & 0 \\ 0 & 0 & 1 \end{bmatrix}\]
  • 平移 \[T(t_x,\ t_y) = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix}\]

二维组合变换

有了这些基础的变换方式,我们就可以组合成一些更复杂的变换形式。例如,我们刚刚提到的旋转变换是基于原点逆时针旋转 角,那如果我们想绕任意一个点 旋转 角要怎么做呢?我们可以将这个过程拆解为三步:

  • 将整个图形和点 一起移动,使得点 被移动到原点
  • 将图形绕原点旋转
  • 将整个图形点 移动回原位置

也就是:

\[R_p = T(a,\ b) \ R(\theta) \ T(-a,\ -b)\]

坐标变换

有了这些基础知识后,我们就可以来讨论完整的坐标变换应该怎么做了。文章开头已经提到,在空间中的顶点坐标变换分为三步。而这三步中的每一步都可以用一个对应的变换矩阵实现,它们是:

  • 模型空间到世界空间:Model Matrix
  • 世界空间到观察空间:View Matrix
  • 观察空间到裁剪空间:Projection Matrix

即经过这三个变换后,我们可以将一个模型空间的向量转变为裁剪空间的向量:

\[\vec{v}_{clip} = M_{projection} \ M_{view} \ M_{model} \ \vec{v}_{model}\]

根据这三个变换的首字母,这个过程又被称为 MVP 变换,如下图2所示:

模型变换

模型空间到世界空间是比较简单的情况,它其实就是一些基础的变换或者是基础变换的组合,将物体的顶点从模型中定义的坐标系移动到世界坐标系中,例如一个正方体的盒子的一个顶点在 的位置上,那么当这个正方体移动到了 位置上时,这个顶点也自然应该被移动到 位置上了。

前文已经说明如何对二维情况下的点和向量进行变换,对于三维情况,我们也可以做类似的处理3。我们首先通过齐次坐标将三维空间中的点 扩充为 ,将三维空间中的向量 扩充为 。然后,对应的变换矩阵就变为:

  • 缩放 \[S(s_x,\ s_y,\ s_z) = \begin{bmatrix} s_x & 0 & 0 & 0 \\ 0 & s_y & 0 & 0 \\ 0 & 0 & s_z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\]
  • 平移 \[T(t_x,\ t_y,\ t_z) = \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix}\]
  • 旋转 旋转比较特殊,在二维空间中,绕原点逆时针旋转含义是非常明确的,但在三维空间中,我们则无法说绕原点逆时针旋转,而是需要确定是绕哪个轴旋转,它们的公式分别为: 绕 轴旋转 \[R_x(\theta) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos{\theta} & -\sin{\theta} & 0 \\ 0 & \sin{\theta} & \cos{\theta} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\] 绕 轴旋转 注意这里的负号的位置与绕 轴、 轴旋转的情况不同,这是因为 ,而 并不是 而是 ,顺序恰好相反。 \[R_y(\theta) = \begin{bmatrix} \cos{\theta} & 0 & \sin{\theta} & 0 \\ 0 & 1 & 0 & 0 \\ -\sin{\theta} & 0 & \cos{\theta} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\] 绕 轴旋转 \[R_z(\theta) = \begin{bmatrix} \cos{\theta} & -\sin{\theta} & 0 & 0 \\ \sin{\theta} & \cos{\theta} & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\]

视图变换

世界空间到观察空间这个过程虽然我们在概念上将其理解为移动相机,但是本质上也可以看作是为移动物体。如何理解这件事呢?首先我们来看相机的位置如何定义。当我们定义相机的位置时,一般会采用这样的方式:

  • 位置 Position,用向量 表示
  • 朝向 Look-at Direction,用单位向量 表示
  • 上方 Up Direction,用单位向量 表示

我们知道,位置是相对的,假设我们正拿着一个相机在拍摄一个物体,固定好位置并拍出一张相片后,我们将相机和被拍摄物体都向前移动一段相同的距离,再向左移动相同的距离,然后再拍摄一张照片,在不考虑背景的情况下,这两张照片拍摄出来的结果显然是一模一样的。这也就意味着,我们可以根据计算的便利性,选择一个坐标系,来将所有物体和相机都按照这个坐标系进行移动。在这里我们选择就以相机的位置为原点,相机的上方向 轴的正方向,相机看向的方向 轴的负方向,以此为基础构建一个右手的坐标系(也就是 轴向右, 轴向上的情况下, 轴向屏幕外,)。一旦规定好 轴和 轴,那么 轴的方向也就可以通过叉乘来计算得出了:

也就是说,我们需要先对场景中所有物体,包括相机,应用一个变换矩阵,使得相机恰好在原点,相机的上方向 轴的正方向,相机看向的方向 轴的负方向。为了达成这个目的,我们先对场景中的物体应用一个平移变换,这个变换是非常直观的:

\[T_{view} = \begin{bmatrix} 1 & 0 & 0 & -x_e \\ 0 & 1 & 0 & -y_e \\ 0 & 0 & 1 & -z_e \\ 0 & 0 & 0 & 1 \end{bmatrix}\]

在这个基础上,我们再应用旋转变换,使相机的 符合我们的需求。在这里,由于这个过程并不直观,也不好求解,因此我们需要应用一个小技巧。旋转矩阵是一个正交矩阵,根据前面给出的定义,一个方阵 如果是一个正交矩阵,那么

也就是说,假设我们要求的旋转矩阵是 ,那么我们可以先计算这个旋转变换的逆变换对应的矩阵 ,再通过其转置得到该旋转变换矩阵。那么,这个 是什么呢?显然,将单位向量 从任意方向转到 轴的正方向的逆变换就是将 轴正方向的单位向量 (注意向量的齐次坐标表示最后一个元素是 )转到 方向。也就是说,这个 矩阵需要满足:

\[R_{view}^{-1} \ \begin{bmatrix} 0 & 1 & 0 & 0 \end{bmatrix}^\mathrm{T} = \hat{t} = \begin{bmatrix} x_t & y_t & z_t & 0 \end{bmatrix}^\mathrm{T}\]

根据矩阵乘法的计算方式我们可以知道,这个 矩阵的第二列必然为 ,否则就无法得出这样的结果。

类似地,这个矩阵也需要满足:

\[R_{view}^{-1} \ \begin{bmatrix} 0 & 0 & 1 & 0 \end{bmatrix}^\mathrm{T} = -\hat{g} = \begin{bmatrix} x_{-g} & y_{-g} & z_{-g} & 0 \end{bmatrix}^\mathrm{T}\]

这里之所以需要加一个负号,是因为我们看向的是 轴负方向,也就是将 轴转到了 的方向。由此我们可以知道,这个 矩阵的第三列必然为

同理,由于相机的 轴方向的单位向量 可以通过 叉乘得到,即 ,因此,我们也知道了第一列的值:

最终,我们得到了如下的矩阵:

\[R_{view}^{-1} = \begin{bmatrix} x_{\hat{g} \times \hat{t}} & x_t & x_{-g} & 0 \\ y_{\hat{g} \times \hat{t}} & y_t & y_{-g} & 0 \\ z_{\hat{g} \times \hat{t}} & z_t & z_{-g} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\]

我们可以简单地将这个矩阵与 相乘来进行验算,确认其正确性。然后,根据正交矩阵的性质,我们有:

\[R_{view} = {R_{view}^{-1}}^\mathrm{T} = \begin{bmatrix} x_{\hat{g} \times \hat{t}} & y_{\hat{g} \times \hat{t}} & z_{\hat{g} \times \hat{t}} & 0 \\ x_t & y_t & z_t & 0 \\ x_{-g} & y_{-g} & z_{-g} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\]

此时我们就知道了 View Matrix 为:

\[M_{view} = R_{view} \ T_{view} = \begin{bmatrix} x_{\hat{g} \times \hat{t}} & y_{\hat{g} \times \hat{t}} & z_{\hat{g} \times \hat{t}} & 0 \\ x_t & y_t & z_t & 0 \\ x_{-g} & y_{-g} & z_{-g} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & -x_e \\ 0 & 1 & 0 & -y_e \\ 0 & 0 & 1 & -z_e \\ 0 & 0 & 0 & 1 \end{bmatrix}\]

投影变换

从观察空间到裁剪空间这个过程是定义了一个区域,最终这个区域外的内容都在图像中不可见(被裁剪了),而区域内的内容会被投影到一个平面上形成图像。在说这个投影矩阵之前,我们需要先区分两种不同的投影方式,一种是透视投影(Perspective Projection),另一种是正交投影(Orthographic Projection)4

所谓透视投影就是符合我们一般视觉规律的投影,也就是画面中的物体会近大远小,而正交投影中无论物体远近,在最终成像的结果中都是一样大。从上图中可以看出,投影变换定义了一个近裁剪平面(Near Clip Plane)和远裁剪平面(Far Clip Plane),当我们的相机看向场景的时候,可以将相机所在的点和远裁剪平面的四个顶点连线形成一个四棱锥,近裁剪平面则进一步将这个锥体切成一个平截头体,在近裁剪平面和远裁剪平面中间的截头体内部的物体就是最终会被投影到近裁剪平面的物体。当相机离近裁剪平面越近,则近裁剪平面越小,透视效果越明显,反之,当相机离得越远,近裁剪平面越大,透视效果越不明显,当相机离得无穷远时,近裁剪平面将和远裁剪平面一样大,此时的投影就是正交投影。对于投影变换而言,我们需要做的事情,就是将裁剪空间这个平截头体转换为标准正方体 ,这个正方体以坐标原点为中心,边与坐标轴平行,边长为 。在这步操作完成后,我们之后就可以很容易地将这个标准正方体中的坐标映射到屏幕空间上了。

正交投影

虽然透视投影比较符合我们的视觉直觉,但是我们将先描述正交投影,因为它相对而言比较简单。下图描述了正交投影所需要做的变换4

由于正交投影所形成的平截头体是一个长方体,因此我们可以用六个平面的坐标值来描述这个长方体,分别是左右( ),上下( )和远近( )。从上图中可以看出,我们需要先对这个长方体应用一个平移变换,再应用一个缩放变换。根据我们之前的知识,可以很容易得到这两个矩阵:

  • 平移 \[T_{ortho} = \begin{bmatrix} 1 & 0 & 0 & -\frac{r + l}{2} \\ 0 & 1 & 0 & -\frac{t + b}{2} \\ 0 & 0 & 1 & -\frac{n + f}{2} \\ 0 & 0 & 0 & 1 \end{bmatrix}\]
  • 缩放 \[S_{ortho} = \begin{bmatrix} \frac{2}{r - l} & 0 & 0 & 0 \\ 0 & \frac{2}{t - b} & 0 & 0 \\ 0 & 0 & \frac{2}{n - f} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\] 这里需要稍微注意的是,由于我们相机看向 。另外,由于我们最终产生的正方体的边长是 ,因此这里的缩放因子都有一个 作为分子。

因此,我们可以得出正交投影的变换矩阵为:

\[M_{ortho} = S_{ortho} T_{ortho} = \begin{bmatrix} \frac{2}{r - l} & 0 & 0 & 0 \\ 0 & \frac{2}{t - b} & 0 & 0 \\ 0 & 0 & \frac{2}{n - f} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & -\frac{r + l}{2} \\ 0 & 1 & 0 & -\frac{t + b}{2} \\ 0 & 0 & 1 & -\frac{n + f}{2} \\ 0 & 0 & 0 & 1 \end{bmatrix}\]

透视投影

得出正交投影的变换矩阵后,我们可以在此基础上计算透视投影的变换矩阵。所谓透视投影的变换矩阵,可以被看作是先对透视投影的远裁剪平面进行「挤压」,使其变得和近裁剪平面一样大,这使得平截头体被「挤压」成一个长方体,之后我们就可以应用上面算出的正交投影变换矩阵来进行后续的变换了。

那么这个所谓的「挤压」是怎么做的呢?首先我们需要明确这个「挤压」的定义,也就是我们需要确保这个过程中的一些不变量。我们进行如下的约束:

  1. 近裁剪平面上任意一点经过「挤压」后不变
  2. 远裁剪平面上任意一点经过「挤压」后 值不变
  3. 远裁剪平面上的中点经过「挤压」后不变

我们要求一个矩阵 ,使得在满足这些约束的条件下,将平截头体「挤压」成长方体。我们先看 会怎么改变。首先,我们将平截头体中的任意一点 与相机所在位置连一条线,这条线会与近裁剪平面相交于一点 。此时从侧面看过去,我们可以看到一个相似三角形4:

由此我们可以得出:

\[y^\prime = \frac{n}{z} \ y\]

类似地,从顶部向下看,我们可以得出:

\[x^\prime = \frac{n}{z} \ x\]

因此,我们现在已经知道了变化后坐标值中的两个元素,我们可以得出这样的等式(其中 是未知待填入的值):

\[M_{persp \rightarrow ortho} \ \begin{bmatrix} x & y & z & 1 \end{bmatrix}^\mathrm{T} = \begin{bmatrix} \frac{n}{z} \ x & \frac{n}{z} \ y & \square & 1 \end{bmatrix}^\mathrm{T} = \begin{bmatrix} nx & ny & \square & z \end{bmatrix}^\mathrm{T}\]

注意到上面的等式中后半部分看起来有点奇怪。为什么我们会认为 相等呢?这里涉及到我们对齐次坐标的一个定义1,即:当 不为 时, 表示的是一个三维空间中的点 。也就是说, 在空间中表示的是同一个点。

回到前面的等式,由于结果中 不包含除了 之外的参数, 不包含除了 之外的参数,而且两者都没有常数项,另外, 则不包含除了 之外的任何项,我们可以根据矩阵乘法的规则可知,这个 矩阵大致的样子必然为:

\[M_{persp \rightarrow ortho} = \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ a & b & c & d \\ 0 & 0 & 1 & 0 \end{bmatrix}\]

根据前面提到的约束 1 我们可知:

\[M_{persp \rightarrow ortho} \ \begin{bmatrix} x & y & n & 1 \end{bmatrix}^\mathrm{T} = \begin{bmatrix} \frac{n}{n} \ x & \frac{n}{n} \ y & n & 1 \end{bmatrix}^\mathrm{T} = \begin{bmatrix} nx & ny & n^2 & n \end{bmatrix}^\mathrm{T}\]

由于 都无关,因此我们可以知道 。因此这个 矩阵可以进一步具体化为:

\[M_{persp \rightarrow ortho} = \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & c & d \\ 0 & 0 & 1 & 0 \end{bmatrix}\]

因此,我们有:

\[\begin{bmatrix} 0 & 0 & c & d \end{bmatrix} \begin{bmatrix} x & y & n & 1 \end{bmatrix}^\mathrm{T} = cn + d = n^2\]

根据约束 3,我们还可以知道:

\[M_{persp \rightarrow ortho} \begin{bmatrix} 0 & 0 & f & 1 \end{bmatrix}^\mathrm{T} = \begin{bmatrix} 0 & 0 & f & 1 \end{bmatrix}^\mathrm{T} = \begin{bmatrix} 0 & 0 & f^2 & f \end{bmatrix}^\mathrm{T}\]

因此,我们又有:

\[\begin{bmatrix} 0 & 0 & c & d \end{bmatrix} \begin{bmatrix} 0 & 0 & f & 1 \end{bmatrix}^\mathrm{T} = cf + d = f^2\]

联立两个式子可得:

\[\begin{align} & cn + d = n^2 \\ & cf + d = f^2 \\ \Rightarrow & \\ & c = n + f \\ & d = -nf \end{align}\]

于是,我们可以得出这个矩阵最终的形态:

\[M_{persp \rightarrow ortho} = \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n + f & -nf \\ 0 & 0 & 1 & 0 \end{bmatrix}\]

因此透视投影矩阵 就可以表示为:

\[\begin{align} M_{persp} &= M_{ortho} \ M_{persp \rightarrow ortho} \\ &= \begin{bmatrix} \frac{2}{r - l} & 0 & 0 & 0 \\ 0 & \frac{2}{t - b} & 0 & 0 \\ 0 & 0 & \frac{2}{n - f} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & -\frac{r + l}{2} \\ 0 & 1 & 0 & -\frac{t + b}{2} \\ 0 & 0 & 1 & -\frac{n + f}{2} \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n + f & -nf \\ 0 & 0 & 1 & 0 \end{bmatrix} \end{align}\]

另外,在实际工程中,我们一般用于构建透视投影矩阵的参数有如下几项:

  • 宽高比 Aspect Ratio:
  • 近裁剪平面 Near Clip Plane:
  • 远裁剪平面 Far Clip Plane:
  • 纵向视野角度 Field of View:

我们同时假设 的中线与 轴重合,使得远近裁剪面的中心在 轴上,且两个平面都垂直于 轴。也就是说,我们有 以及 ,因此在 轴和 轴方向并不需要进行任何移动,只需要移动 轴即可。根据上面这些数据,我们可以知道(别忘了我们的相机看向 轴负方向, 为负值,且

\[\begin{align} t - b &= h \\ &= -2 \ z_n \ \tan{\frac{\theta}{2}} \\ r - l &= w \\ &= r \ h \\ &= -2 \ r \ z_n \ \tan{\frac{\theta}{2}} \end{align}\]

因此我们在实际工程中使用到的矩阵 可以表示为:

\[\begin{bmatrix} -\frac{1}{r \ z_n \ \tan{\theta / 2}} & 0 & 0 & 0 \\ 0 & -\frac{1}{z_n \ \tan{\theta / 2}} & 0 & 0 \\ 0 & 0 & \frac{2}{z_n - z_f} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & -\frac{z_n + z_f}{2} \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} z_n & 0 & 0 & 0 \\ 0 & z_n & 0 & 0 \\ 0 & 0 & z_n + z_f & - z_n \ z_f \\ 0 & 0 & 1 & 0 \end{bmatrix}\]

相乘可得:

\[\begin{bmatrix} -\frac{1}{r \ \tan{\theta / 2}} & 0 & 0 & 0 \\ 0 & -\frac{1}{\tan{\theta / 2}} & 0 & 0 \\ 0 & 0 & \frac{z_n + z_f}{z_n - z_f} & \frac{2 \ z_n \ z_f}{z_f - z_n} \\ 0 & 0 & 1 & 0 \end{bmatrix}\]

参考资料

  1. 变换矩阵 - Wikipedia ↩2
  2. 坐标系统 - LearnOpenGL CN
  3. Transformation slide - Games 101
  4. Transformation Cont slide - Games 101 ↩2 ↩3

/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */ var disqus_shortname = 'ZhiruiLi'; // required: replace example with your forum shortname /* * * DON'T EDIT BELOW THIS LINE * * */ (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; dsq.src = 'https://' + disqus_shortname + '.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); /* * * DON'T EDIT BELOW THIS LINE * * */ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; s.src = 'https://' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); comments powered by Disqus

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Computer Graphics note(2):视图变换&投影变换
Games101 lecture4-lecture5 考虑将三位物体转换二维图像需要的步骤,我们需要以下变换来达成目的, model transformation(模型(基础)变换–Object就位) view/camera transformation(视图变换–调整相机) projection transformation(投影变换–变换到[−1,1]3[-1,1]^3[−1,1]3,忽略深度信息zzz,变成[−1,1]2[-1,1]^2[−1,1]2) viewport transformation(视口变换–投影到屏幕空间) 如下图所示(Fundamentals of Computer Graphics (3rd Edition) 7.1 Viewing Transformations Figure 7.2):
Enterprise_
2020/08/02
5970
【机器学习数学基础】线性代数基础
线性代数 一、基本知识 本书中所有的向量都是列向量的形式: \[\mathbf{\vec x}=(x_1,x_2,\cdots,x_n)^T=\begin{bmatrix}x_1\\x_2\\ \vdots \\x_n\end{bmatrix}\] 本书中所有的矩 \(\mathbf X\in \mathbb R^{m\times n}\) 都表示为: \[\mathbf X = \begin{bmatrix} x_{1,1}&x_{1,2}&\cdots&x_{1,n}\\ x_{2,1}&x_{2
10JQKA
2018/12/11
6410
【机器学习数学基础】线性代数基础
​四大院校携手 GraphBEV | 将激光雷达和相机信息融合到BEV,比 BEVFusion性能高出8.3% !
三维目标检测是自动驾驶系统的一个关键组成部分,旨在准确识别和定位汽车、行人以及三维环境中的其他元素[49, 58]。为了鲁棒和高品质的检测,当前的实践主要遵循像BEVFusion[29, 34]这样的多模态融合范式。不同的模态通常提供互补的信息。例如,图像含有丰富的语义表示,但缺乏深度信息。相比之下,点云提供了几何和深度信息,但却是稀疏的且缺乏语义信息。因此,有效利用多模态数据的优势同时减轻其局限性,对于提高感知系统的鲁棒性和准确性至关重要[58]。
AIGC 先锋科技
2024/07/08
9280
​四大院校携手 GraphBEV  |  将激光雷达和相机信息融合到BEV,比 BEVFusion性能高出8.3% !
图形学复习
​ 令 \Delta_{1} = 2a,\Delta_{2} = 2(a+b) ;
努力的Greatiga
2022/07/25
1.8K0
相机标定(一)-原理及内参、外参
相机标定(一)-原理及内参、外参 相机标定(二)-畸变校正,张正友标定法 相机标定(三)-相机成像模型
全栈程序员站长
2022/09/02
1.5K0
透视投影矩阵推导[通俗易懂]
透视投影矩阵(Perspective Projection Matrix)的作用是进行规范化透视投影变换,即 观察空间 → \rightarrow →规范化观察空间。
全栈程序员站长
2022/11/09
1.6K0
透视投影矩阵推导[通俗易懂]
图形学入门(二):光栅化
光栅化(Rasterize)就是将一些矢量形状转换为位图(Raster Image)形式。经过这样的变换后,这些形状才可以在屏幕上进行显示,也可以被打印机打印出来。
zhiruili
2021/08/10
4.2K0
图形学入门(二):光栅化
【GAMES101-现代计算机图形学课程笔记】Lecture 02 Review of Linear Algebra
矩阵在图形学里常用于表示变换(Transformations),比如 translation,rotation,shear,scale等。
marsggbo
2020/06/12
6770
PDF标准详解(三)—— PDF坐标系统和坐标变换
之前我们了解了PDF文档的基本结构,并且展示了一个简单的hello world。这个hello world 虽然只在页面中显示一个hello world 文字,但是包含的内容却是不少。这次我们仍然以它为切入点,来了解PDF的坐标系统以及坐标变换的相关知识
Masimaro
2024/06/16
6810
PDF标准详解(三)—— PDF坐标系统和坐标变换
图形学入门(三):基础着色
在掌握了上一篇文章的知识之后,我们现在可以通过逐个绘制三角形面组合出一个模型了。但是我们现在绘制出来的结果看起来是一个色块,效果不太自然。在现实中,我们看到物体是因为这个物体反射了光线,而在这个过程中,根据物体形状以及与光线的相对位置关系,物体的表面总会呈现不同的明暗效果。这种明暗的变化使我们感觉这个物体是「立体的」。也就是说,我们更希望看到下图1中右侧的渲染效果而非左侧的渲染效果:
zhiruili
2021/08/10
1.6K0
图形学入门(三):基础着色
变换(Transform)(2)-坐标空间变换
在刚接触图形学,看games101课程时,观察变换与投影变换就给我了相当大的麻烦,同样的(l, r, t, b, n, f)参数,网上每个人给出来的矩阵形式有所不同,让我永远分不清。随着学习资源的丰富以及自己稍微有了些成长,这篇文章终于解决了这个困惑。
Zero Two
2024/08/24
1800
使用 C# 入门深度学习:线性代数
张量(Tensor):在 Pytorch 中,torch.Tensor 类型数据结构就是张量,结构跟数组或矩阵相似。
痴者工良
2025/03/26
490
使用 C# 入门深度学习:线性代数
ML特训营笔记1
设A = (a_{ij}),B = (b_{ij})是两个m \times n矩阵,则m \times n 矩阵C = c_{ij}) = a_{ij} + b_{ij}称为矩阵A与B的和,记为A + B = C
皮大大
2021/03/02
5030
行为感知Transformer:用于多行为序列推荐的
本文主要针对序列推荐中的多行为序列推荐,即行为序列中包含不同的行为类型,比如点击,加购,购买等。为了捕获用户的个性化行为模式和行为间的复杂协作关系,作者提出PBAT方法:
秋枫学习笔记
2024/02/27
6510
行为感知Transformer:用于多行为序列推荐的
重拾图形图像处理 ---- 笔试面试题:三维重建相关(1)
参考:https://baike.baidu.com/item/%E4%B8%89%E8%A7%92%E5%BD%A2%E9%9D%A2%E7%A7%AF%E5%85%AC%E5%BC%8F/8491990
流川疯
2022/05/10
2K0
重拾图形图像处理 ---- 笔试面试题:三维重建相关(1)
变换(Transform)(1)-向量、矩阵、坐标系与基本变换
如果要将右侧坐标系变为左侧那种,我们只需要做一些旋转操作,将右侧坐标系顺时针旋转180度,再将整个坐标系水平翻转即可。我们可以通过一定的旋转操作将两个坐标系重合,那么我们就称它们具有相同的旋向性(handedness)。
Zero Two
2024/07/21
4830
Transformers是SSMs:通过结构化状态空间对偶性的广义模型和高效算法(一)
尽管Transformer一直是深度学习在语言建模中取得成功的主要架构,但最近的研究表明,如Mamba之类的状态空间模型(SSMs)在小到中等规模上能够匹敌或超越Transformer的性能。我们表明,这两类模型实际上是非常相关的,并在一个经过充分研究的结构化半可分离矩阵类的各种分解之间,发展出SSM和注意力变体之间丰富的理论联系框架。我们的状态空间对偶性(SSD)框架使我们能够设计一种新的架构(Mamba-2),其核心层是对Mamba的选择性SSM的改进,速度提高了2-8倍,同时在语言建模方面继续与Transformer保持竞争力。
AI浩
2024/10/22
2700
Transformers是SSMs:通过结构化状态空间对偶性的广义模型和高效算法(一)
【GAMES101-现代计算机图形学课程笔记】Lecture 04 Transformation Cont.
Euler angles常用在飞机的旋转,即旋转划分成roll,pitch,yaw三个操作。
marsggbo
2020/06/12
1.8K0
WebGL简易教程(五):图形变换(模型、视图、投影变换)
通过之前的教程,对WebGL中可编程渲染管线的流程有了一定的认识。但是只有前面的知识还不足以绘制真正的三维场景,可以发现之前我们绘制的点、三角形的坐标都是[-1,1]之间,Z值的坐标都是采用的默认0值,而一般的三维场景都是很复杂的三维坐标。为了在二维视图中绘制复杂的三维场景,需要进行相应的的图形变换;这一篇教程,就是详细讲解WebGL的图形变换的过程,这个过程同样也适合OpenGL/OpenGL ES,甚至其他3D图形接口。
charlee44
2019/10/08
2.9K0
WebGL简易教程(五):图形变换(模型、视图、投影变换)
CLIFF : 结合整帧位置信息的人体姿态和形状估计
基于参数化人体从单张RGB图像重建三维人体姿态与形状,由于图片的深度模糊性,是一个欠约束问题。由于这个问题在 AR/VR 、动作分析等领域的重要性,它引起了很多关注。
用户1324186
2022/11/07
1.6K0
CLIFF : 结合整帧位置信息的人体姿态和形状估计
推荐阅读
相关推荐
Computer Graphics note(2):视图变换&投影变换
更多 >
加入讨论
的问答专区 >
1大客户架构高级咨询顾问擅长3个领域
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档