简体   繁体   中英

4x4 matrix reset scaling?

Using C++/OpenGL.

As an example, zooming camera at cursor position:

Vector3 target = GetScreenToWorldPosition(in_position);

Matrix4 s(MATRIX4_IDENTITY);
s.SetScale(0.12648); /// Arbitrary value for simplicity sake.

Matrix4 t(MATRIX4_IDENTITY);
t.SetTranslation(target);

Matrix4 tMinus(MATRIX4_IDENTITY);
tMinus.SetTranslation(-target);

Camera *camera = GetCurrentCamera();
Matrix4 matrix = camera->GetWorldMatrix();
matrix *= t * s * tMinus;

l_camera->SetScale(1, 1, 1);
l_camera->SetTranslation(?);
l_camera->SetRotation(?);

Is there a way to reset the scale while keeping the absolute translation/rotation?

Matrix4:
float m[16];

Row-Major:
m[00], m[01], m[02], m[03]
m[04], m[05], m[06], m[07]
m[08], m[09], m[10], m[11]
m[12], m[13], m[14], m[15]

m[00], m[04], m[08] = Cross vector
m[01], m[05], m[09] = Up vector
m[02], m[06], m[10] = Normal vector
m[12], m[13], m[14] = Translation vector

Based on rabbid76 suggestion:

Vector3 l_scale;

l_scale.x = sqrt(m[0] * m[0] + m[4] * m[4] + m[8] * m[8]);
l_scale.y = sqrt(m[1] * m[1] + m[5] * m[5] + m[9] * m[9]);
l_scale.z = sqrt(m[2] * m[2] + m[6] * m[6] + m[10] * m[10]);

m[0] /= l_scale.x;
m[4] /= l_scale.y;
m[8] /= l_scale.z;

m[1] /= l_scale.x;
m[5] /= l_scale.y;
m[9] /= l_scale.z;

m[2] /= l_scale.x;
m[6] /= l_scale.y;
m[10] /= l_scale.z;

It looks promising but so far, what used to be zooming in is now zooming out and vise-versa. Plus, there is some wobbling effects which indicates that something is not quite right.

Knowing the matrix, you can calculate the scale for each axis. In the following cm is the matrix:

float scaleX = sqrt(cm[0][0]*cm[0][0] + cm[0][1]*cm[0][1] + cm[0][2]*cm[0][2]);
float scaleY = sqrt(cm[1][0]*cm[1][0] + cm[1][1]*cm[1][1] + cm[1][2]*cm[1][2]);
float scaleZ = sqrt(cm[2][0]*cm[2][0] + cm[2][1]*cm[2][1] + cm[2][2]*cm[2][2]);

If you want to "reset" the scale while keeping the absolute translation and rotation, you need to normalize the axis. The length of a normalized vector ( Unit vector ) is 1:

for (int i = 0; i < 3; ++i)
{
    cm[0][i] /= scaleX;
    cm[1][i] /= scaleY;
    cm[2][i] /= scaleZ;
}

If the scale for the 3-axes is identical, the result for scaleX , scaleY , scaleZ will also be identically. Hence, you can tweak the code and only calculate one scale.


Don't change the translation of the matrix. The fields 12-14 contains the absolute translation. If you change it, the cube "moves".

The matrix multiplication is not commutative . If you have 2 scale matrices and a translation matrix and multiply them in the following order:

m = scale2 * translate * scale1

Then translate is scaled with scale2 , but not with scale1 . Hence, you cannot reset the translation scaling, as this information will be lost. The matrix does not save the history of its creation.

You can store the transformation as 3 vectors/matrices for translation, rotation and scale and create a matrix from them when needed:

void CreateMatrix(Matrix4 translation, Matrix4 rotation, Matrix4 scale, Matrix4& out) {
    out = scale * rotation * translation;
}

void Translate(Matrix4 translate, Matrix4& translation, Matrix4& rotation, Matrix4& scale) {
    translation *= translate;
}

void Rotate(Matrix4 rotate, Matrix4& translation, Matrix4& rotation, Matrix4& scale) {
    translation *= rotate;
    scale *= rotate;
    rotation *= rotate;
}

void Scale(Matrix4 scaling, Matrix4& translation, Matrix4& rotation, Matrix4& scale) {
    translation *= scaling;
    scale *= scaling;
}

To reset the scale, set the scale matrix to identity / the scale vector to zero.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM