[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