【问题标题】:OpenGL Object rotation using your own Matrix class in C++在 C++ 中使用您自己的 Matrix 类进行 OpenGL 对象旋转
【发布时间】:2019-01-11 03:45:22
【问题描述】:

我正在使用 OpenGL,我决定做的一件事是创建自己的 Matrix 类,而不是使用 glm 的矩阵。 Matrix 类具有平移、旋转和缩放对象的方法,如下所示:

Matrix4 Matrix4::translate(Matrix4& matrix, Vector3& translation)
{
     Vector4 result(translation, 1.0f);

     result.multiply(matrix);

     matrix.mElements[3 * 4 + 0] = result.x;
     matrix.mElements[3 * 4 + 1] = result.y;
     matrix.mElements[3 * 4 + 2] = result.z;

     return matrix; 
}

Matrix4 Matrix4::rotate(Matrix4& matrix, float angle, Vector3& axis)
{
    if (axis.x == 0 && axis.y == 0 && axis.z == 0)
        return matrix;

    float r = angle;
    float s = sin(r);
    float c = cos(r);
    float omc = 1.0f - cos(r);

    float x = axis.x;
    float y = axis.y;
    float z = axis.z;

    matrix.mElements[0 + 0 * 4] = c + x * x * omc;
    matrix.mElements[1 + 0 * 4] = x * y * omc - z * s;
    matrix.mElements[2 + 0 * 4] = z * x * omc + y * s;

    matrix.mElements[0 + 1 * 4] = x * y * omc + z * s;
    matrix.mElements[1 + 1 * 4] = c + y * y * omc;
    matrix.mElements[2 + 1 * 4] = z * y * omc - x * s;

    matrix.mElements[0 + 2 * 4] = x * z * omc - y * s;
    matrix.mElements[1 + 2 * 4] = y * z * omc + x * s;
    matrix.mElements[2 + 2 * 4] = c + z * z * omc;

    return matrix;
}

Matrix4 Matrix4::scale(Matrix4& matrix, Vector3& scaler)
{

    matrix.mElements[0 + 0 * 4] *= scaler.x;
    matrix.mElements[1 + 0 * 4] *= scaler.x;
    matrix.mElements[2 + 0 * 4] *= scaler.x;

    matrix.mElements[0 + 1 * 4] *= scaler.y;
    matrix.mElements[1 + 1 * 4] *= scaler.y;
    matrix.mElements[2 + 1 * 4] *= scaler.y;

    matrix.mElements[0 + 2 * 4] *= scaler.z;
    matrix.mElements[1 + 2 * 4] *= scaler.z;
    matrix.mElements[2 + 2 * 4] *= scaler.z;

    matrix.mElements[3 + 3 * 4] = 1;

    return matrix;
}

当我在 while 循环中调用 translate、rotate 和 scale 方法时(按此特定顺序),它会执行我想要的操作,即平移对象,然后围绕其本地原点旋转并缩放它。但是,当我想切换顺序所以我先调用旋转然后平移时,我希望它这样做:

但我的代码并没有这样做。相反,它这样做:

我该怎么做才能让我的对象只围绕屏幕中心旋转,而不是围绕它的本地原点旋转? 我唯一的猜测是我在转换矩阵上添加旋转计算时做错了,但我仍然不知道它是什么。

编辑:我需要指出的一件事是,如果我忽略了旋转方法,而我只处理平移和缩放,它们会按照我期望的方式进行,首先是平移,其次是旋转,然后是旋转,其次是平移顺序。

编辑 2:这是我在 while 循环中调用这些函数的方式。

Matrix4 trans = Matrix4(1.0f);
trans = Matrix4::rotate(trans, (float)glfwGetTime(), Vector3(0.0f, 0.0f, 1.0f));
trans = Matrix4::translate(trans, Vector3(0.5f, -0.5f, 0.0f));
trans = Matrix4::scale(trans, Vector3(0.5f, 0.5f, 1.0f));

shader.setUniformMatrix4f("uTransform", trans);

【问题讨论】:

  • 您是否尝试过将最原始的操作写成矩阵,然后手动将它们相乘以获得结果矩阵?直接输入很容易出错。顺便说一句,您使用 3D 坐标(“z 轴”)但您只有 3x3 矩阵 - 为什么不是齐次坐标?没有这些你怎么做?
  • 我不认为你的矩阵乘法是正确的。一般来说,旋转和缩放也会修改存储平移的元素。
  • @Aziuth,是的,我已经尝试过两个矩阵的简单乘法,我知道我的矩阵乘法方法是正确的。
  • @BDL,我确保所有的平移、旋转和缩放计算都存储在一个矩阵中。
  • 添加主代码,调用这两个函数的地方。

标签: c++ opengl matrix transformation coordinate-transformation


【解决方案1】:

您必须通过矩阵乘法来连接矩阵。

矩阵乘法C = A * B 的工作方式如下:

Matrix4x4 A, B, C;

// C = A * B
for ( int k = 0; k < 4; ++ k )
    for ( int j = 0; j < 4; ++ j )
        C[k][j] = A[0][j] * B[k][0] + A[1][j] * B[k][1] + A[2][j] * B[k][2] +  A[3][j] * B[k][3];

我建议以某种方式创建指定矩阵类:

#include <array>

class Matrix4
{
public:

    std::array<float, 16> mElements{
      1, 0, 0, 0, 
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1 };

    const float * dataPtr( void ) const { return mElements.data(); }

    Matrix4 & multiply( const Matrix4 &mat );
    Matrix4 & translate( const Vector3 &translation );
    Matrix4 & scale( const Vector3 &scaler );
    Matrix4 & rotate( float angle, const Vector3 &axis );
};

实现矩阵乘法。请注意,您必须将结果存储在缓冲区中。 如果您将结果直接写回矩阵成员,那么您将更改元素,稍后将在嵌套循环中再次读取,结果将不正确:

Matrix4& Matrix4::multiply( const Matrix4 &mat )
{
    // multiply the existing matrix by the new and store the result in a buffer
    const float           *A = dataPtr();
    const float           *B = mat.dataPtr();
    std::array<float, 16>  C;

    for ( int k = 0; k < 4; ++ k ) {
        for ( int j = 0; j < 4; ++ j ) {
            C[k*4+j] =
                A[0*4+j] * B[k*4+0] +
                A[1*4+j] * B[k*4+1] +
                A[2*4+j] * B[k*4+2] +
                A[3*4+j] * B[k*4+3];
        }
    }

    // copy the buffer to the attribute
    mElements = C;

    return *this;
}

像这样调整平移、旋转和缩放的方法:

Matrix4 & Matrix4::translate( const Vector3 &translation )
{
     float x = translation.x;
     float y = translation.y;
     float z = translation.z;

     Matrix4 transMat;
     transMat.mElements = {
          1.0f, 0.0f, 0.0f, 0.0f,
          0.0f, 1.0f, 0.0f, 0.0f,
          0.0f, 0.0f, 1.0f, 0.0f,
          x,    y,    z,    1.0f };

     return multiply(transMat);
}

Matrix4 & Matrix4::rotate( float angle, const Vector3 &axis )
{
    float x = axis.x;
    float y = axis.y;
    float z = axis.z;

    float c = cos(angle);
    float s = sin(angle);

    Matrix4 rotationMat;
    rotationMat.mElements = {
       x*x*(1.0f-c)+c,   x*y*(1.0f-c)-z*s, x*z*(1.0f-c)+y*s, 0.0f,
       y*x*(1.0f-c)+z*s, y*y*(1.0f-c)+c,   y*z*(1.0f-c)-x*s, 0.0f,
       z*x*(1.0f-c)-y*s, z*y*(1.0f-c)+x*s, z*z*(1.0f-c)+c,   0.0f,
       0.0f,             0.0f,             0.0f,             1.0f };

    return multiply(rotationMat);
}

Matrix4 & Matrix4::scale( const Vector3 &scaler )
{
    float x = scaler.x;
    float y = scaler.y;
    float z = scaler.z;

    Matrix4 scaleMat;
    scaleMat.mElements = {
        x,    0.0f, 0.0f, 0.0f,
        0.0f, y,    0.0f, 0.0f,
        0.0f, 0.0f, z,    0.0f,
        0.0f, 0.0f, 0.0f, 1.0f };

    return multiply(scaleMat);
}

如果你像这样使用矩阵类,

float   angle_radians = ....;
Vector3 scaleVec{ 0.2f, 0.2f, 0.2f };
Vector3 transVec{ 0.3f, 0.3f, 0.0f };
Vector3 rotateVec{ 0.0f, 0.0f, 1.0f };

Matrix4 model;
model.rotate( angle_rad, rotateVec );
model.translate( transVec );
model.scale( scaleVec );

那么结果将如下所示:

【讨论】:

    【解决方案2】:

    函数rotate() 没有执行实际的旋转。仅生成部分旋转矩阵,并将其覆盖在原始矩阵上。
    您需要构造一个完整的矩阵并将其与原始矩阵相乘。

    Matrix4 Matrix4::rotate(const Matrix4& matrix, float angle, const Vector3& axis)
    {
        if (axis.x == 0 && axis.y == 0 && axis.z == 0)
            return matrix;
    
        float r = angle;
        float s = sin(r);
        float c = cos(r);
        float omc = 1.0f - cos(r);
    
        float x = axis.x;
        float y = axis.y;
        float z = axis.z;
    
        Matrix4 r;
    
        r.mElements[0 + 0 * 4] = c + x * x * omc;
        r.mElements[1 + 0 * 4] = x * y * omc - z * s;
        r.mElements[2 + 0 * 4] = z * x * omc + y * s;
        r.mElements[3 + 0 * 4] = 0;
    
        r.mElements[0 + 1 * 4] = x * y * omc + z * s;
        r.mElements[1 + 1 * 4] = c + y * y * omc;
        r.mElements[2 + 1 * 4] = z * y * omc - x * s;
        r.mElements[3 + 1 * 4] = 0;
    
        r.mElements[0 + 2 * 4] = x * z * omc - y * s;
        r.mElements[1 + 2 * 4] = y * z * omc + x * s;
        r.mElements[2 + 2 * 4] = c + z * z * omc;
        r.mElements[3 + 2 * 4] = 0;
    
        r.mElements[0 + 3 * 4] = 0;
        r.mElements[1 + 3 * 4] = 0;
        r.mElements[2 + 3 * 4] = 0;
        r.mElements[3 + 3 * 4] = 1;
    
        return r * matrix;
    }
    

    【讨论】:

    • 我之前试过这个,但是对象根本没有被渲染。
    猜你喜欢
    • 2014-12-05
    • 1970-01-01
    • 2019-08-17
    • 1970-01-01
    • 2020-05-02
    • 1970-01-01
    • 2016-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多