суббота, 4 августа 2012 г.

Помещение актёров на сцену OpenGl Qt

В прошлом посте я рассказал про создание OpenGl окна. Сегодня я расскажу , как туда поместить объект. Сначала создадим окно , как описано тут http://alexkutsan.blogspot.com/2012/04/opengl-qt.html .

Всё не так сложно.
Изменять я буду  функцию paintGL(). В ней происходит отрисовка.Вызывается она всякий раз, когда окно перерисовывается. Мы нарисуем  8 угольник. Определим его вершины:


Да, мы можем заставить компьютер считать все эти косинусы и синусы, но зачем? Компьютер будет раскладывать в ряд каждое выражение. Лучше попросим любого 7-миклассника посчитать нам эти значения. Он нам с первого взгляда всё выведет (стыдно, да?)


Ближе к коду.


void SceneGL::paintGL(){
  glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
  glBegin(GL_POLYGON);
      glVertex3f( 0.5f, 1.0f,0.0f);
      glVertex3f(1.0f,0.5f,  0.0f);
      glVertex3f( 1.0f,-0.5f,0.0f);

      glVertex3f(0.5f,-1.0f,0.0f);
      glVertex3f(-0.5f,-1.0f,0.0f);
      glVertex3f(-1.0f,-0.5f,0.0f);

      glVertex3f(-1.0f,0.5f,0.0f);
      glVertex3f(-0.5f,1.0f,0.0f);
   glEnd();
}



Немного описания.

Мы работаем уже в матричной модели. Изменяем ведь саму модель - glMatrixMode(GL_MODELVIEW);

Мы очищаем буфер цветов -  glClear(GL_COLOR_BUFFER_BIT); , ведь у нас появляются здесь цвета( пока-что только белый)

Загружаем единичную матрицу -  glLoadIdentity(); . Никаких трансформаций над матрицей мы не делаем, поэтому этот шаг не обязателен,но делать его принято. Попробуйте вставить
...
glRotated(30,0,0,1);
glBegin(GL_POLYGON);
...
Вы  увидите, что матрица повернётся относительно единичной. Теперь уберите  glLoadIdentity();. Видите? Матрица поворачивается всякий раз при перерисовке окна относительно прошлого её состояния. Это потому, что функция glRotated(30,0,0,1); не поворачивает объект, это мы с вами думаем, что она поворачивает объект. glRotated умножает текущую матрицу на матрицу поворота, параметры которой мы задаём.

Всё серо и грустно.
Давайте добавим радости - добавим цвета.
Перед каждой точкой будем задавать цвет функцией glColor3f. Её параметры - RGB (0.0f-1.0f);
  glBegin(GL_POLYGON);
       glColor3f(1,0,0);
      glVertex3f( 0.5f, 1.0f,-1.0f);
       glColor3f(0,1,0);
      glVertex3f(1.0f,0.5f,  -1.0f);
       glColor3f(0,0,1);
      glVertex3f( 1.0f,-0.5f,-1.0f);

       glColor3f(1,0,1);
      glVertex3f(0.5f,-1.0f,-1.0f);
       glColor3f(1,1,0);
      glVertex3f(-0.5f,-1.0f,-1.0f);
       glColor3f(1,1,1);
      glVertex3f(-1.0f,-0.5f,-1.0f);

       glColor3f(0.5,0.5,0.5);
      glVertex3f(-1.0f,0.5f,-1.0f);
       glColor3f(0,0.5,1);
      glVertex3f(-0.5f,1.0f,-1.0f);
      glEnd();



 
И результат:



Хочу, чтоб всё вертелось!
Когда мы смотрим на объект, мы смотрим на него сверху.
Для простоты , сейчас мы повертим его относительно оси Z.
Если вы посмотрите код выше, то увидите, что наш 6-угольник находился в плоскости Z на уровне  -  -1. Давайте это изменим, и поставим его на уровень 0. И воспользуемся функцией смещения glTranslated(0,0,-3); Тем самым, мы отведём от глаз шестиугольник на 3 шага.

Для того, чтоб повернуть этот шестиугольник , используется функция   glRotatef. Она принимает 4 параметра:
angel - угол на который нужно повернуть объект.
x - поворачивать относительно Ох?
y - поворачивать относительно Оу?
z - поворачивать относительно Oz?

Давайте создадим private переменную  int rot; И создадим public slot void update();
Внутри update напишем.
void SceneGL::update()
{
    rot++;
    updateGL();
}

Теперь, изменим код initializeGL
void SceneGL::initializeGL(){
    glClearColor(0.0f,0.0f,0.0f,1.0f);
    glEnable(GL_DEPTH_TEST);
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(10);
    
}

Как видите, мы запускаем таймер, и через каждые 10 миллисекунд будет выполняться update(), тем самым увеличивая переменную rot и перерисовывая сцену. Стоит изменить функцию paintGL
void SceneGL::paintGL(){
    glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslated(0,0,-4);
    glRotatef(rot,0,0,1);
    ...
После загрузки единичной матрицы я поворачиваю сцену на rot градусов, а rot постоянно меняется таймером. Запускайте, любуйтесь.

Теперь, давайте повертим наш шести угольник не только относительно Z, а ещё относительно X и Y. Для этого изменим glRotatef(rot,0,0,1); на glRotatef(rot,1,1,1);. Для большей наглядности нарисуем Оси координат. Создайте private функцию

void SceneGL::drawAxis()
{
    glLineWidth(3.0f);
    // до вызова команды ширина равна 9 пикселю по умолчанию
    // устанавливаем цвет последующих примитивов
    glColor4f(1.00f, 0.00f, 0.00f, 1.0f);
    // ось x кvoid Scene3D::drawAxis()
    {
        glLineWidth(3.0f);
        // до вызова команды ширина равна 9 пикселю по умолчанию

        // устанавливаем цвет последующих примитивов
        glColor4f(1.00f, 0.00f, 0.00f, 1.0f);
        // ось x красного цвета
        glBegin(GL_LINES); // построение линии
        glVertex3f( 9.0f,  0.0f,  0.0f); // первая точка
        glVertex3f(-9.0f,  0.0f,  0.0f); // вторая точка
        glEnd();

        QColor halfGreen(0, 128, 0, 255);
        qglColor(halfGreen);
        glBegin(GL_LINES);
        // ось y зеленого цвета
        glVertex3f( 0.0f,  9.0f,  0.0f);
        glVertex3f( 0.0f, -9.0f,  0.0f);

        glColor4f(0.00f, 0.00f, 1.00f, 9.0f);
        // ось z синего цвета
        glVertex3f( 0.0f,  0.0f,  9.0f);
        glVertex3f( 0.0f,  0.0f, -9.0f);
        glEnd();
    }

}

Теперь изменим функцию paintGL вот так:
    ...
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslated(0,0,-4);
    glRotatef(30,1,1,0);
    drawAxis();
    glRotatef(rot,1,1,1);
    ...
и функцию initializeGL
void SceneGL::initializeGL(){
    glClearColor(0.0f,0.0f,0.0f,1.0f);
    glEnable(GL_DEPTH_TEST);
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(10);

}
Обратите внимание на glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); и glEnable(GL_DEPTH_TEST);. Я включил тест глубины, если его не включить, то openGl не будет учитывать дальность объектов. Виден будет объект, которые нарисуется первым. С включённым тестом глубины будет виден тот объект, который ближе к наблюдателю(к нам). Ещё я поместил сюда второй glRotatef, который поворачивает сцену на 30 градусов по X и Y - это даст нам удобный угол просмотра. После этого я рисую оси и поворачиваю сцену на rot градусов по всем осям, и рисую 6-угольник. Благодоря этой последовательности прорисовки, оси у меня остаются всегда в 30 градусах, а объект крутится.
Итог получается вот такой:
 
 В следующий раз я расскажу как сделать управляемую сцену, чтоб мы в ней могли перемещаться динамически. Ещё я хочу рассказать, как с помощью примитивов нарисовать сферу. На сегодня всё.Спасибо всем, кто это прочёл. Исходники можно получить здесь : Arctors.zip

7 комментариев:

  1. По-моему, это ВОСЬМИугольник, не?

    ОтветитьУдалить
  2. Перезалейте исходники, пожалуйста

    ОтветитьУдалить
    Ответы
    1. Вот так сюрприз, дропбокс решил, что ему больше незачем хранить мои файлы.
      Извиняйте за ненадёжные сервисы. На днях перезалью в другое место.

      Удалить
  3. закиньте пожалуйста, исходники!

    ОтветитьУдалить
  4. Этот комментарий был удален автором.

    ОтветитьУдалить
  5. на виндовсе почему-то мерцает картинка, перерисовывается многоугольник не всегда полностью

    ОтветитьУдалить