gluLookAt 和 glFrustum 与移动物体
原始问题/代码
我正在微调 3D 对象的渲染,并尝试使用 gluLookAt
实现跟随对象的相机,因为对象的中心 y 位置一旦达到这是最大高度。下面是我设置 ModelView 和投影矩阵的代码部分:
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);
当被观察的物体开始移动时,我仍然遇到一个问题,它不能完全保持在中心位置。更新时,它似乎几乎向上抖动了一个像素,然后又向下抖动了 2 个像素。最终该对象离开当前视图。如何解决这个抖动问题?
Original Question/Code
I am fine tuning the rendering for a 3D object and attempting to implement a camera following the object using gluLookAt
because the object's center y position constantly increases once it reaches it's maximum height. Below is the section of code where I setup the ModelView and Projection matrices:
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);
Currently the object displays very far away and appears to move further back into the screen (-z) and down (-y) until it eventually disappears.
What am I doing wrong? How can I get my surface to appear in the center of the screen, taking up the full view, and the camera moving with the object as it is updated?
Updated Code and Current Issue
This is my current code, which is now putting the object dead center and filling up my window.
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);
I still have one problem when the object being viewed starts moving it does not stay perfectly centered. It appears to almost jitter up by a pixel and then down by 2 pixels when it updates. Eventually the object leaves the current view. How can I solve this jitter?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您的问题在于理解投影的作用。在你的情况下glFrustum。我认为解释 glFrustum 的最好方法是通过图片(我刚刚手绘)。您开始一个名为Eye Space的空间。这是顶点经过模型视图矩阵变换后所在的空间。该空间需要转换为称为标准化设备坐标空间的空间。这发生在两个过程中:
其视觉效果类似于 OpenGL 的“镜头”。在下图中,您可以在眼睛空间中看到一个绿色突出显示的区域(从技术上讲,它是一个 3 体积),它是 NDC 空间反投影到其中的区域。在大写字母中,显示了对称平截头体的效果,即
left = -right
、top = -bottom
。下图中显示了一个不对称的平截头体,即左≠右
,顶部≠底部
。请注意,应用这种不对称(通过中心偏移)不会转动,即旋转你的视锥体,但是倾斜它。然而,“相机”将停留在原点,仍然指向 -Z 轴。当然,图像投影的中心会发生移动,但这不是您想要的情况。
像这样倾斜平截头体是有应用的。最重要的是,它是实现左眼和右眼不同视图以及立体渲染设置的正确方法。
Your problem is with the understanding what the projection does. In your case glFrustum. I think the best way to explain glFrustum is by a picture (I just drew -- by hand). You start of a space called Eye Space. It's the space your vertices are in after they have been transformed by the modelview matrix. This space needs to be transformed to a space called Normalized Device Coordinates space. This happens in a two fold process:
The visible effect of this is that of kind of a "lens" of OpenGL. In the below picture you can see a green highlighted area (technically it's a 3 volume) in eye space that, is the NDC space backprojected into it. In the upper case the effect of a symmetric frustum, i.e.
left = -right
,top = -bottom
is shown. In the bottom picture an asymmetric frustum, i.e.left ≠ -right
,top ≠ -bottom
is shown.Take note, that applying such an asymmetry (by your center offset) will not turn, i.e. rotate your frustum, but skew it. The "camera" however will stay at the origin, still pointing down the -Z axis. Of course the center of image projection will shift, but that's not what you want in your case.
Skewing the frustum like that has applications. Most importantly it's the correct method to implement the different views of left and right eye an a stereoscopic rendering setup.
尼可波拉斯的回答几乎告诉了你做错了什么,所以我会跳过它。您正在寻找解决方案,而不是告诉您出了什么问题,所以让我们直接进入解决方案。
这是我用于投影矩阵的代码:
一些文字来描述它:
glViewport 设置 openGL 窗口内显示位置的大小和位置。不知道为什么,我总是将其包含在投影更新中。如果你像我一样使用它,其中 mySize 是指定窗口尺寸的 2D 矢量,openGL 渲染区域将占据整个窗口。您应该熟悉接下来的 2 个调用,最后是
gluPerspective
。第一个参数是“Y 轴视野”。它指定了您将看到的角度(以度为单位),我从未使用过 45 度以外的任何角度。虽然它可以用于缩放,但我更喜欢将其留给相机操作。第二个参数是方面。它处理的是,如果您渲染正方形并且窗口大小不是 1:1 的比例,那么它仍然是正方形。第三个是近裁剪平面,比这更接近相机的几何体将不会被渲染,与farPlane相同,但相反,它设置了渲染几何体的最大距离。这是模型视图矩阵的代码
,还有一些您应该知道的事情:同样,您可以使用前 2 个调用,因此我们跳到
gluLookAt
。我有一个相机类来处理所有的运动、旋转等。 Eye、LookAt 和 Up 是 3D 矢量,这 3 个实际上是相机指定的所有内容。眼睛是相机在空间中的位置。 LookAt 是您正在查看的对象的位置,或者更好的是您正在查看的 3D 空间中的点,因为它实际上可以位于任何地方,而不仅仅是中心对象。如果您担心 Up 向量是什么,那其实很简单。它是垂直于向量(LookAt-Eye)的向量,但由于此类向量的数量是无限的,因此您必须指定一个。如果您的相机位于(0,0,0)
,而您正在看着(0,0,-1)
并且您想要双腿站立,请向上向量将为(0,1,0)
。如果您想倒立,请使用(0,-1,0)
。如果您不明白,请写在评论中。由于你没有任何相机类,所以你需要自己单独存储这3个向量。我相信你有像你正在移动的 3D 对象的中心之类的东西。每次更新后将该位置设置为 LookAt。同样在初始化阶段(当您制作 3D 对象时)选择相机的位置和向上向量。每次更新对象位置后,以相同的方式更新相机位置。如果将对象在 Y 轴上移动 1 个点,请对相机位置执行相同的操作。如果您不想旋转相机,则向上向量保持不变。每次更新后,使用更新的向量调用 gluLookAt 。
更新后的帖子:
如果没有更大的参考框架,我真的不明白发生了什么(但无论如何我不想知道)。很少有事情让我感到好奇。如果中心是存储对象位置的 3D 矢量,为什么要将此对象的中心设置在窗口的右上角?如果它是中心,你应该在
glOrtho
的第二个和第四个参数中包含这些 +diam ,如果这样做导致事情变得糟糕,那么你使用了错误的变量名称或者在这个错误之前的某个地方做了一些事情。您在更新的帖子中设置了 LookAt 位置,但我不明白您为什么要对 Eye 使用这些参数。您应该将类似于:centerX、centerY、centerZ-diam
的内容作为gluLookAt
中的前 3 个参数。这使相机与物体处于相同的 X 和 Y 位置,但您将从距离diam
沿 Z 轴查看它The answer by Nicol Bolas pretty much tells what you're doing wrong so I'll skip that. You are looking for an solution rather than telling you what is wrong, so let's step right into it.
This is code I use for projection matrix:
Some words to describe it:
glViewport sets the size and position of display place for openGL inside window. Dunno why, I alsways include this for projection update. If you use it like me, where mySize is 2D vector specifying window dimensions, openGL render region will ocuppy whole window. You should be familiar with 2 next calls and finaly that
gluPerspective
. First parameter is your "field of view on Y axis". It specifies the angle in degrees how much you will see and I never used anything else than 45. It can be used for zooming though, but I prefer to leave that to camera operating. Second parameter is aspect. It handles that if you render square and your window sizes aren't in 1:1 ratio, it will be still square. Third is near clipping plane, geometry closer than this to camera won't get rendered, same with farPlane but on contrary it sets maximum distance in what geometry gets rendered.This is code for modelview matrix
And again something you should know: Again, you can use first 2 calls so we skip to
gluLookAt
. I have camera class that handles all the movement, rotations, things like that. Eye, LookAt and Up are 3D vectors and these 3 are really everything that camera is specified by. Eye is the position of camera, where in space it is. LookAt is the position of object you're looking at or better the point in 3D space at which you're looking because it can be really anywhere not just center object. And if you are worried about what's Up vector, it's really simple. It's vector perpedicular to vector(LookAt-Eye), but becuase there's infinite number of such vectors, you must specify one. If your camera is at(0,0,0)
and you are looking at(0,0,-1)
and you want to be standing on your legs, up vector will be(0,1,0)
. If you'd like to stand on your head instead, use(0,-1,0)
. If you don't get the idea, just write in comment.As you don't have any camera class, you need to store these 3 vectors separately by yourself. I believe you have something like center of 3D object you're moving. Set that position as LookAt after every update. Also in initialization stage(when you're making the 3D object) choose position of camera and up vector. After every update to object position, update the camera position the same way. If you move your object 1 point up at Y axis, do the same to camera position. The up vectors remains constant if you don't want to rotate camera. And after every such update, call
gluLookAt
with updated vectors.For updated post:
I don't really get what's happening without bigger frame of reference (but I don't want to know it anyway). There are few things I get curious about. If center is 3D vector that stores your object position, why are you setting the center of this object to be in right top corner of your window? If it's center, you should have those +diam also in 2nd and 4th parameter of
glOrtho
, and if things get bad by doing this, you are using wrong names for variables or doing something somewhere before this wrong. You're setting the LookAt position right in your updated post, but I don't find why you are using those parameters for Eye. You should have something more like:centerX, centerY, centerZ-diam
as first 3 parameters ingluLookAt
. That gives you the camera on the same X and Y position as your object, but you will be looking on it along Z axis from distancediam
由
glFrustum
生成的透视投影矩阵定义了一个相机空间(它作为输入的顶点空间),相机位于原点。您正在尝试使用不在原点的相机创建透视矩阵。glFrustum
无法做到这一点,因此您尝试执行此操作的方式根本行不通。有多种方法可以生成相机不在原点的透视矩阵。但这样做确实没有意义。
通常处理移动相机的方式是简单地添加从世界空间到透视投影的相机空间的变换。这只是相对于相机旋转和平移世界。这就是
gluLookAt
的工作。但你的参数也是错误的。前三个值是相机的世界空间位置。接下来的三个应该是相机应该查看的世界空间位置(物体的位置)。
The perspective projection matrix generated by
glFrustum
defines a camera space (the space of vertices that it takes as input) with the camera at the origin. You are trying to create a perspective matrix with a camera that is not at the origin.glFrustum
can't do that, so the way you're attempting to do it simply will not work.There are ways to generate a perspective matrix where the camera is not at the origin. But there's really no point in doing that.
The way a moving camera is generally handled is by simply adding a transform from the world space to the camera space of the perspective projection. This just rotates and translates the world to be relative to the camera. That's the job of
gluLookAt
. But your parameters to that are wrong too.The first three values are the world space location of the camera. The next three should be the world-space location that the camera should look at (the position of your object).