Функция OpenGL и gluLookAt

У меня есть функция moveCamera(), которая вращает камеру вокруг центральной сферы.

void moveCamera()
{
    glLoadIdentity();
    int vec = ceil(theta / 3.1415);
    int y;
    if (vec%2)
        y = 1;
    else
        y = -1;
    gluLookAt(e_x, e_y, e_z, 0, 0, 0, 0, y, 0);
}

Эта функция вызывается внутри display()

void display()
{
    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    moveCamera();

    glCallList(idList);

    GLfloat pos[] = {0, 0, 0, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, pos);

    glFinish();
}

В этой ситуации все работает нормально. Но я заметил некоторые вещи, которые не могу объяснить.

Например, если я изменяю функцию display() для рисования красной линии в направлении оси X, как это

void display()
{
    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glColor3ub(255, 0, 0);
    glLineWidth(5);
    glBegin(GL_LINES);
        glVertex3i(0, 0, 0);
        glVertex3i(1000, 0, 0);
    glEnd();

    moveCamera();

    glCallList(idList);

    GLfloat pos[] = {0, 0, 0, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, pos);

    glFinish();
}

... ничего не произошло. Почему???

Другое, если я уберу glLoadIdentity() после glClear(), то будет рисоваться красная линия, но когда я перемещаю камеру кнопками клавиатуры, я вижу какой-то баг в отрисовке этой линии (линия меняет свои координаты для рисования). Чтобы увидеть это, запустите мой код.

Полный код:

#include <iostream>
#include <glut.h>
#include <math.h>
#include <QDebug>
using namespace std;

#define WIDTH 1024
#define HEIGHT 600

void display();
void reshape(int width, int height);
void keyboard(unsigned char key, int x, int y);
void moveCamera();
void init();
void mkList();
void enableLight();
//double z(const double &x, const double &y);
void printMatrix(double *m)
{
    for (int i = 0; i < 4; i++)
        qDebug() << QString("%1   %2   %3   %4").arg(m[i*4]).arg(m[i*4+1]).arg(m[i*4+2]).arg(m[i*4+3]);
    qDebug() << "\n";
}

GLuint idList = 0;

double e_x = 0;
double e_y = 0;
double e_z = 0;
double r = 300;
double phi = 0;
double theta = 1.5;

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutInitWindowPosition(30, 100);
    glutCreateWindow("Lab#2");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);

    glEnable(GL_DEPTH_TEST);
    init();
    mkList();
    enableLight();

    glutMainLoop();
    return 0;
}

void display()
{
    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glColor3ub(255, 0, 0);
    glLineWidth(5);
    glBegin(GL_LINES);
        glVertex3i(0, 0, 0);
        glVertex3i(1000, 0, 0);
    glEnd();

    moveCamera();

    glCallList(idList);

    GLfloat pos[] = {0, 0, 0, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, pos);

    glFinish();
}

void reshape(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, 2, 100, 2000);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)
{
#define ESCAPE '\033'

    switch (key)
    {
    case ESCAPE:
        exit(0);
        break;
    case 's':
        theta += 0.1;
        break;
    case 'w':
        theta -= 0.1;
        break;
    case 'a':
        phi += 0.1;
        break;
    case 'd':
        phi -= 0.1;
        break;
    case 'q':
        r-=5;
        break;
    case 'e':
        r+=5;
        break;
    default:
        break;
    }
    e_x = r * sin(theta) * cos(phi);
    e_z = r * sin(theta) * sin(phi);
    e_y = r * cos(theta);
    glutPostRedisplay();
}

void moveCamera()
{
    glLoadIdentity();
    int vec = ceil(theta / 3.1415);
    int y;
    if (vec%2)
        y = 1;
    else
        y = -1;
    gluLookAt(e_x, e_y, e_z, 0, 0, 0, 0, y, 0);
}

void init()
{
    e_x = r * sin(theta) * cos(phi);
    e_z = r * sin(theta) * sin(phi);
    e_y = r * cos(theta);
}

void mkList()
{
    int idInnerList = glGenLists(1);
    glNewList(idInnerList, GL_COMPILE);

    glColor3ub(255, 0, 0);
    glPushMatrix();
    glTranslatef(-200, 0, 0);
    glutWireSphere(50, 10, 10);
    glPopMatrix();

    glColor3ub(0, 255, 0);
    glPushMatrix();
    glTranslatef(200, 0, 0);
    glutSolidCube(100);
    glPopMatrix();

    glEndList();

    idList = glGenLists(1);
    glNewList(idList, GL_COMPILE);
    glCallList(idInnerList);

    glPushMatrix();
    glTranslatef(0, 0, 200);
    glCallList(idInnerList);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(0, 0, -200);
    glCallList(idInnerList);
    glPopMatrix();
    glEndList();
}

void enableLight()
{
    glEnable(GL_LIGHTING);

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

    GLfloat diffuse[] = {0.7, 0.7, 0.7, 1};

    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);

    glEnable(GL_LIGHT0);
}

person Community    schedule 09.10.2016    source источник
comment
Итак, я заметил, что функция moveCamera() и функция gluLookAt() должны вызываться сначала в функции отображения(). Почему???   -  person    schedule 09.10.2016


Ответы (1)


Для начала хочу сказать так: сейчас 2016 год — не используйте устаревший OpenGL. Узнайте о современном OpenGL с помощью вашей любимой поисковой системы.

Если под «ничего не произошло» вы имеете в виду «я не мог видеть красную линию», и если я правильно понял ваш код, это только потому, что строка будет полностью обрезана. Позволь мне объяснить.

Первое, что вы делаете, что влияет на трансформацию вершин внутри display(), это вызов glLoadIdentity(). Это установит верхнюю часть текущего матричного стека на единичную матрицу I. Предположим, что текущий стек равен MODELVIEW, а текущая матрица проекции, P, установлена ​​в reshape(), тогда ваша строка подвергнется следующему преобразованию из локального пространства в пространство клипа:

Vx_clip = P * I * Vx_local
Vy_clip = P * I * Vy_local

Учитывая, что матрица модели-представления является идентичной, обе локальные координаты останутся неизменными в мировом пространстве, прежде чем будут преобразованы в пространство клипа. Поскольку ваша ближняя плоскость находится на z=-100, линия будет полностью вне усеченного конуса, то есть не в пространстве между ближней и дальней плоскостями, и, таким образом, будет полностью обрезана. Математически это немного сложнее, но концептуально точно.

Вы можете убедить себя, определив красную линию следующим образом.

glVertex3i(-500, 0, -100);
glVertex3i(500, 0, -100);

Теперь часть линии будет находиться внутри вашей усеченной пирамиды, даже с матрицей представления модели идентичности, потому что она частично пересекает ближнюю плоскость и, следовательно, не должна быть полностью обрезана.

Если вы сначала примените неидентификационную матрицу представления модели, вызвав moveCamera() до рендеринга красной линии, вы также сможете частично увидеть красную линию. Почему? Что ж, давайте предположим, что ваши полярные координаты выбраны так, что камера по-прежнему будет смотреть в направлении (0, 0, -z), тогда с заданным радиусом вы определяете, что камера находится в (0, 0, 300) в мировом пространстве, что gluLookAt() будет отображать. в матрицу преобразования, которая просто добавит сдвиг (0, 0, -300) к обеим вершинам.

Учитывая начальное определение вашей линии и новую матрицу представления модели, вершины линии будут в (0, 0, -300) и (1000, 0, -300) после левого умножения матрицы представления модели. После преобразования в пространство отсечения путем умножения матрицы проекции влево линия будет только частично обрезана и, следовательно, будет видна.

Как я уже сказал, это намного сложнее, и на самом деле это только часть полного конвейера преобразования вершин. Прочтите это и это для математического трактата на тему преобразования вершин. Это компьютерная графика 101. И, пожалуйста: сосредоточьтесь на математике, а не на использовании устаревшего API, который вы там, к сожалению, увидите.

person thokra    schedule 11.10.2016