LookAt 矩阵在向上或向下看时扭曲

LookAt matrix distorts when looking up or down

我正在使用一个在我为 LWJGL 找到的名为 JOML 的开源数学库中计算的 lookAt 矩阵,用于我的游戏中的免费 cam。它在左右旋转时效果很好,但向上和向下看似乎会导致类似于大幅增加 FOV 的重大扭曲问题。

向前看:

但是抬头的时候:

低头时:

我没能找到有类似错误的人,也没有人使用 JOML 报告过这个问题。我不是最擅长矩阵数学的,所以我所有计算自己的 lookAt 矩阵的尝试都失败了。 如果有人可以使用 JOML 制作 lookAt 矩阵,或者说出我(最有可能)可能出现的任何一个错误,将不胜感激,谢谢。

好吧,该库提供的 lookAt 代码就是这样(我将实际的源代码留在外面,只保留注释,因为它们很好地解释了完成的步骤):

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 是 ab 之间的角度。因此,单位长度假设仅在向量已经正交 时才成立。由于向量在叉积后从未重新归一化,因此生成的基础不是正交的,但会引入一些非均匀缩放。 lookAt 假设可以通过转置矩阵计算逆旋转,在这种情况下将完全失败。

当观察方向和向上矢量之间的角度偏离 90 度时,您看到的失真会变得更严重。

处理这个问题的正确方法只是在不同的点进行规范化。不要在叉积之前对向上向量进行归一化,而是对其结果进行归一化。然后,您有两个相互正交的单位长度向量,第二个叉积也将按预期工作。所以实际的 lookAt 函数应该是:

        // Compute direction from position to lookAt
        // Normalize direction
        // right = direction x up
        // Normalize right
        // up = right x direction
        // Set matrix elements