Рисуем на QGLWidget и QPainter.

Элементы того, что будет описано в данной статье, используются, к примеру в данной программе-игре

К чему эта статья:


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

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


Где можно почерпнуть информацию по данной теме? Я воспользовался прекрасной, встроенной в Qt помощью, одним из Demo проектов Qt – OpenGL -> OverPainting , документацией по OpenGL на OpenGL и (неким русским вариантом) GameDev
Итак. 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) можно просмотреть по следующему адресу
2012-07-13