绝大部分内容来自于《Introduction to 3D Game Programming with DirectX12 Frank D. Luna》
3.变换
这里提到的变换指的是3D世界中的几何变换,这其中包括平移(translation),旋转(rotation)以及缩放(scaling),这些变换的操作大多依靠矩阵和向量的计算来完成,所以——我最近没时间看矩阵的相关东西呢……
按照书中的顺序,先要知道这次的学习目标都包括什么:
- 理解线性变换(Linear transformation)和仿射变换(Affine transformation),并且要知道如何用矩阵去表示它们。
引用:关于线性变换和仿射变换:http://www.matongxue.com/madocs/244.html#/madoc- 学会如何用坐标变换的形式来表示几何体的平移、旋转和缩放。
- 知道如何通过矩阵的乘法将多个矩阵变换转化成一个矩阵的变换(通过一个矩阵来计算这种过程)。
- 学习如何将一个坐标从一个坐标系转换到另一个坐标系中,并且如何用矩阵来表示这种变化。
- 学会使用DirectX Math类库为我们提供的各种函数,来用于构造出变换矩阵。
3.1 线性变换
3.1.1 线性变换的定义
定义函数
那么称τ函数的操作为线性变换(此处只针对三维向量考虑)。其中
这种数学概念的定义总是令人厌恶——举个例子:我们定义一个函数
我随便取一个向量值和一个常数:
则有:
但是另一个结果却并不满足线性变换的定义:
所以,此时
另外,如果 τ 是一个 线性变换,那么:
3.1.2 线性变换的矩阵表示法
令
其中,i, j, k 为三个单位向量(标准基向量)。
那么,此时的线性变换可以表示为:
然后……
(别问我怎么得到的,我也是想了好一会……它是把三维矩阵看做成三个向量,而线性变换得到的结果是也一个向量。)
此时,矩阵 A 则被称之为 线性变换的矩阵表示。
有了这个矩阵,我们就可以将任何目标向量或矩阵乘以变换矩阵,就能直接得到变换后的结果。
抛去得到变换矩阵的过程,直接给出两种变换矩阵的表现形式:
a.缩放
b.旋转
分别绕 X 轴(
3.2 仿射变换
3.2.1 齐次坐标(Homogeneous Coordinates)
对于坐标变换来说,它只能用于在“点(point)”上,而不能被用于“向量(vector)”,因为向量只是方向和数值的一种描述。那么,对于任意一个3D元组(
通过齐次坐标,让3D的元组增加一维,成为(
(x,y,z,0) 表示向量.(x,y,z,1) 表示点.为什么这么定义?而不是0代表点,1代表向量呢?
向量的w=0 能够避免在转换的过程中,向量被修改。另外,两个点的差值为一个向量,一个点与一个向量的和值为一个点,这样符合数学规律。
3.2.2 仿射变换的矩阵表示
在有些情况下,线性变换无法完成我们想要的变换结果(比如,平移),这时需要仿射变换(Affine Transformations)登场。
设平移向量为
直接给出矩阵表示:
仿射变换是通过线性变换加上一个平移向量来完成的。所以很容易看出上述的矩阵所代表的意义。
当矩阵
当矩阵
3.3 变换的结合
设想以下情况:存在3个变换矩阵
按照一般的思路来说,若要完成3次变换,只需要将点
但实际的情况是:我们往往操作的不仅仅是一个点,而是一个3D物体模型,这个模型中存在着许多的点,或许几个,或许几十个,甚至成千上万。假设一个物体
这并不是最好的方案。
别忘了,矩阵的乘法满足结合律。如果在第一步计算中,首先将3个变换矩阵化为一个矩阵,那么将减少很多不必要的计算步骤:
这样,原本30000次的操作现在仅需要近10000次计算就能完成。但是,矩阵乘法不满足交换律,切记。
3.4 坐标在不同坐标系中的转换
有时候,我们需要将同一个点在不同的坐标系下表示,这就像同一个温度用摄氏度和华氏度来表示一样。但看起来,这个问题没有意义,而实际上,这个问题就相当于一个物体在不同的世界场景中表示出来。具体细节在第五章中有所阐释。
假设点
那么,如何用数学的方式来表示这种变化?
这种变化的原理与矩阵的乘法原理相同。矩阵用来描述一种运动,而这种运动并非是点自己在运动,而是坐标系本身在运动。
如何理解矩阵的乘法?
引用:http://mp.weixin.qq.com/s/z5RSAFJcnr1amovcUrM85Q
就像引用中提到的例子一样。人坐在公交车里没有移动(参照系为公交车),但车中的人却因为车的移动而改变了位置(参照系为地球)。因为坐标系的变化而让坐标中的点发生了变化。
更多细节:《Introduction to 3D Game Programming with DirectX12 Frank D. Luna》(PDF) Page.130 - 139.
3.4.1 向量
现在开始推导向量如何在不同的坐标系中进行转换。
向量主要由两个因素决定:方向和长度,与位置无关。
现在假设有两个向量,
则向量
3.4.2 点
与向量有所不同,决定一个点的因素是“位置”。一个向量无论放在哪里,只要它的方向和长度不变,那么这个向量就不会变。而一个点呢?
如上图所示:先将点 P 通过 B 坐标系的单位向量进行转换。由于点 P 的位置发生了变化,所以再利用向量Q将点P的位置纠正过来。因此:
其中,点 P 在原坐标系 A 的表示为:
上述过程已知了在二维坐标系下的表示,同理可以得到三维坐标系下向量和点的不同坐标系下的变换:
(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
);