4x4矩阵重置缩放?

5

使用C++/OpenGL。

以缩放相机为例,使其始终对准光标位置:

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(?);

有没有一种方法可以在保持绝对平移/旋转的同时重置比例尺?

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

根据rabbid76的建议:

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;

看起来很有前途,但到目前为止,曾经的放大现在是缩小,反之亦然。此外,还存在一些晃动效果,表明某些地方不太对劲。


1
Matrix4 是哪种数据类型? - Rabbid76
就像我之前提到的那样,重置比例后,转换并不完全相同。有发现任何异常情况吗? - neosettler
“重置比例不应该移动立方体。” 是的,我知道。m[12] /= l_scale.x; m[13] /= l_scale.y;, m[14] /= l_scale.z; 这部分是错误的。将其删除,它就可以正常工作了。字段12-14包含绝对平移量,如果更改它,立方体就会“移动”。 - Rabbid76
我不明白。问题已经得到解答。你不能更改问题。如果你有新的问题,那么请提出公开问题。无论如何,如果你想要缩放翻译,你必须执行以下操作:t = l_matrix.GetScale() * l_matrix.GetTranslation()。矩阵乘法不满足交换律。 - Rabbid76
非常好。问题已经得到解答,但我的问题仍然存在。我会像您建议的那样提出另一个问题。我想混淆是因为l_camera->SetTranslation(?)需要一个向量3而不是矩阵。我还在努力探索,感谢您的贡献。 - neosettler
显示剩余8条评论
2个回答

3

了解矩阵后,您可以计算每个轴的比例。下面的cm是矩阵:

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]);

如果您想在保持绝对平移和旋转的同时“重置”比例尺,您需要将轴归一化。 归一化向量(单位向量)的长度为1:
for (int i = 0; i < 3; ++i)
{
    cm[0][i] /= scaleX;
    cm[1][i] /= scaleY;
    cm[2][i] /= scaleZ;
}

如果三个轴的比例相同,scaleXscaleYscaleZ的结果也将相同。因此,您可以调整代码并仅计算一个比例。
不要更改矩阵的翻译。字段12-14包含绝对翻译值。如果更改,立方体将“移动”。
矩阵乘法不是可交换的。如果您有2个比例矩阵和一个平移矩阵,并按以下顺序相乘:
m = scale2 * translate * scale1

然后,translate 会与 scale2 缩放,但不会与 scale1 缩放。因此,您无法重置翻译缩放,因为此信息将丢失。矩阵不保存其创建历史记录。

在这种情况下,translate将与scale1一起缩放,但不会与scale2一起缩放。 - GDavid

0

你可以将变换存储为3个向量/矩阵,分别用于平移、旋转和缩放,并在需要时从它们创建一个矩阵:

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;
}

要重置比例,将比例矩阵设置为单位矩阵/比例向量设置为零。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接