理解glm::lookAt()函数

72
我正在跟随一个教程学习OpenGL,他们使用了glm::lookAt()函数来构建视图,但我无法理解glm::lookAt()的工作原理,而且似乎也没有关于GLM的详细文档。有人能帮助我理解glm::lookAt()的参数和工作原理吗?
GLM文档说:
detail::tmat4x4<T> glm::gtc::matrix_transform::lookAt   
(   
    detail::tvec3< T > const &  eye,
    detail::tvec3< T > const &  center,
    detail::tvec3< T > const &  up 
)

我的理解是相机位于眼睛位置,并面向中心。(我不知道上方是什么)


8
了解视图矩阵的作用可能会有所帮助。它提供了您的观察向量空间的基础,换句话说,它定义了您坐标系统的所有轴(X、Y、Z)。您需要三个向量来完成这个过程。 up 向量明确定义了上(Y)轴,其他两个向量是根据从 eyecenter 的方向(有时称为 forward 向量或 Z 轴)以及 Z 轴和 Y 轴之间的叉积来计算出 X 轴(left 向量),该轴垂直于两个轴。除了轴之外,LookAt 还定义了原点。 - Andon M. Coleman
4个回答

92

up向量基本上定义了你的世界中的“上方”方向。在几乎所有常规情况下,这将是向正Y轴方向的向量(0, 1, 0)eye是相机视点的位置,而center则是你正在看向的位置。如果你想使用方向向量D代替中心位置,你可以简单地使用eye + D作为中心位置,其中D可以是一个单位向量。

至于内部工作原理或更多细节,这是构建视图矩阵的常见基本函数。尝试阅读gluLookAt()的文档,它具有等效的功能。


我正在跟随LearnOpenGL教程,在这个中,他使用了Up,实际上应该使用相机的上方向,而不是世界的上方向吗? - Aadhish
1
在这种情况下,相机可能会被滚动(绕其视角轴旋转),作者可能希望支持这种可能性。在典型的游戏和虚拟场景中,相机滚动通常受到限制,而俯仰和偏航是允许的。一个常见的例外是飞行/太空模拟,其中滚动是模拟和视图的重要方面。 - Preet Kukreti
文档似乎将第二个参数称为中心点,但更像第二个参数应该是一个向量,起点在眼睛处,穿过你正在观察的点 - 这意味着它不是你实际看着的点,而是一个指向那个方向的向量。我理解得对吗? - blueether

59
在这里,Up向量定义了你的3D世界中的“向上”方向(对于此相机)。例如,vec3(0, 0, 1)的值表示Z轴朝上。 Eye是虚拟3D相机所在的点。
Center是相机所看的点(场景的中心)。
理解一件事情最好的方法就是自己动手做。以下是如何使用3个向量EyeCenterUp构建相机变换的方法。
LMatrix4 LookAt( const LVector3& Eye, const LVector3& Center, const LVector3& Up )
{
    LMatrix4 Matrix;

    LVector3 X, Y, Z;

创建一个新的坐标系:
    Z = Eye - Center;
    Z.Normalize();
    Y = Up;
    X = Y.Cross( Z );

重新计算 Y = Z cross X:
    Y = Z.Cross( X );

叉积的长度等于平行四边形的面积,对于非垂直单位长度向量,其小于1.0;因此在此规范化 XY

    X.Normalize();
    Y.Normalize();

将所有内容放入结果为 4x4 的矩阵中:
    Matrix[0][0] = X.x;
    Matrix[1][0] = X.y;
    Matrix[2][0] = X.z;
    Matrix[3][0] = -X.Dot( Eye );
    Matrix[0][1] = Y.x;
    Matrix[1][1] = Y.y;
    Matrix[2][1] = Y.z;
    Matrix[3][1] = -Y.Dot( Eye );
    Matrix[0][2] = Z.x;
    Matrix[1][2] = Z.y;
    Matrix[2][2] = Z.z;
    Matrix[3][2] = -Z.Dot( Eye );
    Matrix[0][3] = 0;
    Matrix[1][3] = 0;
    Matrix[2][3] = 0;
    Matrix[3][3] = 1.0f;

    return Matrix;
}

10
虽然代码很好,但如果原帖作者不知道 lookAt 函数是如何工作以及参数的含义,那么这并没有帮助他理解任何东西。例如,“叉积给出平行四边形的面积,对于非垂直单位长度的向量来说,面积小于1.0”——这完全无关紧要,更重要的是它给出了两个操作数所对应的正交向量(其长度就是平行四边形的面积),而这正是我们需要的几何特性。但好吧,最后一部分从被点踩的命运中拯救了它。 - Christian Rau
@ChristianRau:我已经更新了我的回答,并用简单易懂的英语解释了参数的含义。将非标准化向量放入结果矩阵中会产生错误的结果。 - Sergey K.
“定义了你的3D世界中的“向上”方向。” - 是你的3D世界中的方向还是相机的方向?但没关系,它已经可以点赞了。 - Christian Rau
可能缺少的一个要点是关于为什么glm::lookAt()的返回值不仅仅是一个glm::mat4,而是一个detail::tmat4x4<T, P>的讨论。 - BitTickler
@SergeyK。叉积产生的向量垂直于另外两个向量。它产生的是一个向量,而不是标量。点积产生的是标量。面积不是向量。因此,更正确的说法是,叉积的大小等于由两个操作数形成的平行四边形的面积:||a x b|| = ||a|| ||b|| sin(theta)。 - Fox
@Fox 谢谢。已更新。 - Sergey K.

10
设置好相机(或眼睛)和目标中心)后,使相机面向目标,我们仍然可以旋转相机以获得不同的图片,这时就需要上方向向量,它可以使相机固定且无法旋转。 enter image description here

-32
detail::tmat4x4<T> glm::gtc::matrix_transform::lookAt   
(   
    detail::tvec3< T > const &  //eye position in worldspace
    detail::tvec3< T > const &  //the point where we look at
    detail::tvec3< T > const &  //the vector of upwords(your head is up)
)

这并不难,也许你需要复习一下三个坐标系: 物体(或模型)坐标系世界坐标系相机(或视图)坐标系


2
告诉一个不理解的人“这并不难”,既没有帮助,也没有激励作用。 - William Wolseley-Charles

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