开发者

OpenGL gluLookAt() not working as intended

开发者 https://www.devze.com 2023-04-12 19:36 出处:网络
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-R

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 th开发者_Go百科e 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.


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

}


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

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


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.


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.
0

精彩评论

暂无评论...
验证码 换一张
取 消