我对 GLM lookAt 函数的理解不一致

Tho*_*mas 4 c++ opengl graphics vector matrix

首先,如果您想了解 GLM lookAt 算法,请查看此问题提供的答案:https ://stackoverflow.com/a/19740748/1525061

mat4x4 lookAt(vec3  const & eye, vec3  const & center, vec3  const & up)
{
vec3  f = normalize(center - eye);
vec3  u = normalize(up);
vec3  s = normalize(cross(f, u));
u = cross(s, f);

mat4x4 Result(1);
Result[0][0] = s.x;
Result[1][0] = s.y;
Result[2][0] = s.z;
Result[0][1] = u.x;
Result[1][1] = u.y;
Result[2][1] = u.z;
Result[0][2] =-f.x;
Result[1][2] =-f.y;
Result[2][2] =-f.z;
Result[3][0] =-dot(s, eye);
Result[3][1] =-dot(u, eye);
Result[3][2] = dot(f, eye);
return Result;
}
Run Code Online (Sandbox Code Playgroud)

现在我要告诉你为什么我似乎对这个算法有概念上的问题。这个视图矩阵有两部分,平移和旋转。平移进行正确的逆变换,将相机位置带到原点,而不是将原点位置带到相机。类似地,您也希望相机定义的旋转在放入此视图矩阵之前反转。我看不到这里发生这种情况,这是我的问题。

考虑前向矢量,这是您的相机所观察的位置。因此,这个前向向量需要映射到 -Z 轴,这是 openGL 使用的前向方向。这个视图矩阵的工作方式是在视图矩阵的列中创建一个正交基,所以当你在这个矩阵的右侧乘以一个顶点时,你本质上只是将它的坐标转换为不同轴的坐标.

当我在脑海中播放由于这种变换而发生的旋转时,我看到的旋转不是相机的逆旋转,就像假设发生的那样,而是我看到的非逆旋转。也就是说,我发现 -Z 轴被映射到相机前方,而不是发现相机向前映射到 -Z 轴。

如果您不明白我的意思,请考虑此处发生的相同类型事物的 2D 示例。假设前向向量是 (sqr(2)/2 , sqr(2)/2) 或 45 度的正弦/余弦,并且还假设此 2D 相机的侧向向量是 -45 度的正弦/余弦。我们想将此前向向量映射到 (0,1),即正 Y 轴。正 Y 轴可以被认为是 openGL 空间中的 -Z 轴的类比。让我们考虑一个与前向向量同方向的顶点,即 (1,1)。通过使用 GLM.lookAt 的逻辑,我们应该能够通过使用由第一列中的前向向量和第二列中的侧向向量组成的 2x2 矩阵将 (1,1) 映射到 Y 轴。http://www.wolframalpha。

请注意,您的 (1,1) 顶点并没有像您想要的那样映射到正 Y 轴,而是将它映射到正 X 轴。如果应用此变换,您还可以考虑正 Y 轴上的顶点会发生什么情况。果然,转化为前向向量。

因此,似乎 GLM 算法正在发生一些非常可疑的事情。然而,我怀疑这个算法是不正确的,因为它太流行了。我错过了什么?

use*_*587 5

看看 Mesa 中的 GLU 源代码:http : //cgit.freedesktop.org/mesa/glu/tree/src/libutil/project.c

首先在 gluPerspective 的实现中,注意-1正在使用索引[2][3]-2 * zNear * zFar / (zFar - zNear)正在使用[3][2]。这意味着索引是[column][row].

现在在执行中gluLookAt,第一行设置为side,下一行设置为,up最后一行设置为-forward。这为您提供了旋转矩阵,该矩阵后乘以将眼睛带到原点的平移。

GLM 似乎使用相同的[column][row]索引(来自代码)。并且您刚刚发布的那篇文章lookAt与更标准的gluLookAt(包括翻译部分)一致。所以至少 GLM 和 GLU 同意。

然后让我们一步一步推导出完整的构造。注意C中心位置和E眼睛位置。

  1. 移动整个场景以将眼睛位置置于原点,即应用 的平移-E

  2. 旋转场景以将相机的轴与标准(x, y, z)轴对齐。

    2.1 计算相机的正正交基:

    f = normalize(C - E) (pointing towards the center)
    s = normalize(f x u) (pointing to the right side of the eye)
    u = s x f            (pointing up)
    
    Run Code Online (Sandbox Code Playgroud)

    有了这个,(s, u, -f)是相机的正正交基。

    2.2 找到R(s, u, -f)轴与标准轴对齐的旋转矩阵(x, y, z)。逆旋转矩阵R^-1做相反的事情并将标准轴与相机轴对齐,根据定义,这意味着:

           (sx ux -fx)
    R^-1 = (sy uy -fy)
           (sz uz -fz)
    
    Run Code Online (Sandbox Code Playgroud)

    由于R^-1 = R^T,我们有:

        ( sx  sy  sz)
    R = ( ux  uy  uz)
        (-fx -fy -fz)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 将平移与旋转相结合。一个点M被“看”变换映射到R (M - E) = R M - R E = R M + t。所以“看”的最终 4x4 变换矩阵确实是:

        ( sx  sy  sz tx )   ( sx  sy  sz -s.E )
    L = ( ux  uy  uz ty ) = ( ux  uy  uz -u.E )
        (-fx -fy -fz tz )   (-fx -fy -fz  f.E )
        (  0   0   0  1 )   (  0   0   0    1 )
    
    Run Code Online (Sandbox Code Playgroud)

所以当你写:

也就是说,我发现 -Z 轴被映射到相机前方,而不是发现相机向前映射到 -Z 轴。

这是非常令人惊讶的,因为通过构造,“看”变换将相机前轴映射到 -z 轴。这种“看”变换应该被认为是移动整个场景以将相机与标准原点/轴对齐,这确实是它的作用。

使用您的 2D 示例:

通过使用 GLM.lookAt 的逻辑,我们应该能够通过使用由第一列中的前向向量和第二列中的侧向向量组成的 2x2 矩阵将 (1,1) 映射到 Y 轴。

恰恰相反,按照我描述的构造,您需要一个 2x2 矩阵,其中前向和行向量作为而不是列来映射 (1, 1) 和另一个向量到 y 和 x 轴。要使用矩阵系数的定义,您需要通过变换获得标准基向量的图像。这直接给出了矩阵的列。但是由于您正在寻找的是相反的(将您的向量映射到标准基向量),您必须反转变换(转置,因为它是一个旋转)。然后你的参考向量变成而不是列。