Answers:
是的,如果需要翻译,可以添加一个向量。使用矩阵的原因归结为具有统一的方式来处理不同的组合变换。
例如,旋转通常是使用矩阵完成的(检查@MickLH注释以了解其他处理旋转的方式),因此,为了以统一的方式处理多个变换(旋转/平移/缩放/投影等),您需要将它们编码为矩阵。
好吧,从技术上来讲;转换将一个点/向量映射到另一个点/向量。
p` = T(p);
其中p`是变换点,T(p)是变换函数。
假设我们不使用矩阵,则需要这样做以组合多个转换:
p1 = T(p);
p final = M(p1);
矩阵不仅可以将多种类型的变换组合到单个矩阵中(例如,仿射,线性,射影)。
使用矩阵使我们有机会组合转换链,然后将它们成批相乘。通常通过GPU,这可以为我们节省大量的周期(感谢@ChristianRau指出了这一点)。
T 最终 = T * R * P; //翻译旋转项目
p final = T final * p;
还要指出,GPU甚至某些CPU已针对矢量操作进行了优化。设计上使用SIMD的CPU和GPU是数据驱动的并行处理器,因此使用矩阵与硬件加速非常匹配(实际上,GPU被设计为适合矩阵/矢量运算)。
翻译不能用3D矩阵表示
一个简单的论点是翻译可以采用原点向量:
0
0
0
远离原点,对x = 1
:
1
0
0
但这将需要一个矩阵,使得:
| a b c | |0| |1|
| d e f | * |0| = |0|
| g h i | |0| |0|
但这是不可能的。
另一个论点是奇异值分解定理,它说每个矩阵都可以由两个旋转和一个缩放操作组成。那里没有翻译。
为什么可以使用矩阵?
许多建模对象(例如汽车底盘)或建模对象的一部分(例如汽车轮胎,驱动轮)都是实体:顶点之间的距离永远不变。
我们要对其进行的唯一转换是旋转和平移。
矩阵乘法可以编码旋转和平移。
旋转矩阵具有明确的公式,例如:角度的2D旋转矩阵a
的形式为:
cos(a) -sin(a)
sin(a) cos(a)
对于3D有类似的公式,但请注意3D旋转需要3个参数,而不仅仅是1个。
翻译不太琐碎,将在后面讨论。这就是我们需要4D矩阵的原因。
为什么使用矩阵很酷?
因为可以通过矩阵乘法来预先计算多个矩阵的组成。
例如,如果我们要v
使用matrix 转换汽车底盘的一千个向量,T
然后使用matrix旋转R
,而不是执行以下操作:
v2 = T * v
然后:
v3 = R * v2
对于每个向量,我们可以预先计算:
RT = R * T
然后为每个顶点做一个乘法:
v3 = RT * v
甚至更好:如果我们想要相对于汽车放置轮胎和驱动轮的顶点,我们只需将先前的矩阵乘以RT
相对于汽车本身的矩阵即可。
这自然会导致维护一堆矩阵:
如何添加一维解决问题
让我们考虑一下从1D到2D的情况,这种情况更易于可视化。
一维矩阵只是一个数字,正如我们在3D中所见,它不能转换,只能缩放。
但是,如果我们将额外的维度添加为:
| 1 dx | * |x| = | x + dx |
| 0 1 | |1| | 1 |
然后我们忘记了新的额外维度,我们得到:
x + dx
如我们所愿。
这个2D转换非常重要,因此有一个名称:剪切转换。
可视化此转换很酷:
图片来源。
请注意如何平移每条水平线(固定y
)。
我们刚好y = 1
把这条线作为新的1D线,并用2D矩阵对其进行转换。
事情在3D中类似,其形式为4D剪切矩阵:
| 1 0 0 dx | | x | | x + dx |
| 0 1 0 dy | * | y | = | y + dy |
| 0 0 1 dz | | z | | z + dz |
| 0 0 0 1 | | 1 | | 1 |
我们以前的3D旋转/缩放现在具有以下形式:
| a b c 0 |
| d e f 0 |
| g h i 0 |
| 0 0 0 1 |
杰米·金(Jamie King)的视频教程也值得一看。
仿射空间
仿射空间是我们所有3D线性变换(矩阵乘法)与4D剪切(3D平移)一起生成的空间。
如果我们将剪切矩阵与3D线性变换相乘,则总会得到以下形式:
| a b c dx |
| d e f dy |
| g h i dz |
| 0 0 0 1 |
这是最通用的仿射变换,可进行3D旋转/缩放和平移。
一个重要的特性是,如果我们将2个仿射矩阵相乘:
| a b c dx | | a2 b2 c2 dx2 |
| d e f dy | * | d2 e2 f2 dy2 |
| g h i dz | | g2 h2 i2 dz2 |
| 0 0 0 1 | | 0 0 0 1 |
我们总是得到另一种形式的仿射矩阵:
| a3 b3 c3 (dx + dx2) |
| d3 e3 f3 (dy + dy2) |
| g3 h3 i3 (dz + dz2) |
| 0 0 0 1 |
数学家将此属性称为闭包,并且需要定义一个空间。
对我们来说,这意味着我们可以继续进行矩阵乘法以快乐地对最终变换进行逐个计算,这就是为什么首先使用二手矩阵而无需获得不仿射的更通用的4D线性变换的原因。
视锥投影
但是,等等,我们一直都在做一个更重要的变换:glFrustum
,它使对象再扩大2 倍,显得小2 倍。
首先在以下网址获得有关glOrtho
vs的直觉glFrustum
:https://stackoverflow.com/questions/2571402/explain-the-usage-of-glortho/36046924#36046924
glOrtho
可以只用平移+缩放来完成,但是如何glFrustum
用矩阵实现呢?
假设:
z = -1
长度为2的正方形处z = -2
如果仅允许我们使用以下类型的更通用的4个向量:
(x, y, z, w)
用w != 0
,此外,我们确定每一个(x, y, z, w)
用(x/w, y/w, z/w, 1)
,然后用矩阵截锥转型将是:
| 1 0 0 0 | | x | | x | | x / -z |
| 0 1 0 0 | * | y | = | y | identified to | y / -z |
| 0 0 1 0 | | z | | z | | -1 |
| 0 0 -1 0 | | w | | -z | | 0 |
如果我们扔掉z
,并w
在最后,我们得到:
x_proj = x / -z
y_proj = y / -z
这正是我们想要的!我们可以验证一些值,例如:
z == -1
正好在我们要投影的飞机上,x_proj == x
并且y_proj == y
。z == -2
,则x_proj = x/2
:对象为一半大小。注意glFrustum
转换不是仿射形式的:不能仅通过旋转和平移来实现。
相加w
并除以的数学“小技巧” 称为齐次坐标
另请参阅:相关的堆栈溢出问题:https : //stackoverflow.com/questions/2465116/understanding-opengl-matrices