Transforms

"《Real-Time Rendering 4th》笔记——第四章 转换"

Posted by Ciel on May 21, 2019

Transforms

本章介绍了转换的基本知识,这是处理几何图形的基本工具。从基本的仿射变换开始,还讨论了矩阵、四元数、顶点混合、变形和投影矩阵。理解这些,有助于辨别如何处理计算,提高代码效率。本章涉及许多线性代数相关内容。

1 基础转换

  • 线性变换是一种保留向量加法和标量乘法的变换。满足以下公式:

\img\in-post\rtr4\e1

  • 使用齐次表示法,方向向量表示为$v = (v_x,v_y,v_z,0)$,点表示为$v = (v_x,v_y,v_z,1)$。

  • 所有平移、旋转、缩放、反射和剪切矩阵都是仿射矩阵(affine)。仿射矩阵的主要特征是它保持了直线的并行性,但长度和角度不一定。仿射变换也可以是单个仿射变换的任意序列。

  • 正交矩阵的逆就是其转置矩阵。

\img\in-post\rtr4\t1

表4.1,本章讨论的大多数转换的摘要。

1.1 平移

  • 从一个位置到另一个位置的变化用一个平移矩阵T表示。这个矩阵用一个向量$t = (t_x, t_y, t_z)$来转换一个实体。

\img\in-post\rtr4\e2

  • 如下图,给出了一个平移变换例子。注意,方向向量$v = (v_x,v_y,v_z,0)$不受乘以T的影响,因为方向向量不能被平移。相反,点和向量都受到其余仿射变换的影响。平移矩阵的逆矩阵 $T^1(t)=T(-t)$ ,也就是说向量t是负的。

\img\in-post\rtr4\4-1

原书图4.1,左边的正方形用一个平移矩阵T(5,2,0)进行变换,其中正方形向右移动5个距离单位,向上移动2个距离单位。

  • 有时在计算机图形学中看到,使用矩阵中平移向量在底部行,例如DirectX使用这种形式。在这种方案中,应用程序将从左到右读取矩阵。这样的向量和矩阵被称为行主形式,因为这些向量是行向量。在这本书中,使用了列主形式。无论使用哪一种,都是纯粹的符号分歧,当矩阵存储在内存中时,这16个值的后4个值是三个平移值,后面跟着一个1。

1.2 旋转

  • 旋转变换使矢量围绕经过原点的给定轴旋转给定的角度。是一个刚体变换(保留了变换后点的间距,也不会导致左右交换)。方向矩阵是与摄像机视图或对象相关联的旋转矩阵,定义其在空间中的方向,即其向上和向前的方向。

  • 二维空间旋转矩阵推导:参数化一个向量$v=(v_x,v_y)=(rcos\theta,rsin\theta)$。如果我们逆时针旋转弧度$\phi$,会得到$u=(rcos(\theta+\phi),rsin(\theta+\phi))$。可以推导出以下公式:

\img\in-post\rtr4\e3

  • 三维空间旋转矩阵为以下公式,分别绕x,y,z轴旋转$\phi$:

\img\in-post\rtr4\e4

  • 如果从4X4矩阵中删除最下面一行和最右边的列,就得到一个3X3矩阵。对于每一个3X3旋转矩阵R,它围绕任意轴旋转弧度$\phi$,轨迹(矩阵中对角线元素的和)是独立于轴的常数,公式为:

\img\in-post\rtr4\e5

  • 所有旋转矩阵的行列式都是1并且是正交的。这也适用于任意数量的这些转换的连接。还可以这样求矩阵倒数:$R_i^{-1}(\phi)=R_i(-\phi)$,即沿同一轴反向旋转。

  • 例如图4.2,假设让一个物体绕z轴旋转弧度$\phi$,旋转中心是某个点p。由于绕点旋转的特征是点本身受到旋转影响,所以转换从平移对象开始,使点p与原点重合,通过$T(-p)$完成。之后是实际的旋转:$R_z(\phi)$。最后使用$T(p)$将对象转换回原来的位置。得到的变换X为:

\img\in-post\rtr4\e6

注意上面的旋转顺序

\img\in-post\rtr4\4-2

1.3 缩放

  • 一个缩放矩阵$S(s)=S(s_x,s_y,s_z)$在x,y,z方向上分别用因子$s_x,s_y,s_z$来缩放对象。任一分量为1则表示不缩放。如果$s_x=s_y=s_z$则是均匀缩放,否则称为非均匀缩放操作。有时用各向同性缩放和各向异性缩放来代替均匀和非均匀。逆矩阵$S^{-1}(s)=S(1/s_x,1/s_y,1/s_z)$。

\img\in-post\rtr4\e7

  • 另一种创建均匀缩放矩阵的有效方法是通过使用齐次坐标,操作位置(3,3)处的矩阵元素,即右下角的元素。执行此操作的两个不同矩阵如下所示,与使用S进行均匀缩放相反,使用S’必须始终进行均匀化。这可能是低效的,因为它涉及均匀化过程中的除法; 如果右下角的元素为1,则不需要除法。当然,如果系统总是在没有测试是否是1的情况下进行这种操作,那么就没有额外的成本。

\img\in-post\rtr4\e8

  • s的一个或三个分量上是负值给出了一种反射矩阵,也称为镜像矩阵。如果只有两个比例因子是-1,那么我们将旋转弧度$\pi$。需要注意的是,一个旋转矩阵与一个反变换矩阵连接也是一个反变换矩阵。因此,下面是一个反射矩阵:

\img\in-post\rtr4\e9

检测到反射矩阵时,通常需要特殊处理。例如,一个顶点按逆时针顺序排列的三角形在被一个反射矩阵变换时将得到一个顺时针顺序。这一顺序的改变可能导致不正确的照明和背面剔除发生。为了检测一个给定的矩阵是否以某种方式反射,计算矩阵左上3X3个元素的行列式。如果值是负数,则矩阵是反射的。例如,方程4.12中矩阵的行列式是$0\cdot0-(-1)\cdot(-1)=-1$

  • 缩放矩阵S只沿x轴、y轴和z轴缩放。如果要在其他方向进行缩放,则需要进行复合变换。假设缩放应该沿着标准正交、右手向量fx、fy和fz的轴进行。首先,构造矩阵F,改变基底,如下所示:

\img\in-post\rtr4\e10

其思想是使三个坐标轴给出的坐标系与标准坐标轴重合,然后利用标准缩放矩阵,再进行变换。第一步是乘以F的转置,然后做实际的缩放,再返回一个变换。变换公式为$X=FS(s)F^T$。

1.4 剪切

  • 剪切矩阵可以在游戏中用于扭曲整个场景以创建迷幻效果,或者扭曲模型的外观。有六个基本的剪切矩阵,分别表示为$H_{xy}(s),H_{xz}(s),H_{yx}(s),H_{yz}(s),H_{zx}(s),H_{zy}(s)$,第一个下标用来表示哪个坐标被剪切矩阵改变了,第二个下标表示进行剪切的坐标。

\img\in-post\rtr4\4-3

原书图4.3,用$H_{xz}(s)$剪切单位正方形的效果。y值和z值都被转换所影响,而x值是旧的x值和s乘以z值的和,导致正方形倾斜。这种转换是保留面积的,可以看到虚线区域相同。

  • 下标可以用来求参数s在矩阵中的位置。如图xz代表第一行第三列:

\img\in-post\rtr4\e11

将这个矩阵与点p相乘得到一个点$(p_x+sp_z\ \ p_y\ \ p_z)^T$。从图形上看,图4.3中的单位正方形显示了这一点。$H_{ij}(s)$的逆(第i个坐标相对于第j个坐标进行剪切,其中i ≠ j)是通过反向剪切得到的,即$H_{ij}^{-1}(s)=H_{ij}(-s)$。

  • 可以使用稍微不同的剪切矩阵

\img\in-post\rtr4\e12

然而,在这里,这两个下标都用来表示这些坐标将被第三个坐标剪切。这两种不同的描述之间的联系是$H_{ij}^{‘}(s,t)=H_{ik}(s)H_{jk}(s)$,其中k用作第三个坐标的索引。需要注意的是,由于任意剪切矩阵|H|= 1,这是一个保持体积的变换,如图4.3所示。

\img\in-post\rtr4\4-4

原书图4.4,说明矩阵相乘时的顺序依赖性。在第一行中,应用旋转矩阵$R_z(\pi/6)$,然后缩放$S(s)$,其中S = (2,0.5,1).则复合矩阵为$S(s)R_z(\pi/6)$。在下一行,矩阵按相反的顺序应用,得到$R_z(\pi/6)S(s)$。结果明显不同。一般认为,对于任意矩阵M和N, MN ≠ NM。

1.5 变换的连接

  • 由于矩阵乘法运算的不可交换性,矩阵出现的顺序很重要。因此,变换的连接被称为顺序相关的。

  • 将一系列矩阵连接成一个矩阵的明显原因是为了获得效率。例如,假设你有一个拥有数百万个顶点的游戏场景,并且场景中的所有对象都必须缩放、旋转和最终平移。现在,不是将所有顶点与三个矩阵相乘,而是将三个矩阵连接成一个矩阵。然后将这个矩阵应用于顶点。这个复合矩阵是C = TRS。注意这里的顺序。缩放矩阵S应该首先应用于顶点,因此在合成中出现在右边。这个顺序意味着TRSp = (T(R(Sp)),其中p是要转换的点。TRS是场景图系统常用的顺序。

  • 值得注意的是,虽然矩阵连接是顺序相关的,但可以按需要对矩阵进行分组。例如,假设您想用TRSp计算一次刚体运动变换TR。将这两个矩阵(TR)(Sp)组合在一起并用中间结果替换是有效的。因此,矩阵连接是关联的。

1.6 刚体变换

  • 只有平移和旋转连接而成的变换称为刚体变换。它具有保持长度、角度和坐标系方向的特点。

  • 任何刚体矩阵可以写成一个平移矩阵和旋转矩阵的连接,如矩阵方程4.17所示:

\img\in-post\rtr4\e13

矩阵X的逆计算为:$X^{-1}=(T(t)R)^{-1}=R^{-1}T(t)^{-1}=R^TT(-t)$。另一种计算X逆的方法是考虑R和X的符号:

\img\in-post\rtr4\e14

其中$r_0$表示旋转矩阵的第一列,$r_0^T$表示列矩阵的第一行,0是一个带有0的3X1的列向量,计算得到如4.19所示

\img\in-post\rtr4\e15

  • 例如给相机定位。在图形学中,常见的任务是调整相机的方向,使它能看到特定的位置。在这里将展示gluLookAt()(来自OpenGL实用程序库,简称GLU)所做的工作。假设摄像机位于c点,我们希望摄像机观察目标l,并且给定摄像机方向为u’,如图4.5所示:

\img\in-post\rtr4\4-5

原书图4.5,计算相机在c点的方向变换所涉及的几何知识,用向上的向量u‘,来观察点l。为此,我们需要计算r,u和v。

我们要计算一个由三个向量组成的基,{r,u,v}。计算视图向量$v=(c-l)/|c-l|$,即目标到摄像机位置的归一化向量。向右看的向量可以计算为$r=-(v×u’)/|v×u’|$。u’向量通常不能保证精确地指向上,所以最终的上向量是另一个叉乘$u=v\times r$,它被保证是标准化的,因为v和r都是标准化的,并且通过垂直构造。我们要构造的摄像机变换矩阵M,它的思想是首先平移所有的东西所以摄像机的位置在原点(0,0,0),然后改变基底,使r与(1,0,0),u与(0,1,1),v 与(0,0,1):

\img\in-post\rtr4\e16

注意,当把平移矩阵与基底变换矩阵连接起来时,平移矩阵t在右边,因为它应该首先应用。记住r,u,v分量的位置的一种方法是:我们让r变成(1,0,0),所以当基底变换矩阵乘以(1,0,0),我们可以看到矩阵中的第一行必须是r的元素,因为$r\cdot r=1$。此外,第二行和第三行必须由垂直于r的向量组成,即$r\cdot x=0$。当把同样的思想应用于u和v时,我们得到了上面的基底变换矩阵。

1.7 法线变换

\img\in-post\rtr4\4-6

原书图4.6,左边是原始的几何图形,侧面显示一个三角形和它的法线。中图显示了如果模型沿着x轴缩放0.5,法线使用相同的矩阵,会发生什么。右图表示法线的适当变换。

  • 单个矩阵可用于一致地转换点、线、三角形和其他几何形状。同样的矩阵也可以变换沿着这些直线或三角形表面的切向量。然而,这个矩阵不能用来转换一个重要的几何属性,表面法线(和顶点光照法线)。

  • 正确的方法不是乘以矩阵本身,而是使用矩阵的伴随矩阵的转置。伴随函数的计算在我们的在线线性代数附录中有描述。伴随函数总是存在的。法线在转换后不能保证是单位长度的,因此通常需要标准化。

  • 对法线进行变换的传统方法是计算其逆矩阵的转置。这种方法通常有效,但是完全反转不必要,偶尔也不能创建。 逆是伴随矩阵除以原始矩阵的行列式。如果该行列式为零,则矩阵是异常的,并且逆不存在。

  • 即使只是计算一个完整的4X4矩阵的伴随矩阵也是很昂贵的,而且通常是不必要的。由于法线是一个矢量,平移不影响它。此外,大多数模型变换都是仿射变换。它们不改变传入的齐次坐标的w分量,即它们不执行投影。 在这些(常见的)情况下,正常变换所需的全部是计算左上3X3个分量的伴随。

  • 通常甚至不需要这种伴随计算。假设我们知道转换矩阵完全由平移、旋转和均匀缩放操作(不拉伸或压扁)的串联组成。变换不影响法线。均匀尺度因子只是简单地改变了法线的长度。剩下的是一系列的旋转,它总会产生某种净旋转,仅此而已。逆矩阵的转置可以用来变换法线。一个旋转矩阵的定义是它的转置是它的逆矩阵。为了得到法向变换,两个转置(或两个逆变换)给出了原始的旋转矩阵。综上所述,原始变换本身也可以直接用于这种情况下的法线变换。

  • 并非总是需要完全重新规范化所产生的法线。如果只将平移和旋转连接在一起,法线在被矩阵转换时不会改变长度,因此不需要重新规范化。如果连续统一缩放,则可以使用整体比例因子直接标准化所产生的法线。例如使对象大5.2倍的缩放,该矩阵变换的法线将通过除以5.2来重新归一化。 或者为了创建一个可以产生归一化结果的法线变换矩阵,原始矩阵的左上角3x3可以除以该比例因子一次。

  • 注意,法线变换在系统中不是问题,在变换之后,表面法线是从三角形导出的(例如,使用三角形边缘的叉积)。 切线向量与自然界中的法线不同,并且总是由原始矩阵直接变换。

1.8 计算逆

在许多情况下,例如在坐标系之间来回变换时,需要用到逆变换。根据有关转换的可用信息,可以使用以下三种计算矩阵逆的方法:

  • 如果矩阵是具有给定参数的单变换或简单变换序列,则可以通过“反求参数”和矩阵顺序轻松地计算出矩阵。例如,如果$M=T(t)R(\phi)$,那么$M^{-1}=R(-\phi)T(-t)$。这很简单并且保留了变换的准确性,在渲染巨大的世界时非常重要。

  • 如果已知矩阵正交,则$M^{-1}=M^T$,即转置是它的逆矩阵。任何旋转序列都是旋转,所以是正交的。

  • 如果什么都不知道,那么可以使用伴随法、克拉默法则、LU分解或高斯消去法来计算逆矩阵。克拉姆规则和伴随方法一般较好,因为它们具有较少的分支操作;在现代架构上,最好避免使用“if”测试。有关如何使用伴随函数对法线进行逆变换,请参阅第4.1.7节。

优化时也可考虑逆计算的目的。例如,如果逆计算用于对向量进行变换,那么通常只需要对矩阵左上方的3X3部分进行逆变换(参见前一节)。

2 特殊矩阵变换和运算

在本节中,将介绍和推导对实时图形至关重要的几个矩阵转换和操作。首先,我们提出了欧拉变换(及其参数提取),这是一种描述方向的直观方法。然后我们讨论从单个矩阵中检索一组基本变换。最后,推导出一种绕任意轴旋转实体的方法。

2.1 欧拉变换

\img\in-post\rtr4\4-7

原书图4.7,欧拉变换以及它与你改变头部(head)、俯仰(pitch)和滚转角(roll angles)的方式关系。默认视图方向如图所示,沿着负z轴向前,沿着y轴向上。

  • 欧拉变换是三个矩阵的乘法,最常用的是:$E(h,p,r)=R_z(r)R_x(p)R_y(h)$,因为E是旋转的串联,所以它显然也是正交的。因此$E^{-1}=E^T=(R_zR_xR_y)^T=R_y^TR_x^TR_z^T$。欧拉角h,p,r分别表示头部、俯仰和横滚应该沿着各自轴旋转的程度。有时这些角度都被称为rolls。head是y-roll,pitch是x-roll。另外head有时也被称为yaw,例如在飞行模拟中。这种转换很直观,不仅可以定位相机,还可以定位任何对象或实体。这些转换可以使用世界空间的全局轴或相对于局部参照物来执行。

  • 需要注意的是,欧拉角在不同领域有着不同的向上轴。在计算机图形学中,对于如何看待世界以及如何生成内容有一个划分:y-up或z-up。大多数制造过程,包括3D打印,都认为z方向在世界空间中是向上的;航空和海上交通工具公司认为-z是向上。建筑业通常使用z轴向上,因为建筑平面一般是二维用x,y表示。而与媒体相关的建模系统通常将y方向视为世界坐标的上方向,这与我们通常在计算机图形学中描述相机屏幕的上方向相匹配。这两种选择仅仅是一个90度的旋转。在本章中,除非另有说明,否则将使用y-up的世界方向。

  • 相机在其视图空间中的上升方向与世界的上升方向没有特别的关系。

  • 虽然欧拉角对于小角度的变化或观察者的方向很有用,但它也有一些严重的限制。将两组欧拉角结合使用是不容易的。例如,一个集合和另一个集合之间的插值并不是对每个角进行插值的简单问题。事实上,两组不同的欧拉角可以给出相同的方向,所以任何插值都不应该旋转物体。这些是使用其他方向表示(如本章后面讨论的四元数)的一些原因。使用欧拉角,还有万向锁问题,这将在下一节解释。

2.2从欧拉变换中提取参数

\img\in-post\rtr4\e17

在这里,我们将4x4矩阵替换为3x3矩阵,因为后者为旋转矩阵提供了所有必要的信息。也就是说,等价4x4矩阵的其余部分总是包含0和位于右下角的1。将方程4.22中的三个旋转矩阵串联起来,得到:

\img\in-post\rtr4\e18

由此可见,pitch参数由$sinp$给出。$e_{01}/e_{11}$和$e_{20}/e_{22}$,得到head和roll参数的提取方程如下:

\img\in-post\rtr4\e19

因此,利用函数atan2(y,x)从矩阵E中提取欧拉参数h (head)、p (pitch)和r (roll):

\img\in-post\rtr4\e20

但是,我们需要处理一个特殊情况。 如果$cosp$= 0,出现了万向锁,旋转角度r和h将围绕同一轴旋转(尽管可能在不同的方向,取决于p旋转角度是-π/ 2还是 π/ 2),因此只需要导出一个角度。如果我们设h = 0,得到:

\img\in-post\rtr4\e21

由于p不影响第一列的值,当$cosp=0$ ,可以用$sinr/cosr=tanr=e_{10}/e_{00}$,得到$r=atan2(e_{10},e_{00})$

请注意,根据arcsin的定义,$-\pi/2\le p\le \pi/2,$这意味着如果在此区间之外创建了值为p的E,则无法提取原始参数。 h,p和r不是唯一的意味着可以使用多于一组的Euler参数来产生相同的变换。 上面所述的简单方法可能导致数值不稳定的问题,这在花费某些成本上是可以避免的。

  • 当使用欧拉变换时,可能会发生万向锁。例如,假设变换顺序是x,y,z,如果第二次旋转,绕y轴旋转90度,会旋转本地z轴,使其与原始x轴对齐。一旦选择±90°作为pitch角,就会导致第一次旋转和第三次旋转轴相同,旋转等价,整个旋转表示系统被限制在只能绕竖直轴旋转,丢失了一个表示维度。

  • 在建模系统中,欧拉角通常以x/y/z的顺序表示,围绕每个局部轴旋转,其他顺序也是可行的。例如,z/x/y用于动画,z/x/z用于动画和物理。所有这些都是指定三个单独旋转的有效方法。最后一个顺序,z/x/z,对于某些应用程序可能是更好的,因为只有当围绕x旋转π弧度(半旋转)时才会发生万向锁。没有完美的序列可以避免万向锁。尽管如此,欧拉角仍然是常用的,因为动画师更喜欢使用曲线编辑器来指定角度随时间的变化。

2.3 矩阵分解

  • 从连接矩阵中检索各种变换称为矩阵分解。检索一组转换有很多原因。用途包括:

    • 提取对象的缩放因子。

    • 查找特定系统所需的转换。(例如,一些系统可能不允许使用任意4x4矩阵。)

    • 确定一个模型是否只经历了刚体变换。

    • 在动画中的关键帧之间进行插值,其中只有对象的矩阵可用。

    • 从旋转矩阵中移除剪切。

  • 之前提出了两种分解,一种是求刚体变换的平移和旋转矩阵(第4.1.6节),另一种是求正交矩阵的欧拉角(第4.2.2节)。检索平移矩阵很简单,只需要4x4矩阵最后一列中的元素。还可以通过检查矩阵的行列式是否为负来确定是否发生了反射。要分离出旋转、缩放和剪切需要做更多工作(具体需要看书中举例的其他参考文章)。

2.4 绕任意轴旋转

  • 有时,实现实体绕任意轴旋转一定角度的过程是很方便的。 假设旋转轴r被标准化,并且创建一个绕r轴旋转变换$\alpha$。为此,我们首先转换到我们想要旋转轴是x轴的空间。这是通过一个M矩阵完成的。然后执行实际旋转,然后使用$M^{-1}$转换回来。

\img\in-post\rtr4\4-8

原书图4.8,绕任意的r轴旋转,是由r, s和t组成的标准正交基完成的。然后我们将这个基与标准基对齐,使r与x轴对齐。绕x轴的旋转是在这里完成的,最后我们再转换回来。

  • 为了计算M,我们需要找出两个正交于r和彼此正交的坐标轴s,t。$t=r\times s$,因此先专注于求s轴。一种数值稳定的方法是找出r的最小分量(绝对值),并将其设置为0。交换剩下的两个元素,然后逆值它们中的第一个(事实上,任何一个非零元素都可以逆值)。数学上表示为:

\img\in-post\rtr4\e22

这保证了s垂直于r,(r,s,t)是一组标准正交基。书中还提到了其他方法计算这组基。无论使用哪种技术,这三个向量都被用来创建一个旋转矩阵M。

\img\in-post\rtr4\e23

由此得到$X=M^TR_x(\alpha)M$。这里因为M是正交的,因此M的逆为$M^T$。在第4.3.2节中,介绍了另一种使用四元数的方法。

3 四元数

虽然四元数(quaternions)是1843年由威廉·罗文·汉密尔顿爵士发明的,是复数的延伸,但直到1985年,休马克才将四元数引入计算机图形学领域。四元数用于表示旋转和方向。它在很多方面都优于欧拉角和矩阵。任何三维方向都可以表示为围绕特定轴的单个旋转。给定这个轴和角度表示,转换到四元数或从一个四元数转换过来是直接的,而欧拉角转换在任何方向都是具有挑战性的。四元数可用于稳定和恒定的方向插值,这是欧拉角不能很好地完成的。

四元数有四个部分。前三个值与旋转轴密切相关,旋转角度影响所有四个部分(详见4.3.2节)。因为四元数有四个分量,我们选择用向量来表示它们,但是为了表示它们,我们给它们加上一个帽子:$\hat{q}$。

3.1 数学背景

  • 定义:四元数$\hat{q}$可以用以下方法来表示,它们都是等价的。

\img\in-post\rtr4\e24

变量$q_w$是四元数的实部,$q_v$是虚部,i,j,k被称为虚单位。

  • 对于虚部$q_v$我们可以使用所有向量运算,如加法,缩放,点积,叉乘等。利用四元数的定义,导出了如图乘法运算。注意虚数单位的乘法是不可交换的。

\img\in-post\rtr4\e25

从这个方程中可以看出,我们用叉乘和点乘来计算两个四元数的乘法。

  • 随着四元数的定义,需要加法、共轭、范数和恒等式的定义:

\img\in-post\rtr4\e26

可以看到norm计算简化(如上所示的结果)后,虚数部分抵消了,只剩下实部。norm有时被定义为$|\hat{q}|=n(\hat{q})$

  • 上面的一个结果是一个乘法逆,用$\hat{q}^{-1}$表示可以导出$\hat{q}^{-1}\hat{q}=\hat{q}\hat{q}^{-1}=1$。我们从范数的定义推导出一个公式:

\img\in-post\rtr4\e27

这就得到了乘法逆,如下图所示:

\img\in-post\rtr4\e28

  • 反函数的公式采用标量乘法,是由式4.31中的乘法推导而来的运算:$s\hat{q}=(0,s)(q_v,q_w)=(sq_v,sq_w)$以及$\hat{q}s=(q_v,q_w)(0,s)=(sq_v,sq_w)$这意味着标量乘法是可交换的:$s\hat{q}=\hat{q}s=(sq_v,sq_w)$.

  • 下面的等式很容易从这些中派生出来:

\img\in-post\rtr4\e29

  • 一个单位四元数,$\hat{q}=(q_v,q_w)$,令$n(\hat{q})=1$。由此推出:

\img\in-post\rtr4\e30

对于某个三维向量$u_q$,比如$|u_q|=1$,因为

\img\in-post\rtr4\e31

当且仅当$u_q\cdot u_q=1=|u_q|^2$

  • 对于复数,二维单位向量可以写成$cos\phi+isin\phi=e^{i\phi}$,四元数的等价形式是:

\img\in-post\rtr4\e32

  • 单位四元数的对数和幂函数如图所示:

\img\in-post\rtr4\e33

3.2 四元数转换

四元数、欧拉角、矩阵的相互转换推导过程略,网上有很多资料

  • 单位四元数可以表示任何三维旋转。
  • 将一个点或向量$p=(p_x,p_y,p_z,p_w)^T$转换为四元数$\hat{p}$,假设有一个单位四元数$\hat{q}=(sin\phi u_q,cos\phi)$,可以证明:$R_q(p)=\hat{q}\hat{p}\hat{q}^{-1}$

\img\in-post\rtr4\4-9

原书图4.9,用单位四元数表示的旋转变换的说明,$\hat{q}=(sin\phi u_q,cos\phi)$。变换围绕$u_q$轴旋转$2\phi$。

  • 由于通常需要将几个不同的变换组合起来,而且大多数变换都是矩阵形式,因此需要一种方法将四元数方程转化为矩阵。

\img\in-post\rtr4\e34

  • 反向转换,从一个正交矩阵转换到一个单位四元数要复杂一点。

\img\in-post\rtr4\e35

  • 球面线性插值:给定两个单位四元数$\hat{q},\hat{r}$,一个参数$t\in[0,1]$,计算一个内插的四元数。这对于动画对象非常有用,但对于插值相机的方向不是很有用,因为在插值过程中相机的向上矢量会发生倾斜,这通常会造成一些干扰。这个运算的代数形式这样表示:

\img\in-post\rtr4\e36

然而对于软件实现,下面的形式(slerp代表球面线性插值)更合适:

\img\in-post\rtr4\e37

  • slerp函数非常适合在两个方向之间进行插值,但计算昂贵因为会用到三角函数,书中提到了几种加速slerp计算的方法。

\img\in-post\rtr4\4-10

原书图4.10,单位四元数表示为单位球面上的点。利用函数slerp对四元数之间进行插值,插值后的路径是球面上的一个大圆弧。注意,从$\hat{q}_1$到$\hat{q}_2$的插值和从$\hat{q}_1$到$\hat{q}_3$到$\hat{q}_2$的插值是不一样的,即使它们的方向相同。

  • slerp插值有时会出现抖动,更好的插值方法是使用样条曲线(详见第17章)。

  • 从一个向量旋转到另一个向量,一个常见的操作是通过最短路径从一个方向s变换到另一个方向t。使用四元数大大简化了这一过程。

\img\in-post\rtr4\e38

4 顶点混合

假设角色的手臂使用前臂和上臂两部分动画,如图4.11所示。这个模型可以用刚体变换来进行动画。然而,这两部分之间的关节将不会像一个真正的肘部。这是因为使用了两个单独的对象,因此关节由这两个单独对象的重叠部分组成。显然,最好只使用一个对象。然而,静态模型部件并不能解决关节问题。

  • 顶点混合(Vertex blending)是解决这个问题的一种常用方法。这种技术还有其他几个名称,如linear-blend skinning, enveloping, 或者skeleton-subspace deformation。

\img\in-post\rtr4\4-11

原书图4.11,左边是由前臂和上臂两个独立对象的刚体变换来做动画。肘部看起来不真实。右边是一个对象使用顶点混合。右边手臂说明了顶点混合发生了什么,有些顶点混合了不同的权值weight:(2/3,1/3)。表示对顶点来说上臂变换权重为2/3,前臂变换权重为1/3。同时也显示了顶点混合的缺点。可以看到肘部内侧的折叠。如果有更多的骨骼和更精确的权重可以达到更好的效果。

  • 顶点混合就是说允许一个单一的顶点被几个不同的矩阵转换,结果的位置加权并混合在一起。这是通过一个动画对象的骨骼来实现的,其中每个骨骼的转换可以根据用户定义的权重来影响每个顶点。许多商业建模系统都具有类似的骨骼建模特性。尽管它们并不一定是刚性的。例如,增加额外关节以使肌肉膨胀等功能得以实现,使用可以挤压和拉伸的骨骼进行动画蒙皮。

\img\in-post\rtr4\4-12

原书图4.12,一个顶点混合例子。左上方显示了手臂的两根骨骼。右上网格,用顶点颜色表述每个顶点被骨骼影响的权重。底部显示前臂移动到一个稍微不同的位置。

  • 数学上,它表示为式4.59,其中p为原始顶点,u(t)为变换后的顶点,其位置依赖于时间t

\img\in-post\rtr4\e39

确定p的位置有n根骨头,用世界坐标表示。值$w_i$为顶点p的骨骼i的权值。矩阵$M_i$从原始坐标系变换为世界坐标系。一般来说,骨骼在其坐标系的原点有控制关节。例如,前臂骨会将肘关节移动到原点,用一个动画旋转矩阵将手臂的这部分绕着关节移动。$B_i(t)$矩阵是第i个骨骼世界坐标下变换,它随着时间的推移而变化,以使对象具有动画效果,它通常是几个矩阵的连接,比如之前的骨骼层次变换和局部动画矩阵。

  • 所发生的是顶点被转换成几个位置然后在它们之间插值。法线通常也可以用公式4.59来转换。根据所使用的变换,可能需要用$B_i(t)M_i^{-1}$的逆的转置来代替,如4.1.7节所讨论的。

  • 顶点混合非常适合在GPU上使用(GPU Skinning)。网格中的顶点集可以放置在一个静态buffer中,该buffer一次发送给GPU并被重用。在每一帧中,只有骨骼矩阵发生变化,用顶点着色器计算它们对存储网格的影响。通过这种方式,CPU处理和传输的数据量最小化,允许GPU高效渲染网格。如果模型的所有骨骼矩阵可以一起使用,则最简单;否则,必须对模型进行分割并复制一些骨骼。另外,骨骼转换可以存储在顶点访问的纹理中,从而避免触及寄存器存储限制。通过使用四元数来表示旋转,每个转换只能存储在两个纹理中。如果可用,无序访问视图存储允许重用蒙皮结果。

  • 权值一般是非负的,和为1。可以指定超出[0,1]范围或总和不等于1的权重集。但是,只有在使用了其他一些混合算法,例如morph目标(第4.5节)时,这才有意义。

  • 基本顶点混合的一个缺点是可能发生不必要的折叠、扭曲和自相交。更好的解决方案是使用双四元数。这种蒙皮的技术有助于保持原始变形的硬度,因此避免了类似糖果包装的扭曲。该方法计算量小于线性混合的1.5倍,计算结果良好,应用迅速。然而,双四元数蒙皮会导致皮肤凸起,Le和Hodgins提出旋转中心蒙皮是更好的选择。它们依赖于这样的假设:局部变换应该是刚体的,并且具有相似权值的顶点wi应该具有相似的变换。每个顶点的旋转中心都是预先计算好的,同时施加正交(刚体)约束,以防止肘部塌陷和糖果包装扭曲。在运行时,该算法类似于线性混合蒙皮,因为GPU实现在旋转中心执行线性混合蒙皮,然后执行四元数混合步骤。

\img\in-post\rtr4\4-13

原书图4.13,左侧显示了使用线性混合蒙皮时关节处的问题。在右边,使用双四元数混合可以改善。

5 变形

从一个三维模型到另一个模型的变形(Morph)在执行动画中很有用。假设一个模型在t0时刻显示,我们希望它在t1时刻变成另一个模型。对t0 ~ t1之间的所有时间,利用某种插值方法,得到了一个连续的混合模型。变形的一个例子如下所示:

\img\in-post\rtr4\4-14

原书图4.14,顶点变形。每个顶点定义两个位置和法线。在每一帧中,中间位置和法线由顶点着色器进行线性插值。

  • 变形要解决两个主要问题,即顶点对应问题和插值问题。给定两个任意模型,它们可能具有不同的拓扑结构、不同的顶点数和不同的网格连接性,通常必须首先设置这些顶点对应关系。(这个问题有很多研究,原文推荐看Alexa的文章)

  • 如果这两个模型之间已经存在一个一对一的顶点对应关系,那么可以在每个顶点的基础上进行插值。也就是说,对于第一个模型中的每个顶点,第二个模型中必须只有一个顶点,反之亦然。这使得插值成为一项简单的任务。例如,线性插值可以直接在顶点上使用(17.1节介绍了其他插值方法)。计算时间$t\in[t_0,t_1]$的变形顶点,首先计算$s=(t-t0)/(t_1-t_0)$,然后是线性顶点混合:$m=(1-s)p_0+sp_1$。其中$p_0$和$p_1$对应于同一个顶点,但是在不同的时刻$t_0$和$t_1$。

  • 变形的变体,让用户有更直观的控制被称为morph targets或blend shapes。

\img\in-post\rtr4\4-15

原书图4.15,给出了两种姿态,计算一组不同的矢量来控制插值,甚至推断。在变形目标中,不同的矢量被用来在中性人脸上添加“动作”。对于不同的向量,当权重为正时,我们会得到一个微笑的嘴巴,而负的权重则会给出相反的结果。

  • 从一个中性模型开始,在这个例子中是一张脸。用N表示这个模型。此外还有一套不同的面部姿势。在这个例子中只有笑脸。一般情况下,允许k≥1个不同姿势,记做$P_i,i\in[1,…,k]$。作为预处理,计算$D_i=P_i-N$,即从每个姿态中减去中性模型。得到变形后模型M:$M=N+\sum_{i=1}^kw_iD_i$。在中性模型基础上,根据需要添加不同姿势的特征,使用权重$w_i$。对于图4.15,设置$w_1=1$正好给出了插图中间的笑脸。使用$w_1=0.5$则是半个笑脸,以此类推。也可以使用负权数和大于1的权数。

  • 变形目标是一种强大的技术,它为动画师提供了很多控制,因为模型的不同特性可以独立于其他特性进行操作。Lewis等引入位置空间变形(pose-space deformation),将顶点混合和变形目标相结合。Senior使用预先计算的顶点纹理来存储和检索目标姿态之间的位移。硬件支持流输出和每个顶点的ID允许在一个模型中使用更多的目标,并且只在GPU上计算效果。使用低分辨率网格,然后通过细分阶段和位移映射生成高分辨率网格,避免了在一个非常精细的模型中为每个顶点蒙皮的成本。

\img\in-post\rtr4\4-16

原书图4.16,在《inFAMOUS Second Son》中,德尔森的脸使用混合形状动画。所有这些照片都使用相同的休息姿势,然后修改不同的权重,使脸部看起来不同。

6 几何缓存回放

在过场动画中,可能需要使用高质量的动画,例如对于无法使用上述任何方法表示的动作。一种简单的方法是存储所有帧的所有顶点,从磁盘读取它们并更新网格。然而,对于一个简短动画中使用的30,000个顶点的简单模型,这可能达到50mb /s。Gneiting提出了几种将内存成本降低到10%左右的方法。

首先,使用量子化。例如,每个坐标使用16位整数存储位置和纹理坐标。这一步是有损的,因为在执行压缩之后无法恢复原始数据。为了进一步减少数据,进行了空间和时间预测,并对这些差异进行了编码。对于空间压缩,可以使用平行四边形预测。对于一个三角形带,下一个顶点的预测位置仅仅是当前三角形在边周围反射的三角形平面上,形成一个平行四边形。然后对来自此新位置的引用进行编码。通过良好的预测,大多数值将接近于零,这对于许多常用的压缩方案来说是理想的。与MPEG压缩相似,在时间维度上也进行了预测。也就是说,每n帧,执行空间压缩。在这两者之间,预测是在时间维度上进行的,例如,如果某个顶点被向量从n-1帧移动到n帧,那么它很可能移动相同的距离到n+1帧。这些技术大大降低了系统的存储量,使系统能够实现数据的实时流化。

7 投影

在实际渲染场景之前,场景中的所有相关对象都必须投射到某种平面或某种简单的体块中。然后执行剪切和呈现。

到目前为止,在本章中看到的转换没有影响到第四个坐标w值。也就是说,点和向量在变换后保留了它们的类型。同样,4X4矩阵的底行一直是(0 0 0 1)。透视投影矩阵是这两个性质的例外:底行包含向量和点操作数,通常需要均一化。也就是说,w通常不等于1,所以需要除以w才能得到非齐次点。正交投影是一种比较简单的投影,也是常用的一种投影。它不影响w分量。

在本节中,假设观察者正沿着相机的负z轴观察,y轴向上,x轴向右。这是一个右手坐标系。一些文本和软件,如DirectX,使用左手系统(观察者沿着相机的正z轴看)。这两种系统都是同样有效的,最终达到了同样的效果。

7.1正交投影

  • 正交投影的一个特点是,平行线在投影后保持平行。当使用正交投影观看场景时,无论距离摄像机有多远,物体的大小都保持不变。如下图所示,$P_o$是一个简单的正交投影矩阵,保持点的x、y分量不变,同时将z分量设为零,即,它垂直投影到平面z = 0上。

\img\in-post\rtr4\e40

显然,$P_o$是不可逆的,因为它的行列式为0。换句话说,转换从三维下降到二维,并且无法检索下降的维度。使用这种投影观察的一个问题是,它将z值为正的点和z值为负的点投影到投影平面上。将z值(以及x-和y值)限制在一个特定的区间内通常是有用的,比如从n(近平面)到f(远平面)。这就是下一个变换的目的。

\img\in-post\rtr4\4-17

原书图4.17,由式4.62生成的简单正交投影三种不同视图。可以看做是观察者沿着负z轴观察,意味着投影在保持x,y坐标不变的情况下跳过z坐标(或者设置为零)。注意,z=0两边的对象都投影到投影平面上。

  • 一种更常用的正投影矩阵由六元组(l,r,b,t,n,f),表示左、右、底、顶、近、远平面。AABB(见第22.2节的定义)的最小转角为(l,b,n),最大转角为(r,t,f)。在OpenGL中,轴向对齐的立方体的最小角为(-1,-1,-1)和最大角(1,1,1);在DirectX中,界限为(-1,-1,0)到(1,1,1)。这个立方体称为标准视图体(canonical view volume),其中的坐标称为标准设备坐标(normalized device coordinates)。转换过程如下所示,转换成标准视图的原因是在那里更容易执行剪裁。

\img\in-post\rtr4\4-18

原书图4.18,在标准视图上转换轴向对齐框。先使其中心与原点重合,然后对其进行缩放,以获得标准视图大小,如右图所示。

  • 在转换到规范化视图之后,要呈现的几何图形的顶点将被剪切到这个立方体上。非立方体外的几何图形最终通过将剩余的单元正方形映射到屏幕上呈现。这个正投影变换显示在这里:

\img\in-post\rtr4\e41

由于在DirectX中使用左手坐标系,推导得到的正交矩阵是

\img\in-post\rtr4\e42

它通常以转置形式表示,因为DirectX使用行主形式来编写矩阵。

7.2 透视投影

透视投影的平行线投影后一般不平行。相反,它们可能在极值处收敛于一个点。视角更符合我们对世界的认知:越远的物体越小。

  • 假设摄像机位于原点,要将点p投影到平面z=-d,d>0上,得到一个新点$q=(q_x,q_y,-d)$。如下图所示

\img\in-post\rtr4\4-19

原书图4.19,用于推导透视投影矩阵。图中所示的相似三角形,可以得到q的x分量的:$q_x=-d \frac{p_x}{p_z}$

q的其他分量类似的可以得到$q_y=-d \frac{p_y}{p_z}$,$q_z=-d$。结合上面公式,得到透视投影矩阵:

\img\in-post\rtr4\e44

由这个矩阵得到正确的透视投影:

\img\in-post\rtr4\e43

  • 与正交投影转换一样,还有一个透视转换,它不是实际投影到一个平面上(这是不可逆转的),而是将视图截锥转换为前面描述的规范视图。

\img\in-post\rtr4\4-20

原属图4.20,矩阵$p_p$将视图截锥转换为单元立方体,称为标准视图体。

  • 参数(lr;b;t;n;f)确定相机的视台。水平视野是由视台左右平面的角度决定的(由l和r决定)。同样的,垂直视野是由顶部和底部平面之间的角度决定的(由t和b决定)。视野越大,相机看到的就越多。可以生成不对称的截锥体(如$r\ne -l或t\ne-b$)。例如,非对称的截锥体用于立体视觉和虚拟现实(见章节21.2.3)。

  • 视野是提供场景感的一个重要因素。与电脑屏幕相比,眼睛本身有一个物理的视野。这种关系是$\phi=2arctan(w/(2d))$,其中$\phi$是视野,w为垂直于视线的物体的宽度,d为到物体的距离。例如,35mm相机的标准50mm镜头(其宽帧尺寸为36mm)的角度为2arctan(36/(2·50)) = 39:6度。

  • 与物理设置相比,使用更窄的视野将减少透视效果,因为观看者将在场景中放大。设置较宽的视野将使对象看起来扭曲(如使用广角相机镜头),尤其是靠近屏幕边缘,并会夸大附近物体的比例。然而,更宽的视野使观察者感觉到物体更大且更令人印象深刻,并且具有向用户提供关于周围环境更多信息的优点。

  • 采用透视变换(任何形式)$P_p$,然后剪切均匀(除以w),得到归一化的设备坐标。

这里是OpenGL中方程:

\img\in-post\rtr4\e45

这里是DirectX中方程:

\img\in-post\rtr4\e46

  • 使用透视变换计算出的深度值不会随输入pz值线性变化。下图显示了改变近平面到原点距离的效果。近平面和远平面的布置影响z缓冲器的精度。这一影响将在第23.7节进一步讨论。

\img\in-post\rtr4\4-21

原书图4.21,改变近平面到原点的距离的效果。距离f’-n’保持在100不变。当近平面离原点越来越近时,离远平面越近的点使用的归一化设备坐标(NDC)深度空间范围越小。这样做的效果是使z缓冲区在更大的距离上精度更低。

  • 有几种方法可以提高深度精度。一个常见的方法,我们称之为反转z(reversed z),是存储$1.0-z_{NDC}$既可以是浮点数也可以是整数。比较如下图所示:

\img\in-post\rtr4\4-22

原书图4.22,使用DirectX变换设置深度buffer的不同方法,$z_{NDC}\in[0,+1]$。左上角:标准整数深度缓冲区,这里显示了4位精度(因此y轴上有16个标记)。右上角:远处的平面设置为1,两个轴上的小位移表明这样做并不会损失多少精度。左下角:3个指数位和3个尾数位作为测深点。注意y轴上的分布是非线性的,这使得x轴上的分布更糟糕。右下角:反交点深度,即1-zNDC,具有更好的分布。

  • Lloyd提出使用深度值的对数来提高阴影图的精度。Lauritzen等利用前一帧的z-buffer来确定最大近平面和最小远平面。对于屏幕空间深度,Kemen建议对每个顶点使用以下重映射:

\img\in-post\rtr4\e47

其中w为投影矩阵后顶点的w值,z为顶点着色器输出的z。常数$f_c$是$f_c=2/log_2(f+1)$其中f是远平面。当这个变换只应用于顶点着色器时,深度仍然由GPU在顶点的非线性变换深度之间对三角形进行线性插值。由于对数是一个单调函数,只要分段线性插值与精确的非线性变换深度值之间的偏差很小,遮挡剔除硬件和深度压缩技术仍然有效。这是对大多数情况下的表面几何镶嵌。不过,也可以对每个片段应用转换。这是通过输出每个顶点的值e = 1 + w来完成的,然后由GPU在三角形上插值。然后像素着色器将片段深度修改为$log_2(e_i)f_c/2$,其中ei是e的插值值。当GPU中没有浮点深度时,当使用深度较大的距离渲染时,这种方法是一个很好的选择。

  • Cozzi提出使用多个截锥体,可以有效地提高精度,达到任何期望的速率。视锥体在深度方向上划分为几个不重叠的较小的子锥体,它们的结合就是视锥体。子实体按前后顺序呈现。首先,颜色和深度都被清除,所有要渲染的对象都被分类到它们重叠的子实体中。对于每个子实体,建立其投影矩阵,清除深度步长,然后渲染与子实体重叠的对象。

进一步阅读和参考资料

沉浸式线性代数网站

本书官网

Farin and Hansford’s The Geometry Toolbox

Lengyel’s Mathematics for 3D Game Programming and Computer Graphics

Gems系列介绍了各种与转换相关的算法,其中许多算法的代码都可以在线获得

关于变形/顶点混合和形状插值的更多信息可以阅读Lewis等人的SIGGRAPH论文

Hart等和Hanson提供了四元数的可视化。Pletinckx和Schlag提出了在一组四元数之间平滑插值的不同方法。

更多资料在原书中有提到,这里只列出了部分。