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++;
}

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


person Logan Serman    schedule 11.10.2011    source источник


Ответы (4)


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

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

Вместо того, чтобы выполнять какие-либо 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 = ...;
}

}

person Christian Rau    schedule 11.10.2011
comment
Извините, glPopMatrix() в конце кода скайбокса, перед американскими горками (поскольку я не включил код скайбокса, поэтому забыл его в вопросе) - это то, куда он должен идти, или он должен быть после рисунок американских горок? - person Logan Serman; 11.10.2011
comment
@Logan Принимая во внимание, что поток управления может быть одним и тем же во время выполнения, glPopMatrix должен находиться в той же области, что и glPushMatrix, поскольку оба представляют собой единство и всегда должны идти парой. - person Christian Rau; 11.10.2011
comment
Я имею в виду, должен ли glPopMatrix() быть после закомментированного кода скайбокса (до того, как RC будет отрисован с помощью glVertex3f) или после цикла для рисования RC с помощью glVertex3f? - person Logan Serman; 11.10.2011
comment
@LoganSerman Зависит. Что касается вашего кода, он должен быть после кода скайбокса и перед кодом rc. Таким образом, вы фактически получаете преобразование, которое вы установили в функции обновления. Но теперь применяется мой второй абзац, и вам нужно вызвать glLoadIdentity перед вызовом gluLookAt, так как последний всегда обновляет (а не заменяет) текущую матрицу. Но, как сказано в ответе, вам лучше установить камеру в процедуре отображения. - person Christian Rau; 11.10.2011
comment
Спасибо. Я переместил gluLootAt в конец display, так что теперь нижняя часть display выглядит так: glutTimerFunc(50, update, 1); glLoadIdentity(); gluLookAt(...); и update заканчиваются только glutPostRedisplay();. К сожалению, камера по-прежнему вообще не двигается, когда я запускаю программу. Мысли? Кстати, спасибо за быстрые ответы, очень помогли! - person Logan Serman; 11.10.2011
comment
@LoganSerman Почему в конце, а не в начале? Я обновлю свой ответ. - person Christian Rau; 11.10.2011
comment
Я отредактировал свой код на основе вашего примера и отразил свои изменения в ОП. К сожалению, теперь мой экран снова черный :( что мне не хватает? - person Logan Serman; 11.10.2011
comment
@LoganSerman Выглядит довольно разумно. Может быть, какая-то проблема с glutTimerFunc? Вызывается ли он в запрошенные интервалы? И, кстати, вызовите это после glEnd, это не ошибка, но действительно странно вызывать его внутри блока glBegin/glEnd. - person Christian Rau; 11.10.2011
comment
Если я поставлю glPushMatrix(); после glClear и перед glMatrixMode в display, я смогу увидеть часть трека, но не скайбокс (это просто черный фон). Камера по-прежнему не двигается, хотя таймеры работают правильно. Если я передвину glPushMatrix туда, где он находится в OP, я ничего не увижу. Это поможет понять мою проблему? - person Logan Serman; 11.10.2011
comment
@LoganSerman Немного. Размещение glPushMatrix заставит вас убрать gluLookAt изменения после рисования скайбокса. Итак, вы рисуете американские горки с видом по умолчанию (это просто идентичность, если смотреть от начала координат вдоль -z). Я надеюсь, что ваши функции обновления камеры в порядке, хотя они выглядят разумными. - person Christian Rau; 11.10.2011
comment
@LoganSerman Подождите, вы меняете массив allpoints при каждом вызове display, а затем используете его для получения данных камеры в update. Выглядит странно, хотя я не полностью вижу ваш код из-за того, что отступ кода SO не работает должным образом. - person Christian Rau; 11.10.2011
comment
Хорошо, но ничего не дает понять, почему я получаю полностью черный экран с кодом в OP? Должен ли я вывести что-нибудь, чтобы дать вам представление о том, почему это происходит (например, положение камеры, генерируемое update?) И да, массив allpoints генерируется каждый раз, когда вызывается display. Он перебирает контрольные точки на треке, интерполирует новые точки (catmull-rom), а затем добавляет эти точки в массив. Я предполагаю, что это означает, что allpoints добавляет тонну элементов каждые 50 мс. Я должен создать allpoints один раз, а затем отображать это каждые display, может быть? - person Logan Serman; 11.10.2011
comment
@LoganSerman Я думаю, что нахожусь в той точке, когда только просмотр всего вашего кода поможет решить проблему, поскольку она, вероятно, связана с вычислением вашей камеры и настройкой всего массива точек. - person Christian Rau; 11.10.2011
comment
Вы хотите, чтобы я отправил вам по электронной почте файл .cpp? Я не хочу тратить много вашего времени на изучение всего исходного кода, хотя на самом деле он не такой уж большой. Дайте мне знать, что вы хотели бы сделать, и еще раз спасибо за всю помощь! - person Logan Serman; 11.10.2011
comment
@LoganSerman Честно говоря, у меня нет ни времени, ни желания копаться в вашем коде, извините. Но общий совет, который я мог бы вам дать, заключается в том, что, возможно, предварительное вычисление всей кривой в виде массива, а затем использование этого массива точек для рисования линейной полосы и вычисления камеры должно быть хорошей идеей, поскольку ваша текущая конфигурация действительно выглядит немного странно. И в любом случае это не должно повредить производительности. Извините, что больше не помогаю, но общие советы, данные в моем ответе, по-прежнему применимы к вашей первоначальной проблеме. И я все еще был бы заинтересован в любых достижениях, которые вы можете сделать по этому вопросу. - person Christian Rau; 11.10.2011
comment
Да, я именно так и сделал (предвычисление массива) и отразил изменения в ОП. Тем не менее, кости на черном экране все еще нет. - person Logan Serman; 11.10.2011
comment
@LoganSerman Вы пытались посмотреть на это со статической камерой, чтобы увидеть, действительно ли точки кривой вычисляются правильно? А также имейте в виду, что установка камеры точно на кривой делает первую часть кривой невидимой (так как она обрезается по ближней плоскости), хотя эта невидимая часть должна быть небольшой. - person Christian Rau; 11.10.2011
comment
Да, я посмотрел на это со статической камерой, и кривая выглядит хорошо, проблема заключается в перемещении камеры вокруг нее. Я чувствую, что это не проблема самой кривой, которая вызывает черный экран. По крайней мере, я должен иметь возможность видеть скайбокс и кривую, как я мог, прежде чем я реализовал свой код так, как вы описали (но таким образом я не мог перемещать камеру). Мне нужно лучшее из обоих миров! - person Logan Serman; 11.10.2011

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

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

person james82345    schedule 11.10.2011

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

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

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

person crazyjul    schedule 11.10.2011

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

  • С каждым вызовом display() вы рисуете скайбокс с текущей матрицей, а затем загружаете матрицу идентичности, чтобы нарисовать американские горки. Возможно, загрузите личность в push/pop, чтобы скайбокс был постоянным, но применялись ваши преобладающие преобразования на американских горках.
  • Вам нужно вызывать gluPerspective и glMatrixMode при каждом вызове display()?
  • Многократное вычисление бинормы вверх, а затем вверх из бинормы, вероятно, даст вам неожиданные результаты с точки зрения вращения камеры вокруг оси z экрана.
  • Вызов gluLookAt, по-видимому, меняет местами nextpos и currpos, указывая камеру в противоположном направлении.
  • (Только мнение) Это все еще может выглядеть странно с полностью стационарным скайбоксом. Сопоставление вращения камеры (но не перемещения) при рисовании скайбокса и американских горок может выглядеть лучше.
person Dale    schedule 11.10.2011
comment
Спасибо, ваше предложение работает нормально (я думаю), так как сами американские горки теперь вращаются вместе с камерой. Однако сама камера капризничает, она не следует за треком и в конечном итоге выходит из-под контроля и выходит за пределы скайбокса. Я поменял местами nextpos и currpos, так что, возможно, это связано с расчетами бинорм/вверх? Вы знаете, как их рассчитать, потому что в моем коде есть то, что было предложено нашим ТА. - person Logan Serman; 11.10.2011
comment
Вычисление вверх должно влиять только на вращение камеры, что не объясняет ее перемещение за пределы скайбокса. Используют ли ваши контрольные точки ту же ориентацию xyz, что и OpenGL? - person Dale; 12.10.2011
comment
Между тем, если ваши американские горки никогда не смотрят точно вверх или вниз между ступенями, вы должны быть в состоянии уйти, сохраняя постоянство. Я бы даже временно подправил рельс, чтобы использовать этот метод, пока мне не удастся отладить/проверить положение камеры по осям XYZ. Тогда я бы побеспокоился о более безопасном вычислении up впоследствии. К сожалению, у меня нет готовых рекомендаций для такого расчета. - person Dale; 12.10.2011