Войти
ПрограммированиеСтатьиГрафика

OpenGL на Qt 4. Это просто! (часть 1) (3 стр)

Автор:

Наконец, рассмотрим слот updateGL(). Слоты являются как бы командами и используются Qt в механизме сигналов и слотов, но здесь этот механизм объясняться не будет. Слот updateGL() вызывает через glDraw() (функция класса QGLWidget, а не OpenGL) выполнение функции paintGL(), которая в свою очередь выполняет рисование сцены. Этот слот не нужно определять в своём классе, он просто берётся из QGLWidget. Пример:

void Scene3D::example_calculate()
{
   // вычисления
   // преобразования
   // ...

   updateGL();
   // вызывается функция paintGL() -> перерисование сцены -> обновление изображения
}

Теперь настало время для написания 3D-приложения. Приступим!

Рисуем икосаэдр

lecture1 | OpenGL на Qt 4. Это просто! (часть 1)

Мы рассмотрели основные функции Qt для работы с OpenGL и сопутствующие команды самого OpenGL. Но для минимального набора знаний нам также потребуется методы обработки нажатия клавиш и событий мыши, определенные в Qt. Всё это будет продемонстрировано в последнем листинге полной программы. А в качестве геометрической фигуры мы нарисуем икосаэдр, используя массив вершин. Программа компилировалась и тестировалась на Windows XP + Qt 4.3.0 и openSUSE 11.2 + Qt 4.5. Листинг состоит из трёх файлов: scene3D.h, scene3D.cpp и main.cpp.

scene3D.h

#ifndef SCENE3D_H 
#define SCENE3D_H

#include <QGLWidget> // подключаем класс QGLWidget

class Scene3D : public QGLWidget // класс Scene3D наследует встроенный класс QGLWidget
{ 
   private: 
      GLfloat xRot; // переменная хранит угол поворота вокруг оси X
      GLfloat yRot; // переменная хранит угол поворота вокруг оси Y   
      GLfloat zRot; // переменная хранит угол поворота вокруг оси Z
      GLfloat zTra; // переменная хранит величину трансляции оси Z 
      GLfloat nSca; // переменная отвечает за масштабирование объекта
      
      QPoint ptrMousePosition; // координата указателя мыши
      
      void scale_plus();     // приблизить сцену
      void scale_minus();    // удалиться от сцены  
      void rotate_up();      // повернуть сцену вверх
      void rotate_down();    // повернуть сцену вниз
      void rotate_left();    // повернуть сцену влево
      void rotate_right();   // повернуть сцену вправо
      void translate_down(); // транслировать сцену вниз
      void translate_up();   // транслировать сцену вверх      
      void defaultScene();   // наблюдение сцены по умолчанию      
      
      void drawAxis();       // построить оси координат
      
      void getVertexArray(); // определить массив вершин
      void getColorArray();  // определить массив цветов вершин
      void getIndexArray();  // определить массив индексов вершин      
      void drawFigure();     // построить фигуру
        
   protected:
      // метод для проведения инициализаций, связанных с OpenGL 
      /*virtual*/ void initializeGL();         
      // метод вызывается при изменении размеров окна виджета            
      /*virtual*/ void resizeGL(int nWidth, int nHeight);  
      // метод, чтобы заново перерисовать содержимое виджета  
      /*virtual*/ void paintGL();                          
       
      // метод обработки события мыши при нажатии клавиши мыши
      /*virtual*/ void mousePressEvent(QMouseEvent* pe); 
      // метод обработки события мыши при перемещении мыши  
      /*virtual*/ void mouseMoveEvent(QMouseEvent* pe);  
      // метод обработки событий мыши при отжатии клавиши мыши  
      /*virtual*/ void mouseReleaseEvent(QMouseEvent* pe); 
      // метод обработки событий колесика мыши 
      /*virtual*/ void wheelEvent(QWheelEvent* pe);  
      // методы обработки события при нажатии определенной клавиши       
      /*virtual*/ void keyPressEvent(QKeyEvent* pe);       
      
   public: 
      Scene3D(QWidget* parent = 0); // конструктор класса
}; 
#endif 

Значение ptrMousePosition типа QPoint хранит координаты на экране: x и y. Они нам понадобятся для определения положения мыши. Класс QPoint находится в модуле QtCore, который здесь явно можно не подключать. В функциях getVertexArray(), getColorArray() и getIndexArray() инициализируются массив вершин VertexArray, массив цветов вершин ColorArray и массив индексов вершин IndexArray. Чуть позже мы поговорим о них подробнее. Функция drawFigure() строит икосаэдр, используя перечисленные массивы.

Обработка пользовательского ввода

Функции обработки событий мыши mousePressEvent() и mouseMoveEvent() мы будем использовать для вращения сцены (на самом деле, вращения наблюдателя, но это эквивалентно). Первая функция вызывается нажатием клавиши мыши, вторая перемещением мыши. Нам нужно знать только, что вторая функция выполняется, когда клавиша мыши нажата — трекинг (отслеживание) выключен.  Функцию wheelEvent() — обработка событий колёсика мыши — мы будем использовать для масштабирования сцены. Также приведена функция при отжатии клавиши мыши mouseReleaseEvent(), её удобно использовать, когда нужно определить, какое действие должно выполниться мышью: например, изменение наблюдения сцены или какое-то изменение на сцене. Функция keyPressEvent() вызывается при нажатии клавиши. Все перечисленные функции принадлежат классу QWidget и переопределяются (динамический полиморфизм).

scene3D.cpp

#include <QtGui>      // подключаем модуль QtGui
//#include <QtCore>     // подключаем модуль QtCore
//#include <QtOpenGL>   // подключаем модуль QtOpenGL
#include <math.h>     // подключаем математическую библиотеку
#include "scene3D.h"  // подключаем заголовочный файл scene3D.h

const static float pi=3.141593, k=pi/180; // глобальная переменная

GLfloat VertexArray[12][3]; // декларируем массив вершин
GLfloat ColorArray[12][3];  // декларируем массив цветов вершин
GLubyte IndexArray[20][3];  // декларируем массив индексов вершин

// конструктор класса Scene3D
Scene3D::Scene3D(QWidget* parent/*= 0*/) : QGLWidget(parent) 
{ 
   // setFormat(QGLFormat(QGL::DepthBuffer)); // использовать буфер глубины
                                              // установлено по умолчанию в контексте 
   
   // начальные значения данных-членов класса
   xRot=-90; yRot=0; zRot=0; zTra=0; nSca=1; 

   // передает дальше указатель на объект parent
} 

/*virtual*/ void Scene3D::initializeGL() // инициализация
{
   // цвет для очистки буфера изображения - будет просто фон окна
   qglClearColor(Qt::white); 
   glEnable(GL_DEPTH_TEST);  // устанавливает режим проверки глубины пикселей
   glShadeModel(GL_FLAT);    // отключает режим сглаживания цветов 
   // устанавливаем режим, когда строятся только внешние поверхности 
   glEnable(GL_CULL_FACE);  
   
   getVertexArray(); // определить массив вершин
   getColorArray();  // определить массив цветов вершин
   getIndexArray();  // определить массив индексов вершин
   
   glEnableClientState(GL_VERTEX_ARRAY); // активизация массива вершин
   glEnableClientState(GL_COLOR_ARRAY);  // активизация массива цветов вершин
}

/*virtual*/void Scene3D::resizeGL(int nWidth, int nHeight) // окно виджета
{ 
   glMatrixMode(GL_PROJECTION); // устанавливает текущей проекционную матрицу
   glLoadIdentity();            // присваивает проекционной матрице единичную матрицу
 
   // отношение высоты окна виджета к его ширине
   GLfloat ratio=(GLfloat)nHeight/(GLfloat)nWidth;
   
   // мировое окно
   if (nWidth>=nHeight)
      // параметры видимости ортогональной проекции
      glOrtho(-1.0/ratio, 1.0/ratio, -1.0, 1.0, -10.0, 1.0); 
   else
      glOrtho(-1.0, 1.0, -1.0*ratio, 1.0*ratio, -10.0, 1.0);      
   // плоскости отсечения (левая, правая, верхняя, нижняя, передняя, задняя)
   
   // параметры видимости перспективной проекции
   // glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10.0);
   // плоскости отсечения (левая, правая, верхняя, нижняя, ближняя, дальняя)
  
   // поле просмотра
   glViewport(0, 0, (GLint)nWidth, (GLint)nHeight);
}

/*virtual*/ void Scene3D::paintGL() // рисование
{ 
   // glClear(GL_COLOR_BUFFER_BIT); // окно виджета очищается текущим цветом очистки
   // очистка буфера изображения и глубины
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

   // устанавливаем положение и ориентацию матрице моделирования
   glMatrixMode(GL_MODELVIEW); 
  // загружаем единичную матрицу моделирования
   glLoadIdentity();           

   // последовательные преобразования
   glScalef(nSca, nSca, nSca);        // масштабирование
   glTranslatef(0.0f, zTra, 0.0f);    // трансляция     
   glRotatef(xRot, 1.0f, 0.0f, 0.0f); // поворот вокруг оси X         
   glRotatef(yRot, 0.0f, 1.0f, 0.0f); // поворот вокруг оси Y
   glRotatef(zRot, 0.0f, 0.0f, 1.0f); // поворот вокруг оси Z
 
   drawAxis();   // рисование осей координат
   drawFigure(); // нарисовать фигуру
}  

/*virtual*/void Scene3D::mousePressEvent(QMouseEvent* pe) // нажатие клавиши мыши
{
   // при нажатии пользователем кнопки мыши переменной ptrMousePosition 
   // будет присвоена координата указателя мыши 
   ptrMousePosition = pe->pos();  

   // ptrMousePosition = (*pe).pos(); // можно и так написать                          
} 

/*virtual*/void Scene3D::mouseReleaseEvent(QMouseEvent* pe) // отжатие клавиши мыши
{ 
   // некоторые функции, которые должны выполняться при отжатии клавиши мыши            
} 

// изменение положения стрелки мыши
/*virtual*/void Scene3D::mouseMoveEvent(QMouseEvent* pe) 
{   
   // вычисление углов поворота
   xRot += 180/nSca*(GLfloat)(pe->y()-ptrMousePosition.y())/height(); 
   zRot += 180/nSca*(GLfloat)(pe->x()-ptrMousePosition.x())/width(); 
   
   ptrMousePosition = pe->pos();
   
   updateGL(); // обновление изображения
}

// вращение колёсика мыши
/*virtual*/void Scene3D::wheelEvent(QWheelEvent* pe) 
{ 
   if ((pe->delta())>0) scale_plus(); else if ((pe->delta())<0) scale_minus();   
    
   updateGL(); // обновление изображения          
}

// нажатие определенной клавиши
/*virtual*/void Scene3D::keyPressEvent(QKeyEvent* pe) 
{  
   switch (pe->key())
   {         
      case Qt::Key_Plus:  
         scale_plus();     // приблизить сцену
      break;
         
      case Qt::Key_Equal:  
         scale_plus();     // приблизить сцену   
      break;
         
      case Qt::Key_Minus: 
         scale_minus();    // удалиться от сцены
      break;

      case Qt::Key_Up:  
         rotate_up();      // повернуть сцену вверх
      break;
         
      case Qt::Key_Down:  
         rotate_down();    // повернуть сцену вниз
      break;         
         
      case Qt::Key_Left:  
        rotate_left();     // повернуть сцену влево
      break;
         
      case Qt::Key_Right:  
         rotate_right();   // повернуть сцену вправо
      break;                           
         
      case Qt::Key_Z:
         translate_down(); // транслировать сцену вниз
      break;  
         
      case Qt::Key_X:
         translate_up();   // транслировать сцену вверх
      break; 
         
      case Qt::Key_Space:  // клавиша пробела
         defaultScene();   // возвращение значений по умолчанию
      break;
      
      case Qt::Key_Escape: // клавиша "эскейп"
         this->close();    // завершает приложение
      break;                                                           
   }
   
   updateGL(); // обновление изображения
}

void Scene3D::scale_plus() // приблизить сцену
{
   nSca = nSca*1.1;
}

void Scene3D::scale_minus() // удалиться от сцены 
{
   nSca = nSca/1.1;
}

void Scene3D::rotate_up() // повернуть сцену вверх
{
   xRot += 1.0;
}

void Scene3D::rotate_down() // повернуть сцену вниз
{
   xRot -= 1.0;
}

void Scene3D::rotate_left() // повернуть сцену влево
{
   zRot += 1.0;
}

void Scene3D::rotate_right() // повернуть сцену вправо
{
   zRot -= 1.0;
}

void Scene3D::translate_down() // транслировать сцену вниз
{
   zTra -= 0.05;
}

void Scene3D::translate_up() // транслировать сцену вверх
{
   zTra += 0.05;
}

void Scene3D::defaultScene() // наблюдение сцены по умолчанию
{
   xRot=-90; yRot=0; zRot=0; zTra=0; nSca=1;
}

void Scene3D::drawAxis() // построить оси координат
{
   // устанавливаем ширину линии приближенно в пикселях
   glLineWidth(3.0f); 
   // до вызова команды ширина равна 1 пикселю по умолчанию
   
   // устанавливаем цвет последующих примитивов
   glColor4f(1.00f, 0.00f, 0.00f, 1.0f); 
   // ось x красного цвета
   glBegin(GL_LINES); // построение линии
      glVertex3f( 1.0f,  0.0f,  0.0f); // первая точка
      glVertex3f(-1.0f,  0.0f,  0.0f); // вторая точка
   glEnd();  
   
   QColor halfGreen(0, 128, 0, 255);
   qglColor(halfGreen);
   glBegin(GL_LINES);
      // ось y зеленого цвета
      glVertex3f( 0.0f,  1.0f,  0.0f);
      glVertex3f( 0.0f, -1.0f,  0.0f);
  
      glColor4f(0.00f, 0.00f, 1.00f, 1.0f);
      // ось z синего цвета
      glVertex3f( 0.0f,  0.0f,  1.0f);
      glVertex3f( 0.0f,  0.0f, -1.0f);
   glEnd();
}

void Scene3D::getVertexArray() // определить массив вершин
{
   GLfloat R=0.75; // радиус сферы
   
   // начальные значения для икосаэдра
   GLfloat a=4*R/sqrt(10+2*sqrt(5)); // сторона икосаэдра
   GLfloat alpha=acos((1-a*a/2/R/R)); // первый угол поворота по тэта 
   // cos(alpha)=(1-a*a/2/R/R)
   
   // вычисляем точки икосаэдра
   //0 точка
   VertexArray[0][0]=0;   // x
   VertexArray[0][1]=0;   // y
   VertexArray[0][2]=R;   // z   
   
   //1 точка
   VertexArray[1][0]=R*sin(alpha)*sin(0);
   VertexArray[1][1]=R*sin(alpha)*cos(0); 
   VertexArray[1][2]=R*cos(alpha);
   
   //2 точка
   VertexArray[2][0]=R*sin(alpha)*sin(72*k);
   VertexArray[2][1]=R*sin(alpha)*cos(72*k);
   VertexArray[2][2]=R*cos(alpha);
   
   //3 точка
   VertexArray[3][0]=R*sin(alpha)*sin(2*72*k);
   VertexArray[3][1]=R*sin(alpha)*cos(2*72*k);
   VertexArray[3][2]=R*cos(alpha); 
   
   //4 точка
   VertexArray[4][0]=R*sin(alpha)*sin(3*72*k);
   VertexArray[4][1]=R*sin(alpha)*cos(3*72*k);
   VertexArray[4][2]=R*cos(alpha);  
   
   //5 точка
   VertexArray[5][0]=R*sin(alpha)*sin(4*72*k);
   VertexArray[5][1]=R*sin(alpha)*cos(4*72*k);
   VertexArray[5][2]=R*cos(alpha);
   
   //6 точка
   VertexArray[6][0]=R*sin(pi-alpha)*sin(-36*k);
   VertexArray[6][1]=R*sin(pi-alpha)*cos(-36*k);
   VertexArray[6][2]=R*cos(pi-alpha);
   
   //7 точка
   VertexArray[7][0]=R*sin(pi-alpha)*sin(36*k);
   VertexArray[7][1]=R*sin(pi-alpha)*cos(36*k);
   VertexArray[7][2]=R*cos(pi-alpha);
   
   //8 точка
   VertexArray[8][0]=R*sin(pi-alpha)*sin((36+72)*k);
   VertexArray[8][1]=R*sin(pi-alpha)*cos((36+72)*k);
   VertexArray[8][2]=R*cos(pi-alpha);  
   
   //9 точка   
   VertexArray[9][0]=R*sin(pi-alpha)*sin((36+2*72)*k);
   VertexArray[9][1]=R*sin(pi-alpha)*cos((36+2*72)*k);
   VertexArray[9][2]=R*cos(pi-alpha);
   
   //10 точка
   VertexArray[10][0]=R*sin(pi-alpha)*sin((36+3*72)*k);
   VertexArray[10][1]=R*sin(pi-alpha)*cos((36+3*72)*k);
   VertexArray[10][2]=R*cos(pi-alpha); 
   
   //11 точка
   VertexArray[11][0]=0;
   VertexArray[11][1]=0;
   VertexArray[11][2]=-R;    
}

void Scene3D::getColorArray() // определить массив цветов вершин
{
   for (int i=0; i<12; i++)
   {      
      ColorArray[i][0]=0.1f*(qrand()%11); // R - красная составляющая
      ColorArray[i][1]=0.1f*(qrand()%11); // G - зелёная составляющая
      ColorArray[i][2]=0.1f*(qrand()%11); // B - синяя составляющая
      // qrand()%11 - псевдослучайное число от 0 до 10 
   }
}

void Scene3D::getIndexArray()   // определить массив индексов
{
   // 0 треугольник
   IndexArray[0][0]=0; // индекс (номер) 1-ой вершины
   IndexArray[0][1]=2; // индекс (номер) 2-ой вершины
   IndexArray[0][2]=1; // индекс (номер) 3-ей вершины
   
   // 1 треугольник
   IndexArray[1][0]=0;
   IndexArray[1][1]=3;
   IndexArray[1][2]=2;
   
   // 2 треугольник
   IndexArray[2][0]=0;
   IndexArray[2][1]=4;
   IndexArray[2][2]=3;
   
   // 3 треугольник
   IndexArray[3][0]=0;
   IndexArray[3][1]=5;
   IndexArray[3][2]=4;
   
   // 4 треугольник    
   IndexArray[4][0]=0;
   IndexArray[4][1]=1;
   IndexArray[4][2]=5;
   
   // 5 треугольник
   IndexArray[5][0]=6;
   IndexArray[5][1]=1;
   IndexArray[5][2]=7;
   
   // 6 треугольник
   IndexArray[6][0]=7;
   IndexArray[6][1]=1;
   IndexArray[6][2]=2;
   
   // 7 треугольник
   IndexArray[7][0]=7;
   IndexArray[7][1]=2;
   IndexArray[7][2]=8;
   
   // 8 треугольник
   IndexArray[8][0]=8;
   IndexArray[8][1]=2;
   IndexArray[8][2]=3;
   
   // 9 треугольник
   IndexArray[9][0]=8;
   IndexArray[9][1]=3;
   IndexArray[9][2]=9;
   
   // 10 треугольник
   IndexArray[10][0]=9;
   IndexArray[10][1]=3;
   IndexArray[10][2]=4;
   
   // 11 треугольник
   IndexArray[11][0]=9;
   IndexArray[11][1]=4;
   IndexArray[11][2]=10;
   
   // 12 треугольник
   IndexArray[12][0]=10;
   IndexArray[12][1]=4;
   IndexArray[12][2]=5;
   
   // 13 треугольник
   IndexArray[13][0]=10;
   IndexArray[13][1]=5;
   IndexArray[13][2]=6;
   
   // 14 треугольник
   IndexArray[14][0]=6;
   IndexArray[14][1]=5;
   IndexArray[14][2]=1;
   
   // 15 треугольник
   IndexArray[15][0]=7;
   IndexArray[15][1]=11;
   IndexArray[15][2]=6;
   
   // 16 треугольник
   IndexArray[16][0]=8;
   IndexArray[16][1]=11;
   IndexArray[16][2]=7;
   
   // 17 треугольник
   IndexArray[17][0]=9;
   IndexArray[17][1]=11;
   IndexArray[17][2]=8;
   
   // 18 треугольник
   IndexArray[18][0]=10;
   IndexArray[18][1]=11;
   IndexArray[18][2]=9;
   
   // 19 треугольник
   IndexArray[19][0]=6;
   IndexArray[19][1]=11;
   IndexArray[19][2]=10; 
}

void Scene3D::drawFigure() // построить фигуру
{
   // указываем, откуда нужно извлечь данные о массиве вершин
   glVertexPointer(3, GL_FLOAT, 0, VertexArray); 
   // указываем, откуда нужно извлечь данные о массиве цветов вершин
   glColorPointer(3, GL_FLOAT, 0, ColorArray);  
   // используя массивы вершин и индексов, строим поверхности
   glDrawElements(GL_TRIANGLES, 60, GL_UNSIGNED_BYTE, IndexArray);
}

Используя массивы вершин

В начале программы мы декларируем массивы вершин, цветов и индексов. Чтобы иметь представление, скажу, что икосаэдр имеет 12 вершин и 20 (равносторонних) треугольников. В массив вершин мы запишем декартовы координаты вершин: x, y, z. В массив цветов запишем цвета вершин с тремя составляющими RGB, сгенерировав их псевдослучайно. В массив индексов запишем индексы вершин — три номера (индекса) вершин. Для чего это всё нужно? Чтобы построить какую-нибудь фигуру, мы должны как минимум знать вершины фигуры и знать каким способом из них построить примитивы, например, треугольники. Другими словами, как соединить вершины, чтобы получить примитивы. Поэтому в двумерный массив вершин мы заносим декартовы координаты вершин, а в массив индексов (фактически массив треугольников) записываем последовательные номера вершин (фактически три последовательные вершины треугольника). Каждой вершине также можно дать цвет - для этого и служит массив цветов. Координаты вершин мы рассчитываем сами, индексы примитива также задаём самостоятельно. Но как вы понимаете, их можно загрузить из файла. А перед этим создать модель в визуальном 3D-графическом редакторе, например в Blender, сохранить модель в файл и, зная структуру файла, сделать загрузчик моделей. Более того, массивы вершин, цветов, индексов и т.д. можно сразу записать в видеопамять графической карты, что существенно сэкономит время на их передачу, а значит повысит скорость рисования. Но здесь мы не будем этого делать, просто отошлём читателя к теме буферных объектов.

Настройки

В конструкторе класса Scene3D мы задаём начальные значения. Команда setFormat(QGLFormat(QGL::DepthBuffer)) указывает, что нужно использовать буфер глубины, о котором говорилось раньше. Эта команда эквивалентна glutInitDisplayMode(GLUT_DEPHT) библиотеки GLUT. Соответственно, команда setFormat() класса QGLContext устанавливает контекст (режим работы) OpenGL. Вообще говоря, буфер глубины можно явно и не устанавливать, т.к. он по умолчанию автоматически установится в контексте. Так же как и состояние RGBA (RGBA mode). Обратите внимание, что в конструкторе мы поворачиваем сцену вокруг оси x на -90 градусов. 

При инициализации initializeGL() мы отключаем режим сглаживания цветов командой glShadeModel(GL_FLAT). В этом случае примитив закрашивается цветом последней вершины. Команда glShadeModel(GL_SMOOTH) установит обратно режим сглаживания цветов, если это будет нужно.

Чтобы активировать массивы вершин и цветов, используются соответственно команды glEnableClientState(GL_VERTEX_ARRAY) и glEnableClientState(GL_COLOR_ARRAY).  Слово «клиент» (client) здесь относится к приложению. Дело в том, что работа с OpenGL происходит по принципу «клиент-сервер». Клиент — это наше приложение, а сервер — это там, где выполняется реализация OpenGL (как правило графический ускоритель).

В функции resizeGL() мы изменяем мировое окно и подстраиваем его под размеры окна виджета. Теперь сцена не будет растягиваться по ширине при развертывании окна на весь экран. Мы могли бы и не изменять мировое окно, а преобразовать поле просмотра.

Попробуйте закомментировать glOrtho() и раскомментировать glFrustum(). В результате вы увидите пустое окно. Подумайте почему)

Ещё раз про обработку сигналов

Как уже говорилось, функция mousePressEvent() вызывается автоматически при нажатии клавиши мыши. В теле этой функции мы запоминаем координату мыши в момент нажатия с помощью метода pos() класса QPoint. Координата мыши понадобится нам при вычислении углов поворота в теле mouseMoveEvent(), используя методы x(), y() класса QPoint и методы height() и width() класса QWidget. Обратите внимание, например, на такую запись Qt::Key_Escape в функции keyPressEvent(). В данном случае Qt есть пространство имен (namespace), которому принадлежит константа Key_Escape. Эта константа имеет значение 0x01000000 (шестнадцатиричная система). Очевидно, что при нажатии клавиши должно производиться сравнение с указанными константными значениями клавиш. Вам не составит труда самостоятельно разобраться, что происходит в функциях wheelEvent(), keyPressEvent(), а так же в scale_plus(), scale_minus(), rotate_up(), rotate_down(), rotate_left(), rotate_right(), translate_down(), translate_up() и defaultScene().

Собираем всё вместе и не только!

Наконец, в функции drawFigure() мы строим фигуру с помощью массивов. Сначала указываем, откуда нужно извлечь данные о массивах, т.е. какие массивы мы будем использовать. Это осуществляют команды glVertexPointer() и glColorPointer(). Команда glVertexPointer() устанавливает текущий массив вершин, команда glColorPointer() — текущий массив значений цвета. Третий параметр 0 для обоих команд означает, что промежуток между элементами массивов равен нулю байтам, т.е. мы считываем все элементы подряд. Первый параметр 3 для glVertexPointer() есть ничто иное как три декартовы координаты (x,y,z) вершины, а первый параметр 3 для glColorPointer() — три компонеты цвета RGB вершины. Потом мы указываем, что и как нужно построить и откуда взять массив индексов с помощью glDrawElements(). Второй параметр 60 в glDrawElements() вычисляется очень легко: 60=20(треугольников)*3(вершины треугольника)= числу элементов массива. Кстати, если массив индексов достаточно большой, то использование типа GLubyte (GL_UNSIGNED_BYTE) для него выбрано неудачно. Недостатком использования этих методов является то, что мы не сможем построить примитивы своими собственными цветами, как если бы мы рисовали через glBegin() и glEnd() с предварительной командой glColor4f(). Поэтому мы пошли на хитрость, а какую именно, подумайте сами.

Страницы: 1 2 3 4 Следующая »

#3D, #графика, #OpenGL, #Qt, #Qt4

19 января 2011 (Обновление: 2 янв. 2013)

Комментарии [98]