8.4. Графика OpenGL.
OpenGL — это стандарт API, для отображения двух- и трехмерной графики. Приложения Qt могут использовать OpenGL, посредством модуля QGL. Мы полагаем, что вы уже имеете некоторое знакомство с OpenGL. Если это не так, то рекомендуем начать изучение с посещения сайта http://www.opengl.org/.
Рисование трехмерных объектов, с помощью OpenGL, не так сложно, как может показаться на первый взгляд. Все что вам нужно сделать — создать дочерний класс от QGLWidget, перекрыть некоторые виртуальные методы предка и связать приложение с модулем QGL и библиотекой OpenGL. Поскольку QGLWidget ведет свою родословную от QWidget, то здесь вполне применимы знания, которые вы уже получили. Основное отличие здесь состоит в том, что теперь, вместо QPainter, вам придется использовать стандартные функции рисования из OpenGL.

Рисунок 8.20. Приложение Cube.
Для демонстрации возможностей библиотеки OpenGL, напишем приложение Cube, изображенное на рисунке 8.20. Приложение рисует трехмерный куб, грани которого окрашены в различные цвета. Пользователь может вращать куб и перемещать его. Двойным щелчком мыши по грани куба, он сможет изменить ее цвет, с помощью диалога выбора цвета QColorDialog.
class Cube : public QGLWidget < public: Cube(QWidget *parent = 0, const char *name = 0); protected: void initializeGL(); void resizeGL(int width, int height); void paintGL(); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent *event); private: void draw(); int faceAtPosition(const QPoint &pos); GLfloat rotationX; GLfloat rotationY; GLfloat rotationZ; QColor faceColors[6]; QPoint lastPos; >;
Класс Cube порожден от QGLWidget. Функции initializeGL(), resizeGL() и paintGL() перекрывают методы родительского класса QGLWidget. Обработчики событий от мыши перекрывают обработчики, унаследованные от QWidget. Определение класса QGLWidget находится в заголовке .
Cube::Cube(QWidget *parent, const char *name) : QGLWidget(parent, name)
В конструкторе вызывается QGLWidget::setFormat(), чтобы задать контекст устройства отображения OpenGL, и инициализируются приватные переменные-члены класса.
void Cube::initializeGL()
Функция initializeGL() вызывается один раз, перед вызовом paintGL(). Здесь выполняется настройка контекста отображения.
Все функции являются стандартными вызовами из библиотеки OpenGL, за исключением qglClearColor() -- метода класса QGLWidget. Если задаться целью, до конца следовать стандарту OpenGL, то мы могли бы вызвать функцию glClearColor(), в режиме RGBA, или glClearIndex(), в режиме индексированных цветов.
void Cube::resizeGL(int width, int height) < glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); GLfloat x = (GLfloat)width / height; glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0); glMatrixMode(GL_MODELVIEW); >
Функция resizeGL() вызывается один раз, перед paintGL(), но после того, как будет вызвана функция initializeGL(). Здесь настраивается область просмотра (viewport), проекция и прочие настройки, которые зависят от размера виджета.
void Cube::paintGL()
Функция paintGL() вызывается всякий раз, когда возникает необходимость перерисовать содержимое виджета. Она напоминает обработчик события QWidget::paintEvent(), только вместо QPainter здесь используются обращения к функциям OpenGL. Собственно рисование выполняется внутри приватной функции draw():
void Cube::draw() < static const GLfloat coords[6][4][3] = < < < +1.0, -1.0, +1.0 >, < +1.0, -1.0, -1.0 >, < +1.0, +1.0, -1.0 >, < +1.0, +1.0, +1.0 >>, < < -1.0, -1.0, -1.0 >, < -1.0, -1.0, +1.0 >, < -1.0, +1.0, +1.0 >, < -1.0, +1.0, -1.0 >>, < < +1.0, -1.0, -1.0 >, < -1.0, -1.0, -1.0 >, < -1.0, +1.0, -1.0 >, < +1.0, +1.0, -1.0 >>, < < -1.0, -1.0, +1.0 >, < +1.0, -1.0, +1.0 >, < +1.0, +1.0, +1.0 >, < -1.0, +1.0, +1.0 >>, < < -1.0, -1.0, -1.0 >, < +1.0, -1.0, -1.0 >, < +1.0, -1.0, +1.0 >, < -1.0, -1.0, +1.0 >>, < < -1.0, +1.0, +1.0 >, < +1.0, +1.0, +1.0 >, < +1.0, +1.0, -1.0 >, < -1.0, +1.0, -1.0 >> >; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -10.0); glRotatef(rotationX, 1.0, 0.0, 0.0); glRotatef(rotationY, 0.0, 1.0, 0.0); glRotatef(rotationZ, 0.0, 0.0, 1.0); for (int i = 0; i < 6; ++i) < glLoadName(i); glBegin(GL_QUADS); qglColor(faceColors[i]); for (int j = 0; j < 4; ++j) < glVertex3f(coords[i][j][0], coords[i][j][1], coords[i][j][2]); >glEnd(); > >
Внутри функции draw() выполняется рисование куба, с учетом вращения по осям x, y и z и цветов граней, находящихся в массиве faceColors. Все вызовы являются стандартными для OpenGL, за исключением qglColor(). Мы могли бы использовать вместо нее стандартные функции OpenGL glColor3d() или glIndex(), в зависимости от выбранного режима цветопередачи.
void Cube::mousePressEvent(QMouseEvent *event) < lastPos = event->pos(); > void Cube::mouseMoveEvent(QMouseEvent *event) < GLfloat dx = (GLfloat)(event->x() - lastPos.x()) / width(); GLfloat dy = (GLfloat)(event->y() - lastPos.y()) / height(); if (event->state() & LeftButton) < rotationX += 180 * dy; rotationY += 180 * dx; updateGL(); >else if (event->state() & RightButton) < rotationX += 180 * dy; rotationZ += 180 * dx; updateGL(); >lastPos = event->pos(); >
Функции mousePressEvent() и mouseMoveEvent() позволяют пользователю вращать куб и перемещать его по поверхности экрана. Левой кнопкой мыши выполняется вращение по осям x и y, правой -- по осям x и z.
После изменения переменных rotationX и/или rotationY и rotationZ, вызывается функция updateGL(), которая перерисовывает изображение.
void Cube::mouseDoubleClickEvent(QMouseEvent *event) < int face = faceAtPosition(event->pos()); if (face != -1) < QColor color = QColorDialog::getColor(faceColors[face], this); if (color.isValid()) < faceColors[face] = color; updateGL(); >> >
Обработчик mouseDoubleClickEvent() позволяет пользователю изменить цвет грани по двойному щелчку мыши. Для определения номера грани вызывается функция faceAtPosition(). Если под указателем мыши действительно находится какая либо грань куба, вызывается QColorDialog::getColor(), чтобы получить от пользователя новый цвет грани. Затем он заносится в массив faceColors и вызывается updateGL(), чтобы перерисовать изображение.
int Cube::faceAtPosition(const QPoint &pos) < const int MaxSize = 512; GLuint buffer[MaxSize]; GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); glSelectBuffer(MaxSize, buffer); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix((GLdouble)pos.x(), (GLdouble)(viewport[3] - pos.y()), 5.0, 5.0, viewport); GLfloat x = (GLfloat)width() / height(); glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0); draw(); glMatrixMode(GL_PROJECTION); glPopMatrix(); if (!glRenderMode(GL_RENDER)) return -1; return buffer[3]; >
Функция faceAtPosition() возвращает либо номер грани, находящейся в заданных координатах, либо -1, если точка с заданными координатами не входит ни в одну из граней. Код, выполняющий проверку, достаточно сложен. По сути -- он переводит сцену в режим GL_SELECT, чтобы мы могли воспользоваться дополнительными возможностями OpenGL, и отыскивает номер грани ("name").
Далее приводится содержимое файла main.cpp:
#include #include "cube.h" int main(int argc, char *argv[])
Если система не поддерживает OpenGL, то, с помощью вызова qFatal(), приложение выводит сообщение об ошибке и завершает работу.
Чтобы связать приложение Cube с модулем QGL и библиотекой OpenGL, в файл .pro нужно добавить строчку:
CONFIG += opengl
За дополнительной информацией о модуле QGL, обращайтесь к сопроводительной документации по классам QGLWidget, QGLFormat, QGLContext и QGLColormap.
| Пред. | В начало | След. |
| Вывод на печать. | На уровень выше | Drag and Drop. |
Введение в OpenGL на Qt/C++. Часть 1
В данном уроке мне хотелось бы изложить основы программирование графики с использованием OpenGL на платформе Qt/C++.
Первая часть является вводной, и для понимания материала читателю достаточно понимать основы программирования на С++.
Чтобы подключить OpenGL в Qt достаточно в файле проекта (*.pro) прописать
Qt += opengl
Мы создадим класс Widget, в котором и будет производится отрисовка.
Рисовать мы будем квадрат в 3d пространстве. В следующей части мы научимся его вращать и приближать.
Создаваемый класс должен наследоваться от QGLWidget.
class Widget: public QGLWidget { };
Теперь нам надо переобпределить некоторые методы:
1) void initializeGL();
2) void resizeGL(int nWidth, int nHeight);
3) void paintGL();
Первый метод инициализирует само окно, второй вызывается при изменении размеров окна, а в третьем реализовывается сама отрисовка.
Так как кода получается не очень много, приведу сразу весь:
#include "widget.h" Widget::Widget(QWidget *parent) // конструктор : QGLWidget(parent) { resize(300,300); // задаем размеры окна } void Widget::initializeGL() { qglClearColor(Qt::white); // заполняем экран белым цветом glEnable(GL_DEPTH_TEST); // задаем глубину проверки пикселей glShadeModel(GL_FLAT); // убираем режим сглаживания цветов glEnable(GL_CULL_FACE); // говорим, что будем строить только внешние поверхности glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); // фигуры будут закрашены с обеих сторон } void Widget::resizeGL(int nWidth, int nHeight) { glViewport(0, 0, nHeight, nHeight); // установка точки обзора glMatrixMode(GL_PROJECTION); // установка режима матрицы glLoadIdentity(); // загрузка матрицы } void Widget::paintGL() // рисование glClear(GL_COLOR_BUFFER_BIT
Вот и всё, несложными манипуляциями мы отрисовали квадрат в 3d пространстве с помощью OpenGL.
Теперь в файле main.cpp создаем экземпляр нашего класса и вызываем метод отображения.
Все исходники можно скачать в прикрепленных к статье материалах.

Пример работы программы:
| Прикрепленный файл | Размер |
|---|---|
| KatyaOGL_pt1.zip | 1.53 кб |
Qt+OpenGL — Основы. Часть 1
Данная cтатья вводная, рассчитана на знакомство с Qt+OpenGL для новичков, которые планируют изучать Qt (как кросс-платформенный инструментарий разработки ПО на языке программирования C++) + OpenGL (как графическую библиотеку).
Что потребуется новичку:
1) Qt Creator (имеет хорошую встроенную документацию и подсказки во время набора кода). Скчаать
2) doc.qt.nokia.com — официальная документация на английском языке
3) doc.crossplatform.ru — документация на русском языке
4) Обязательно прочесть про Qt и OpenGL
5) Отличная статья для начала изучения
Что мы будем делать
Поскольку данная статья посвящена конкретно основам, в нашей задаче будет следующее:
1) Разобрать как создается приложение
2) Как рисовать объекты
3) Как работать с указателем мыши и событиями(нажатие клавиш на клавиатуре и на мышке)
4) Работа с таймером
5) Создадим нашу первую банальную игру. Будем с помощью таймера, случайным образом перемещать квадрат. После наведения на квадрат указателя и кликнув по нему левой кнопки мышки, в случае попадания по квадрату, будем прибавлять к полученным очкам +1.
Создаем проект
При открытии Qt Creator, начинаем создавать новый проект.
Выбираем проект Qt Widget -> GUI приложение Qt
В разделе Информация о классе снимает галочку для создания формы.
В результате действий мы получим проект с файлами:
opengl.pro — необходим для компиляции нашего проекта
mainwindow.h — для объявления всех глобальных данных
main.cpp
mainwindow.cpp — методы нашей программы
Подключение библиотек
В файле *.pro вашего проекта в строке Qt += необходимо дописать opengl для того, чтоб подключить использование библиотеки opengl. Таким же образом подключаются и другие библиотеки.
В файле mainwindow.h — если у вас имя по умолчанию выбрано, необходимо подключить:
#include #include #include
Предопределение для нас нужных методов и переменных
Открываем mainwindow.h
В первую очередь сменим:
class MainWindow: public QMainWindow
на
class MainWindow: public QGLWidget
Это потому, что QMainWindow — класс для вывода простого окна, а т.к. мы будем работать с opengl, нам понадобится QGLWidget — это класс для вывода графики, реализующий функции библиотеки OpenGL.
Теперь предопределим переменные и методы
protected: int geese_size; // Сторона квадрата int point; // набранные очки int gdx, gdy; // Координаты квадрата int cax, cay, cbx, cby; // Координаты курсора (текущие и начальные(при зажатии клавиши мыши) для выделение области) int wax ,way; // Размеры окна нашей программы bool singling; // Для выделение области, если true то рисуем прямоугольник по координатам cax, cay, cbx, cby void self_cursor(); // метод для рисования своего курсора void initializeGL(); // Метод для инициализирования opengl void resizeGL(int nWidth, int nHeight); // Метод вызываемый после каждого изменения размера окна void paintGL(); // Метод для вывода изображения на экран void keyPressEvent(QKeyEvent *ke); // Для перехвата нажатия клавиш на клавиатуре void mouseMoveEvent(QMouseEvent *me); // Метод реагирует на перемещение указателя, но по умолчанию setMouseTracking(false) void mousePressEvent(QMouseEvent *me); // Реагирует на нажатие кнопок мыши void mouseReleaseEvent(QMouseEvent *me); // Метод реагирует на "отжатие" кнопки мыши void singling_lb(); // Рисуем рамку выделенной области void geese(); // Рисуем квадрат по которому кликать для получения очков
Так же у нас есть один слот, для того, чтоб по таймеру пересчитывать новые координаты квадрата по которому кликать.
protected slots: void geese_coord(); // Определяем координаты объектов
Принцип построения изображения
QGLWidget так устроен, что при первой инициализации класса он автоматически вызывает методы в следующем порядке:
При запуске: initializeGL()->resizeGL()->paintGL()
При изменении размера окна: resizeGL()->paintGL()
updateGL() вызывает paintGL()
initializeGL — необходимо использовать для глобальных настрое построения изображения, которые нет необходимости указывать при построении кадра.
resizeGL — служит для построения размера окна. Если в ходе работы изменится размер окна, но не изменить область просмотра, то при увеличении размера можно наблюдать непредсказуемые явления.
paintGL — этот метод будет выстраивать каждый наш кадр для отображения.
glClear(GL_COLOR_BUFFER_BIT); // чистим буфер glMatrixMode(GL_PROJECTION); // устанавливаем матрицу glLoadIdentity(); // загружаем матрицу glOrtho(0,500,500,0,1,0); // подготавливаем плоскости для матрицы // BlendFunc позволяет работать в альфа режиме, например если нам нужно указывать прозрачность // glEnable(GL_BLEND); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglColor(Qt::white); // Дальше рисуем белым цветом // renderText позволяет писать текст на экран, так же можно указать различный стиль (читаем QFont) renderText(10, 10 , 0, QString::fromUtf8("Вы набрали %1 очков:").arg(17), QFont() , 2000); // glBegin и glEnd - обозначают блок для рисования объекта(начало и конец), glBegin принимает параметр того, что нужно рисовать. glBegin(GL_POLYGON); glColor4f(0,1,0, 0.25);// Цвет которым рисовать glVertex2f(200, 300); // Точка 1 из 4 - отсчет по часовой стрелке glVertex2f(300, 300); glVertex2f(300, 400); glVertex2f(200, 400); glEnd(); swapBuffers();
Для чего двойная буферизация
PaintGL сразу картинку не рисует на экран, а заносит в буфер, а по запросу swapBuffers() заменяет текущие изображение на то, что появилось в буфере. Сама по себе буфериция позволяет более корректно заменять изображение, чтоб не происходили скачки на экране.
События клика мыши
mousePressEvent() — метод автоматически вызывается при нажатии клавиш мыши. В передаваемых параметрах можно получить различную информацию например какой именно кнопкой было сделано нажатие и по какой точке по координатам.
-Данное событие в нашем примере используется для определения куда кликнули мышью, затем если наши координаты находятся в поле квадрата, то добавляем к нашим очкам + 1 и перестраиваем наш кадр.
-Так же используем для определения начальных координат для выделения области на экране, при зажатии и перемещении указателя.
Событие перемещения указателя мыши
mouseMoveEvent() — автоматически вызывается при изменении координат указателя мыши. Но есть одно Но, по умолчанию установлено setMouseTracking(false), поэтому событие вызывается только при условии нажатия клавиш мыши, для того, чтоб метод вызывался даже без нажатия необходимо установить setMouseTracking(true).
— Данный метод мы используем для получения текущего положения указателя, чтоб перестроить выделение области или нарисовать собственный курсор.
Событие когда «отжимается» кнопка мыши
mouseReleaseEvent() — автоматически вызывается при условии «отжатия» кнопки мыши. Так же принимает различные параметры.
— В данном случае мы используем метод, чтоб стереть с экрана выделенную нами область.
Событие нажатие клавиш на клавиатуре
keyPressEvent() — метод вызывается при событии, когда нажимается кнопка на клавиатуре.
— В нашем примере, мы используем этот метод, для того, чтоб переопределить координаты нашего квадрата и переместить его в новое место.
Таймер
QTimer — позволяет нам создать поток, который будет слушать сигналы и запускать соответственные слоты.
— В данном случае мы создаем таймер, который будет ждать 750мс после чего он завершает свою работу, отправляя нам сигнал timeout() , но мы при окончании сигнала будем не останавливать работу, а снова запускать слот на переопределение координат квадрата, по которому нужно кликать для того, чтоб набрать очки.
QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(geese_coord())); timer->start(750);
Задание по данному материалу для усвоения.
Выложенный мной готовый код работает, но домашним заданием будет модифицировать код так, чтоб при запуске игры было приветствие, при нажатии на которое давалось минута на получение очков. По истечению минуты выводилось количество набранных очков и предлагалось сыграть еще раз.
Заключение!
Большинство из немногого написанного здесь, в нашей первой примитивной игре просто не нужно. Но хочу отметить еще раз: "Статья вводная, рассчитана на знакомство с Qt+OpenGL". Так же если Вы заметили написанные таким образом программы можно компилировать для любой операционной среды.
Готовый вариант рабочего кода можно взять тут:
P.S. В дальнейшем, если не против, буду продолжать написание уроков. Например следующим уроком, будем создавать игру со стрельбой например в уток.
Если есть вопросы — пишите в комментариях или в личку.
Opengl widget qt что это
На этом шаге рассмотрим простой пример OpenGL-программы в Qt.
На рис. 1 показано приложение, которое отображает четырехугольник с вершинами разного цвета, со сглаживанием.
Рис.1. Сглаживание цветов вершин четырехугольника
Рассморим некотрые особенности данного приложения. В проектном файле в опции Qt указан параметр opengl. Это нужно для того, чтобы во время компоновки программы подсоединялся модуль QtOpenGL.
TEMPLATE = app QT += opengl HEADERS = OGLQuad.h SOURCES = OGLQuad.cpp \ main.cpp win32:TARGET = ../OGLQuad
В файле main.cpp создается объект класса OGLQuad, унаследованный от класса QGLWidget.
#include #include "OGLQuad.h" int main(int argc, char** argv)
В заголовочном файле OGLQuad.h класс OGLQuad определяется как класс, наследующий QGLWidget, в котором переопределены три метода: initializeGL(), resizeGL() и paintGL().
#ifndef _OGLQuad_h_ #define _OGLQuad_h_ #include class OGLQuad : public QGLWidget < protected: virtual void initializeGL( ); virtual void resizeGL (int nWidth, int nHeight); virtual void paintGL ( ); public: OGLQuad(QWidget* pwgt = 0); >; #endif //_OGLQuad_h_
Рассмотрим реализацию методов (файл OGLQuad.cpp):
#include "OGLQuad.h" OGLQuad::OGLQuad(QWidget* pwgt) : QGLWidget(pwgt) < > > glLoadIdentity(); glViewPort(0, 0, (GLint)nWidth, (GLint)nHeight); glOrtho(0, 100, 100, 0, -1, 1); > glBegin(GL_QUADS); glColor3f(1, 0, 0); glVertex2f(0, 0); glColor3f(0, 1,0); glVertex2f(100, 0); glColor3f(0, 0, 1); glVertex2f(100, 100); glColor3f(1, 1, 1); glVertex2f(0, 100); glEnd(); >
Файлы приложения можно взять здесь.
На следующем шаге рассмотрим графические примитивы OpenGL.