Урок 18 по OpenGL. Квадратирование



Квадратирование (quadratic) - это способ отображения сложных объектов, обычно для рисования которых, нужно несколько циклов FOR и некоторые основы тригонометрии. (Прим. переводчика: квадратирование - представление сложных объектов с использованием четырехугольников).

  Мы будем использовать код 7-ого урока. Мы добавим 7 переменных и изменим текстуру для разнообразия.

 

#include <windows.h>         // Заголовочный файл для Windows

#include <stdio.h>           // Заголовочный файл для стандартной библиотеки ввода/вывода

#include <gl\gl.h>           // Заголовочный файл для библиотеки OpenGL32

#include <gl\glu.h>          // Заголовочный файл для библиотеки GLu32

#include <gl\glaux.h>        // Заголовочный файл для библиотеки GLaux

 

HDC           hDC=NULL;      // Приватный контекст устройства GDI

HGLRC         hRC=NULL;      // Постоянный контекст рендеринга

HWND          hWnd=NULL;     // Сохраняет дескриптор окна

HINSTANCE     hInstance;     // Сохраняет экземпляр приложения

 

bool   keys[256];            // Массив для работы с клавиатурой

bool   active=TRUE;          // Флаг активации окна, по умолчанию = TRUE

bool   fullscreen=TRUE;      // Флаг полноэкранного вывода

bool   light;                // Освещение Вкл/Выкл

bool   lp;                   // L нажата?

bool   fp;                   // F нажата?

bool   sp;                   // Пробел нажат? ( НОВОЕ )

 

int    part1;                // Начало диска ( НОВОЕ )

int    part2;                // Конец диска  ( НОВОЕ )

int    p1=0;                 // Приращение 1 ( НОВОЕ )

int    p2=1;                 // Приращение 2 ( НОВОЕ )

 

GLfloat xrot;                // X вращение

GLfloat yrot;                // Y вращение

GLfloat xspeed;              // X скорость вращения

GLfloat yspeed;              // Y скорость вращения

 

GLfloat       z=-5.0f;       // Глубина экрана

 

GLUquadricObj *quadratic;    // Место для хранения объекта Quadratic ( НОВОЕ )

 

GLfloat LightAmbient[]=  { 0.5f, 0.5f, 0.5f, 1.0f };       // Фоновое значение света

GLfloat LightDiffuse[]=  { 1.0f, 1.0f, 1.0f, 1.0f };       // Значение рассеянного света

GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };       // Позиция источника

 

GLuint filter;                                 // Какой фильтр использовать

GLuint texture[3];                             // Место для 3-х текстур

GLuint object=0;                               // Какой объект рисовать ( НОВОЕ )

 

LRESULT       CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);       // Объявление WndProc

 

Ок. Теперь обратимся к InitGL(). Мы собираемся добавить 3 строчки кода, для инициализации нашего квадратичного объекта. Добавьте эти 3 строки после инициализации освещения (light1), но до строки return true. Первая строка инициализирует квадратичный объект и создает указатель на то место в памяти, где он будет содержаться. Если он не может быть создан, то будет возвращен 0. Вторая строка кода создает плавные нормали на квадратичном объекте, поэтому освещение будет выглядеть хорошо. Другое возможное значение - GL_NONE и GL_FLAT. Наконец, мы включим текстурирование на нашем квадратичном объекте.

 

       quadratic=gluNewQuadric();     // Создаем указатель на квадратичный объект ( НОВОЕ )

       gluQuadricNormals(quadratic, GLU_SMOOTH); // Создаем плавные нормали ( НОВОЕ )

       gluQuadricTexture(quadratic, GL_TRUE);    // Создаем координаты текстуры ( НОВОЕ )

  Теперь я решил оставить куб в этом уроке, так, чтобы вы смогли увидеть, как текстура отображается на квадратичном объекте. Я решил поместить куб в отдельную функцию, поэтому, когда мы напишем функцию рисования, она станет намного проще. Все узнают этот код.

 

GLvoid glDrawCube()              // Рисование куба

{

       glBegin(GL_QUADS);               // Начинаем рисовать четырехугольники

 

       // Передняя сторона

       glNormal3f( 0.0f, 0.0f, 1.0f);   // Нормаль вперед

       glTexCoord2f(0.0f, 0.0f);

       glVertex3f(-1.0f, -1.0f,  1.0f); // Низ Лево на текстуре и четырехугольнике

       glTexCoord2f(1.0f, 0.0f);

       glVertex3f( 1.0f, -1.0f,  1.0f); // Низ Право на текстуре и четырехугольнике

       glTexCoord2f(1.0f, 1.0f);

       glVertex3f( 1.0f,  1.0f,  1.0f); // Верх Право на текстуре и четырехугольнике

       glTexCoord2f(0.0f, 1.0f);

       glVertex3f(-1.0f,  1.0f,  1.0f); // Верх Лево на текстуре и четырехугольнике

 

       // Задняя сторона

       glNormal3f( 0.0f, 0.0f,-1.0f);   // Обратная нормаль

       glTexCoord2f(1.0f, 0.0f);

       glVertex3f(-1.0f, -1.0f, -1.0f); // Низ Право на текстуре и четырехугольнике

       glTexCoord2f(1.0f, 1.0f);

       glVertex3f(-1.0f,  1.0f, -1.0f); // Верх Право на текстуре и четырехугольнике

       glTexCoord2f(0.0f, 1.0f);

       glVertex3f( 1.0f,  1.0f, -1.0f); // Верх Лево на текстуре и четырехугольнике

       glTexCoord2f(0.0f, 0.0f);

       glVertex3f( 1.0f, -1.0f, -1.0f); // Низ Лево на текстуре и четырехугольнике

 

       // Верхняя грань

       glNormal3f( 0.0f, 1.0f, 0.0f);   // Нормаль вверх

       glTexCoord2f(0.0f, 1.0f);

       glVertex3f(-1.0f,  1.0f, -1.0f); // Верх Лево на текстуре и четырехугольнике

       glTexCoord2f(0.0f, 0.0f);

       glVertex3f(-1.0f,  1.0f,  1.0f); // Низ Лево на текстуре и четырехугольнике

       glTexCoord2f(1.0f, 0.0f);

       glVertex3f( 1.0f,  1.0f,  1.0f); // Низ Право на текстуре и четырехугольнике

       glTexCoord2f(1.0f, 1.0f);

       glVertex3f( 1.0f,  1.0f, -1.0f); // Верх Право на текстуре и четырехугольнике

 

       // Нижняя грань

       glNormal3f( 0.0f,-1.0f, 0.0f);   // Нормаль направлена вниз

       glTexCoord2f(1.0f, 1.0f);

       glVertex3f(-1.0f, -1.0f, -1.0f); // Верх Право на текстуре и четырехугольнике

       glTexCoord2f(0.0f, 1.0f);

       glVertex3f( 1.0f, -1.0f, -1.0f); // Верх Лево на текстуре и четырехугольнике

       glTexCoord2f(0.0f, 0.0f);

       glVertex3f( 1.0f, -1.0f,  1.0f); // Низ Лево на текстуре и четырехугольнике

       glTexCoord2f(1.0f, 0.0f);

       glVertex3f(-1.0f, -1.0f,  1.0f); // Низ Право на текстуре и четырехугольнике

 

       // Правая грань

       glNormal3f( 1.0f, 0.0f, 0.0f);   // Нормаль направлена вправо

       glTexCoord2f(1.0f, 0.0f);

       glVertex3f( 1.0f, -1.0f, -1.0f); // Низ Право на текстуре и четырехугольнике

       glTexCoord2f(1.0f, 1.0f);

       glVertex3f( 1.0f,  1.0f, -1.0f); // Верх Право на текстуре и четырехугольнике

       glTexCoord2f(0.0f, 1.0f);

       glVertex3f( 1.0f,  1.0f,  1.0f); // Верх Лево на текстуре и четырехугольнике

       glTexCoord2f(0.0f, 0.0f);

       glVertex3f( 1.0f, -1.0f,  1.0f); // Низ Лево на текстуре и четырехугольнике

 

       // Левая грань

       glNormal3f(-1.0f, 0.0f, 0.0f);   // Нормаль направлена влево

       glTexCoord2f(0.0f, 0.0f);

       glVertex3f(-1.0f, -1.0f, -1.0f); // Низ Лево на текстуре и четырехугольнике

       glTexCoord2f(1.0f, 0.0f);

       glVertex3f(-1.0f, -1.0f,  1.0f); // Низ Право на текстуре и четырехугольнике

       glTexCoord2f(1.0f, 1.0f);

       glVertex3f(-1.0f,  1.0f,  1.0f); // Верх Право на текстуре и четырехугольнике

       glTexCoord2f(0.0f, 1.0f);

       glVertex3f(-1.0f,  1.0f, -1.0f); // Верх Лево на текстуре и четырехугольнике

       glEnd();                         // Заканчиваем рисование четырехугольника

}

  Следующая функция - DrawGLScene. Я просто только написал case оператор для рисования разных объектов. Так же я использовал статическую переменную (локальная переменная, которая сохраняет свое значение каждый раз при вызове) для крутого эффекта, когда рисуем часть диска. Я собираюсь переписать всю функцию DrawGLScene для ясности.

  Заметьте, что когда я говорю о параметрах, которые используются, я пропускаю первый параметр (quadratic). Этот параметр используется для всех объектов, которые мы  рисуем, за исключением куба, поэтому я его пропускаю, когда говорю о параметрах.

 

int DrawGLScene(GLvoid)                 // Здесь мы все рисуем

{

       // Очистка видео буфера и буфера глубины

       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

       glLoadIdentity();                // Сбрасываем вид

       glTranslatef(0.0f,0.0f,z);       // Перемещаемся вглубь экрана

 

       glRotatef(xrot,1.0f,0.0f,0.0f);  // Вращение по оси X

       glRotatef(yrot,0.0f,1.0f,0.0f);  // Вращение по оси Y

 

       glBindTexture(GL_TEXTURE_2D, texture[filter]); // Выбираем фильтрацию текстуре

 

       // Эта секция кода новая ( НОВОЕ )

       switch(object)                   // Проверяем, какой объект рисовать

       {

       case 0:                          // Рисуем первый объект

              glDrawCube();             // Рисуем наш куб

              break;                    // Закончили

  Второй объект, который мы создадим, будет цилиндр. Первый параметр (1.0f) – радиус основания цилиндра (низ). Второй параметр (1.0f) - это радиус цилиндра сверху. Третий параметр (3.0f) - это высота цилиндра (какой он длины). Четвертый параметр (32) – это сколько делений будет "вокруг" оси Z, и, наконец, пятый (32) - количество делений "вдоль" оси Z. Большее количество делений приведет к увеличению детализации объекта. Увеличивая количество делений, вы добавляете больше полигонов в объект. В итоге вы должны будем пожертвовать скоростью ради качества. Самое сложное - найти золотую середину.

 

       case 1:                          // Рисуем второй объект

              glTranslatef(0.0f,0.0f,-1.5f); // Центр цилиндра

              gluCylinder(quadratic,1.0f,1.0f,3.0f,32,32); // Рисуем наш цилиндр

              break;                    // Закончили

  Третий объект, который мы создадим, будет поверхность в виде CD диска. Первый параметр (0.5f) - внутренний радиус цилиндра. Его значение может быть нулевым, что будет означать, что внутри нет отверстия. Чем больше будет внутренний радиус - тем больше будет отверстие внутри диска. Второй параметр (1.5f) - внешний радиус. Это значение должно будь больше, чем внутренний радиус. Если сделать его значение чуть больше чем внутренний радиус, то получится тонкое кольцо. Если это значение будет намного больше, чем внутренний радиус, то получится толстое кольцо. Третий параметр (32) – количество кусочков, из которых состоит диск. Думайте об этих кусочках, как о частях пиццы. Чем больше кусочков, тем глаже будет внешняя сторона диска. И, наконец, четвертый параметр (32) - это число колец, которые составляют диск. Кольца похожи на треки на записи. Круги внутри кругов. Эти кольца делят диск со стороны внутреннего радиуса к внешнему радиусу, улучшая детализацию. Опять же, чем больше делений, тем медленнее это будет работать.

 

       case 2:                          // Рисуем третий объект

              gluDisk(quadratic,0.5f,1.5f,32,32); // Рисуем диск (в виде CD)

              break;                    // Закончили

  Наш четвертый объект - объект, о котором я знаю то, что многие умерли, создавая его. Это сфера! Создать ее очень просто. Первый параметр - это радиус сферы. Если вы не очень знакомы с понятием радиус/диаметр и т.д., объясняю, радиус - это расстояние от центра объекта, до внешней стороны объекта. В нашем случае радиус равен 1.3f. Дальше идет количество разбиений "вокруг" оси Z (32), и количество разбиений "вдоль" оси Z (32). Большее количество придаст сфере большую гладкость. Для того, чтобы сфера была достаточно гладкой, обычно необходимо большое количество разбиений.

 

       case 3:                          // Рисуем четвертый объект

              gluSphere(quadratic,1.3f,32,32); // Рисуем сферу

              break;                    // Закончили

  Чтобы создать наш пятый объект мы воспользуемся той же командой, что и для цилиндра. Если вы помните, когда мы создавали цилиндр, первые два параметра контролировали радиусы цилиндра сверху и снизу. Для того, чтобы сделать конус, имеет смысл сделать один из радиусов равный нулю. Это создаст точку на конце. Итак, в коде ниже мы делаем радиус на верхней стороне цилиндра равным нулю. Это создаст нашу точку, которая и сделает наш конус.

 

       case 4:                          // Рисуем пятый объект

              glTranslatef(0.0f,0.0f,-1.5f);  // Центр конуса

              // Конус с нижним радиусом .5 и высотой 2

              gluCylinder(quadratic,1.0f,0.0f,3.0f,32,32);

              break;                    // Закончили

  Наш шестой объект создан с помощью gluParticalDisc. Объект, который мы создадим этой командой точно такой же диск, который был до этого, но у команды gluParticalDisc есть еще 2 новых параметра. Пятый параметр (part1) - это угол, с которого мы хотим начать рисование диска. Шестой параметр - это конечный угол (или угол развертки). Это угол, который мы проходим от начального. Мы будем увеличивать этот угол, что позволит постепенно рисовать диск на экране, по направлению часовой стрелки. Как только конечный угол достигнет 360 градусов, мы начнем увеличивать начальный угол. Это будет выглядеть, как будто диск начал стираться, затем мы все начнем сначала!

 

       case 5:                          // Рисуем шестой объект

              part1+=p1;                // Увеличиваем стартовый угол

              part2+=p2;                // Увеличиваем конечный угол

 

              if(part1>359)             // 360 градусов

              {

                    p1=0;               // Хватит увеличивать начальный угол

                    part1=0;            // Устанавливаем начальный угол в 0

                    p2=1;               // Начинаем увеличивать конечный угол

                    part2=0;            // Начиная с 0

              }

              if(part2>359)             // 360 градусов

              {

                    p1=1;               // Начинаем увеличивать начальный угол

                    p2=0;               // Перестаем увеличивать конечный угол

              }

                                        // Диск, такой-же как в прошлый раз

              gluPartialDisk(quadratic,0.5f,1.5f,32,32,part1,part2-part1);

              break;                    // Закончили

       };

 

       xrot+=xspeed;                    // Увеличиваем угол поворота вокруг оси X

       yrot+=yspeed;                    // Увеличиваем угол поворота вокруг оси Y

       return TRUE;                     // Продолжаем

}

 

Теперь, в последней части, обработка клавиш. Просто добавим это, туда, где происходит проверка нажатия клавиш.

 

       if (keys[' '] && !sp)            // Нажата клавиша "пробел"?

       {

              sp=TRUE;                  // Если так, то устанавливаем sp в TRUE

              object++;                 // Цикл по объектам

              if(object>5)              // Номер объекта больше 5?

                    object=0;           // Если да, то устанавливаем 0

       }

       if (!keys[' '])                  // Клавиша "пробел" отпущена?

       {

              sp=FALSE;                 // Если да, то устанавливаем sp в FALSE

       }

  Это все! Теперь вы можете рисовать квадратичные объекты в OpenGL. С помощью морфинга и квадратичных объектов можно сделать достаточно впечатляющие вещи. Анимированный диск - это пример простого морфинга.

  Все у кого есть время зайдите на мой сайт, TipTup.Com 2000. (http://www.tiptup.com)

© GB Schmick (TipTup)

 1 марта 2002 (c)  Валерий Провалов