OpenGL на Qt 4. Это просто! (часть 2)
Автор: xxz
Во 2-ой части продолжается рассмотрение работы с OpenGL на Qt 4. Читатель познакомится с настройкой контекста OpenGL, созданием анимации и простым наложением текстур. Заодно в рамках «чистого» OpenGL будет рассмотрена интерактивная графика с помощью режима выбора (выбираем объекты на экране для дальнейшей манипуляции с ними), в рамках «чистого» Qt будет продемонстрировано создание главного окна, меню (menu bar), задействованы механизм сигналов и слотов и технология дерева объектов (иерархии объектов).
Список частей:
Введение
An opensource of this lecture
scene3D.h
glext.h
scene3D.cpp
mainwindow.h
mainwindow.cpp
main.cpp
pictures.qrc
lecture2.pro
Контекст OpenGL
Классы QGLFormat и QGLContext
Настройка контекста
Версии OpenGL
Анимация
Класс QTimer
QTimer + сигналы и слоты
Другой метод: событие таймера
Синхронизация кадров с дисплеем
Текстуры
Инициализация текстур
Создание текстур
Наложение текстур
Удаление текстур
Пример: наложение текстуры на шар
Рисуем два интерактивных октаэдра
Используя стеки матриц
Накладываемые текстуры
Используя режим выбора
Создаём главное окно и меню
Дерево объектов
Включение изображения в исполняемый файл
Заключение
Литература
Введение
Вопросы, связанные с инициализацией рендеринга OpenGL, с настройкой окна виджета и выводом изображения были рассмотрены в 1-ой статье. Также там была нарисована простая трёхмерная фигура с использованием массивов вершин. Во второй части хотелось бы добавить вопросы, связанные с дополнительной настройкой контекста OpenGL, анимацией и наложением текстур. Может показаться, что всё отличие от многократного изложения темы наложения текстур сводится к загрузке изображения средствами Qt, но это не совсем так: Qt имеет собственные методы для работы с текстурами. В свою очередь, анимация сводится к работе с таймером в Qt. Мне хотелось бы дать столько информации читателю, сколько уже достаточно, чтобы ввести в курс дела и чтобы, отталкиваясь от неё, читатель смог продолжить изучение интересующих вопросов более подробно. Мы будем использовать собственный класс Scene3D, введённый в 1-ой статье. Стиль изложения будет таким, как и раньше: вначале читателю предлагается только исходный код программы статьи. А затем последуют объяснение базовых понятий и подробный разбор самой программы. Рассказать обо всём невозможно, зато вполне возможно предоставить начальные знания, отталкиваясь от которых, можно продолжить совершенствовать свои навыки. Итак, начнём!
An opensource of this lecture
scene3D.h
#ifndef SCENE3D_H #define SCENE3D_H #include <QGLWidget> class Scene3D : public QGLWidget { Q_OBJECT private: GLfloat ratio; GLfloat xRot1; GLfloat yRot1; GLfloat zRot1; GLfloat zTra1; GLfloat xRot2; GLfloat yRot2; GLfloat zRot2; GLfloat zTra2; QTimer *timer; void getVerTexArrays(); void getIndexArray( ); void genTextures( ); void drawFigure( ); void selectFigures( QPoint mp); protected: void initializeGL( ); void resizeGL( int nWidth, int nHeight); void paintGL( ); void mousePressEvent( QMouseEvent* pe); private slots: void change( ); void changeTex( ); void stopTmr( ); void startTmr( ); public: Scene3D( QWidget* parent = 0); ~Scene3D( ); }; #endif
glext.h
Download core API and extension header file "glext.h" from this site.
scene3D.cpp
#include <QtGui> #include <math.h> #include "scene3D.h" #include "glext.h" const GLfloat pi=3.141593, k=pi/180; GLint viewport[4]; GLfloat VertexArray[6][3]; GLfloat TextureArray[6][2]; GLuint IndexArray[8][3]; GLuint textureID[2]; GLint signs[2]={1, 1}; bool motionParameters[2]={1, 1}; int textureParameters[2]={0, 1}; Scene3D::Scene3D(QWidget* parent) : QGLWidget( QGLFormat( QGL::SampleBuffers), parent) { xRot1=-90.0f; yRot1=0.0f; zRot1=0.0f; zTra1=0.0f; xRot2=-90.0f; yRot2=0.0f; zRot2=0.0f; zTra2=0.0f; timer = new QTimer( this); connect( timer, SIGNAL( timeout( )), this, SLOT( change( ))); timer->start( 10); QGLFormat frmt; frmt.setSwapInterval( 1); setFormat( frmt); } Scene3D::~Scene3D( ) { } void Scene3D::initializeGL( ) { qglClearColor( Qt::black); glEnable( GL_DEPTH_TEST); glEnable( GL_CULL_FACE); glEnable( GL_TEXTURE_2D); glEnable( GL_MULTISAMPLE); getVerTexArrays( ); getIndexArray( ); genTextures( ); glEnableClientState( GL_VERTEX_ARRAY); glEnableClientState( GL_TEXTURE_COORD_ARRAY); } void Scene3D::resizeGL( int nWidth, int nHeight) { glMatrixMode( GL_PROJECTION); glLoadIdentity( ); ratio=( GLfloat)nHeight/( GLfloat)nWidth; if ( nWidth>=nHeight) glOrtho( -2.0/ratio, 2.0/ratio, -2.0, 2.0, -10.0, 10.0); else glOrtho( -2.0, 2.0, -2.0*ratio, 2.0*ratio, -10.0, 10.0); glViewport( 0, 0, ( GLint)nWidth, ( GLint)nHeight); glGetIntegerv( GL_VIEWPORT, viewport); } void Scene3D::paintGL( ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode( GL_MODELVIEW); glLoadIdentity( ); glPushMatrix( ); glTranslatef( -1.0f/ratio, zTra1, 0.0f); glRotatef( xRot1, 1.0f, 0.0f, 0.0f); glRotatef( yRot1, 0.0f, 1.0f, 0.0f); glRotatef( zRot1, 0.0f, 0.0f, 1.0f); glBindTexture( GL_TEXTURE_2D, textureID[textureParameters[0]]); drawFigure( ); glPopMatrix( ); glPushMatrix( ); glTranslatef( 1.0f/ratio, zTra2, 0.0f); glRotatef( xRot2, 1.0f, 0.0f, 0.0f); glRotatef( yRot2, 0.0f, 1.0f, 0.0f); glRotatef( zRot2, 0.0f, 0.0f, 1.0f); glBindTexture( GL_TEXTURE_2D, textureID[textureParameters[1]]); drawFigure( ); glPopMatrix( ); } void Scene3D::mousePressEvent( QMouseEvent* pe) { QPoint mousePosition = pe->pos( ); this->selectFigures( mousePosition); } void Scene3D::genTextures( ) { textureID[0]=bindTexture( QPixmap( QString( "../textures/picture1.jpg")), GL_TEXTURE_2D); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); textureID[1]=bindTexture( QPixmap( QString( "../textures/picture2.jpg")), GL_TEXTURE_2D); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } void Scene3D::getVerTexArrays( ) { GLfloat R=1.0f; GLfloat alpha=pi/2; VertexArray[0][0]=0.0f; VertexArray[0][1]=0.0f; VertexArray[0][2]=R; TextureArray[0][0]=0.5f; TextureArray[0][1]=1.0f; VertexArray[1][0]=R*sin( alpha)*sin( 0); VertexArray[1][1]=R*sin( alpha)*cos( 0); VertexArray[1][2]=R*cos( alpha); TextureArray[1][0]=1.0f; TextureArray[1][1]=0.0f; VertexArray[2][0]=R*sin( alpha)*sin( pi/2); VertexArray[2][1]=R*sin( alpha)*cos( pi/2); VertexArray[2][2]=R*cos( alpha); TextureArray[2][0]=0.0f; TextureArray[2][1]=0.0f; VertexArray[3][0]=R*sin( alpha)*sin( pi); VertexArray[3][1]=R*sin( alpha)*cos( pi); VertexArray[3][2]=R*cos( alpha); TextureArray[3][0]=1.0f; TextureArray[3][1]=0.0f; VertexArray[4][0]=R*sin( alpha)*sin( 3*pi/2); VertexArray[4][1]=R*sin( alpha)*cos( 3*pi/2); VertexArray[4][2]=R*cos( alpha); TextureArray[4][0]=0.0f; TextureArray[4][1]=0.0f; VertexArray[5][0]=0.0f; VertexArray[5][1]=0.0f; VertexArray[5][2]=-R; TextureArray[5][0]=0.5f; TextureArray[5][1]=1.0f; } void Scene3D::getIndexArray( ) { IndexArray[0][0]=0; IndexArray[0][1]=2; IndexArray[0][2]=1; IndexArray[1][0]=0; IndexArray[1][1]=3; IndexArray[1][2]=2; IndexArray[2][0]=0; IndexArray[2][1]=4; IndexArray[2][2]=3; IndexArray[3][0]=0; IndexArray[3][1]=1; IndexArray[3][2]=4; IndexArray[4][0]=5; IndexArray[4][1]=1; IndexArray[4][2]=2; IndexArray[5][0]=5; IndexArray[5][1]=2; IndexArray[5][2]=3; IndexArray[6][0]=5; IndexArray[6][1]=3; IndexArray[6][2]=4; IndexArray[7][0]=5; IndexArray[7][1]=4; IndexArray[7][2]=1; } void Scene3D::drawFigure( ) { glVertexPointer( 3, GL_FLOAT, 0, VertexArray); glTexCoordPointer( 2, GL_FLOAT, 0, TextureArray); glDrawElements( GL_TRIANGLES, 24, GL_UNSIGNED_INT, IndexArray); } void Scene3D::selectFigures( QPoint mp) { GLuint selectBuffer[4]; GLint hits; glSelectBuffer( 4, selectBuffer); glMatrixMode( GL_PROJECTION); glPushMatrix( ); glRenderMode( GL_SELECT); glLoadIdentity( ); gluPickMatrix( ( GLdouble)mp.x( ), ( GLdouble)( viewport[3]-mp.y( )), 1.0, 1.0, viewport); if ( width( )>=height( )) glOrtho( -2.0/ratio, 2.0/ratio, -2.0, 2.0, -10.0, 10.0); else glOrtho( -2.0, 2.0, -2.0*ratio, 2.0*ratio, -10.0, 10.0); glMatrixMode( GL_MODELVIEW); glLoadIdentity( ); glInitNames( ); glPushName( 0); glPushMatrix( ); glTranslatef( -1.0f/ratio, zTra1, 0.0f); glRotatef( xRot1, 1.0f, 0.0f, 0.0f); glRotatef( yRot1, 0.0f, 1.0f, 0.0f); glRotatef( zRot1, 0.0f, 0.0f, 1.0f); glLoadName( 1); drawFigure( ); glPopMatrix( ); glPushMatrix( ); glTranslatef( 1.0f/ratio, zTra2, 0.0f); glRotatef( xRot2, 1.0f, 0.0f, 0.0f); glRotatef( yRot2, 0.0f, 1.0f, 0.0f); glRotatef( zRot2, 0.0f, 0.0f, 1.0f); glLoadName( 2); drawFigure( ); glPopMatrix( ); hits=glRenderMode( GL_RENDER); if ( hits>0) { int figureName=selectBuffer[3]; if ( motionParameters[figureName-1]) motionParameters[figureName-1]=0; else motionParameters[figureName-1]=1; } glMatrixMode( GL_PROJECTION); glPopMatrix( ); } void Scene3D::change( ) { if ( motionParameters[0]) { xRot1 -=0.05f; yRot1 -=0.05f; zRot1 +=0.05f; if ( ( xRot1>360)||( xRot1<-360)) xRot1=0.0f; if ( ( yRot1>360)||( yRot1<-360)) yRot1=0.0f; if ( ( zRot1>360)||( zRot1<-360)) zRot1=0.0f; if ( abs( zTra1)>0.5f) signs[0] *=-1; zTra1 -=signs[0]*0.005f; } if ( motionParameters[1]) { xRot2 +=0.05f; yRot2 +=0.05f; zRot2 -=0.05f; if ( ( xRot2>360)||( xRot2<-360)) xRot2=0.0f; if ( ( yRot2>360)||( yRot2<-360)) yRot2=0.0f; if ( ( zRot2>360)||( zRot2<-360)) zRot2=0.0f; if ( abs( zTra2)>0.5f) signs[1] *=-1; zTra2 +=signs[1]*0.005f; } updateGL( ); } void Scene3D::changeTex( ) { if ( textureParameters[0]==0) { textureParameters[0]=1; textureParameters[1]=0; } else { textureParameters[0]=0; textureParameters[1]=1; } updateGL( ); } void Scene3D::stopTmr( ) { timer->stop( ); } void Scene3D::startTmr( ) { timer->start( ); }
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "scene3D.h" class MainWindow : public QMainWindow { Q_OBJECT private: Scene3D* scene1; QMenu* texture_menu; QAction* changeTexAct; QMenu* timer_menu; QAction* stopTimAct; QAction* startTimAct; void createActions(); void createMenus( ); public: MainWindow( ); }; #endif
mainwindow.cpp
#include <QtGui> #include "mainwindow.h" #include "scene3D.h" MainWindow::MainWindow() { scene1 = new Scene3D; setCentralWidget( scene1); this->setWindowTitle( tr( "lecture2")); createActions( ); createMenus( ); } void MainWindow::createActions( ) { changeTexAct = new QAction( tr( "Change"), this); connect( changeTexAct, SIGNAL( triggered( )), scene1, SLOT( changeTex( ))); stopTimAct = new QAction( tr( "Stop"), this); connect( stopTimAct, SIGNAL( triggered( )), scene1, SLOT( stopTmr( ))); startTimAct = new QAction( tr( "Start"), this); connect( startTimAct, SIGNAL( triggered( )), scene1, SLOT( startTmr( ))); } void MainWindow::createMenus( ) { texture_menu = menuBar( )->addMenu( tr( "Texture")); texture_menu->addAction( changeTexAct); timer_menu = menuBar( )->addMenu( tr( "Animation")); timer_menu->addAction( stopTimAct); timer_menu->addAction( startTimAct); }
main.cpp
#include <QtGui> #include "mainwindow.h" int main(int argc, char** argv) { QApplication app( argc, argv); MainWindow mainwindow1; mainwindow1.resize( 500, 500); mainwindow1.showMaximized( ); return app.exec( ); }
pictures.qrc
See you pictures here.
<RCC> <qresource> <file>textures/picture1.jpg</file> <file>textures/picture2.jpg</file> </qresource> </RCC>
lecture2.pro
TEMPLATE = app RESOURCES = pictures.qrc HEADERS += glext.h scene3D.h mainwindow.h SOURCES += main.cpp scene3D.cpp mainwindow.cpp QT += opengl
#3D, #графика, #OpenGL, #Qt, #Qt4
10 августа 2011 (Обновление: 2 янв 2013)