在Unity中,移动(平移)、旋转、缩放的矩阵变换是通过4×4的齐次坐标矩阵实现的,这些矩阵共同构成了物体在3D空间中的变换。以下是每个部分的详细解释及其作用:
1. 平移(Translation)矩阵 #
作用: #
- 移动物体:将物体在空间中沿X、Y、Z轴方向平移,改变物体的位置。
矩阵结构: #
| 1 0 0 tx |
| 0 1 0 ty |
| 0 0 1 tz |
| 0 0 0 1 |
- 参数:
tx,ty,tz是沿X、Y、Z轴的位移量。 - 位置:平移向量位于矩阵的第四列(前三行最后一列)。
关键点: #
- 仅对点有效:平移仅对具有位置的点(
w=1)生效,对方向向量(w=0)无效。 - 变换顺序:平移通常在旋转和缩放之后应用(如复合变换
M = T * R * S)。
2. 旋转(Rotation)矩阵 #
作用: #
- 改变物体方向:绕X、Y、Z轴旋转,保持物体形状不变。
矩阵结构: #
旋转矩阵是3×3的正交矩阵,位于齐次矩阵的左上角(前3行前3列)。具体形式取决于旋转轴:
绕X轴旋转(θ角): #
| 1 0 0 0 |
| 0 cosθ -sinθ 0 |
| 0 sinθ cosθ 0 |
| 0 0 0 1 |
绕Y轴旋转(θ角): #
| cosθ 0 sinθ 0 |
| 0 1 0 0 |
| -sinθ 0 cosθ 0 |
| 0 0 0 1 |
绕Z轴旋转(θ角): #
| cosθ -sinθ 0 0 |
| sinθ cosθ 0 0 |
| 0 0 1 0 |
| 0 0 0 1 |
关键点: #
- 正交性:旋转矩阵的行/列向量是单位向量且彼此正交(确保不扭曲形状)。
- 组合旋转:多个旋转可通过矩阵相乘组合(如欧拉角
Rx * Ry * Rz)。 - Unity中的实现:Unity使用四元数(Quaternion)简化旋转操作,但底层仍通过矩阵计算。
3. 缩放(Scaling)矩阵 #
作用: #
- 改变物体尺寸:沿X、Y、Z轴缩放,控制物体的大小。
矩阵结构: #
| sx 0 0 0 |
| 0 sy 0 0 |
| 0 0 sz 0 |
| 0 0 0 1 |
- 参数:
sx,sy,sz是沿X、Y、Z轴的缩放因子。 - 位置:缩放因子位于矩阵的主对角线(左上3×3子矩阵的对角线)。
关键点: #
- 均匀/非均匀缩放:
- 均匀缩放(
sx = sy = sz):保持物体形状比例。 - 非均匀缩放(如
sx ≠ sy):可能导致物体变形(如拉伸)。
- 均匀缩放(
- 对旋转的影响:非均匀缩放会改变旋转轴的方向,需谨慎使用。
4. 齐次坐标(Homogeneous Coordinates) #
作用: #
- 统一表示变换:通过添加
w分量,将平移、旋转、缩放统一为矩阵乘法。
结构: #
齐次坐标将3D点(x, y, z)扩展为(x, y, z, 1),向量扩展为(x, y, z, 0):
- 点(
w=1):参与平移和线性变换。 - 向量(
w=0):仅参与旋转和缩放(无平移)。
关键点: #
- 矩阵乘法兼容性:齐次坐标允许将平移、旋转、缩放组合成单个4×4矩阵。
- Unity中的应用:所有变换矩阵(如
Model、View、Projection)均基于齐次坐标。
5. 复合变换(Combined Transform) #
在Unity中,物体的变换通常通过缩放→旋转→平移的顺序组合:
M_{\text{复合}} = T \times R \times S
矩阵结构: #
| Rx*x Ry*x Rz*x tx |
| Rx*y Ry*y Rz*y ty |
| Rx*z Ry*z Rz*z tz |
| 0 0 0 1 |
- 左上3×3:旋转后的缩放矩阵(
R × S)。 - 第四列:平移向量
T。 - 最后一行:始终为
(0, 0, 0, 1)。
Unity中的实现: #
- Transform组件:封装了位置(Position)、旋转(Rotation)、缩放(Scale),底层通过矩阵运算实现。
- Matrix4x4类:直接操作矩阵(如
Matrix4x4.TRS生成复合矩阵)。
6. 典型应用示例 #
(1) 移动物体: #
// 生成平移矩阵
Matrix4x4 translation = Matrix4x4.Translate(new Vector3(5, 0, 0));
Vector3 point = new Vector3(1, 2, 3);
Vector3 translatedPoint = translation.MultiplyPoint(point); // 结果(6,2,3)
(2) 旋转物体: #
// 绕Y轴旋转90度
Matrix4x4 rotation = Matrix4x4.Rotate(Quaternion.Euler(0, 90, 0));
Vector3 rotatedPoint = rotation.MultiplyPoint(new Vector3(1, 0, 0)); // 结果(0,0,-1)
(3) 缩放物体: #
// X轴缩放2倍
Matrix4x4 scale = Matrix4x4.Scale(new Vector3(2, 1, 1));
Vector3 scaledPoint = scale.MultiplyPoint(new Vector3(1, 2, 3)); // 结果(2,2,3)
(4) 复合变换: #
// 缩放→旋转→平移
Matrix4x4 transformMatrix = Matrix4x4.TRS(
position: new Vector3(5, 0, 0), // 平移
rotation: Quaternion.Euler(0, 90, 0), // 旋转
scale: new Vector3(2, 1, 1) // 缩放
);
7. 注意事项 #
- 矩阵相乘顺序:
- 右乘优先:
M = T × R × S表示先缩放,再旋转,最后平移(矩阵相乘顺序与操作顺序相反)。
- 右乘优先:
- 非均匀缩放的风险:
- 非均匀缩放会破坏旋转矩阵的正交性,可能导致法线方向错误(需重新计算法线)。
- Unity的优化:
- Unity通过四元数(Quaternion)和欧拉角(EulerAngles)封装旋转,避免直接操作旋转矩阵的复杂性。
总结 #
- 平移矩阵:控制物体位置(第四列)。
- 旋转矩阵:控制物体方向(左上3×3的正交子矩阵)。
- 缩放矩阵:控制物体尺寸(左上3×3的对角线)。
- 齐次坐标:统一平移、旋转、缩放的计算。
通过这些矩阵的组合,Unity实现了物体在3D空间中的灵活变换,开发者可通过Transform组件或直接操作矩阵实现复杂效果。
为什么是这个顺序? #
在3D图形学和Unity引擎中,变换操作的顺序必须遵循“先缩放(Scale)→再旋转(Rotation)→最后平移(Translation)”,这是因为矩阵乘法的非交换性和变换的几何意义所决定的。以下是详细解释:
1. 变换顺序的核心原因:矩阵乘法的非交换性 #
矩阵乘法不满足交换律(即 (
但实际操作顺序是先缩放、再旋转、最后平移(即从右到左读取矩阵相乘的顺序)。
2. 顺序分解:缩放 → 旋转 → 平移 #
(1) 先缩放(Scale) #
- 作用:改变物体的尺寸(沿坐标轴方向放大或缩小)。
- 关键点:
- 缩放操作不改变坐标轴的方向和原点,因此后续的旋转和平移不会受到缩放的影响。
- 如果先缩放,后续的旋转将以缩放后的坐标轴为基准(例如,缩放后旋转会以缩放后的轴方向进行旋转)。
(2) 再旋转(Rotation) #
- 作用:改变物体的方向(绕坐标轴旋转)。
- 关键点:
- 旋转会改变坐标轴的方向,但不改变原点。
- 如果旋转在缩放之后,旋转后的坐标轴方向是基于缩放后的物体尺寸(例如,旋转后的轴方向不会因后续的平移而改变)。
- 如果旋转在平移之前,旋转是围绕原点进行的,而平移后的物体位置不会被旋转影响。
(3) 最后平移(Translation) #
- 作用:移动物体到目标位置。
- 关键点:
- 平移会改变物体的原点位置,但不会改变坐标轴的方向或缩放后的尺寸。
- 如果平移在最后,它将物体从原点移动到目标位置,而缩放和旋转已经确定了物体的尺寸和方向。
3. 顺序错误的后果 #
(1) 先平移再缩放 #
- 问题:平移后的物体距离原点更远,缩放会放大或缩小平移的距离。
- 例如:先平移
(2, 0, 0),再缩放2,最终位置会是(4, 0, 0)(缩放影响了平移的距离)。 - 预期:可能希望缩放仅改变尺寸,但平移后的缩放会扭曲位置。
- 例如:先平移
(2) 先旋转再缩放 #
- 问题:旋转后的坐标轴方向可能与缩放方向不一致,导致物体沿旋转后的轴方向缩放,产生意外变形。
- 例如:先绕Y轴旋转90°,再沿X轴缩放2倍,实际会沿旋转后的X轴(原Z轴方向)缩放,导致方向错误。
(3) 先平移再旋转 #
- 问题:旋转会围绕原点进行,而平移后的物体远离原点,导致旋转中心不正确。
- 例如:先平移
(5, 0, 0),再绕原点旋转90°,物体将绕原点旋转,而非自身的中心。
- 例如:先平移
4. 为什么Unity和其他引擎遵循此顺序? #
(1) 符合数学定义 #
- 复合变换矩阵:
Unity的Transform组件默认的变换顺序是缩放→旋转→平移,其矩阵表达式为:
- 其中,(S)是缩放矩阵,(R)是旋转矩阵,(T)是平移矩阵。
- 这样,物体的变换过程是:先缩放自身,再旋转自身方向,最后平移到目标位置。
(2) 父子对象的层级关系 #
- 本地坐标系的继承:
子对象的变换是基于父对象的变换后的坐标系。例如:- 如果父对象先缩放,子对象的旋转和后续平移会基于父对象缩放后的坐标轴。
- 如果父对象先平移,子对象的缩放和旋转会基于父对象的位置,可能导致位置偏移。
(3) 避免几何错误 #
- 保持旋转中心:
默认顺序确保旋转和缩放均以物体的本地原点(通常是几何中心)为基准,避免因平移导致旋转中心偏移。
5. 实际应用中的验证 #
(1) 场景中的立方体 #
- 正确顺序(缩放→旋转→平移):
一个立方体先缩放为2x2x2,绕Y轴旋转90°,最后平移到(5, 0, 0),结果符合预期。 - 错误顺序(平移→旋转→缩放):
先平移到(5, 0, 0),再旋转会导致物体绕原点旋转(远离中心),缩放会改变平移后的距离,导致位置混乱。
(2) Unity代码示例 #
// 正确顺序:缩放→旋转→平移
transform.localScale = new Vector3(2, 2, 2); // 缩放
transform.Rotate(0, 90, 0); // 旋转(绕Y轴)
transform.position = new Vector3(5, 0, 0); // 平移
// 错误顺序:平移→旋转→缩放
transform.position = new Vector3(5, 0, 0); // 平移
transform.Rotate(0, 90, 0); // 旋转(绕原点,导致位置偏移)
transform.localScale = new Vector3(2, 2, 2); // 缩放会放大平移后的距离
6. 总结 #
| 操作顺序 | 数学意义 | 几何意义 |
|---|---|---|
| 缩放(S) | 改变坐标轴的长度(保持原点和方向) | 改变物体尺寸,但不移动或旋转 |
| 旋转(R) | 改变坐标轴的方向(保持原点和缩放后的长度) | 改变物体方向,但不改变尺寸或位置 |
| 平移(T) | 改变原点位置(保持坐标轴的方向和缩放) | 移动物体到目标位置,但不改变尺寸或方向 |
遵循此顺序的原因:
- 缩放确保后续旋转和移动的基准是物体的原始尺寸。
- 旋转基于缩放后的坐标轴方向,避免方向错误。
- 平移最后执行,确保物体移动到正确的位置,不受缩放或旋转的影响。
通过这种方式,可以精确控制物体的变换,避免因顺序错误导致的逻辑或视觉错误。
Reply by Email