[TOC]

I recently encountered a problem with a skeleton on the model because the transformation matrix of the model was not correct after scaling. It’s a little confusing, so I’ll write it down here

Matrix transformation

I have the following transformation

$M_{Local->World} = M_{Model}$

  • Test UE4

Cylinder and Cone are father and son

The transformation matrix of the cylinder

The transformation matrix of a cone

The coordinates of the cone are as follows before and after scaling 1.5

Summary:

  • You can see that the transformation matrix of the child object in the world coordinate system has scale information, and the coordinates are also scaled
  • Write a simple code and test it
XMMATRIX xmModelScale = XMMatrixScaling(1.0f, 1.0f, 1.0f); XMMATRIX xmModelTrans = XMMatrixTranslation(0.0f, 0.0f, 100.0f); XMMATRIX xmModel = XMMatrixMultiply(xmModelScale, xmModelTrans); XMFLOAT4X4 f4x4Model; XMStoreFloat4x4(&f4x4Model, xmModel); XMMATRIX xmLocalTrans = XMMatrixTranslation(0.0f, 0.0f, 200.0f); XMMATRIX xmBone = XMMatrixMultiply(xmLocalTrans, xmModel); XMFLOAT4X4 f4x4Bone; XMStoreFloat4x4(&f4x4Bone, xmBone); XmModelScale = XMMatrixScaling(1.5F, 1.5f, 1.5f); xmModel = XMMatrixMultiply(xmModelScale, xmModelTrans); xmBone = XMMatrixMultiply(xmLocalTrans, xmModel); XMStoreFloat4x4(&f4x4Bone, xmBone);Copy the code

Test results:

Wrong idea of model scaling

$M_{Local->World} $is scaled after the model is scaled. But the initial idea was to just multiply the old matrix by a new scaling matrix:

However, in the code here obviously wrong, so have not understood!

$M = SRT$

The new understanding is as follows:

  • $M=SRT$it can be seen that the transformation matrix already contains scaling, rotation and translation. And the order is important, so I’ll do that in the next section separately
  • But the way I think about it is that I’ve been transformed twice, that there’s scaling, rotation, and translation in both matrices. The most important point is that the second scaling will scale the position of the previous matrix! $M_{Model_New} = M_R*M_{Model}$ Well, not exactly, let me write it this way, two transformations! $M=SSRT$, and the next scaling matrix happens to be the identity matrix.
  • So, the general approach is to take the scaled, rotated, and shifted components, and then construct a new matrix
                      XMMATRIX matAnimActorWorld = XMMatrixTransformation(
                          g_XMZero,
                          g_XMIdentityR3,
                          g_XMOne,
                          g_XMZero,
                          XMLoadFloat4(&f4AnimActorRotation),
                          XMLoadFloat3(&f3AnimActorPosition)
                      );
    Copy the code

Rotating order

$M=SRT; $M=SRT; $M=SRT; In OpenGL, the order is reversed, but the rows and columns of the matrix are different, so I won’t go into that

The important reason mentioned in reference [2] is that these transformations are for the origin of the coordinate system!

  • DX11 inside the classic graphFigure (a) rotates first and then translates; Figure (b) moves first and then selects

  • Test yourself

      XMMATRIX skullScale = XMMatrixScaling(0.5f, 0.5f, 0.5f);
      XMMATRIX skullOffset = XMMatrixTranslation(0.0f, 1.0f, 0.0f);
      XMStoreFloat4x4(&mSkullWorld, XMMatrixMultiply(skullScale, skullOffset));
    Copy the code

    M=RT

M=TR: You can see that scaling with respect to the origin, after first shifting, the origin shrinks by half and then goes down by half

Problem solving

$M_{Model}$, $M_{BoneWorld}$, $M_{Model}$, $M_{BoneWorld}$

(1) Work out the Local matrix

(2) Reconstruct the scaled matrix, modify S in $M=SRT$to the new scaling factor

(3) Use the formula to calculate the transformation matrix of Bone in the world coordinate system

  • Code problem

$M_{World} = M_{Local1} * M_{BoneLocal} * M_{Model}$$M_{World} = M_{Local1} * M_{Model}$

XMMATRIX matWorldInverse = XMMatrixInverse(NULL, matAnimActorWorld); XMMATRIX mat = XMLoadFloat4x4(&f4x4BoneMatrix); mat = XMMatrixMultiply(mat, matWorldInverse); XMStoreFloat4x4(&f4x4BoneMatrix, mat); XMVECTOR xTrans, xScaling, xRotation; XMFLOAT3 f3Scaling; XMFLOAT4 f4Trans; XMMatrixDecompose(&xScaling, &xRotation, &xTrans, XMLoadFloat4x4(&f4x4BoneMatrix)); XMStoreFloat4((XMFLOAT4*)&f4Trans, xTrans); f4Trans.x = f4Trans.x * fRenderScale; f4Trans.y = f4Trans.y * fRenderScale; f4Trans.z = f4Trans.z * fRenderScale; XMStoreFloat4x4(&f4x4BoneMatrix, XMMatrixAffineTransformation(xScaling, g_XMZero, xRotation, XMLoadFloat4(&f4Trans))); matModelMatrix = XMMatrixMultiply(matModelMatrix, XMMatrixMultiply(XMLoadFloat4x4(&f4x4BoneMatrix), matAnimActorWorld));  XMStoreFloat4x4(&f4x4BoneMatrix, matModelMatrix);Copy the code

So parts ->Bone-> World coordinates:

$M = M1*M2=S_1R_1T_1 * S_2R_2T_2$; $M = M1*M2=S_1R_1T_1 * S_2R_2T_2$ Correct way to write it:

                    XMMatrixDecompose(&xScaling, &xRotation, &xTrans, matAnimActorWorld);
                    XMStoreFloat3((XMFLOAT3*)&f3Scaling, xScaling);
                    f3Scaling.x = f3Scaling.x * fRenderScale;
                    f3Scaling.y = f3Scaling.y * fRenderScale;
                    f3Scaling.z = f3Scaling.z * fRenderScale;
                    matAnimActorWorld = XMMatrixAffineTransformation(XMLoadFloat3(&f3Scaling), g_XMZero, xRotation, xTrans);
                    matModelMatrix = XMMatrixMultiply(matModelMatrix, XMMatrixMultiply(mat, matAnimActorWorld));
Copy the code
  • The other way of writing it
                    XMVECTOR xTrans, xScaling, xRotation;
                    XMFLOAT3 f3Scaling;
                    XMFLOAT4 f4Trans;
                    XMMatrixDecompose(&xScaling, &xRotation, &xTrans, matBone);
                    XMStoreFloat3((XMFLOAT3*)&f3Scaling, xScaling);
                    f3Scaling.x = f3Scaling.x * m_fRenderScale;
                    f3Scaling.y = f3Scaling.y * m_fRenderScale;
                    f3Scaling.z = f3Scaling.z * m_fRenderScale;

                    XMStoreFloat4((XMFLOAT4*)&f4Trans, xTrans);
                    f4Trans.x = f4Trans.x * m_fRenderScale;
                    f4Trans.y = f4Trans.y * m_fRenderScale;
                    f4Trans.z = f4Trans.z * m_fRenderScale;

                    matBone = XMMatrixAffineTransformation(XMLoadFloat3(&f3Scaling), g_XMZero, xRotation, XMLoadFloat4(&f4Trans));
Copy the code

$M = M1M2=S_1R_1T_1 * S_2R_2T_2$ So? This is true most of the time, but if the original scale of the model is not the identity matrix, such as 0.5, and you now want 1.5, it is buggy

reference

[1] A detailed explanation of the reason for the transformation order of objects (child to parent) and the transformation order in different coordinate systems

[2] Why is the conversion order important