DirectX 12 持续整理 ——3. 变换

版权声明:转载请标注源地址。 https://blog.csdn.net/rkexy/article/details/73497955

绝大部分内容来自于《Introduction to 3D Game Programming with DirectX12 Frank D. Luna》



3.变换

  这里提到的变换指的是3D世界中的几何变换,这其中包括平移(translation)旋转(rotation)以及缩放(scaling),这些变换的操作大多依靠矩阵和向量的计算来完成,所以——我最近没时间看矩阵的相关东西呢……
  按照书中的顺序,先要知道这次的学习目标都包括什么:

  1. 理解线性变换(Linear transformation)仿射变换(Affine transformation),并且要知道如何用矩阵去表示它们。
    引用:关于线性变换和仿射变换:http://www.matongxue.com/madocs/244.html#/madoc
  2. 学会如何用坐标变换的形式来表示几何体的平移、旋转和缩放。
  3. 知道如何通过矩阵的乘法将多个矩阵变换转化成一个矩阵的变换(通过一个矩阵来计算这种过程)。
  4. 学习如何将一个坐标从一个坐标系转换到另一个坐标系中,并且如何用矩阵来表示这种变化。
  5. 学会使用DirectX Math类库为我们提供的各种函数,来用于构造出变换矩阵。

3.1 线性变换


3.1.1 线性变换的定义

  定义函数 τ(v)=τ(x,y,z)=(x',y',z') .其中 (x,y,z) (x,y,z) 为三维向量,前者为输入,后者为输出,并且满足

τ(u+v)=τ(u)+τ(v)

τ(ku)=kτ(u)

  那么称τ函数的操作为线性变换(此处只针对三维向量考虑)。其中 u=(ux,uy,uz) v=(vx,vy,vz) 是任意的三维向量, k 为标量。

  这种数学概念的定义总是令人厌恶——举个例子:我们定义一个函数

τ(x,y,z)=(x2,y2,z2)

  我随便取一个向量值和一个常数:
u=(x,y,z)=(1,2,3) k=2

  则有:
τ(ku)=τ(2,4,6)=(4,16,36)

  但是另一个结果却并不满足线性变换的定义:
kτ(u)=2(1,4,9)=(2,8,18)=>τ(ku)kτ(u)

  所以,此时 τ(x,y,z)=(x2,y2,z2) 并非线性变换。

  另外,如果 τ 是一个 线性变换,那么:

τ(au+bv+cw)=τ(au+(bv+cw))=aτ(u)+τ(bv+cw)=aτ(u)+bτ(v)+cτ(w)


3.1.2 线性变换的矩阵表示法

  令 u=(x,y,z) ,则有:

这里写图片描述

  其中,i, j, k 为三个单位向量(标准基向量)。
  那么,此时的线性变换可以表示为:
这里写图片描述

  然后……
这里写图片描述

  (别问我怎么得到的,我也是想了好一会……它是把三维矩阵看做成三个向量,而线性变换得到的结果是也一个向量。)
  此时,矩阵 A 则被称之为 线性变换的矩阵表示
  有了这个矩阵,我们就可以将任何目标向量或矩阵乘以变换矩阵,就能直接得到变换后的结果。


  抛去得到变换矩阵的过程,直接给出两种变换矩阵的表现形式:

a.缩放

这里写图片描述

b.旋转

这里写图片描述

  分别绕 X 轴( x=1,y=0,z=0 ), Y 轴( x=0,y=1,z=0 ),Z 轴( x=0,y=0,z=1 )旋转:
这里写图片描述


3.2 仿射变换

3.2.1 齐次坐标(Homogeneous Coordinates)

  对于坐标变换来说,它只能用于在“点(point)”上,而不能被用于“向量(vector)”,因为向量只是方向和数值的一种描述。那么,对于任意一个3D元组( x,y,z ),怎么才能知道它到底是一个点还是一个向量?
  通过齐次坐标,让3D的元组增加一维,成为( x,y,z,w )的形式:

  1. (x,y,z,0) 表示向量.
  2. (x,y,z,1) 表示点.

  为什么这么定义?而不是0代表点,1代表向量呢?
  向量的 w=0 能够避免在转换的过程中,向量被修改。另外,两个点的差值为一个向量,一个点与一个向量的和值为一个点,这样符合数学规律。

3.2.2 仿射变换的矩阵表示

  在有些情况下,线性变换无法完成我们想要的变换结果(比如,平移),这时需要仿射变换(Affine Transformations)登场。
  设平移向量为 b ,则有:


  直接给出矩阵表示:

  仿射变换是通过线性变换加上一个平移向量来完成的。所以很容易看出上述的矩阵所代表的意义。
  当矩阵 A 为一个单位矩阵时,那么上述矩阵所代表的意义仅仅是将一个点沿着向量 b 进行平移。此时的变换矩阵如下:

  当矩阵 A 为前面3.1.2中的缩放或旋转矩阵时,则仿射变换的矩阵所代表的意义为,一个点沿着 b 向量进行平移的同时,进行了缩放或旋转操作。深入了解线性变换和仿射变换的区别,能够更容易了解这里的内容。

3.3 变换的结合

  设想以下情况:存在3个变换矩阵 S , R , T ,怎样才能更好地完成一个已知点 p 经过以上的3种变换?
  按照一般的思路来说,若要完成3次变换,只需要将点 p 分别乘以这3个矩阵,就能够完成操作:

p=((pS)R)T

  但实际的情况是:我们往往操作的不仅仅是一个点,而是一个3D物体模型,这个模型中存在着许多的点,或许几个,或许几十个,甚至成千上万。假设一个物体 V 由10000个点组成,那么我们需要计算次数为:
calcCount=100003=30000

  这并不是最好的方案。
  别忘了,矩阵的乘法满足结合律。如果在第一步计算中,首先将3个变换矩阵化为一个矩阵,那么将减少很多不必要的计算步骤:
p=((pS)R)T=p(SRT)

  这样,原本30000次的操作现在仅需要近10000次计算就能完成。但是,矩阵乘法不满足交换律,切记。

3.4 坐标在不同坐标系中的转换

  有时候,我们需要将同一个点在不同的坐标系下表示,这就像同一个温度用摄氏度和华氏度来表示一样。但看起来,这个问题没有意义,而实际上,这个问题就相当于一个物体在不同的世界场景中表示出来。具体细节在第五章中有所阐释。
  假设点 pA 某个坐标系下的点坐标表示为 (x,y) 。当坐标系改变时,点 pA 的位置并没有变化,但是它的坐标是会变的。换句话说,它的本质没有变,但它的名字变了。此时,设经过坐标系变化后,点 pA 的表示为:

pB=(x,y)

  那么,如何用数学的方式来表示这种变化?
  这种变化的原理与矩阵的乘法原理相同。矩阵用来描述一种运动,而这种运动并非是点自己在运动,而是坐标系本身在运动。

如何理解矩阵的乘法?
引用:http://mp.weixin.qq.com/s/z5RSAFJcnr1amovcUrM85Q

  就像引用中提到的例子一样。人坐在公交车里没有移动(参照系为公交车),但车中的人却因为车的移动而改变了位置(参照系为地球)。因为坐标系的变化而让坐标中的点发生了变化。

更多细节:《Introduction to 3D Game Programming with DirectX12 Frank D. Luna》(PDF) Page.130 - 139.

3.4.1 向量

  现在开始推导向量如何在不同的坐标系中进行转换。
  向量主要由两个因素决定:方向和长度,与位置无关。
  现在假设有两个向量, pA=(xA,yA) pB=(xB,yB) 。如果这两个向量是相同向量在不同坐标系下的表示, uA vA 是坐标系 A 中两坐标轴方向的单位向量,那么:

pA=(xA,yA)=xAuA+yAvA

则向量 pB 为:
pB=xAuB+yAvB

3.4.2 点

  与向量有所不同,决定一个点的因素是“位置”。一个向量无论放在哪里,只要它的方向和长度不变,那么这个向量就不会变。而一个点呢?


  如上图所示:先将点 P 通过 B 坐标系的单位向量进行转换。由于点 P 的位置发生了变化,所以再利用向量Q将点P的位置纠正过来。因此:
pB=xuB+yvB+QB

其中,点 P 在原坐标系 A 的表示为: pA=(x,y) .

上述过程已知了在二维坐标系下的表示,同理可以得到三维坐标系下向量和点的不同坐标系下的变换:

(x',y',z')=xuB+yvB+zwB (for vectors)

(x',y',z')=xuB+yvB+zwB+QB (for points)

依照3.2.1,同理可得其通用表示为:
(x',y',z',w)=xuB+yvB+zwB+wQB

同时可以得出其矩阵表示为:

3.5 DirectX Math 有关变换的函数

// Constructs a scaling matrix:
XMMATRIX XM_CALLCONV XMMatrixScaling(
    // Scaling factors
    float ScaleX,
    float ScaleY,
    float ScaleZ
); 

// Constructs a scaling matrix from components in vector:
XMMATRIX XM_CALLCONV XMMatrixScalingFromVector( 
    FXMVECTOR Scale    // Scaling factors (sx, sy, sz)
); 

// Constructs a x-axis rotation matrix Rx:
XMMATRIX XM_CALLCONV XMMatrixRotationX(
    float Angle    // Clockwise angle θ to rotate
); 

// Constructs a y-axis rotation matrix Ry:
XMMATRIX XM_CALLCONV XMMatrixRotationY(
    float Angle    // Clockwise angle θ to rotate
); 

// Constructs a z-axis rotation matrix Rz:
XMMATRIX XM_CALLCONV XMMatrixRotationZ(
    float Angle    // Clockwise angle θ to rotate
); 

// Constructs an arbitrary axis rotation matrix Rn:
XMMATRIX XM_CALLCONV XMMatrixRotationAxis(
    FXMVECTOR Axis,    // Axis n to rotate about
    float Angle    // Clockwise angle θ to rotate
); 

// Constructs a translation matrix:
XMMATRIX XM_CALLCONV XMMatrixTranslation(
    // Translation factors
    float OffsetX,
    float OffsetY,
    float OffsetZ
); 

// Constructs a translation matrix from components in a vector:
XMMATRIX XM_CALLCONV XMMatrixTranslationFromVector(
    FXMVECTOR Offset    // Translation factors (tx, ty, tz)
); 

// Computes the vector-matrix product vM where vw = 1 for transforming points:
XMVECTOR XM_CALLCONV XMVector3TransformCoord(
    FXMVECTOR V,    // Input v
    CXMMATRIX M    // Input M
); 

// Computes the vector-matrix product vM where vw = 0 for transforming vectors:
XMVECTOR XM_CALLCONV XMVector3TransformNormal(
    FXMVECTOR V,    // Input v
    CXMMATRIX M    // Input M
); 

猜你喜欢

转载自blog.csdn.net/rkexy/article/details/73497955
今日推荐