了解此转换矩阵是如何工作的



我使用SFML库的Transform组件作为代码的基础,但我不了解它是如何工作的。我们将其初始化为3x3矩阵,表示平移、旋转和缩放,但随后我们使用4x4矩阵来存储它,并对一些值进行硬编码。从那开始,我无法理解后来的所有操作。

我正在找一个能向我解释这个剧本是如何运作的人。提前感谢!

转换.h

class Transform
{
public:
static const Transform Identity;
Transform();
Transform(
float a00, float a01, float a02,
float a10, float a11, float a12,
float a20, float a21, float a22
);
const float* GetMatrix() const;
Transform GetInverse() const;
D3DXVECTOR2 TransformPoint(float x, float y) const;
D3DXVECTOR2 TransformPoint(const D3DXVECTOR2& point) const;
FloatRect TransformRect(const FloatRect& rectangle) const;
Transform& Combine(const Transform& transform);
Transform& Translate(float x, float y);
Transform& Translate(const D3DXVECTOR2& offset);
Transform& Rotate(float angle);
Transform& Rotate(float angle, float centerX, float centerY);
Transform& Rotate(float angle, const D3DXVECTOR2& center);
Transform& Scale(float scaleX, float scaleY);
Transform& Scale(float scaleX, float scaleY, float centerX, float centerY);
Transform& Scale(const D3DXVECTOR2& factors);
Transform& Scale(const D3DXVECTOR2& factors, const D3DXVECTOR2& center);
private:
float _matrix[16];
};
Transform operator *(const Transform& left, const Transform& right);
Transform& operator *=(Transform& left, const Transform& right);
D3DXVECTOR2 operator *(const Transform& left, const D3DXVECTOR2& right);
bool operator ==(const Transform& left, const Transform& right);
bool operator !=(const Transform& left, const Transform& right);

转换.cpp

const Transform Transform::Identity;
Transform::Transform()
{
_matrix[0] = 1.f; _matrix[4] = 0.f; _matrix[8] = 0.f; _matrix[12] = 0.f;
_matrix[1] = 0.f; _matrix[5] = 1.f; _matrix[9] = 0.f; _matrix[13] = 0.f;
_matrix[2] = 0.f; _matrix[6] = 0.f; _matrix[10] = 1.f; _matrix[14] = 0.f;
_matrix[3] = 0.f; _matrix[7] = 0.f; _matrix[11] = 0.f; _matrix[15] = 1.f;
}
Transform::Transform(
float a00, float a01, float a02,
float a10, float a11, float a12,
float a20, float a21, float a22
)
{
_matrix[0] = a00; _matrix[4] = a01; _matrix[8] = 0.f; _matrix[12] = a02;
_matrix[1] = a10; _matrix[5] = a11; _matrix[9] = 0.f; _matrix[13] = a12;
_matrix[2] = 0.f; _matrix[6] = 0.f; _matrix[10] = 1.f; _matrix[14] = 0.f;
_matrix[3] = a20; _matrix[7] = a21; _matrix[11] = 0.f; _matrix[15] = a22;
}
const float* Transform::GetMatrix() const
{
return _matrix;
}
Transform Transform::GetInverse() const
{
float det = _matrix[0] * (_matrix[15] * _matrix[5] - _matrix[7] * _matrix[13]) -
_matrix[1] * (_matrix[15] * _matrix[4] - _matrix[7] * _matrix[12]) +
_matrix[3] * (_matrix[13] * _matrix[4] - _matrix[5] * _matrix[12]);
if (det != 0.f)
{
return Transform((_matrix[15] * _matrix[5] - _matrix[7] * _matrix[13]) / det,
-(_matrix[15] * _matrix[4] - _matrix[7] * _matrix[12]) / det,
(_matrix[13] * _matrix[4] - _matrix[5] * _matrix[12]) / det,
-(_matrix[15] * _matrix[1] - _matrix[3] * _matrix[13]) / det,
(_matrix[15] * _matrix[0] - _matrix[3] * _matrix[12]) / det,
-(_matrix[13] * _matrix[0] - _matrix[1] * _matrix[12]) / det,
(_matrix[7] * _matrix[1] - _matrix[3] * _matrix[5]) / det,
-(_matrix[7] * _matrix[0] - _matrix[3] * _matrix[4]) / det,
(_matrix[5] * _matrix[0] - _matrix[1] * _matrix[4]) / det
);
}
else
{
return Identity;
}
}
D3DXVECTOR2 Transform::TransformPoint(float x, float y) const
{
return D3DXVECTOR2(_matrix[0] * x + _matrix[4] * y + _matrix[12],
_matrix[1] * x + _matrix[5] * y + _matrix[13]);
}
D3DXVECTOR2 Transform::TransformPoint(const D3DXVECTOR2& point) const
{
return TransformPoint(point.x, point.y);
}
FloatRect Transform::TransformRect(const FloatRect& rectangle) const
{
const D3DXVECTOR2 points[] =
{
TransformPoint(rectangle.left(), rectangle.top()),
TransformPoint(rectangle.left(), rectangle.top() + rectangle.height()),
TransformPoint(rectangle.left() + rectangle.width(), rectangle.top()),
TransformPoint(rectangle.left() + rectangle.width(), rectangle.top() + rectangle.height())
};
float left = points[0].x;
float top = points[0].y;
float right = points[0].x;
float bottom = points[0].y;
for (int i = 1; i < 4; ++i)
{
if (points[i].x < left)   left = points[i].x;
else if (points[i].x > right)  right = points[i].x;
if (points[i].y < top)    top = points[i].y;
else if (points[i].y > bottom) bottom = points[i].y;
}
return FloatRect(left, top, right - left, bottom - top);
}
Transform& Transform::Combine(const Transform& transform)
{
const float* a = _matrix;
const float* b = transform._matrix;
*this = Transform(a[0] * b[0] + a[4] * b[1] + a[12] * b[3],
a[0] * b[4] + a[4] * b[5] + a[12] * b[7],
a[0] * b[12] + a[4] * b[13] + a[12] * b[15],
a[1] * b[0] + a[5] * b[1] + a[13] * b[3],
a[1] * b[4] + a[5] * b[5] + a[13] * b[7],
a[1] * b[12] + a[5] * b[13] + a[13] * b[15],
a[3] * b[0] + a[7] * b[1] + a[15] * b[3],
a[3] * b[4] + a[7] * b[5] + a[15] * b[7],
a[3] * b[12] + a[7] * b[13] + a[15] * b[15]);
return *this;
}
Transform& Transform::Translate(float x, float y)
{
Transform translation(1, 0, x,
0, 1, y,
0, 0, 1);
return Combine(translation);
}
Transform& Transform::Translate(const D3DXVECTOR2& offset)
{
return Translate(offset.x, offset.y);
}
Transform& Transform::Rotate(float angle)
{
float rad = angle * 3.141592654f / 180.f;
float cos = std::cos(rad);
float sin = std::sin(rad);
Transform rotation(cos, -sin, 0,
sin, cos, 0,
0, 0, 1);
return Combine(rotation);
}
Transform& Transform::Rotate(float angle, float centerX, float centerY)
{
float rad = angle * 3.141592654f / 180.f;
float cos = std::cos(rad);
float sin = std::sin(rad);
Transform rotation(cos, -sin, centerX * (1 - cos) + centerY * sin,
sin, cos, centerY * (1 - cos) - centerX * sin,
0, 0, 1);
return Combine(rotation);
}
Transform& Transform::Rotate(float angle, const D3DXVECTOR2& center)
{
return Rotate(angle, center.x, center.y);
}
Transform& Transform::Scale(float scaleX, float scaleY)
{
Transform scaling(scaleX, 0, 0,
0, scaleY, 0,
0, 0, 1);
return Combine(scaling);
}
Transform& Transform::Scale(float scaleX, float scaleY, float centerX, float centerY)
{
Transform scaling(scaleX, 0, centerX * (1 - scaleX),
0, scaleY, centerY * (1 - scaleY),
0, 0, 1);
return Combine(scaling);
}
Transform& Transform::Scale(const D3DXVECTOR2& factors)
{
return Scale(factors.x, factors.y);
}
Transform& Transform::Scale(const D3DXVECTOR2& factors, const D3DXVECTOR2& center)
{
return Scale(factors.x, factors.y, center.x, center.y);
}
Transform operator *(const Transform& left, const Transform& right)
{
return Transform(left).Combine(right);
}
Transform& operator *=(Transform& left, const Transform& right)
{
return left.Combine(right);
}
D3DXVECTOR2 operator *(const Transform& left, const D3DXVECTOR2& right)
{
return left.TransformPoint(right);
}
bool operator ==(const Transform& left, const Transform& right)
{
const float* a = left.GetMatrix();
const float* b = right.GetMatrix();
return ((a[0] == b[0]) && (a[1] == b[1]) && (a[3] == b[3]) &&
(a[4] == b[4]) && (a[5] == b[5]) && (a[7] == b[7]) &&
(a[12] == b[12]) && (a[13] == b[13]) && (a[15] == b[15]));
}
bool operator !=(const Transform& left, const Transform& right)
{
return !(left == right);
}

这是一个非常宽泛的问题,因此很可能被标记为"偏离主题">

这里的关键见解是为什么3x3矩阵被编码为4x4。这是由于使用了齐次坐标。这是一个数学解决方案,不仅可以对仿射变换(如旋转、缩放和平移(进行编码,还支持常用于三维视图的投影变换。然后,您可以将所有的4x4矩阵相乘,以将所有转换连接到一个乘法中。

在齐次坐标中,要返回"3D"坐标,需要进行4向量*4x4矩阵乘法运算,然后将结果除以"w"元素,使其返回[x y z1]。

4x4齐次矩阵被组织起来,使标准的3x3矩阵嵌入其中。这就是这个ctor的作用,例如:

Transform::Transform(
float a00, float a01, float a02,
float a10, float a11, float a12,
float a20, float a21, float a22
)

这个库的矩阵在内存中的实际布局似乎有点奇怪,但这对作者来说是个问题。

在大多数平台上,4x4矩阵在内存中的排列也比3x3矩阵更好,尤其是在使用通常为2面或4面浮点矢量的SIMD运算时。一些系统具有具有隐式[0 0 1]列/行的4x3矩阵,这既提供了良好的对齐,又为仿射变换提供了一点紧凑性。

另请参阅DirectXMath和它的SimpleMath包装器,了解这种以图形为中心的数学库的另一个示例。

有许多关于3D图形的介绍性文本,它们都涵盖了使用具有4x4矩阵的齐次坐标的3D图形转换管道的基础知识。如果您有访问权限&意思是,你可能会发现得到其中一个有用。

最新更新