gluLookAt和glFrustum与移动物体的应用

3

原问题/代码

我正在微调一个3D对象的渲染,并尝试使用gluLookAt实现相机跟随对象,因为一旦对象达到最大高度,其中心y位置会不断增加。以下是我设置模型视图和投影矩阵的代码部分:

float diam = std::max(_framesize, _maxNumRows);
float centerX = _framesize / 2.0f;
float centerY = _maxNumRows / 2.0f + _cameraOffset;
float centerZ = 0.0f;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(centerX - diam,
          centerX + diam,
          centerY - diam,
          centerY + diam,
          diam,
          40 * diam);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0., 0., 2. * diam, centerX, centerY, centerZ, 0, 1.0, 0.0);

目前,该对象显示得非常遥远,并且似乎向屏幕后方(-z)和向下(-y)移动,直到最终消失。

我做错了什么?我如何使我的表面出现在屏幕中央,占用整个视图,并且随着对象的更新而使相机移动?


已更新的代码和当前问题

这是我的当前代码,现在将对象放置在屏幕正中央并填充我的窗口。

float diam = std::max(_framesize, _maxNumRows);
float centerX = _framesize / 2.0f;
float centerY = _maxNumRows / 2.0f + _cameraOffset;
float centerZ = 0.0f;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(centerX - diam,
          centerX,
          centerY - diam,
          centerY,
          1.0,
          1.0 +  4 * diam);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(centerX, _cameraOffset, diam, centerX, centerY, centerZ, 0, 1.0, 0.0);

当被查看的对象开始移动时,我仍然有一个问题,它不能保持完全居中。当更新时,它似乎会抖动一个像素,然后向下抖动两个像素。最终,对象离开了当前视图。如何解决这个抖动问题?

3个回答

5
你的问题在于理解投影的作用,对于你的情况而言是glFrustum函数。我认为最好的方法是通过一张图片(我手工画的)来解释glFrustum。你从一个称为“眼空间”的空间开始。这是经过模型视图矩阵变换后,你的顶点所处的空间。这个空间需要被变换到称为“标准化设备坐标”空间的空间中。这是一个双重过程:
1. 投影(矩阵)将“眼空间”变换为“裁剪空间” 2. 进行透视除法 {X,Y,Z} = {x,y,z}/w,将其带入“标准化设备坐标”空间。
这样做的可见效果是OpenGL的一种“镜头”效果。在下面的图片中,可以看到一个绿色突出显示的区域(技术上是三个体积),它是NDC空间反向投影到眼空间中的结果。在上面的情况下,对称的棱台效果被展示,即left = -righttop = -bottom。在下面的图片中,展示了一个非对称的棱台,即left ≠ -righttop ≠ -bottom
请注意,应用这样的不对称性(通过中心偏移)不会旋转棱台,而是使其倾斜。然而,“相机”仍将位于原点,并且仍然指向-Z轴。当然,图像投影的中心将发生变化,但这不是你想要的情况。
这样倾斜棱台有着应用价值。最重要的是,这是在立体渲染设置中实现左右眼不同视角的正确方法。

3
尼古拉斯·博拉斯的答案已经解释了你做错了什么,所以我就不重复了。你寻求的是解决方案而不是告诉你哪里出了问题,所以让我们直接进入主题。
这是我用于投影矩阵的代码:
glViewport(0, 0, mySize.x, mySize.y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fovy, (float)mySize.x/(float)mySize.y, nearPlane, farPlane);

一些描述它的词语: glViewport设置了openGL在窗口内显示区域的大小和位置。我通常会将其用于投影更新,但不知道为什么。如果您像我一样使用它,其中mySize是指定窗口尺寸的2D向量,则openGL渲染区域将占据整个窗口。接下来的两个调用应该很熟悉,最后是gluPerspective。第一个参数是您在Y轴上的“视野”。它以度数表示您将看到多少,并且我从未使用过除45之外的任何其他值。它可以用于缩放,但我更喜欢将其留给相机操作。第二个参数是aspect。如果您渲染正方形而窗口大小不是1:1的比例,则它将仍然是正方形。第三个参数是近裁剪平面,距离相机比这更近的几何体将不会被渲染,与farPlane相同,但恰恰相反,它设置了几何体被渲染的最大距离。
这是模型视图矩阵的代码。
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(  camera.GetEye().x,camera.GetEye().y,camera.GetEye().z,
camera.GetLookAt().x,camera.GetLookAt().y,camera.GetLookAt().z,
camera.GetUp().x,camera.GetUp().y,camera.GetUp().z);

再次提醒一件事:你可以使用前两个函数调用,因此我们跳到gluLookAt。我有一个相机类来处理所有的移动、旋转等操作。Eye、LookAt和Up是三维向量,这三个向量就是相机的全部属性。Eye表示相机的位置,即它在空间中的位置。LookAt表示你正在查看的对象的位置,或者更好地说,你正在查看的3D空间中的点,因为它可以是任何位置,不仅仅是中心对象。如果你担心什么是Up向量,那很简单。它是垂直于向量(LookAt-Eye)的向量,但是由于这样的向量有无数个,所以必须指定一个。如果你的相机位于(0,0,0),并且你正在查看(0,0,-1),并且想要站立,那么Up向量将是(0,1,0)。如果你想要倒立,使用(0,-1,0)。如果你不明白,请在评论中写下来。
由于你没有任何相机类,你需要自己分别存储这三个向量。我相信你有像你正在移动的3D对象的中心这样的东西。在每次更新后将其位置设置为LookAt。还在初始化阶段(当你制作3D对象时)选择相机的位置和up向量。在每次更新对象位置后,以相同的方式更新相机位置。如果你将你的物体向Y轴上移动1个点,请对相机位置进行同样的操作。如果你不想旋转相机,则Up向量保持不变。在每次这样的更新后,使用更新后的向量调用gluLookAt
对于更新后的帖子: 我没有真正理解发生了什么,因为没有更大的参考框架(但我也不想知道)。有几件事让我感到好奇。如果中心是存储你要移动的物体位置的3D向量,为什么你要将该对象的中心设置为窗口右上角?如果它是中心,你应该在glOrtho的第二个和第四个参数中也加上+diam,并且如果这样做会出现问题,那么你可能在变量名称上使用错误的名称或在此之前做了一些错误的操作。你在更新后正确设置了LookAt的位置,但我不明白为什么要使用那些参数作为Eye的参数。你应该有类似于:centerX,centerY,centerZ-diam作为gluLookAt的前三个参数。这样可以让你的相机与你的物体在相同的X和Y位置,但你将从距离diam的Z轴上望着它。

好的,看起来我的最新迭代非常接近这个问题,我的理解并没有像我想象的那样偏差。我正在更新我的问题,展示我最近的代码和我目前遇到的唯一问题。 - Corey D
gluLookAt的第二个参数更改后,解决了物体移出画面的问题,但它仍然会上下抖动1像素,尽管我期望这种效果在运行真实数据时会消失。 - Corey D
@Corey D 好的,我很高兴。至于你所说的抖动 - 不要忘记,在图形卡内工作时,理论上可以有最小浮点值的分辨率,但你的对象必须以某种方式映射到显示器屏幕的像素上。这可能是在更新之间舍入那些精细浮点值的时刻,你看到的是抖动,但实际上它正在尽可能精确地在屏幕上显示你的对象。 - Raven

2
glFrustum生成的透视投影矩阵定义了一个相机空间(作为输入的顶点空间),其中相机位于原点。您正在尝试创建一个相机不在原点的透视矩阵。 glFrustum无法做到这一点,所以您尝试的方法根本行不通。
有方法可以生成一个相机不在原点的透视矩阵。但实际上没有必要这样做。
通常处理移动相机的方法是简单地添加从世界空间到透视投影的相机空间的变换。这只是将世界旋转和平移以相对于相机。这就是gluLookAt的工作。但您提供给它的参数也是错误的。
前三个值是相机的世界空间位置。接下来的三个值应该是相机所看向的世界空间位置(对象的位置)。

你能详细说明一下吗?你的回答并没有告诉我太多我不知道的(我的代码无法运行)。我已经离我的目标更近了一点,但是当对象被更新时,它仍然离开了我的裁剪平面。 - Corey D

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