5

我在 OpenGL 的天空盒内制作过山车,并且没有太多关于它的功能或计算机图形的背景知识,这被证明是非常困难的。我使用 Catmull-Rom 样条插值绘制了一个过山车,并使用 glVertex3f 绘制了每个点。现在我想update()每 50 毫秒调用一个函数来围绕轨道移动相机。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,其中包括样条曲线的控制点和插值点。完成后,我update()每 50 毫秒调用一次,并沿allpoints. 在项目的早期版本中,我可以看到过山车被正确绘制。这gluLookAt()似乎并不像我想要的那样工作。使用上面的代码,程序开始时相机用过山车的一部分看着天空盒的一侧,然后当update()被调用时,过山车消失但相机不动。我一直在搞乱我放置 OpenGL 矩阵函数的位置,并且根据它们的位置,有时update()也会导致空白屏幕。

4

4 回答 4

6

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

始终牢记:gluLookAtglOrthogluPerspectiveglTranslate、以及所有其他矩阵和变换函数始终对当前选定矩阵堆栈(由 更改)glRotate的顶部元素(由 更改)起作用。他们总是乘以当前矩阵,而不是替换它。所以像 for 一样,你应该在调用之前调用。并且整个相机更改应该在渲染例程中完成,而不是更新例程。glPush/PopMatrixglMatrixModegluPerspectiveglLoadIdentitygluLookAt

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

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 = ...;
}

}

于 2011-10-11T16:59:23.457 回答
3

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

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

于 2011-10-11T16:49:19.680 回答
0

gluLookAt 始终将其结果应用于当前矩阵,在您的情况下为 GL_MODEL_VIEW。但是当你渲染你的过山车时,你会在那个矩阵中加载身份,这会删除你使用 gluLookAt 输入的值。

在这种情况下,您无需触摸模型视图。实际上,GL_MODEL_VIEW 代表模型矩阵乘以视图矩阵。在这种情况下,您可以在渲染之后glPushMatrix()进行。有了这个,您可以将视图矩阵保留在 GL_MODEL_VIEW 中,并且仍然为每个对象使用不同的模型矩阵glMulMatrix( myModelMatrix )glPopMatrix()

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

于 2011-10-11T06:35:03.640 回答
0

接触 OpenGL 已经很久了,但这里有几点需要考虑:

  • 每次调用 display() 时,您都在使用当前矩阵绘制天空盒,然后加载单位矩阵以绘制过山车。也许在 push/pop 中加载身份以使天空盒保持不变,但应用过山车上的主要转换。
  • 每次调用 display() 时都需要调用 gluPerspective 和 glMatrixMode 吗?
  • 反复计算 binorm 从 up 然后从 binorm 可能会在相机绕屏幕 z 轴旋转方面给您带来意想不到的结果。
  • 对 gluLookAt 的调用似乎使 nextpos 和 currpos 反转,将相机指向相反的方向。
  • (仅供参考)对于完全静止的天空盒,它可能看起来仍然很奇怪。绘制天空盒和过山车时匹配相机旋转(但不是平移)可能看起来更好。
于 2011-10-11T06:35:41.783 回答