Первые шаги на Qt OpenGL

в 18:59, , рубрики: c++, OpenGL, qt, метки: ,

Предлагаю попробовать разобрать основы работы с Qt OpenGL, понять последовательность вызова функций и получить набор «начальных инструментов»

Вводные слова

Не столь давно я заинтересовался одной вещью – работа с графическими библиотеками.
В качестве «подопытного» был выбран язык Qt и графическая библиотека OpenGL.
По данной теме на хабре я нашел только одну статью. Этот пост, на мой взгляд, имеет право на существование, но хотелось бы описать свой опыт, благо, даже повод есть – пришла пора писать курсовую.
Ознакомиться с тем, что получилось (под Windows), можно здесь: архив с программой.

К чему эта статья: «Чтобы рассказать о самых-самых азах Qt OpenGL и предложить «инструменты» для «пытающихся»»

Приступаем к знакомству

Где можно почерпнуть информацию по данной теме? Я воспользовался прекрасной, встроенной в Qt помощью, одним из Demo проектов Qt – OpenGL -> OverPainting, документацией по OpenGL на http://www.opengl.org и (неким русским вариантом) http://opengl.gamedev.ru/

Итак. Qt OpenGL.
Создаем новый проект и добавляем класс, который будет наследоваться от QGLWidget. Для корректной работы проверяем наличие: (данные файлы пригодятся чуть позже)

#include <QGLWidget> 
#include <QtOpenGL>
#include <QBrush>
#include <QImage>
#include <QTimer>
#include <QtGui>

Теперь переходим к разбору последовательных вызовов в OpenGL:
При первоначальном создании виджета происходит следующий порядок последовательных вызовов:

  1. Конструктор виджета
  2. glDraw()

При изменении размеров окна виджета вызывается также функция glDraw(). Разберем данную функцию. glDraw() (а также и glInit()) принадлежат классу QGLWidget. Их не перегружают. Данные функции вызывают следующие методы: initializeGL(), resizeGL(), paintGL(). Эти три функции переопределяются программистом.

Посмотрим на данные функции:

Инициализация

initializeGL() – позволяет инициализировать (если по каким-то причинам не сделали в конструкторе) переменные, включить определенные возможности графической библиотеки(glEnable) и т.п.

Ресайз

resizeGL – реакция виджета на изменение размеров окна. Как правило, данная функция принимает два параметра – ширину и высоту. В ней необходимо задать область просмотра с помощью glViewport(x,y,width,height), установить матричный режим glMatrixMode(mode), где mode – матричный режим, который может быть одним из четырех:

  • GL_MODELVIEW — объектно-видовая матрица.
  • GL_PROJECTION — матрица проекций.
  • GL_TEXTURE — текстурная матрица.
  • GL_COLOR — цветовая матрица.

Прорисовка

Третья функция, подлежащая разбору – paintGL(). Оговорюсь сразу, функцию paintGL() можно заменить на аналогичную paintEvent(QPaintEvent *event) – ее преимущество, на мой взгляд, в том, что в нее передается параметр QPaintEvent*, с помощью которого мы можем получить QRect(прямоугольник), в котором происходит прорисовка (к примеру).
И в случае с paintGL, и с paintEvent работа выполняется аналогично. А именно – затираем предыдущий кадр:

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    qglClearColor(QColor(100,150,80));
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
 

В данном примере — поместили матрицу в стек, очистили кадр, «забрали» матрицу из стека.
После «затирки» необходимо создать следующий кадр (собственно, за что paintGL и отвечает)
Рисовать будем следующим образом – создадим переменную типа QPainter, у которой QPaintDevice* =this, т.е.

QPainter* painter = new QPainter(this);
С помощью данной переменной и будем рисовать кадр.
Прорисовка будет иметь следующий вид:

  1. Устанавливаем кисть (QBrush)
  2. Рисуем, используя (к примеру) геометрические фигуры.

Разберем прорисовку блока и окружности с помощью данного способа
Окружность:

painter->save(); //сохранили состояние QPainter
    painter->translate(position.x() - radius, position.y() - radius);//переводим систему координат, смещая ее на заданные параметры x и y
/* Теперь блок, в котором создаем градиентную заливку (для кисти). Как рекомендация – лучше вынести данную работу в отдельную функцию и создать переменную для кисти (типа QBrush), 
чтобы не терять лишнее время. */

QRadialGradient gradient(QPointF(radius, radius), radius,
                             QPointF(radius*0.5, radius*0.5));
    gradient.setColorAt(0, QColor(255255255255));
    gradient.setColorAt(0.25, innerColor);
    gradient.setColorAt(1, outerColor);
//Конец блока описания заливки
  painter->setBrush(QBrush(gradient));//установили кисть
    painter->drawEllipse(00int(2*radius)int(2*radius));//Нарисовали эллипс (в данном случае окружность)
   painter->restore();// Восстановили состояние QPainter

Для прямоугольника работа будет почти такой же, изменению подвергнется только drawEllipse. Он изменится на drawRect
Описывать входные параметры для них я не буду, т.к. они более-менее понятны, да и встроенная помощь Qt c этим отлично справится.
Остановимся немного на QBrush. Было бы невесело, если бы он мог работать с одним цветом или градиентной заливкой. Поэтому, предупреждая вопрос о прорисовке картинок, скажу – QBrush позволяет «грузить» картинку. В качестве примера:

QBrush(QImage("Theme.bmp"));

В заключении о QPainter скажу – необходимо следить за работой с ним, а именно – то, что прорисовано выше по коду, будет соответственно отображаться «под» описанным ниже и наоборот. И после работы с данной переменной имеет смысл логически завершить ее работу, вызвав метод end();

Вот три основные функции, которые программист должен переопределить в своем проекте. Замечу, что данные функции не вызываются периодически, и поэтому, если на экране происходит какое-то действие, то необходимо ввести таймер, который по срабатыванию будет вызывать update(). Эта функция вызовет функцию прорисовки.

Заключение

В данном небольшом посте я попытался немного рассказать о простейших вещах, которые обязательно пригодятся для работы с Qt OpenGL. Надеюсь, что данная статься поможет сформировать цепочку вызовов функций и упростит новичкам создание первых приложений с использованием OpenGL
Если у кого-то появился интерес к реализованному небольшому приложению, то исходники (.h и .cpp) можно просмотреть по следующему адресу — гитхаб

Автор: xnim

* - обязательные к заполнению поля