计算机图形学(六): 渲染管线-空间变换(下)

空间变换-下(Spatial Transformation)

书接上文,本章我们继续讲变换,上一章介绍了旋转和反射,这章我们开始介绍剩下几种变换:缩放/平移/错切

缩放

均匀缩放

每一个向量被映射到自身与缩放因子的乘积.

<math xmlns="http://www.w3.org/1998/Math/MathML"> f ( u ) = a u a ∈ R f(\mathbf{u}) = a\mathbf{u} \ \ \ \ a \in R </math>f(u)=au a∈R

旋转变换保持变换后向量的模不变,缩放保持方向不变:

<math xmlns="http://www.w3.org/1998/Math/MathML"> u ∣ u ∣ = a u ∣ a u ∣ \frac{\mathbf{u}}{|\mathbf{u}|} = \frac{a\mathbf{u}}{|a\mathbf{u}|} </math>∣u∣u=∣au∣au

缩放是线性变换吗?可以用线性变化的代数定义来证明:

通过上图可知,缩放是线性变换!

矩阵表示: 假如我们想用因数 <math xmlns="http://www.w3.org/1998/Math/MathML"> a a </math>a缩放向量 <math xmlns="http://www.w3.org/1998/Math/MathML"> u = ( u 1 , u 2 , u 3 ) \mathbf{u} = (u_{1},u_{2},u_{3}) </math>u=(u1,u2,u3),我们如何用矩阵来表示该变换?

用缩放因子 <math xmlns="http://www.w3.org/1998/Math/MathML"> a a </math>a构造一个对角矩阵( <math xmlns="http://www.w3.org/1998/Math/MathML"> d i a g o n a l m a t r i x D diagonal \ matrix \ D </math>diagonal matrix D)即可:

<math xmlns="http://www.w3.org/1998/Math/MathML"> [ a 0 00 a 0 0 0 a ] [ u 1 u 2 u 3 ] = [ a u 1 a u 2 a u 3 ] \begin{bmatrix} a & 0 & 0 0 & a & 0 \\ 0 & 0 & a \end{bmatrix}\begin{bmatrix} u_{1}\\ u_{2}\\ u_{3} \end{bmatrix} = \begin{bmatrix} au_{1}\\ au_{2}\\ au_{3} \end{bmatrix} </math>[a00000aa0]⎣ ⎡u1u2u3⎦ ⎤=⎣ ⎡au1au2au3⎦ ⎤

负数缩放

如果缩放因子为负,假设 <math xmlns="http://www.w3.org/1998/Math/MathML"> a = − 1 a = -1 </math>a=−1,可以把缩放分解为一系列的反射变换.负因子缩放的这种性质在二维和三维中的表现是不同的,因为二维缩放分解为两个反射矩阵的乘积,而三维缩放分解为三个反射矩阵的乘积:

<math xmlns="http://www.w3.org/1998/Math/MathML"> [ − 1 0 0 − 1 ] = [ − 1 0 0 1 ] [ 1 0 0 − 1 ] \begin{bmatrix} -1 & 0\\ 0 & -1 \end{bmatrix} = \begin{bmatrix} -1 & 0\\ 0 & 1 \end{bmatrix}\begin{bmatrix} 1 & 0\\ 0 & -1 \end{bmatrix} </math>[−100−1]=[−1001][100−1]

对于二维负数缩放,可以将其分解为沿着 <math xmlns="http://www.w3.org/1998/Math/MathML"> Y Y </math>Y轴和 <math xmlns="http://www.w3.org/1998/Math/MathML"> X X </math>X轴做两次镜像,两次朝向反转相互抵消,最终面朝向保持不变,本质上这是一次180°的旋转变换.

<math xmlns="http://www.w3.org/1998/Math/MathML"> [ − 1 0 0 0 − 1 0 0 0 − 1 ] = [ − 1 0 0 0 1 0 0 0 1 ] [ 1 0 0 0 − 1 0 0 0 1 ] [ 1 0 0 0 1 0 0 0 − 1 ] \begin{bmatrix} -1 & 0 & 0 \\ 0 & -1 & 0 \\ 0 & 0 & -1 \end{bmatrix} = \begin{bmatrix} -1 & 0 & 0\\ 0 & 1 & 0\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} 1 & 0 & 0\\ 0 & -1 & 0\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} 1 & 0 & 0\\ 0 & 1 & 0\\ 0 & 0 & -1 \end{bmatrix} </math>⎣ ⎡−1000−1000−1⎦ ⎤=⎣ ⎡−100010001⎦ ⎤⎣ ⎡1000−10001⎦ ⎤⎣ ⎡10001000−1⎦ ⎤

对于三维负数缩放,可以分解为沿着 <math xmlns="http://www.w3.org/1998/Math/MathML"> Y Y </math>Y轴 <math xmlns="http://www.w3.org/1998/Math/MathML"> X X </math>X和 <math xmlns="http://www.w3.org/1998/Math/MathML"> Z Z </math>Z轴做三次镜像,两次抵消一次反转,最终面朝向是反转的.

非均匀缩放(轴向)

上边介绍的几种缩放缩放因子都是统一的,每个轴的缩放系数保持一致,如果每个轴按照不同的因子缩放,该如何去表达呢?

<math xmlns="http://www.w3.org/1998/Math/MathML"> f ( u 1 , u 2 , u 3 ) = ( a u 1 , b u 2 , c u 3 ) f(\mathbf{u_{1}}, \mathbf{u_{2}}, \mathbf{u_{3}}) = (a\mathbf{u_{1}}, b\mathbf{u_{2}}, c\mathbf{u_{3}}) </math>f(u1,u2,u3)=(au1,bu2,cu3)

<math xmlns="http://www.w3.org/1998/Math/MathML"> a , b , c ∈ R a,b,c \in R </math>a,b,c∈R

和均匀缩放矩阵表示一样,只不过在对角线上替换成不同的缩放因子即可:

<math xmlns="http://www.w3.org/1998/Math/MathML"> [ a 0 0 0 b 0 0 0 c ] [ u 1 u 2 u 3 ] = [ a u 1 b u 2 c u 3 ] \begin{bmatrix} a & 0 & 0 \\ 0 & b & 0 \\ 0 & 0 & c \end{bmatrix}\begin{bmatrix} \mathbf{u_{1}}\\ \mathbf{u_{2}}\\ \mathbf{u_{3}} \end{bmatrix}=\begin{bmatrix} a\mathbf{u_{1}}\\ b\mathbf{u_{2}}\\ c\mathbf{u_{3}} \end{bmatrix} </math>⎣ ⎡a000b000c⎦ ⎤⎣ ⎡u1u2u3⎦ ⎤=⎣ ⎡au1bu2cu3⎦ ⎤

非轴向缩放

非轴向缩放即未按照当前标准基进行缩放,因此无法直接写出这样的矩阵,但是可以换个角度,可以先将其转换(旋转)到新的坐标空间,应用轴向缩放,再转换回原始的坐标空间,基本步骤分解如下:

  1. 应用旋转矩阵转换到新的坐标系空间( <math xmlns="http://www.w3.org/1998/Math/MathML"> R R </math>R)
  2. 应用轴向缩放矩阵( <math xmlns="http://www.w3.org/1998/Math/MathML"> D D </math>D)
  3. 旋转回原始的坐标空间( <math xmlns="http://www.w3.org/1998/Math/MathML"> R T R^T </math>RT)

注:旋转矩阵的逆等于其转置,因此可以用其转置来表示逆变换

可以将非轴向缩放用对称矩阵( <math xmlns="http://www.w3.org/1998/Math/MathML"> S y m m e t r i c m a t r i x Symmetric \ matrix </math>Symmetric matrix)来表示:

<math xmlns="http://www.w3.org/1998/Math/MathML"> f ( X ) = R T D R X f(\mathbf{X}) = R^TDR\mathbf{X} </math>f(X)=RTDRX

对称矩阵:对称矩阵的转置等于其自身: <math xmlns="http://www.w3.org/1998/Math/MathML"> A = A T A = A^T </math>A=AT

那么所有的对阵矩阵都表示了非均匀缩放吗?答案是肯定的.下一小节我们根据谱定理来推导为什么所有的对称矩阵都代表了非均匀缩放?

谱定理( <math xmlns="http://www.w3.org/1998/Math/MathML"> S p e c t r a l t h e o r e m Spectral \ theorem </math>Spectral theorem)

谱定理告诉我们对于任意一个对称矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> A = A T A = A^T </math>A=AT包含:

  • 正交特征向量: <math xmlns="http://www.w3.org/1998/Math/MathML"> e 1 , . . . , e n ∈ R n e_{1},...,e_{n} \in R^n </math>e1,...,en∈Rn
  • 特征值: <math xmlns="http://www.w3.org/1998/Math/MathML"> λ 1 , . . . , λ n ∈ R \lambda_{1},...,\lambda_{n} \in R </math>λ1,...,λn∈R

根据特征向量和特征值的定义可以得到:

<math xmlns="http://www.w3.org/1998/Math/MathML"> A e i = λ i e i Ae_{i} = \lambda_{i}e_{i} </math>Aei=λiei

也可以换一种表述方式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> A R = R D AR = RD </math>AR=RD

<math xmlns="http://www.w3.org/1998/Math/MathML"> R = [ e 1 . . . e n ] R = [e_{1}...e_{n}] </math>R=[e1...en]

<math xmlns="http://www.w3.org/1998/Math/MathML"> D = [ λ 1 ⋅ ⋅ ⋅ λ n ] D = \begin{bmatrix} \lambda_{1} & & \\ & \cdot \cdot \cdot & \\ & & \lambda_{n} \end{bmatrix} </math>D=⎣ ⎡λ1⋅⋅⋅λn⎦ ⎤

<math xmlns="http://www.w3.org/1998/Math/MathML"> ⇒ \Rightarrow </math>⇒

<math xmlns="http://www.w3.org/1998/Math/MathML"> A = R D R T A = RDR^T </math>A=RDRT

因此得出结论,所有的对称矩阵都代表了沿某一方向的非均匀缩放,这也给对称矩阵提供了一种几何视角,当我们看到一个对称矩阵,就明白它代表了某个方向上的非均匀缩放.

切变( <math xmlns="http://www.w3.org/1998/Math/MathML"> S h e a r Shear </math>Shear)

先从简单的二维切变着手,如下图表示了一种切变变换过程:

通过观察发现,变换后点的 <math xmlns="http://www.w3.org/1998/Math/MathML"> y y </math>y不变, <math xmlns="http://www.w3.org/1998/Math/MathML"> x x </math>x的变化和 <math xmlns="http://www.w3.org/1998/Math/MathML"> y y </math>y成一定的比例关系,取一个特殊点, <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 0 , 1 ) (0, 1) </math>(0,1)在转换后变成 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( a , 1 ) (a, 1) </math>(a,1),中间的任一点转换满足这种线性关系即:

<math xmlns="http://www.w3.org/1998/Math/MathML"> ( x , y ) ⟶ ( x + a y , y ) (x,y) \longrightarrow \ (x + ay, y) </math>(x,y)⟶ (x+ay,y)

用矩阵表示:

<math xmlns="http://www.w3.org/1998/Math/MathML"> [ x 丶 y 丶 ] = [ 1 a 0 1 ] [ x y ] \begin{bmatrix} x^丶 \\ y^丶 \end{bmatrix} = \begin{bmatrix} 1 & a\\ 0 & 1 \end{bmatrix}\begin{bmatrix} x\\ y \end{bmatrix} </math>[x丶y丶]=[10a1][xy]

切变严格定义: 切变使每个点 <math xmlns="http://www.w3.org/1998/Math/MathML"> x x </math>x向u方向位移,移动距离和点 <math xmlns="http://www.w3.org/1998/Math/MathML"> x x </math>x在 <math xmlns="http://www.w3.org/1998/Math/MathML"> v v </math>v方向上的投影长度成正比:

<math xmlns="http://www.w3.org/1998/Math/MathML"> f u , v ( x ) = x + < v , x > u f_{u,v}(\mathbf{x}) = \mathbf{x} + <\mathbf{v},\mathbf{x}>\mathbf{u} </math>fu,v(x)=x+<v,x>u

<math xmlns="http://www.w3.org/1998/Math/MathML"> A u , v = I + u v T A_{u,v} = I + \mathbf{u}\mathbf{v^T} </math>Au,v=I+uvT

举例:

<math xmlns="http://www.w3.org/1998/Math/MathML"> u = ( cos ⁡ ( t ) , 0 , 0 ) \mathbf{u} = (\cos(t), 0, 0) </math>u=(cos(t),0,0)

<math xmlns="http://www.w3.org/1998/Math/MathML"> v = ( 0 , 1 , 0 ) \mathbf{v} = (0 , 1, 0) </math>v=(0,1,0)

<math xmlns="http://www.w3.org/1998/Math/MathML"> A u , v = [ 1 cos ⁡ ( t ) 0 0 1 0 0 0 1 ] A_{u,v} = \begin{bmatrix} 1 & \cos(t) &0 \\ 0 & 1 & 0\\ 0 & 0 & 1 \end{bmatrix} </math>Au,v=⎣ ⎡100cos(t)10001⎦ ⎤

组合变换

通过这些基本变换(旋转,反射,缩放,切变)的组合,我们可以构建一个复合变换,只需要将这些矩阵相乘即可:

应用变换的顺序:从右到左

假设现在有一个代表变换的矩阵,如何将其分解为这些基础的变换呢?

线性变换的分解

有许多线性变换分解的方法:

  1. 奇异值分解( <math xmlns="http://www.w3.org/1998/Math/MathML"> S i n g u l a r v a l u e d e c o m p o s i t i o n Singular \ value \ decomposition </math>Singular value decomposition) (信号处理领域)
  2. LU分解( <math xmlns="http://www.w3.org/1998/Math/MathML"> L U f a c t o r i z a t i o n LU factorization </math>LUfactorization)(解线性方程组)
  3. 极分解( <math xmlns="http://www.w3.org/1998/Math/MathML"> P o l a r d e c o m p o s i t i o n Polar \ decomposition </math>Polar decomposition)(空间变换领域)

对于本章主题,着重介绍奇异值分解和极分解.

考虑如下变换:

极分解 & 奇异值分解

极分解将任意矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> A A </math>A分解为一个正交矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> Q Q </math>Q和对阵矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P(缩放因子非负):

<math xmlns="http://www.w3.org/1998/Math/MathML"> Q P QP </math>QP被称为矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> A A </math>A极分解.

由于 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P是对称矩阵,根据谱分解定理,可以将 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P继续分解为: <math xmlns="http://www.w3.org/1998/Math/MathML"> P = V D V T P = VDV^T </math>P=VDVT(V是正交矩阵,D是对角矩阵),因此:

<math xmlns="http://www.w3.org/1998/Math/MathML"> A = Q V D V T A = QVDV^T </math>A=QVDVT

将QV合成一个旋转矩阵:

<math xmlns="http://www.w3.org/1998/Math/MathML"> A = Q V D V T = U D V T A = QVDV^T = UDV^T </math>A=QVDVT=UDVT

<math xmlns="http://www.w3.org/1998/Math/MathML"> U D V T UDV^T </math>UDVT被称为矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> A A </math>A的奇异值分解.

线性变换分解的意义

这些线性变换的分解方法有什么作用,具体在图形学中有什么重要意义呢?

考虑在两个线性变换 <math xmlns="http://www.w3.org/1998/Math/MathML"> A 0 , A 1 A_{0},A_{1} </math>A0,A1之间做插值:

最简单的做法就是在两个矩阵之间根据时间( <math xmlns="http://www.w3.org/1998/Math/MathML"> t ∈ [ 0 , 1 ] t \in [0, 1] </math>t∈[0,1])做线性插值:

<math xmlns="http://www.w3.org/1998/Math/MathML"> A ( t ) = ( 1 − t ) A 0 + t A 1 A(t) = (1- t)A_{0} + tA_{1} </math>A(t)=(1−t)A0+tA1

我们发现在开始和结束时,变换是正确的,但是中间的插值过程看起来却很奇怪,现在正是矩阵分解该上场的时候了,其基本思想为分别对极分解生成的两个矩阵做插值而非原始矩阵自身.

我们先将 <math xmlns="http://www.w3.org/1998/Math/MathML"> A 0 和 A 1 A_{0}和A_{1} </math>A0和A1分别分解为 <math xmlns="http://www.w3.org/1998/Math/MathML"> Q 0 P 0 和 Q 1 P 1 Q_{0}P_{0}和Q_{1}P_{1} </math>Q0P0和Q1P1:

<math xmlns="http://www.w3.org/1998/Math/MathML"> A 0 = Q 0 P 0 A_{0} = Q_{0}P_{0} </math>A0=Q0P0

<math xmlns="http://www.w3.org/1998/Math/MathML"> A 1 = Q 1 P 1 A_{1} = Q_{1}P_{1} </math>A1=Q1P1

接下来我们不对矩阵本身做插值,而是对分解后的 <math xmlns="http://www.w3.org/1998/Math/MathML"> Q P QP </math>QP做插值:

平移(Translations)

截止到现在我们介绍了 旋转/反射/缩放/错切变换,却忽略了一种最基础的变换:平移,平移用代数很好表达:

<math xmlns="http://www.w3.org/1998/Math/MathML"> f u ( X ) = X + U f_{u}(\mathbf{X}) = \mathbf{X} + \mathbf{U} </math>fu(X)=X+U

平移变换是线性的吗?让我们用线性变换的两条规则验证下:

显然平移变换不满足线性变换的加性法则和乘性法则,因此平移变换不是线性变换,相反它是仿射变换.

回忆前边说过的复合线性变换可以用矩阵乘法来表示:

<math xmlns="http://www.w3.org/1998/Math/MathML"> A 3 ( A 2 ( A 1 X ) ) = A 3 A 2 A 1 X A_{3}(A_{2}(A_{1}\mathbf{X})) = A_{3}A_{2}A_{1}\mathbf{X} </math>A3(A2(A1X))=A3A2A1X

同样复合平移变换可以用向量的加法来表示:

<math xmlns="http://www.w3.org/1998/Math/MathML"> f u 3 ( f u 2 f u 1 ( X ) ) = f u 1 + u 2 + u 3 ( X ) f_{u3}(f_{u2}{f_{u1}(\mathbf{X})}) = f_{u1+u2+u3}(\mathbf{X}) </math>fu3(fu2fu1(X))=fu1+u2+u3(X)

但是如果一个变换中既包含线性变换又包含平移变换:

<math xmlns="http://www.w3.org/1998/Math/MathML"> A 2 ( A 1 X + b 1 ) + b 2 = ( A 2 A 1 ) X + ( A 2 b 1 + b 2 ) A_{2}(A_{1}\mathbf{X} + \mathbf{b_{1}}) + \mathbf{b_{2}} = (A_{2}A{1})\mathbf{X} + (A_{2}\mathbf{b_{1}} + \mathbf{b_{2}}) </math>A2(A1X+b1)+b2=(A2A1)X+(A2b1+b2)

既要处理矩阵计算又要处理向量计算,有没有什么方法统一二者,用简单明了的方式来表达?

或许可以通过提高维度的方式将平移变换转换为线性变换....这其实就是齐次坐标的基本思想.

齐次坐标( <math xmlns="http://www.w3.org/1998/Math/MathML"> H o m o g e n e o u s C o o r d i n a t e s Homogeneous \ Coordinates </math>Homogeneous Coordinates)

齐次坐标的概念起源于绘画中的透视法,由Möbius引入(为直线分配坐标):

是一个很重要的概念,广泛应用在图形学的方方面面:

  • 3D变换
  • 透视投影
  • 曲面简化
  • 预乘计算透明度
  • 阴影生成
  • ....

基本思想

想象三维空间中任意二维平面(与原点不相交),每一条穿过三维空间原点的直线 <math xmlns="http://www.w3.org/1998/Math/MathML"> L L </math>L都会在二维平面上找到相应唯一的点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P:

因此,在直线 <math xmlns="http://www.w3.org/1998/Math/MathML"> L L </math>L上的任意一点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P ^ \hat{P} </math>P^可以被用来"表示"点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P

二维空间下的齐次坐标

先从二维空间来解释,假设二维平面上的任意一点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P = ( x , y ) P = (x, y) </math>P=(x,y),二维平面在三维空间中的 <math xmlns="http://www.w3.org/1998/Math/MathML"> Z Z </math>Z值为1.

任何满足 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( a / c , b / c ) = ( x , y ) (a/c, b/c) = (x,y) </math>(a/c,b/c)=(x,y)的点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P ^ = ( a , b , c ) \hat{P} = (a, b, c) </math>P^=(a,b,c)即为点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P的齐次坐标.例如:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x , y , 1 ) (x, y, 1) </math>(x,y,1)
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> ( c x , c y , c ) f o r c ! = 0 (cx,cy,c) \ for \ c != 0 </math>(cx,cy,c) for c!=0

因此可以得出结论,两个点 <math xmlns="http://www.w3.org/1998/Math/MathML"> p ^ , q ^ ∈ R 3 \hat{p},\hat{q} \in R^3 </math>p^,q^∈R3描述的是二维空间中相同的点,只要满足 <math xmlns="http://www.w3.org/1998/Math/MathML"> p ^ = λ q ^ \hat{p} = \lambda\hat{q} </math>p^=λq^并且 <math xmlns="http://www.w3.org/1998/Math/MathML"> λ ! = 0 \lambda \ != 0 </math>λ !=0,同样我们可以说齐次坐标是低维空间坐标的高维度空间表示.

齐次坐标下的二维平移变换

想象一下在齐次坐标空间下应用二维平移变换会发生什么:

这种变换和三维空间下的切变很像....其实它就是三维空间上的切变( <math xmlns="http://www.w3.org/1998/Math/MathML"> Z Z </math>Z不变)...

让我们尝试证明齐次坐标下的平移变换是一个仿射变换:

假设将一个二维点 <math xmlns="http://www.w3.org/1998/Math/MathML"> p = ( p 1 , p 2 ) \mathbf{p} = (p_{1}, p_{2}) </math>p=(p1,p2)平移 <math xmlns="http://www.w3.org/1998/Math/MathML"> u = ( u 1 , u 2 ) \mathbf{u} = (u_{1}, u_{2}) </math>u=(u1,u2)得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> p 丶 = ( p 1 + u 1 , p 2 + u 2 ) p^丶 = (p_{1} + u_{1}, p_{2} + u_{2}) </math>p丶=(p1+u1,p2+u2)

在齐次坐标空间下用 <math xmlns="http://www.w3.org/1998/Math/MathML"> p ^ \hat{p} </math>p^来表示 <math xmlns="http://www.w3.org/1998/Math/MathML"> p p </math>p:

<math xmlns="http://www.w3.org/1998/Math/MathML"> p ^ = ( c p 1 , c p 2 , c ) ⇒ p 丶 ^ = ( c p 1 + c u 1 , c p 2 + c u 2 , c ) \hat{p} = (cp_{1}, cp_{2}, c) \ \ \ \Rightarrow \ \ \ \hat{p^丶} = (cp_{1} + cu_{1}, cp_{2} + cu{2}, c) </math>p^=(cp1,cp2,c) ⇒ p丶^=(cp1+cu1,cp2+cu2,c)

注意我们将点 <math xmlns="http://www.w3.org/1998/Math/MathML"> p ^ \hat{p} </math>p^沿着 <math xmlns="http://www.w3.org/1998/Math/MathML"> u u </math>u方向位移,位移距离c和与第三个轴的距离成正比,这正是切变的定义.

重要结论:利用齐次坐标,可以用三维空间的线性变换(切变)来表示二维空间的仿射变换(平移)

齐次坐标下二维空间平移变换的矩阵表示

既然用三维空间的切变来表示二维空间的平移,根据前文所述切变的代数定义可知:

<math xmlns="http://www.w3.org/1998/Math/MathML"> f u , v ( x ) = x + < v , x > u f_{u,v}(\mathbf{x}) = \mathbf{x} + <\mathbf{v}, \mathbf{x}>\mathbf{u} </math>fu,v(x)=x+<v,x>u

矩阵形式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> f u , v ( x ) = ( I + u v T ) x f_{u,v}(\mathbf{x}) = (\mathbf{I} +\mathbf{u}\mathbf{v}^T)\mathbf{x} </math>fu,v(x)=(I+uvT)x

在我们表达平移这种情况下 <math xmlns="http://www.w3.org/1998/Math/MathML"> v = ( 0 , 0 , 1 ) \mathbf{v} = (0, 0, 1) </math>v=(0,0,1),因此我们得到矩阵:

<math xmlns="http://www.w3.org/1998/Math/MathML"> [ 1 0 u 1 0 1 u 2 0 0 1 ] \begin{bmatrix} 1 & 0 & u_{1}\\ 0 & 1 & u_{2}\\ 0 & 0 & 1 \end{bmatrix} </math>⎣ ⎡100010u1u21⎦ ⎤

齐次坐标空间下的其他变换

二维空间下的原始形状在三维齐次坐标空间下可以被表示为很多份均匀沿着 <math xmlns="http://www.w3.org/1998/Math/MathML"> x 3 x_{3} </math>x3方向缩放的拷贝.

齐次坐标空间下的旋转:

齐次坐标空间下的二维旋转可以被表示为沿着 <math xmlns="http://www.w3.org/1998/Math/MathML"> x 3 x_{3} </math>x3的旋转

齐次坐标空间下的缩放:

齐次坐标空间下的缩放可以被看作缩放 <math xmlns="http://www.w3.org/1998/Math/MathML"> x 1 和 x 2 x_{1}和x_{2} </math>x1和x2保持 <math xmlns="http://www.w3.org/1998/Math/MathML"> x 3 x_{3} </math>x3不变

齐次坐标空间下的平移:

二维空间的平移被看作三维空间的切变( <math xmlns="http://www.w3.org/1998/Math/MathML"> Z Z </math>Z不变)

以上我们就将几种基本变换完美的统一了起来,在齐次坐标空间下计算变换矩阵,将平移转换为切变,这样就可以用矩阵乘法来表示所有的变换组合,用一个矩阵表示任意的变换.

三维空间平移的齐次坐标表示

和二维空间下一样,三维空间下的齐次坐标用四维空间表示:

点 vs 向量

齐次坐标有另外一个有用的特性:区分空间中的点和向量

假设三维空间下一个三角形,由三个顶点组成, <math xmlns="http://www.w3.org/1998/Math/MathML"> a , b , c ∈ R 3 a,b,c \in R^3 </math>a,b,c∈R3,法线为 <math xmlns="http://www.w3.org/1998/Math/MathML"> n ∈ R 3 \mathbf{n} \in R^3 </math>n∈R3:

假设我们对三个顶点和法线应用同样的变换:

我们发现在变换后,法线不再垂直于三角形平面,为什么?我们来看下当对法线做变换时发生了什么

但是当我们旋转 & 平移一个三角形时,它的法线不应该被平移,因为法线是一个向量,代表空间中的一个方向,它没有自己的起点( <math xmlns="http://www.w3.org/1998/Math/MathML"> B a s e P o i n t Base Point </math>BasePoint),空间中任意两个向量方向相同长度相同,则它们应该是相等的,因此平移变换对于向量不应该起作用,有什么办法能避免法线被平移呢?我们只需要将向量的 <math xmlns="http://www.w3.org/1998/Math/MathML"> w w </math>w分量设置为0即可:

<math xmlns="http://www.w3.org/1998/Math/MathML"> [ n 1 n 2 n 3 0 ] \begin{bmatrix} n_{1}\\ n_{2}\\ n_{3}\\ 0 \end{bmatrix} </math>⎣ ⎡n1n2n30⎦ ⎤

这样经过变换后,我们得到的三角形法线是垂直于其表面的:

因此,齐次坐标下的点和向量表示是不同的,向量应该忽略平移变换,其 <math xmlns="http://www.w3.org/1998/Math/MathML"> w w </math>w分量应该被设置为0,点的 <math xmlns="http://www.w3.org/1998/Math/MathML"> w w </math>w分量为1

但是最终齐次坐标需要除以 <math xmlns="http://www.w3.org/1998/Math/MathML"> w w </math>w得到原空间的坐标,但是对于向量 <math xmlns="http://www.w3.org/1998/Math/MathML"> w w </math>w分量为0的情况,这意味着什么?

假设我们不断缩小除数 <math xmlns="http://www.w3.org/1998/Math/MathML"> c c </math>c,当 <math xmlns="http://www.w3.org/1998/Math/MathML"> c c </math>c无限趋近于0时,可以认为向量是在无限远处的一个点:

齐次坐标空间下的投影矩阵

用齐次坐标如何表达一个投影矩阵呢,根据小孔相机模型的定义:

<math xmlns="http://www.w3.org/1998/Math/MathML"> ( x , y , z ) ⇒ ( x / z , y / z , 1 ) (x,y,z) \Rightarrow (x/z, y/z, 1) </math>(x,y,z)⇒(x/z,y/z,1)

因此我们可以构建一个矩阵:把z坐标赋值给齐次坐标分量 <math xmlns="http://www.w3.org/1998/Math/MathML"> w w </math>w,除以齐次坐标分量 <math xmlns="http://www.w3.org/1998/Math/MathML"> w w </math>w会得到在z平面上的投影:

<math xmlns="http://www.w3.org/1998/Math/MathML"> [ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 ] [ x y z 1 ] = [ x y z z ] ⇒ [ x / z y / z 1 ] \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 1 & 0 \end{bmatrix}\begin{bmatrix} x\\ y\\ z\\ 1 \end{bmatrix} = \begin{bmatrix} x\\ y\\ z\\ z \end{bmatrix} \Rightarrow \begin{bmatrix} x/z\\ y/z\\ 1 \end{bmatrix} </math>⎣ ⎡1000010000110000⎦ ⎤⎣ ⎡xyz1⎦ ⎤=⎣ ⎡xyzz⎦ ⎤⇒⎣ ⎡x/zy/z1⎦ ⎤

NDC To ScreenSpace

经过mvp变换后,原始的顶点被转换为 <math xmlns="http://www.w3.org/1998/Math/MathML"> [ − 1 , 1 ] 3 [-1,1]^3 </math>[−1,1]3的标准立方体中(NDC Space),接下来我们需要将顶点从Ndc空间转换到屏幕空间(多种不同的叫法: <math xmlns="http://www.w3.org/1998/Math/MathML"> S c r e e n S p a c e / P i x e l S p a c e / I m a g e S p a c e ScreenSpace/PixelSpace/ImageSpace </math>ScreenSpace/PixelSpace/ImageSpace),即我们常常看到视口变换( <math xmlns="http://www.w3.org/1998/Math/MathML"> v i e w p o r t viewport </math>viewport)

屏幕空间

什么是屏幕空间?顾名思义就是我们的显示器,由像素组成的数组构成,数组的大小被我们称为分辨率

  • 像素由 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x , y ) (x,y) </math>(x,y)的形式来表示, <math xmlns="http://www.w3.org/1998/Math/MathML"> x 和 y x和y </math>x和y均为整数,假设像素是一个小方块, <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x , y ) (x,y) </math>(x,y)为该小方块左下角的坐标
  • 坐标范围从 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 0 , 0 ) − ( w i d t h − 1 , h e i g h t − 1 ) (0,0) -(width-1,height-1) </math>(0,0)−(width−1,height−1)
  • 像素 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x , y ) (x,y) </math>(x,y)的中心点位于 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x + 0.5 , y + 0.5 ) (x+0.5, y+0.5) </math>(x+0.5,y+0.5)
  • 屏幕空间覆盖范围: <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 0 , 0 ) − ( w i d t h , h e i g h t ) (0,0) - (width, height) </math>(0,0)−(width,height)

<math xmlns="http://www.w3.org/1998/Math/MathML"> v i e w p o r t 矩阵 viewport矩阵 </math>viewport矩阵

先对Ndc应用缩放矩阵,由于Ndc空间是 <math xmlns="http://www.w3.org/1998/Math/MathML"> [ − 1 , 1 ] [-1,1] </math>[−1,1],所以缩放矩阵应该是屏幕w和h各除以2,然后应用平移变换,将原点从中心移到左下角,平移的量正好是宽高的一半 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( w i d t h / 2 , h e i g h t / 2 ) (width/2, height/2) </math>(width/2,height/2),矩阵形式表示如下:

<math xmlns="http://www.w3.org/1998/Math/MathML"> M v i e w p o r t = [ w i d t h / 2 0 0 w i d t h / 2 0 h e i g h t / 2 0 h e i g h t / 2 0 0 1 0 0 0 0 1 ] M_{viewport} = \begin{bmatrix} width/2 & 0 & 0 & width/2\\ 0 & height/2 & 0 & height/2\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{bmatrix} </math>Mviewport=⎣ ⎡width/20000height/2000010width/2height/201⎦ ⎤

在组合转换时,顺序很重要!

实现基于自身旋转

如果对空间中的一个物体直接应用旋转矩阵,物体将围绕原点做旋转而非自身. 物体实现自身旋转的一个常用技巧就是,先将物体平移到原点,应用旋转变换,然后再将其移动到原位置:

总结

让我们总结下在光栅化管线中,从顶点到屏幕坐标一系列的变换过程:

  1. 首先,我们使用场景图对立方体的多个副本应用变换( <math xmlns="http://www.w3.org/1998/Math/MathML"> M o d e l S p a c e − > W o r l d S p a c e ModelSpace->WorldSpace </math>ModelSpace−>WorldSpace)
  1. 然后应用相机变换,即我们观看场景的视角( <math xmlns="http://www.w3.org/1998/Math/MathML"> W o r l d S p a c e − > V i e w S p a c e WorldSpace->ViewSpace </math>WorldSpace−>ViewSpace)

3. 应用投影变换(perspective)( <math xmlns="http://www.w3.org/1998/Math/MathML"> V i e w S p a c e − > N d c S p a c e ViewSpace->NdcSpace </math>ViewSpace−>NdcSpace)

4. 最后将ndc标准立方体转换到屏幕空间( <math xmlns="http://www.w3.org/1998/Math/MathML"> N d c S p a c e − > S c r e e n S p a c e NdcSpace->ScreenSpace </math>NdcSpace−>ScreenSpace)

经过以上四个步骤,我们将顶点转换为了屏幕上的像素点,接下来就是光栅化着色的过程.

本系列文章会不定期更新,如果你觉得文章对你有帮助,可以订阅专栏计算机图形学,第一时间获取文章更新信息,也希望小伙伴们多多关注,多多点赞:)

相关推荐
青花瓷4 天前
空间内任意点到直线和平面的距离推导
数学·平面·解析几何
Lyrella5 天前
拉格朗日反演小记
数学
AI是这个时代的魔法6 天前
The Action Replay Process
数学·算法·随机决策过程
啊阿狸不会拉杆7 天前
人工智能数学基础(十)—— 图论
人工智能·python·数学·算法·图论
程序员爱德华7 天前
计算机图形学中的深度学习
图形学·opengl
Uncertainty!!11 天前
定义一个3D cube,并计算cube每个顶点的像素坐标
计算机图形学·投影
啊阿狸不会拉杆12 天前
人工智能数学基础(五):概率论
人工智能·python·数学·算法·概率论
啊阿狸不会拉杆14 天前
人工智能数学基础(四):线性代数
人工智能·python·数学·算法·机器学习
Mintopia15 天前
图形学与坐标系入门教学
前端·javascript·计算机图形学
量子位16 天前
数学家们仍在追赶天才拉马努金
人工智能·数学