本页将介绍几何模块提供的多种处理 2D 和 3D 旋转以及射影或仿射变换的方法。
Eigen 的 Geometry
模块提供了两种不同类型的几何变换:
抽象变换,例如旋转(由角度和轴或四元数表示),平移,缩放。这些变换不是表示为矩阵,但是仍然可以在表达式中与矩阵和向量混合使用,并在需要时将它们转换为矩阵。
射影或仿射变换矩阵:请参阅 Transform 类。这些确实是矩阵。
注意:
如果你正在使用 OpenGL 4x4 矩阵,那么 Affine3f
和 Affine3d
就是你想要的。由于 Eigen 默认采用列主存储,你可以直接使用 Transform::data()
方法将变换矩阵传递给 OpenGL
。
你可以从抽象转换构造一个 Transform
,如下所示:
Transform t(AngleAxis(angle,axis));
或者,像这样:
Transform t;
t = AngleAxis(angle,axis);
但请注意,由于 C++ 的工作原理,你不能这样做:
Transform t = AngleAxis(angle,axis);
说明:
在 C++ 语言中,需要 Transform
具有来自 AngleAxis
的非显式转换构造函数,但 Eigen 不允许此处隐式转换。
变换类型
2D 旋转某个角度
Rotation2D<float> rot2(angle_in_radian);
沿某个轴 3D 旋转某个角度
AngleAxis<float> aa(angle_in_radian, Vector3f(ax,ay,az));
轴向量必须标准化。
使用四元数进行 3D 旋转
Quaternion<float> q; q = AngleAxis<float>(angle_in_radian, axis);
N 维缩放
Scaling(sx, sy)
Scaling(sx, sy, sz)
Scaling(s)
Scaling(vecN)
N 维变换
Translation<float,2>(tx, ty)
Translation<float,3>(tx, ty, tz)
Translation<float,N>(s)
Translation<float,N>(vecN)
N 维仿射变换
Transform<float,N,Affine> t = concatenation_of_any_transformations;
Transform<float,3,Affine> t = Translation3f(p) * AngleAxisf(a,axis) * Scaling(s);
N 维线性变换
Matrix<float,N> t = concatenation_of_rotations_and_scalings;
Matrix<float,2> t = Rotation2Df(a) * Scaling(s);
Matrix<float,3> t = AngleAxisf(a,axis) * Scaling(s);
纯旋转, 缩放等
关于旋转的注意:
要变换多个向量,首选的表示方法是旋转矩阵,而对于其他用途,四元数是首选的表示方法,因为它们紧凑,快速且稳定。最后,Rotation2D
和 AngleAxis
主要是用于创建其他旋转对象的便捷类型。
关于平移和缩放的注意:
与 AngleAxis
一样,这些类的设计目的是简化线性(Matrix)和仿射(Transform)变换的创建和初始化过程。然而,与 AngleAxis
不同的是,这些类效率不高,但仍然可能有其他通用且高效的算法,以输入任何类型的变换。
上述任何转换类型都可以转换为具有相同性质的任何其他类型,或更通用的类型。以下是一些其他示例:
Rotation2Df r; r = Matrix2f(..); // assumes a pure rotation matrix
AngleAxisf aa; aa = Quaternionf(..);
AngleAxisf aa; aa = Matrix3f(..); // assumes a pure rotation matrix
Matrix2f m; m = Rotation2Df(..);
Matrix3f m; m = Quaternionf(..); Matrix3f m; m = Scaling(..);
Affine3f m; m = AngleAxis3f(..); Affine3f m; m = Scaling(..);
Affine3f m; m = Translation3f(..); Affine3f m; m = Matrix3f(..);
跨变换类型的通用 API
在某种程度上,Eigen 的几何模块允许编写适用于任何变换类型的通用算法:
描述 | 代码 |
---|---|
两个变换的串联 | gen1 * gen2; |
将变换应用于向量 | vec2 = gen1 * vec1; |
获取变换的逆变换 | gen2 = gen1.inverse(); |
球面插补(仅限Rotation2D和 Quaternion ) | rot3 = rot1.slerp(alpha,rot2); |
仿射变换
通用仿射变换由Transform
类表示,其内部实际上是一个 $(Dim+1)^2$ 矩阵。在Eigen中不区分点和向量,因此所有点实际上都由从原点的位移向量表示($p≡p−0$)。考虑到这一点,当应用变换时,真实点和向量是有区别的。
将变换应用于点
VectorNf p1, p2;
p2 = t * p1;
将变换应用于向量
VectorNf vec1, vec2;
vec2 = t.linear() * vec1;
将一般变换应用到法向量
VectorNf n1, n2;
MatrixNf normalMatrix = t.linear().inverse().transpose();
n2 = (normalMatrix * n1).normalized();
将纯旋转变换应用到法向量(无缩放,无剪切)
n2 = t.linear() * n1;
OpenGL 3D 兼容性
glLoadMatrixf(t.data());
OpenGL 2D 兼容性
Affine3f aux(Affine3f::Identity());
aux.linear().topLeftCorner<2,2>() = t.linear();
aux.translation().start<2>() = t.translation();
glLoadMatrixf(aux.data());
组件访问器
对内部矩阵的完全读写访问
t.matrix() = matN1xN1; // N1 means N+1
matN1xN1 = t.matrix();
访问系数
t(i,j) = scalar; <=> t.matrix()(i,j) = scalar;
scalar = t(i,j); <=> scalar = t.matrix()(i,j);
变换部分
t.translation() = vecN;
vecN = t.translation();
线性部分
t.linear() = matNxN;
matNxN = t.linear();
提取旋转矩阵
matNxN = t.rotation();
创建变换
虽然可以通过连接基本变换来创建和更新变换对象,但 Transform
类还具有过程式 API:
Translation
// 过程式API
t.translate(Vector_(tx,ty,..));
t.pretranslate(Vector_(tx,ty,..));
// 等效的自然API
t *= Translation_(tx,ty,..);
t = Translation_(tx,ty,..) * t;
Rotation
在 2D 和过程式 API 中,any_rotation
也可以是一个以弧度表示的角
// 过程式API
t.rotate(any_rotation);
t.prerotate(any_rotation);
// 等效的自然API
t *= any_rotation;
t = any_rotation * t;
Scaling
// 过程式API
t.scale(Vector_(sx,sy,..));
t.scale(s);
t.prescale(Vector_(sx,sy,..));
t.prescale(s);
// 等效的自然API
t *= Scaling(sx,sy,..);
t *= Scaling(s);
t = Scaling(sx,sy,..) * t;
t = Scaling(s) * t;
Shear transformation(剪切变换)
仅限于 2D
// 过程式API
t.shear(sx,sy);
t.preshear(sx,sy);
请注意,在这些 API 中,任意多个变换都可以连接在单个表达式中,如以下两个等效示例所示:
t.pretranslate(..).rotate(..).translate(..).scale(..);
t = Translation_(..) * t * RotationType(..) * Translation_(..) * Scaling(..);
欧拉角
欧拉角是创建旋转对象的方便工具。由于存在24种不同的惯例,它们使用起来非常令人困惑。本例演示如何按照2-1-2惯例创建旋转矩阵。
这里官方文档没给出更多信息,如你有相关更多信息,欢迎留言。
Matrix3f m;
m = AngleAxisf(angle1, Vector3f::UnitZ())
* AngleAxisf(angle2, Vector3f::UnitY())
* AngleAxisf(angle3, Vector3f::UnitZ());