【问题标题】:Understand how this Transformation Matrix works了解此转换矩阵的工作原理
【发布时间】:2020-04-28 18:45:26
【问题描述】:

我使用 SFML library 的 Transform 组件作为我的代码的基础,但我不明白它是如何工作的。我们将其初始化为 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);
}

【问题讨论】:

    标签: c++ matrix directx transform sfml


    【解决方案1】:

    这是一个非常广泛的问题,因此很可能会被标记为“离题”

    这里的关键见解是为什么将 3x3 矩阵编码为 4x4。这是由于使用了homogenous coordinates。这是一个数学解决方案,它不仅允许您对affine transformations 进行编码,例如旋转、缩放和平移,它还支持通常用于 3D 视图的投影变换。然后,您可以将所有 4x4 矩阵相乘,以将所有变换串联成一个乘法。

    在齐次坐标中返回“3D”坐标,执行 4 向量 * 4x4 矩阵相乘,然后将结果除以“w”元素以将其返回到 [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 矩阵在内存中的对齐也比 3x3 矩阵更好,尤其是在使用通常是 2 宽或 4 宽浮点向量的 SIMD 操作时。一些系统有 4x3 矩阵和隐含的 [0 0 0 1] 列/行,它们都提供了很好的对齐和更紧凑的仿射变换。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-20
      • 2012-12-11
      • 2011-02-18
      • 2018-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多