矩阵变换
云间之龙

三维坐标系的矩阵变换

在三维坐标系中,绕 $x$ 轴的旋转可以通过旋转矩阵来表示。旋转矩阵的形式依赖于旋转的角度。对于一个角度 $ \theta $,绕 $ x $ 轴的旋转矩阵 $ R_x(\theta) $ 是:

$R_x(\theta)=\begin{bmatrix}1&0&0\0&\cos(\theta)&-\sin(\theta)\0&\sin(\theta)&\cos(\theta)\end{bmatrix}$

详细解释

  • $ R_x(\theta) $ 是绕 $ x $ 轴旋转 $ \theta $ 角度的旋转矩阵。
  • $ \cos(\theta) $$ \sin(\theta) $ 是角度 $ \theta $ 的余弦和正弦值。
  • 旋转矩阵的结构表明,旋转只会影响 $ y $ 和 $ z $ 坐标,而 $ x $ 坐标保持不变。

旋转的几何意义

假设你有一个点 $ P(x, y, z) $ 在三维空间,使用这个旋转矩阵旋转后得到的新点 $ P’(x’, y’, z’) $ 的坐标可以通过以下公式计算:

$$
\begin{bmatrix}
x’ \
y’ \
z’
\end{bmatrix} = R_x(\theta) \cdot \begin{bmatrix}
x \
y \
z
\end{bmatrix}
$$

具体的计算为:

$$
\begin{bmatrix}
x’ \
y’ \
z’
\end{bmatrix} = \begin{bmatrix}
1 & 0 & 0 \
0 & \cos(\theta) & -\sin(\theta) \
0 & \sin(\theta) & \cos(\theta)
\end{bmatrix} \cdot \begin{bmatrix}
x \
y \
z
\end{bmatrix}
$$

计算结果

通过矩阵乘法可以得到新坐标 $ P’ $:

$$
\begin{bmatrix}
x’ \
y’ \
z’
\end{bmatrix} = \begin{bmatrix}
x \
y \cos(\theta) - z \sin(\theta) \
y \sin(\theta) + z \cos(\theta)
\end{bmatrix}
$$

总结

  • 绕 $ x $ 轴的旋转矩阵是 **$ R_x(\theta) $**。
  • 该矩阵影响 $ y $ 和 $ z $ 坐标,而 $ x $ 坐标保持不变。
  • 通过矩阵乘法可以快速计算旋转后的新坐标。

旋转和平移先后顺序的区别

在使用 Eigen 库中的 Eigen::Isometry3d 进行三维变换时,变换的顺序非常重要,特别是在进行平移(pretranslate)和旋转(rotate)时。为了更好地理解这一点,我们可以将其分解为几个部分。

Eigen::Isometry3d 的构造

Eigen::Isometry3d 是一个表示三维刚体变换(包括平移和旋转)的类。它可以看作是一个 4x4 的齐次变换矩阵。使用 Eigen 可以方便地对三维向量进行变换。

平移和旋转的顺序

在使用 Eigen::Isometry3d 时,平移和旋转的顺序将决定最终的变换结果。具体来说:

  1. Pretranslate(预平移)

    • 先进行平移操作。
    • 对于输入的点 $ P $,首先进行平移变换,然后再进行旋转。
  2. Rotate(旋转)

    • 在平移后进行旋转。

因此,如果你想先平移再旋转,可以用以下方式进行操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <Eigen/Dense>
#include <iostream>

int main() {
// 创建一个 Isometry3d 对象
Eigen::Isometry3d transform = Eigen::Isometry3d::Identity();

// 定义一个平移向量
Eigen::Vector3d translation(1.0, 2.0, 3.0);
transform.pretranslate(translation); // 先平移

// 定义一个旋转角度(例如,绕 Z 轴旋转 90 度)
double angle = M_PI / 2; // 90 degrees in radians
transform.rotate(Eigen::AngleAxisd(angle, Eigen::Vector3d::UnitZ())); // 然后旋转

// 变换一个点
Eigen::Vector3d point(0.0, 0.0, 0.0);
Eigen::Vector3d transformed_point = transform * point;

std::cout << "Transformed point: " << transformed_point.transpose() << std::endl;

return 0;
}

变换顺序的影响

  • 平移后旋转的效果是:首先将点移动到新的位置,然后在新位置上进行旋转。
  • 旋转后平移的效果是:首先在原点旋转点,然后将旋转后的点平移。

例子分析

假设你有一个点 $ P(0, 0, 0) $,如果你先进行平移 $ (1, 2, 3) $ 然后旋转 $ 90^\circ $(绕 $ Z $ 轴):

  1. 预平移
    $$
    P’ = P + (1, 2, 3) = (1, 2, 3)
    $$

  2. 旋转
    旋转 $ (1, 2, 3) $ 90 度绕 $ Z $ 轴的结果为:
    $$
    P’’ = (1, 2, 3) \rightarrow (-2, 1, 3)
    $$

如果你先旋转后平移,结果会不同。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <Eigen/Dense>
#include <iostream>

int main() {
// 创建一个 Isometry3d 对象
Eigen::Isometry3d transform1 = Eigen::Isometry3d::Identity(); // 用于先旋转后平移
Eigen::Isometry3d transform2 = Eigen::Isometry3d::Identity(); // 用于先平移后旋转

// 定义一个旋转角度(例如,绕 Z 轴旋转 90 度)
double angle = M_PI / 2; // 90 degrees in radians

// 先旋转后平移
transform1.rotate(Eigen::AngleAxisd(angle, Eigen::Vector3d::UnitZ())); // 旋转
transform1.pretranslate(Eigen::Vector3d(1.0, 2.0, 3.0)); // 平移

// 先平移后旋转
transform2.pretranslate(Eigen::Vector3d(1.0, 2.0, 3.0)); // 平移
transform2.rotate(Eigen::AngleAxisd(angle, Eigen::Vector3d::UnitZ())); // 旋转

// 定义一个点
Eigen::Vector3d point(0.0, 0.0, 0.0);

// 变换点
Eigen::Vector3d transformed_point1 = transform1 * point; // 先旋转后平移
Eigen::Vector3d transformed_point2 = transform2 * point; // 先平移后旋转

// 输出结果
std::cout << "Transformed point (rotate then translate): " << transformed_point1.transpose() << std::endl;
std::cout << "Transformed point (translate then rotate): " << transformed_point2.transpose() << std::endl;

return 0;
}

总结

  • Eigen::Isometry3d 中,**pretranslate** 和 rotate 的顺序是重要的。
  • 如果你希望先平移再旋转,使用 pretranslate 后再调用 rotate
  • 变换的顺序会影响最终的结果,须根据具体需求选择顺序。

自体坐标系下变换和世界坐标系下变换的区别

在三维图形学和计算机图形学中,自体坐标系(Local Coordinate System)和世界坐标系(World Coordinate System)是两个非常重要的概念。它们在对象变换、场景建模和图形渲染中起着关键作用。下面将详细解释这两个坐标系的区别以及它们在变换中的作用。

自体坐标系(Local Coordinate System)

  • 定义:自体坐标系是指一个对象自身的坐标系。每个对象都有自己的自体坐标系,通常以对象的中心点或坐标原点为中心。
  • 变换作用:在自体坐标系下进行的变换(如旋转、平移、缩放)是相对于对象自身的。
    • 旋转:绕对象自身的轴旋转。例如,旋转一个车轮时,它会围绕自己的中心旋转。
    • 平移:移动物体的中心点。例如,将一个模型向上移动时,是相对于模型的自身坐标系进行的。
  • 优势:使用自体坐标系进行变换可以更直观地控制对象的行为,尤其是在动画和物理模拟中。

世界坐标系(World Coordinate System)

  • 定义:世界坐标系是整个场景的统一坐标系统。在这个坐标系中,所有对象的位置和方向都是相对于场景的固定坐标系来定义的。
  • 变换作用:在世界坐标系下进行的变换是相对于场景的原点进行的。
    • 旋转:绕世界坐标系的轴旋转。例如,一个行星绕太阳旋转时,它是相对于整个世界坐标系来定义的。
    • 平移:在整个场景中移动对象。例如,将一个建筑模型从一个位置移动到另一个位置时,是相对于世界坐标系的。
  • 优势:使用世界坐标系能方便地处理整个场景中的对象的相对位置和方向,特别是在多个对象之间的关系和碰撞检测中。

变换的区别

  • 自体坐标系下的变换

    • 变换操作(如旋转、缩放、平移)是相对于对象自身的坐标系进行的。
    • 典型的应用:在动画中,角色的手臂可能需要围绕肩膀旋转,而肩膀的位置是相对于角色自身的坐标系。
  • 世界坐标系下的变换

    • 变换操作是相对于整个场景的坐标系进行的。
    • 典型的应用:在虚拟场景中,移动一个物体的坐标是基于场景的原点,例如将一个桌子从一个房间移动到另一个房间。

示例

考虑一个简单的场景,其中有一个物体(例如,一个方块):

  1. 自体坐标系下的操作

    • 如果你对方块进行 90 度的旋转,方块会围绕自己的中心旋转,结果与方块的初始朝向有关。
  2. 世界坐标系下的操作

    • 如果你将方块沿 X 轴平移 5 个单位,它将从其当前位置向 X 轴正方向移动 5 个单位,而不考虑方块当前的朝向。

变换矩阵

  • 自体变换矩阵:通常用于描述对象的局部变换,例如:
    $$
    T_{\text{local}} = R_{\text{local}} \cdot S_{\text{local}} \cdot P_{\text{local}}
    $$
    其中 $ R $ 是旋转矩阵,$ S $ 是缩放矩阵,$ P $ 是平移矩阵。

  • 世界变换矩阵:描述对象在世界坐标系中的变换,通常需要将对象的自体变换与世界变换结合起来,例如:
    $$
    T_{\text{world}} = T_{\text{world}} \cdot T_{\text{local}}
    $$

在三维图形学中,进行变换时自体坐标系和世界坐标系的变换处理确实涉及到变换矩阵的左乘(或右乘),但具体的操作会有所不同。下面我们来讨论如何在这两种坐标系中进行变换,并解释矩阵乘法的顺序。

1. 自体坐标系下的变换

在自体坐标系下,变换是相对于对象自身的坐标系进行的。通常,我们会构建一个变换矩阵来描述这个对象的状态(位置、旋转、缩放等),并在自体坐标系下进行变换。

变换矩阵的构造

  • 自体变换通常是通过将旋转、缩放和平移矩阵结合在一起形成一个复合变换矩阵。这个变换矩阵是相对于对象自身的。

示例

1
2
3
Eigen::Isometry3d localTransform = Eigen::Isometry3d::Identity();
localTransform.pretranslate(Eigen::Vector3d(1.0, 2.0, 3.0)); // 平移
localTransform.rotate(Eigen::AngleAxisd(M_PI / 4, Eigen::Vector3d::UnitZ())); // 旋转

2. 世界坐标系下的变换

在世界坐标系下,变换是相对于场景的固定坐标系进行的。对象的自体变换会被应用到世界坐标系中的对象。

变换矩阵的构造

  • 在构建世界坐标系下的变换时,我们需要将对象的自体变换与世界变换结合起来。通常我们会使用左乘的方式将自体变换矩阵应用到世界变换矩阵上。

示例

1
2
3
4
5
Eigen::Isometry3d worldTransform = Eigen::Isometry3d::Identity(); // 世界变换矩阵
worldTransform.pretranslate(Eigen::Vector3d(5.0, 0.0, 0.0)); // 世界坐标系下的平移

// 将对象的自体变换应用于世界变换
Eigen::Isometry3d finalTransform = worldTransform * localTransform; // 左乘

在这里,finalTransform 是结合了世界变换和自体变换的最终变换矩阵。

矩阵乘法顺序

  • 左乘:在这种情况下,左乘是将新变换应用到已有变换的前面。也就是说,最后的变换是先进行左侧的变换,然后再进行右侧的变换。

    • 例如finalTransform = worldTransform * localTransform 表示先应用 localTransform(自体变换)然后应用 worldTransform(世界变换)。
  • 右乘:如果我们使用右乘,变换的顺序会相反,通常不常见于对象的变换。

    • 例如finalTransform = localTransform * worldTransform 表示先应用 worldTransform,后应用 localTransform,这通常不是我们希望的顺序。

3. 变换的总结

  • 自体坐标系变换:是对象相对于自身的变换,通常使用左乘自体变换矩阵。
  • 世界坐标系变换:是对象相对于整个场景的变换,通常是将自体变换矩阵左乘到世界变换矩阵上。
  • 左乘与右乘:在进行组合变换时,使用左乘可以使得新变换在前,保持变换的逻辑顺序,确保先应用某个变换。

变换示意图

假设我们有一个点 $ P $,我们可以用变换矩阵 $ T_{\text{local}} $ 来表示对象的自体变换,然后用 $ T_{\text{world}} $ 来表示世界坐标系的变换:

$$
P’ = T_{\text{world}} \cdot (T_{\text{local}} \cdot P)
$$

在这里,首先应用自体变换,然后应用世界变换。

结论

在自体坐标系和世界坐标系下增加变换时,通常都使用左乘变换矩阵。左乘的顺序决定了变换的应用顺序,确保变换的逻辑符合预期。如果你有其他问题或需要更详细的解释,请告诉我!

左乘变换矩阵和变换矩阵的逆矩阵

在三维图形学和计算机图形学中,变换矩阵的使用和乘法顺序对于场景中对象的操作至关重要。左乘变换矩阵和左乘其逆矩阵的选择依赖于你要实现的变换操作的性质。以下是一些常见场景的解释:

1. 左乘变换矩阵

左乘变换矩阵通常用于将新变换应用于当前坐标系或当前状态。这种操作会在变换应用之前改变对象的状态。

何时左乘变换矩阵:

  • 应用新的变换:当你希望将一个新的变换(如平移、旋转或缩放)应用到对象时,使用左乘变换矩阵。

    1
    2
    3
    4
    5
    Eigen::Isometry3d currentTransform = Eigen::Isometry3d::Identity();
    Eigen::Isometry3d newTransform;
    newTransform.pretranslate(Eigen::Vector3d(1.0, 0.0, 0.0)); // 定义一个新的平移变换

    currentTransform = newTransform * currentTransform; // 左乘新的变换
  • 组合变换:在处理多个变换时,左乘可以确保变换的顺序是自上而下的,即应用新变换后再应用已有的变换。

2. 左乘逆变换矩阵

左乘逆变换矩阵通常用于“撤销”先前的变换,或者将一个点从一个变换后的坐标系转换回原始坐标系。

何时左乘逆变换矩阵:

  • 撤销变换:如果你想要从经过变换的状态回到原始状态,可以使用逆变换矩阵。

    1
    2
    3
    4
    5
    Eigen::Isometry3d transform; // 已定义的变换
    Eigen::Isometry3d inverseTransform = transform.inverse(); // 计算逆变换

    Eigen::Vector3d transformedPoint; // 已经变换后的点
    Eigen::Vector3d originalPoint = inverseTransform * transformedPoint; // 撤销变换
  • 坐标系转换:当你需要将一个点从一个坐标系转换到另一个坐标系(例如,从世界坐标系转换到对象的自体坐标系)时,可以使用逆变换矩阵。

3. 实际应用示例

示例 1:应用变换

假设你有一个对象需要进行平移和旋转:

1
2
3
4
5
6
Eigen::Isometry3d transform = Eigen::Isometry3d::Identity();
transform.pretranslate(Eigen::Vector3d(1.0, 0.0, 0.0)); // 平移
transform.rotate(Eigen::AngleAxisd(M_PI / 4, Eigen::Vector3d::UnitZ())); // 旋转

Eigen::Vector3d point(0.0, 0.0, 0.0);
Eigen::Vector3d transformedPoint = transform * point; // 应用变换

示例 2:撤销变换

如果你想要回到原始坐标:

1
2
Eigen::Isometry3d inverseTransform = transform.inverse(); // 计算逆变换
Eigen::Vector3d originalPoint = inverseTransform * transformedPoint; // 撤销变换

4. 总结

  • 左乘变换矩阵

    • 用于应用新的变换。
    • 组合多个变换时,确保顺序。
  • 左乘逆变换矩阵

    • 用于撤销变换或将坐标从一个坐标系转换到另一个坐标系。
    • 常用于坐标系变换和还原到原始状态。

5. 变换的顺序

变换的顺序在图形学中非常重要,左乘的方式通常意味着“先应用新变换”,而逆变换则用于“返回原状态”。这种逻辑对于实现复杂的动画、物理模拟和变换处理至关重要。