OpenGL gluLookAt() 未按预期工作

发布于 2024-12-09 10:11:25 字数 3287 浏览 8 评论 0原文

我正在 OpenGL 的天空盒内制作过山车,并且没有太多关于其功能或计算机图形的背景,事实证明这是非常困难的。我使用 Catmull-Rom 样条插值绘制了一个过山车,并使用 glVertex3f 绘制了每个点。现在我想每 50 毫秒调用一次 update() 函数来围绕轨道移动相机。 gluLookAt() 产生奇怪的结果,要么从屏幕上删除轨道,产生黑屏等。我想我需要移动一些矩阵函数,但我不知道该放在哪里每一个。到目前为止,这是我的代码:我的

int main(int argc, char** argc)
{
    // ... load track, etc ...

    // Init currpos, nextpos, iter, up
    currpos = Vec3f(0, 0, 0);
    nextpos = currpos;
    iter = 0;
    up = Vec3f(0, 1, 0);

    deque<Vec3f> points;
    Vec3f newpt;

    // Loop through the points and interpolate 
    for (pointVectorIter pv = g_Track.points().begin(); pv != g_Track.points().end(); pv++)
    {
        Vec3f curr(*pv);            // Initialize the current point and a new point (to be drawn)
        points.push_back(curr);     // Push the current point onto the stack
        allpoints.push_back(curr);  // Add current point to the total stack

        if (points.size() == 4) // Check if there are 4 points in the stack, if so interpolate
        {
            for (float u = 0.0f; u < 1.0f; u += 0.01f)
            {
                newpt = interpolate(points[0], points[1], points[2], points[3], u);
                glColor3f(1, 1, 1);
                glVertex3f(newpt.x(), newpt.y(), newpt.z());

                allpoints.push_back(newpt);
            }

            points.pop_front();
        }
    }

    // glutInit, InitGL(), etc...
}

void InitGL(GLvoid)
{
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(100.0, (GLfloat)WINDOW_WIDTH / (GLfloat)WINDOW_HEIGHT, .0001, 999999);
    glMatrixMode(GL_MODELVIEW);
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
}

void display (void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(currpos.x(), currpos.y(), currpos.z(), nextpos.x(), nextpos.y(), nextpos.z(), up.x(), up.y(), up.z());

    glPushMatrix();
    glEnable(GL_TEXTURE_2D); // Enable texturing from now on

    /* draw skybox, this was from previous assignment and renders correctly */

    glPopMatrix();

    // now draw rollercoaster ...

    glPushMatrix();
    glBegin(GL_LINE_STRIP);

    deque<Vec3f> points;
    Vec3f newpt;

    for each (Vec3f pt in allpoints)
    {
        glColor3f(1, 1, 1);
        glVertex3f(pt.x(), pt.y(), pt.z());
    }

    glutTimerFunc(50, update, 1);
    glEnd();
    glPopMatrix();

    // Swap buffers, so one we just drew is displayed
    glutSwapBuffers();
}

void update(int a)
{
    if (iter < allpoints.size())
    {
        currpos = allpoints[iter];
        nextpos = allpoints[iter + 1];

        gaze = nextpos - currpos;
        gaze.Normalize();

        Vec3f::Cross3(binorm, gaze, up);
        binorm.Normalize();

        Vec3f::Cross3(up, binorm, gaze);
        up.Normalize();

        glutPostRedisplay();
    }

    iter++;
}

想法是,我保留一个全局双端队列allpoints,其中包括样条线的控制点和插值点。完成后,我每 50 毫秒调用一次 update(),并沿着 allpoints 中的每个点移动相机。在该项目的先前版本中,我可以看到过山车的绘制正确。 gluLookAt() 似乎没有按照我想要的方式工作。通过上面的代码,程序开始时相机看着天空盒的一侧和过山车的一部分,然后当调用 update() 时,过山车消失但相机不动。我一直在搞乱 OpenGL 矩阵函数的放置位置,并且根据它们所在的位置,有时 update() 也会导致空白屏幕。

I am making a rollercoaster inside of a skybox in OpenGL, and without much background on it's functions or computer graphics it is proving to be very difficult. I drew a rollercoaster using Catmull-Rom spline interpolation, and drew each point with glVertex3f. Now I want to call an update() function every 50ms to move the camera around the track. gluLookAt() is producing weird results, either removing the track from the screen, producing a black screen, etc. I think I need to move some of the matrix functions around but I am not sure where to put each one. Here is my code so far:

int main(int argc, char** argc)
{
    // ... load track, etc ...

    // Init currpos, nextpos, iter, up
    currpos = Vec3f(0, 0, 0);
    nextpos = currpos;
    iter = 0;
    up = Vec3f(0, 1, 0);

    deque<Vec3f> points;
    Vec3f newpt;

    // Loop through the points and interpolate 
    for (pointVectorIter pv = g_Track.points().begin(); pv != g_Track.points().end(); pv++)
    {
        Vec3f curr(*pv);            // Initialize the current point and a new point (to be drawn)
        points.push_back(curr);     // Push the current point onto the stack
        allpoints.push_back(curr);  // Add current point to the total stack

        if (points.size() == 4) // Check if there are 4 points in the stack, if so interpolate
        {
            for (float u = 0.0f; u < 1.0f; u += 0.01f)
            {
                newpt = interpolate(points[0], points[1], points[2], points[3], u);
                glColor3f(1, 1, 1);
                glVertex3f(newpt.x(), newpt.y(), newpt.z());

                allpoints.push_back(newpt);
            }

            points.pop_front();
        }
    }

    // glutInit, InitGL(), etc...
}

void InitGL(GLvoid)
{
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(100.0, (GLfloat)WINDOW_WIDTH / (GLfloat)WINDOW_HEIGHT, .0001, 999999);
    glMatrixMode(GL_MODELVIEW);
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
}

void display (void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(currpos.x(), currpos.y(), currpos.z(), nextpos.x(), nextpos.y(), nextpos.z(), up.x(), up.y(), up.z());

    glPushMatrix();
    glEnable(GL_TEXTURE_2D); // Enable texturing from now on

    /* draw skybox, this was from previous assignment and renders correctly */

    glPopMatrix();

    // now draw rollercoaster ...

    glPushMatrix();
    glBegin(GL_LINE_STRIP);

    deque<Vec3f> points;
    Vec3f newpt;

    for each (Vec3f pt in allpoints)
    {
        glColor3f(1, 1, 1);
        glVertex3f(pt.x(), pt.y(), pt.z());
    }

    glutTimerFunc(50, update, 1);
    glEnd();
    glPopMatrix();

    // Swap buffers, so one we just drew is displayed
    glutSwapBuffers();
}

void update(int a)
{
    if (iter < allpoints.size())
    {
        currpos = allpoints[iter];
        nextpos = allpoints[iter + 1];

        gaze = nextpos - currpos;
        gaze.Normalize();

        Vec3f::Cross3(binorm, gaze, up);
        binorm.Normalize();

        Vec3f::Cross3(up, binorm, gaze);
        up.Normalize();

        glutPostRedisplay();
    }

    iter++;
}

The idea is that I am keeping a global deque allpoints that includes the control points of the spline and the interpolated points. Once that is complete, I call update() every 50ms, and move the camera along each point in allpoints. In a previous version of the project, I could see that the rollercoaster was being drawn correctly. It is gluLookAt() that doesn't seem to work how I want it to. With the code above, the program starts with the camera looking at one side of the skybox with a part of the rollercoaster, and then when update() is called, the rollercoaster disappears but the camera does not move. I have been messing around with where I am putting the OpenGL matrix functions, and depending on where they are sometimes update() will cause a blank screen as well.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

红ご颜醉 2024-12-16 10:11:25

除了缺少 glPopMatrix(user971377 已经发现)之外,您还可以在绘图例程中调用 glLoadIdentity,这当然会覆盖您在 中对模型视图矩阵所做的任何更改。 >update 方法(使用 gluLookAt)。

始终记住:gluLookAtglOrthogluPerspectiveglTranslateglRotate、所有其他矩阵和变换函数始终作用于当前选定矩阵堆栈(由 glMatrixMode 更改)的顶部元素(由 glPush/PopMatrix 更改)。他们总是乘以当前矩阵,而不是替换它。因此,与 gluPerspective 一样,您应该在调用 gluLookAt 之前调用 glLoadIdentity。整个相机的更改应该在渲染例程中完成,而不是更新例程。

与其在 update 中进行任何 GL 转换,您应该更改相机所依赖的变量,并在 display 中设置相机(模型视图矩阵上的 gluLookAt)方法。为了演示这些函数的标准用法,您的代码应该类似于

void display()
{
    <general state setup (glClear, ...)>

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glLookAt(camera);     //view transformation (camera)

    //object 1
    glPushMatrix();       //save modelview
    glTranslate/glRotate/glScale;        //local model transformations
    <draw object 1>
    glPopMatrix();
    ...
    //object n
    glPushMatrix();       //save modelview
    glTranslate/glRotate/glScale;        //local model transformations
    <draw object n>
    glPopMatrix();

    gluSwapBuffers();
}

void update()
{
    camera = ...;
}

Besides the absence of glPopMatrix (which user971377 already spotted), you call glLoadIdentity in your drawing routine, which of course overwrites any changes you did on the modelview matrix in the update method (using gluLookAt).

Always keep in mind: gluLookAt, glOrtho, gluPerspective, glTranslate, glRotate, and all other matrix and transformation functions always work on the top element (changed by glPush/PopMatrix) of the currently selected matrix stack (changed by glMatrixMode). And they always multiply the current matrix, istead of replacing it. So like for gluPerspective, you should call glLoadIdentity before calling gluLookAt. And the whole camera change should be done in the rendering routine, istead of the update routine.

Instead of doing any GL transformations in update you should rather change the variables on which the camera depends and set the camera (gluLookAt on the modelview matrix) in the display method. To demonstrate the standard use of these functions, your code should be something like:

void display()
{
    <general state setup (glClear, ...)>

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glLookAt(camera);     //view transformation (camera)

    //object 1
    glPushMatrix();       //save modelview
    glTranslate/glRotate/glScale;        //local model transformations
    <draw object 1>
    glPopMatrix();
    ...
    //object n
    glPushMatrix();       //save modelview
    glTranslate/glRotate/glScale;        //local model transformations
    <draw object n>
    glPopMatrix();

    gluSwapBuffers();
}

void update()
{
    camera = ...;
}

}

一场春暖 2024-12-16 10:11:25

在您的代码中注意到 glPushMatrix(); 被调用,没有 glPopMatrix();

只是一个想法,这可能与您的问题有关。

Noticed in your code glPushMatrix(); is called with no glPopMatrix();

Just a thought, this might have something to do with you issue.

毁梦 2024-12-16 10:11:25

gluLookAt 始终将其结果应用于当前矩阵,在您的情况下是 GL_MODEL_VIEW。但是,当您渲染过山车时,您会在该矩阵中加载标识,这会擦除您使用 gluLookAt 放置的值。

在这种情况下,您不需要触摸模型视图。事实上,GL_MODEL_VIEW代表模型矩阵乘以视图矩阵。在这种情况下,您可以先使用 glPushMatrix(),然后使用 glMulMatrix( myModelMatrix ),然后再渲染 glPopMatrix()。这样,您可以将视图矩阵保留在 GL_MODEL_VIEW 内,并且仍然为每个对象使用不同的模型矩阵。

我还建议您只每帧更改一次投影矩阵,而不是每帧更改一次。

gluLookAt always applies its result to current matrix, which in your case is GL_MODEL_VIEW. But when you render your roller coaster, you load identity in that matrix, which erase the value you put using gluLookAt.

In this case, you don't need to touch the model view. In fact, GL_MODEL_VIEW stands for model matrix multiply by view matrix. In this case, you can glPushMatrix() followed by glMulMatrix( myModelMatrix ) and after rendering glPopMatrix(). With this, you can keep your view matrix inside the GL_MODEL_VIEW and still use a different model matrix for each object

I also suggest you only change projection matrix once a frame, and not each frame.

酒解孤独 2024-12-16 10:11:25

我已经很久没有接触 OpenGL 了,但这里有一些需要考虑的事情:

  • 每次调用 display() 时,您都会使用当前矩阵绘制天空盒,然后加载单位矩阵来绘制过山车。也许在推/弹出中加载身份,以便天空盒保持不变,但会应用过山车上的流行变换。
  • 每次调用 display() 时是否都需要调用 gluPerspective 和 glMatrixMode?
  • 从上往上重复计算二元范数,然后再从二元范数向上计算,可能会在相机绕屏幕 z 轴旋转方面给您带来意想不到的结果。
  • 对 gluLookAt 的调用似乎颠倒了 nextpos 和 currpos,将相机指向相反的方向。
  • (仅意见)对于完全静止的天空盒,它可能看起来仍然很奇怪。绘制天空盒和过山车时匹配相机旋转(但不平移)可能看起来更好。

It's been a long time since I touched OpenGL, but here are a few things to consider:

  • With each call to display(), you are drawing the skybox with the current matrix then loading the identity matrix to draw the roller coaster. Perhaps load the identity within the push/pop so that the skybox is constant, but your prevailing tranformations on the roller coaster are applied.
  • Do you need to call gluPerspective and glMatrixMode with every call to display()?
  • Repeatedly calculating binorm from up and then up from binorm will probably give you unexpected results in terms of rotation of the camera around the screen's z axis.
  • The call to gluLookAt appears to have nextpos and currpos reversed, pointing the camera in the opposite direction.
  • (Opinion only) It may still look wierd with a completely stationary skybox. Matching camera rotation (but not translation) when drawing the skybox and roller coaster may look better.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文