| |
Разработка графического интерфейса с помощью библиотеки Qt3 | ||
---|---|---|
Пред. | Глава 8. Двух- и трехмерная графика. | След. |
OpenGL -- это стандарт API, для отображения двух- и трехмерной графики. Приложения Qt могут использовать OpenGL, посредством модуля QGL. Мы полагаем, что вы уже имеете некоторое знакомство с OpenGL. Если это не так, то рекомендуем начать изучение с посещения сайта http://www.opengl.org/.
Рисование трехмерных объектов, с помощью OpenGL, не так сложно, как может показаться на первый взгляд. Все что вам нужно сделать -- создать дочерний класс от QGLWidget, перекрыть некоторые виртуальные методы предка и связать приложение с модулем QGL и библиотекой OpenGL. Поскольку QGLWidget ведет свою родословную от QWidget, то здесь вполне применимы знания, которые вы уже получили. Основное отличие здесь состоит в том, что теперь, вместо QPainter, вам придется использовать стандартные функции рисования из OpenGL.
Рисунок 8.20. Приложение Cube.
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 находится в заголовке <qgl.h>.
Cube::Cube(QWidget *parent, const char *name) : QGLWidget(parent, name) { setFormat(QGLFormat(DoubleBuffer | DepthBuffer)); rotationX = 0; rotationY = 0; rotationZ = 0; faceColors[0] = red; faceColors[1] = green; faceColors[2] = blue; faceColors[3] = cyan; faceColors[4] = yellow; faceColors[5] = magenta; }В конструкторе вызывается QGLWidget::setFormat(), чтобы задать контекст устройства отображения OpenGL, и инициализируются приватные переменные-члены класса.
void Cube::initializeGL() { qglClearColor(black); glShadeModel(GL_FLAT); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); }Функция 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() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw(); }Функция 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 <qapplication.h> #include "cube.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); if (!QGLFormat::hasOpenGL()) qFatal("This system has no OpenGL support"); Cube cube; cube.setCaption(QObject::tr("Cube")); cube.resize(300, 300); app.setMainWidget(&cube); cube.show(); return app.exec(); }Если система не поддерживает OpenGL, то, с помощью вызова qFatal(), приложение выводит сообщение об ошибке и завершает работу.
Чтобы связать приложение Cube с модулем QGL и библиотекой OpenGL, в файл .pro нужно добавить строчку:
CONFIG += openglЗа дополнительной информацией о модуле QGL, обращайтесь к сопроводительной документации по классам QGLWidget, QGLFormat, QGLContext и QGLColormap.
Пред. | В начало | След. |
Вывод на печать. | На уровень выше | Drag and Drop. |
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |