嗯,该库提供的 lookAt 代码就是这样(我将保留实际源代码,只保留 cmets,因为它们很好地解释了已完成的步骤):
public final static void lookAt(Vector3f position, Vector3f centre, Vector3f up, Matrix4f dest) {
// Compute direction from position to lookAt
// Normalize direction
// Normalize up
// right = direction x up
// up = right x direction
// Set matrix elements
}
而这段代码只是错误。有趣的是,我以前见过这个错误。实际上,"official" gluLookAt() manpage 仍然包含相同的错误(实际的 glu 实现没有错误,只是文档有误)。
这段代码所做的是建立一个正交基。问题是向上向量在计算right 的叉积之前在 之前被归一化。假设似乎是在构建两个单位长度向量的叉积时,结果也将是一个单位长度向量。但这是一个普遍的误解。真正成立的只是:
length( cross( a, b) ) == lenght(a) * length(b) * sin(alpha)
其中 alpha 是a 和b 之间的角度。因此,单位长度假设仅在向量已经正交时才成立。由于向量在叉积之后永远不会重新归一化,因此得到的基不是正交的,而是会引入一些非均匀缩放。 lookAt 假设可以通过转置矩阵计算逆旋转,在这种情况下将完全失败。
当观察方向和向上矢量之间的角度偏离 90 度时,您看到的失真会变得更加严重。
处理这个问题的正确方法是在不同的点进行标准化。不要在叉积之前对上向量进行归一化,而是对其结果进行归一化。然后,您有两个彼此正交的单位长度向量,并且第二个叉积也将按预期工作。所以实际的lookAt函数应该是:
// Compute direction from position to lookAt
// Normalize direction
// right = direction x up
// Normalize right
// up = right x direction
// Set matrix elements