了解此转换矩阵的工作原理
Understand how this Transformation Matrix works
我正在使用 SFML library 的转换组件作为我的代码的基础,但我不明白它是如何工作的。我们将其初始化为 3x3 矩阵,代表平移、旋转和缩放,但随后我们使用 4x4 矩阵来存储它,并使用一些硬编码的值。从那开始,后面的操作我都看不懂
我正在寻找可以向我解释此脚本如何工作的人。提前致谢!
Transform.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);
Transform.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);
}
This is a very broad question, so this is likely to be marked as 'off-topic'
这里的关键见解是为什么将 3x3 矩阵编码为 4x4。这是由于 homogenous coordinates. This is a mathematical solution that allows you to not only encode affine transformations 的使用,例如旋转、缩放和平移,它还支持 3D 视图常用的投影变换。然后,您可以将所有 4x4 矩阵相乘,将所有变换连接成一个乘法。
In homogenous coordinates to get back to '3D' coordinates, you do a 4-vector * 4x4-matrix multiply and then divide the result by the 'w' element to get it back to [x y z 1].
组织 4x4 齐次矩阵,以便将标准 3x3 矩阵嵌入其中。这就是这个 ctor 所做的,例如:
Transform::Transform(
float a00, float a01, float a02,
float a10, float a11, float a12,
float a20, float a21, float a22
)
这个库的矩阵在内存中的实际布局似乎有点奇怪,但这是作者的问题。
4x4 matrices also align better in memory on most platforms than 3x3 matrices, particularly when using SIMD operations that are usually 2-wide or 4-wide float vectors. Some systems have 4x3 matrices with an implicit [0 0 0 1] column/row which both provide nice alignment and a little more compact for affine transformations.
另请参阅 DirectXMath and it's SimpleMath 包装器,了解这种以图形为中心的数学库样式的另一个示例。
There are numerous introductory texts on 3D graphics that all cover the basics of the 3D graphics transformation pipeline using homogenous coordinates with 4x4 matrices. If you have the access & means, you may find getting one of them useful.
我正在使用 SFML library 的转换组件作为我的代码的基础,但我不明白它是如何工作的。我们将其初始化为 3x3 矩阵,代表平移、旋转和缩放,但随后我们使用 4x4 矩阵来存储它,并使用一些硬编码的值。从那开始,后面的操作我都看不懂
我正在寻找可以向我解释此脚本如何工作的人。提前致谢!
Transform.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);
Transform.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);
}
This is a very broad question, so this is likely to be marked as 'off-topic'
这里的关键见解是为什么将 3x3 矩阵编码为 4x4。这是由于 homogenous coordinates. This is a mathematical solution that allows you to not only encode affine transformations 的使用,例如旋转、缩放和平移,它还支持 3D 视图常用的投影变换。然后,您可以将所有 4x4 矩阵相乘,将所有变换连接成一个乘法。
In homogenous coordinates to get back to '3D' coordinates, you do a 4-vector * 4x4-matrix multiply and then divide the result by the 'w' element to get it back to [x y z 1].
组织 4x4 齐次矩阵,以便将标准 3x3 矩阵嵌入其中。这就是这个 ctor 所做的,例如:
Transform::Transform(
float a00, float a01, float a02,
float a10, float a11, float a12,
float a20, float a21, float a22
)
这个库的矩阵在内存中的实际布局似乎有点奇怪,但这是作者的问题。
4x4 matrices also align better in memory on most platforms than 3x3 matrices, particularly when using SIMD operations that are usually 2-wide or 4-wide float vectors. Some systems have 4x3 matrices with an implicit [0 0 0 1] column/row which both provide nice alignment and a little more compact for affine transformations.
另请参阅 DirectXMath and it's SimpleMath 包装器,了解这种以图形为中心的数学库样式的另一个示例。
There are numerous introductory texts on 3D graphics that all cover the basics of the 3D graphics transformation pipeline using homogenous coordinates with 4x4 matrices. If you have the access & means, you may find getting one of them useful.