OpenGL gluLookAt () работает не так, как предполагалось

#opengl #camera #glut #interpolation #glulookat

#opengl #камера #перенасыщение #интерполяция #glulookat

Вопрос:

Я делаю американские горки внутри скайбокса в 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  ;
}
  

Идея заключается в том, что я сохраняю глобальный deque allpoints , который включает контрольные точки сплайна и интерполированные точки. Как только это будет завершено, я вызываю update() каждые 50 мс и перемещаю камеру вдоль каждой точки allpoints . В предыдущей версии проекта я мог видеть, что американские горки рисуются правильно. Похоже, gluLookAt() это работает не так, как я хочу. С помощью приведенного выше кода программа запускается с камеры, смотрящей на одну сторону скайбокса с частью американских горок, а затем при update() вызове американские горки исчезают, но камера не двигается. Я возился с тем, куда я помещаю матричные функции OpenGL, и в зависимости от того, где они иногда update() находятся, также может появиться пустой экран.

Ответ №1:

Помимо отсутствия glPopMatrix (которое уже заметил пользователь971377), вы вызываете glLoadIdentity свою процедуру рисования, которая, конечно, перезаписывает любые изменения, которые вы внесли в матрицу modelview в update методе (с использованием gluLookAt ).

Всегда имейте в виду: gluLookAt , glOrtho , gluPerspective , glTranslate , glRotate , и все другие функции матрицы и преобразования всегда работают с верхним элементом (измененным glPush/PopMatrix ) текущего выбранного стека матриц (измененного glMatrixMode ). И они всегда умножают текущую матрицу, вместо ее замены. Так что, как и для gluPerspective , вы должны позвонить glLoadIdentity перед вызовом gluLookAt . И вся смена камеры должна выполняться в процедуре рендеринга, а не в процедуре обновления.

Вместо того, чтобы выполнять какие-либо преобразования GL update , вам следует скорее изменить переменные, от которых зависит камера, и установить камеру ( gluLookAt в матрице modelview) в 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 = ...;
}
  

}

Комментарии:

1. Извините, glPopMatrix() находится в конце кода skybox, перед американскими горками (поскольку я не включил код skybox, поэтому я забыл его в вопросе) — это то, куда он должен идти, или это должно быть после рисования американских горок?

2. @Logan В то время как поток управления может быть одинаковым во время выполнения, glPopMatrix он должен находиться в той же области, glPushMatrix что и, поскольку оба представляют собой единство и всегда должны быть в паре.

3. Я имею в виду, должно glPopMatrix() быть после прокомментированного кода skybox (до того, как будет нарисован rc glVertex3f ) или после цикла для рисования rc glVertex3f ?

4. @LoganSerman зависит. Что касается вашего кода, он должен быть после кода skybox и перед кодом rc. Таким образом, вы фактически получаете преобразование, заданное в функции обновления. Но теперь применяется мой второй абзац, и вам нужно позвонить glLoadIdentity перед вызовом gluLookAt , поскольку последний всегда обновляет (а не заменяет) текущую матрицу. Но, как сказано в ответе, вам лучше установить камеру в режим отображения.

5. Спасибо. Я переместил gluLootAt в конец display , так что теперь нижняя часть display выглядит так: glutTimerFunc(50, update, 1); glLoadIdentity(); gluLookAt(...); и update заканчивается просто glutPostRedisplay(); . К сожалению, камера по-прежнему не перемещается вообще, когда я запускаю программу. Мысли? Кстати, спасибо за быстрые ответы, очень полезно!

Ответ №2:

Замечено, что в вашем коде glPushMatrix(); вызывается без glPopMatrix();

Просто мысль, это может иметь какое-то отношение к вашей проблеме.

Ответ №3:

gluLookAt всегда применяет свой результат к текущей матрице, которая в вашем случае является GL_MODEL_VIEW . Но когда вы визуализируете свои американские горки, вы загружаете идентификатор в эту матрицу, которая стирает значение, которое вы ввели с помощью gluLookAt .

В этом случае вам не нужно касаться представления модели. Фактически, GL_MODEL_VIEW означает матрицу модели, умноженную на матрицу представления. В этом случае вы можете glPushMatrix() следовать за glMulMatrix( myModelMatrix ) рендерингом и после glPopMatrix() него . При этом вы можете сохранить свою матрицу представления внутри GL_MODEL_VIEW и по-прежнему использовать другую матрицу модели для каждого объекта

Я также предлагаю вам изменять матрицу проекции только один раз за кадр, а не каждый кадр.

Ответ №4:

Прошло много времени с тех пор, как я касался OpenGL, но вот несколько вещей, которые следует учитывать:

  • При каждом вызове функции display() вы рисуете скайбокс с текущей матрицей, а затем загружаете идентификационную матрицу для рисования американских горок. Возможно, загрузите идентификатор в push / pop, чтобы скайбокс был постоянным, но ваши преобладающие преобразования на американских горках будут применены.
  • Нужно ли вызывать gluPerspective и glMatrixMode при каждом вызове функции display() ?
  • Повторное вычисление бинормы сверху, а затем вверх из бинормы, вероятно, даст вам неожиданные результаты с точки зрения поворота камеры вокруг оси z экрана.
  • Похоже, что вызов gluLookAt меняет местами nextpos и currpos, указывая камеру в противоположном направлении.
  • (Только мнение) Это все равно может выглядеть странно с полностью неподвижным скайбоксом. Сопоставление поворота камеры (но не перевода) при рисовании скайбокса и американских горок может выглядеть лучше.

Комментарии:

1. Спасибо, ваше предложение работает нормально (я думаю), поскольку сами американские горки теперь вращаются вместе с камерой. Однако сама камера барахлит, она не следует за дорожкой и в конечном итоге выходит из-под контроля и выходит за пределы скайбокса. Я переключил nextpos и currpos, так что, возможно, это связано с вычислениями вектора binorm / up? Знаете ли вы, как их вычислить, потому что в моем коде есть то, что было предложено нашим TA.

2. Вычисление up должно влиять только на поворот камеры, что не объясняет его перевод за пределы skybox. Используют ли ваши контрольные точки ту же ориентацию xyz, что и OpenGL?

3. Между тем, если ваши американские горки никогда не поворачиваются точно вверх или вниз между шагами, вам должно сойти с рук поддержание постоянной «вверх». Я бы даже временно подправил рельс, чтобы я мог использовать этот метод, пока мне не удастся отладить / протестировать положение камеры xyz. Тогда я бы беспокоился о более безопасном вычислении «up» впоследствии. К сожалению, у меня нет рекомендации для этого расчета.