Урок Инициализация в Windows я начинаю это пособие непосредственного с кода, разбитого на секции, каждая из которых будет подробно комментироваться. Первое, что вы должны сделать это создать проект в Visual C++



страница6/27
Дата17.11.2018
Размер7.85 Mb.
ТипУрок
1   2   3   4   5   6   7   8   9   ...   27

GL_TEXTURE_GEN_MODE позволяет нам выбрать режим наложения текстуры, который мы хотим использовать по координатам текстуры S и T. Есть три возможности:

GL_EYE_LINEAR - текстура зафиксирована на экране. Она никогда не перемещается. Объект накладывается на любую часть текстуры, которую он захватывает.

 

GL_OBJECT_LINEAR – мы воспользуемся этим режимом. Текстура привязана к объекту, перемещающемуся по экрану.



GL_SPHERE_MAP – всегда в фаворе. Создает металлический отражающий тип объекта.

Важно обратить внимание на то, что я опускаю много кода. Мы также должны задать GL_OBJECT_PLANE, но значение по умолчанию то, которое мы хотим. Купите хорошую книгу, если Вы хотите изучить больше, или поищите в помощи MSDN на CD.

 

    // Текстуризация контура закрепленного за объектом



    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

    // Текстуризация контура закрепленного за объектом

    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

    glEnable(GL_TEXTURE_GEN_S);      // Автоматическая генерация

    glEnable(GL_TEXTURE_GEN_T);      // Автоматическая генерация

  }


 

  if (TextureImage[0])            // Если текстура существует

  {

    if (TextureImage[0]->data)    // Если изображение текстуры существует



    {

      free(TextureImage[0]->data); // Освобождение памяти изображения текстуры

    }

 

    free(TextureImage[0]);         // Освобождение памяти под структуру



  }

 

  return Status;        // Возвращаем статус



}

 

Есть несколько новых строк кода в конце InitGL(). Вызов BuildFont() был помещен ниже кода, загружающего нашу текстуру. Строка с glEnable(GL_COLOR_MATERIAL) была удалена. Если Вы хотите задать текстуре цвет, используйте glColor3f(r, г, b) и добавьте строку glEnable(GL_COLOR_MATERIAL) в конце этой секции кода.



 

int InitGL(GLvoid)        // Все начальные настройки OpenGL здесь

{

  if (!LoadGLTextures())  // Переход на процедуру загрузки текстуры



  {

    return FALSE;         // Если текстура не загружена возвращаем FALSE

  }

  BuildFont();            // Построить шрифт



 

  glShadeModel(GL_SMOOTH);    // Разрешить плавное затенение

  glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Черный фон

  glClearDepth(1.0f);         // Установка буфера глубины

  glEnable(GL_DEPTH_TEST);    // Разрешение теста глубины

  glDepthFunc(GL_LEQUAL);     // Тип теста глубины

  glEnable(GL_LIGHT0);        // Быстрое простое освещение

                              // (устанавливает в качестве источника освещения Light0)

  glEnable(GL_LIGHTING);      // Включает освещение

  // Действительно хорошие вычисления перспективы

  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

 

Разрешение наложения 2D текстуры, и выбор текстуры номер один. При этом будет отображена текстура номер один на любой 3D объект, который мы выводим на экран. Если Вы хотите большего контроля, Вы можете разрешать и запрещать наложение текстуры самостоятельно.



 

  glEnable(GL_TEXTURE_2D); // Разрешение наложения текстуры

  glBindTexture(GL_TEXTURE_2D, texture[0]); // Выбор текстуры

  return TRUE; // Инициализация окончена успешно

}

 

Код изменения размера не изменился, но код DrawGLScene изменился.



 

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

{

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очистка экран и буфера глубины



  glLoadIdentity(); // Сброс просмотра

 

Здесь наше первое изменение. Вместо того чтобы поместить объект в середину экрана, мы собираемся вращать его на экране, используя COS и SIN (это не сюрприз). Мы перемещаемся на 3 единицы в экран (-3.0f). По оси X, мы будем раскачиваться от -1.1 слева до +1.1 вправо. Мы будем использовать переменную rot для управления раскачиванием слева направо. Мы будем раскачивать от +0.8 верх до -0.8 вниз. Мы будем использовать переменнуюrot для этого раскачивания также (можно также задействовать и другие переменные).



 

  // Позиция текста

  glTranslatef(1.1f*float(cos(rot/16.0f)),0.8f*float(sin(rot/20.0f)),-3.0f);

 

Теперь сделаем вращения. Символ будет вращаться по осям X, Y и Z.



 

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

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

  glRotatef(rot*1.4f,0.0f,0.0f,1.0f);   // Вращение по оси Z

 

Мы смещаем символ по каждой оси немного налево, вниз, и вперед, чтобы центрировать его. Иначе, когда он вращается, он будет вращаться не вокруг собственного центра. (-0.35f, -0.35f, 0.1f) те числа, которые подходят. Я потратил некоторое время, прежде чем подобрал их, и они могут изменяться в зависимости от шрифта. Почему шрифты построены не вокруг центральной точки, я не знаю.



 

  glTranslatef(-0.35f,-0.35f,0.1f);      // Центр по осям X, Y, Z

 

Наконец мы выводим наш эмблемы смерти, затем увеличиваем переменную rot, поэтому наш символ вращается и перемещается по экрану. Если Вы не можете понять, почему я получаю череп из символа 'N', сделайте так: запустите Microsoft Word или Wordpad. Вызовите ниспадающие меню шрифтов. Выберите wingdings шрифт. Наберите в верхнем регистре 'N'. Появиться эмблема смерти.



 

  glPrint("N"); // Нарисуем символ эмблемы смерти

  rot+=0.1f;    // Увеличим переменную вращения

  return TRUE;  // Покидаем эту процедуру

}

 

Последнее, что надо сделать добавить KillFont() в конце KillGLWindow() точно так, как показано ниже. Важно добавить эту строку. Это почистит память прежде, чем мы выйдем из нашей программы.



 

  if (!UnregisterClass("OpenGL",hInstance))    // Если класс не зарегистрирован

  {

    MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);



    hInstance=NULL;          // Установить копию приложения в ноль

  }


 

  KillFont();            // Уничтожить шрифт

}

 

Даже притом, что я не вдавался в излишние подробности, Вы должны получить хорошее понимание о том, как заставить OpenGL генерировать координаты текстуры. У Вас не должно возникнуть никаких проблем при наложении текстур на шрифты, или даже на другие объекты. И, изменяя только две строки кода, Вы можете разрешить сферическое наложение, которое является действительно крутым эффектом.



Урок 16 по OpenGL. Эффект тумана на OpenGL

Этот урок представляет Крис Алиотта (Chris Aliotta)... 

Итак, Вы хотите добавить туман в Вашу программу на OpenGL? Что ж, в этом уроке я покажу как сделать именно это. Я первый раз пишу урок, и относительно недавно познакомился с программированием на OpenGL/C++, так что пожалуйста, если вы найдете здесь какие-нибудь ошибки, то пожалуйста, сообщите мне и не накидывайтесь все сразу. Эта программа основана на примере седьмого урока.

 

Подготовка данных:



 

Начнем с того, что подготовим все необходимые переменные, содержащие параметры затуманивания. Массив fogMode будет хранить три значения: GL_EXP, GL_EXP2 и GL_LINEAR, - это три типа тумана. Позже я объясню различия между ними. Объявим переменные в начале кода, после строки GLuint texture[3]. В переменной fogfilter будет храниться какой тип тумана мы будем использовать, fogColor будет содержать цвет, который мы хотим придать туману. Еще я добавил двоичную переменную gp в начало кода, чтобы можно было узнать нажата ли клавиша 'g' во время выполнения программы-примера.

 

bool   gp;                              // G Нажата? ( Новое )



GLuint filter;                          // Используемый фильтр для текстур

GLuint fogMode[]= { GL_EXP, GL_EXP2, GL_LINEAR }; // Хранит три типа тумана

GLuint fogfilter= 0;                    // Тип используемого тумана

GLfloat fogColor[4]= {0.5f, 0.5f, 0.5f, 1.0f}; // Цвет тумана

 

Изменения в DrawGLScene



 

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

 

glClearColor(0.5f,0.5f,0.5f,1.0f);      // Будем очищать экран, заполняя его цветом тумана. ( Изменено )



 

glEnable(GL_FOG);                       // Включает туман (GL_FOG)

glFogi(GL_FOG_MODE, fogMode[fogfilter]);// Выбираем тип тумана

glFogfv(GL_FOG_COLOR, fogColor);        // Устанавливаем цвет тумана

glFogf(GL_FOG_DENSITY, 0.35f);          // Насколько густым будет туман

glHint(GL_FOG_HINT, GL_DONT_CARE);      // Вспомогательная установка тумана

glFogf(GL_FOG_START, 1.0f);             // Глубина, с которой начинается туман

glFogf(GL_FOG_END, 5.0f);               // Глубина, где туман заканчивается.

 

Возьмем сначала первые три строчки этого кода. Первая строка glEnable(GL_FOG) во многом говорит сама за себя. Ее задача - инициализировать туман.



 

Вторая строка, glFogi(GL_FOG_MODE, fogMode[fogfilter]) устанавливает режим фильтрации тумана. Ранее мы объявили массив fogMode. Он содержал GL_EXP, GL_EXP2 и GL_LINEAR. Именно здесь эти переменные входят в игру. Вот что каждая из них значит:

 

         GL_EXP - Обычный туман, заполняющий весь экран. Во многом он напоминает туман отдаленно, но легко справляется со своей работой даже на старых PC.



         GL_EXP2 - Это следующий шаг после GL_EXP. Затуманит весь экран, за то придает больше глубины всей сцене.

         GL_LINEAR - Это лучший режим прорисовки тумана. Объекты выходят из тумана и исчезают в нем гораздо лучше.

 

Третья, glFogfv(GL_FOG_COLOR, fogcolor) задает цвет тумана. Раньше мы задали его как (0.5f,0.5f,0.5f,1.0f) через переменную fogcolor - получился приятный серый цвет.



 

Дальше, посмотрим на четыре последних строки. Строка glFogf(GL_FOG_DENSITY, 0.35f) устанавливает, насколько густым будет туман. Увеличьте число, и туман станет более густым, уменьшите - менее густым.

 

Eric Desrosiers добавляет небольшое объяснение glHint(GL_FOG_HINT, hintval):



 

hintval может быть: GL_DONT_CARE, GL_NICEST или GL_FASTEST 

         GL_DONT_CARE - позволяет OpenGL выбрать формулу для расчета тумана (по вершинам или по пикселям).

         GL_NICEST - Создает туман по пикселям (хорошо смотрится).

         GL_FASTEST - Вычисляет туман по вершинам (быстрее, но не так красиво)) .

 

Следующая строка glFogf(GL_FOG_START, 1.0f) устанавливает насколько близко к экрану начинается затуманивание. Вы можете изменить это число на что угодно, в зависимости от того, где бы Вы хотели, чтобы начался туман. Следующая, похожая, строка glFogf(GL_FOG_END, 5.0f) сообщает программе OpenGL насколько глубоко в экран должен уходить туман.



 

События при нажатии клавиш

 

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



 

if (keys['G'] && !gp)                   // Нажата ли клавиша "G"?

{

       gp=TRUE;                         // gp устанавливаем в TRUE



       fogfilter+=1;                    // Увеличиние fogfilter на 1

       if (fogfilter>2)                 // fogfilter больше 2 ... ?

       {

              fogfilter=0;              // Если так, установить fogfilter в ноль

       }

       glFogi (GL_FOG_MODE, fogMode[fogfilter]); // Режим тумана

}

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



{

       gp=FALSE;                        // Если да, gp установить в FALSE

}

 

Вот и все! Мы закончили. В Ваших программах с OpenGL есть туман. Я бы даже сказал, что это было достаточно безболезненно. Если есть какие вопросы или комментарии, легко можете со мной связаться: chris@incinerated.com. Так же заходите ко мне на сайт: http://www.incinerated.com/   и http://www.incinerated.com/precursor  .



Урок 17 по OpenGL. Двухмерные шрифты из текстур

Этот урок написан NeHe & Giuseppe D'Agat.

 

Я знаю, что все устали от шрифтов. Те уроки, которые уже были рассмотрены ранее, не только показывали текст, но они отображали 3-х мерный текст, текстурированный текст, и могли быть привязаны к переменным. Но что будет, если вы перенесете свой проект на машину, которая не поддерживает Bitmap или Outline шрифты?



 

Благодаря Guiseppe D'Agata у нас есть еще один урок со шрифтами. Вы спросите, что же еще осталось? Если вы помните, в первом уроке про шрифты, я упоминал об использовании текстур для рисования букв на экран. Обычно, когда вы используете текстуры для рисования текста на экране, вы загружаете свою любимую программу рисования, выбираете шрифт и набираете букву или фразу, которую хотите отобразить на экране. Дальше вы сохраняете изображение и загружаете его в свою программу, как текстуру. Это не очень эффективно для программ, которые используют большое количество текста, или текст, который непрерывно меняется!

 

Эта программа использует только одну текстуру для отображения любого из 256 различных символов на экран. Имейте в виду, что каждый символ всего лишь 16 пикселов в ширину и 16 в высоту. Если взять стандартную текстуру 256*256, легко заметить, что в ней можно разместить только 16 символов поперек и получится 16 строк. Если нужно более детальное объяснение то: текстура 256 пикселов в ширину, а символ 16 пикселов в ширину. 256 делим на 16, получаем 16 :)



 

Итак. Давайте сделаем демонстрационную программу 2-х мерных шрифтов. Эта программа дополняет код из первого урока. В первой части программы мы включим библиотеки math и stdio. Математическая библиотека нужна, чтобы двигать буквы по экрану, используя синус и косинус, а библиотека stdio нужна, чтобы убедиться в том, что файлы картинок, которые мы загружаем действительно существуют, перед тем как мы попытаемся сделать из них текстуры.

 

#include         // Заголовочный файл для Windows



#include            // Заголовочный файл для математической

                            // библиотеки Windows  (Добавлено)

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

                            //ввода/вывода (Добавлено)

#include           // Заголовочный файл для библиотеки OpenGL32

#include          // Заголовочный файл для библиотеки GLu32

#include        // Заголовочный файл для библиотеки GLaux

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

HGLRC           hRC=NULL;   // Постоянный контекст визуализации

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

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

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

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

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

 

Сейчас мы добавим переменную base указывающую на наш список отображения. Так же мы добавим texture[2], для хранения 2-х текстур, используемых для создания простого 3-х мерного объекта.



 

Добавим также переменную loop, которую будем использовать для циклов. И, наконец, cnt1 и cnt2, которые будут использоваться для движения текста по экрану и для вращения простого 3-х мерного объекта.

 

GLuint  base;       // Основной список отображения для шрифта



GLuint  texture[2]; // Место для текстуры нашего шрифта

GLuint  loop;       // Общая переменная для циклов

GLfloat cnt1;       // Первый счетчик для движения и раскрашивания текста

GLfloat cnt2;       // Второй счетчик для движения и раскрашивания текста

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

 

Теперь код для загрузки текстуры. Он точно такой же, как в предыдущих уроках по текстурированию.



 

AUX_RGBImageRec *LoadBMP(char *Filename) // Загрузка изображения

{

       FILE *File=NULL;                  // Дескриптор файла



       if (!Filename)                    // Удостоверимся, что имя файла передано

       {


              return NULL;               // Если нет, возвратим NULL

       }


       File=fopen(Filename,"r");         // Проверка, существует ли файл

       if (File)                         // Существует?

       {

              fclose(File);              // Закрываем файл

              // Загружаем изображение и возвращаем указатель

             return auxDIBImageLoad(Filename);

       }

       return NULL;                      // Если загрузка не удалась, возвращаем NULL

}

 

Данный код тоже немного изменился, в отличие от кода в предыдущих уроках. Если вы не уверены в том, для чего каждая строка, вернитесь и просмотрите предыдущие примеры заново.



 

Отметим, что TextureImage[] будет хранить 2 записи о rgb изображении. Очень важно дважды проверить код, который работает с загрузкой и сохранением текстур. Одно неверное число может привести к зависанию!

 

int LoadGLTextures()                           // Загрузка и преобразование текстур



{

       int Status=FALSE;                       // Индикатор статуса

       AUX_RGBImageRec *TextureImage[2];       // Место хранения для текстур

 

Следующая строка самая важная. Если изменить 2 на любое другое число, точно возникнут проблемы. Проверьте дважды! Это число должно совпадать с тем, которое вы используете, когда определяете TextureImage[].



 

Две текстуры, которые мы загрузим, будут font.bmp (наш шрифт) и bumps.bmp. Вторая текстура может быть любой, какую вы захотите. Я не очень творческий человек, поэтому я решил воспользоваться простой текстурой.

 

memset(TextureImage,0,sizeof(void *)*2);          // Устанавливаем указатель в NULL



 

if ((TextureImage[0]=LoadBMP("Data/Font.bmp")) &&// Загружаем изображение шрифта (TextureImage[1]=LoadBMP("Data/Bumps.bmp")))     // Загружаем текстуру

       {

              Status=TRUE;                        // Устанавливаем статус в TRUE

 

Другая важная строка, на которую нужно посмотреть дважды. Я не могу сказать, сколько писем я получил от людей, спрашивавших "почему я вижу только одну текстуру, или почему моя текстура вся белая!?!". Обычно проблема в этой строке. Если заменить 2 на 1, будет создана только одна текстура, а вторая будет в виде белой текстуры. Если заменить 2 на 3, то программа может зависнуть!



 

Вы должны вызывать glGenTextures() один раз. После вызова glGenTexture, необходимо сгенерировать все ваши текстуры. Я видел людей, которые вставляют вызов glGenTextures() перед созданием каждой текстуры. Обычно они ссылаются на то, что новая текстура перезаписывает все уже созданные текстуры. Было бы неплохо, сначала решить, сколько текстур необходимо сделать, а затем вызвать один раз glGenTextures(), а потом создать все текстуры. Не хорошо помещать вызов glGenTextures() в цикл без причины.

 

glGenTextures(2, &texture[0]);            // Создание 2-х текстур



 

for (loop=0; loop<2; loop++)              // Цикл для всех текстур

{

            // Создание всех текстур



            glBindTexture(GL_TEXTURE_2D, texture[loop]);

            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

            glTexImage2D(GL_TEXTURE_2D, 0, 3,

TextureImage[loop]->sizeX, TextureImage[loop]->sizeY, 0,

GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);

}

}

 



Следующие стоки кода проверяют, занимает ли загруженное нами rgb изображение для создания текстуры память. Если да, то высвобождаем ее. Заметьте, мы проверяем и освобождаем обе записи для изображений. Если мы используем три различные изображения для текстур, то необходимо проверить и освободить память из-под 3-х изображений.

 

for (loop=0; loop<2; loop++)



       {

       if (TextureImage[loop])                // Если текстура существует

              {

              if (TextureImage[loop]->data)   // Если изображение текстуры существует

              {

                    // Освобождаем память от изображения текстуры

                    free(TextureImage[loop]->data);

              }

              free(TextureImage[loop]);   // Освобождаем память от структуры изображения

              }

       }

return Status;                                // Возвращаем статус

}

 

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



 

GLvoid BuildFont(GLvoid)                   // Создаем список отображения нашего шрифта

{

 

Следующие две переменные будем использовать для сохранения позиции каждой буквы внутри текстуры шрифта. cx будет содержать позицию в текстуре по горизонтали, а cy содержать позицию по вертикали.



 

       float       cx;                           // Содержит X координату символа

       float       cy;                           // Содержит Y координату символа

 

Дальше мы скажем OpenGL, что хотим 256 списков отображения. Переменная base будет указывать на положение первого списка отображения. Второй будет base+1, третий base+2, и т.д. Вторая строка кода ниже, выбирает нашу текстуру шрифта (texture[0]).



 

       base=glGenLists(256);                    // Создаем списки

       glBindTexture(GL_TEXTURE_2D, texture[0]);// Выбираем текстуру шрифта

 

Дальше мы начнем наш цикл. В цикле создадим 256 символов, сохраняя каждый символ в своем собственном списке отображения.



 

       for (loop=0; loop<256; loop++)           // Цикл по всем 256 спискам

       {

 

Первая строка ниже, может показаться загадочной. Символ % означает остаток от деления loop на 16. cx будет двигаться по текстуре шрифта слева направо. Позже вы заметите в коде, что мы вычитаем из 1 cy, чтобы двигаться сверху вниз, вместо того, чтобы двигаться снизу вверх. Символ % довольно трудно объяснить, но я попробую.



 

Все, о чем мы говорим, (loop % 16) /16.0f просто переводит результат в координаты текстуры. Поэтому, если loop было равно 16, cx будет равно остатку от деления 16 на 16, то есть 0. А cy равно 16/16, то есть 1. Поэтому мы двигаемся вниз на высоту одного символа, и совсем не двигаемся вправо. Теперь, если loop равно 17, cx будет равно 17/16, что равно 1.0625. Остаток .0625 или 1/16-ая. Это значит, что мы двигаемся на один символ вправо. cy все еще будет равно 1, потому что нам важны только знаки слева от десятичной запятой. 18/16 даст нам 2/16, двигая на 2 символа вправо, и все еще на один вниз. Если loop равно 32, cx будет опять 0, потому что остатка от деления нет, когда делим 32 на 16, но cy равно 2. Потому что число слева от десятичной запятой будет 2, двигая нас на 2 символа вниз от самого верха текстуры шрифта. Не так ли?

 

       cx=float(loop%16)/16.0f;        // X координата текущего символа



       cy=float(loop/16)/16.0f;        // Y координата текущего символа

 

Вау! Ок. Итак, теперь мы построим наш 2D шрифт, выбирая каждый символ из текстуры шрифта, в зависимости от значений cx и cy. В строках ниже мы добавим loop к значению base, если мы этого не сделаем, то каждая буква будет построена в первом дисплейном списке. Мы точно не хотим, чтобы это случилось, поэтому добавим loop к base и каждый следующий символ, который мы создаем, сохранится в следующем доступном списке отображения.



 

       glNewList(base+loop,GL_COMPILE); // Начинаем делать список

 

Теперь, когда мы выбрали список отображения, который мы построили, мы создадим символ. Этого мы добъемся, создавая четырехугольник, и затем текстурируя его одним символом из текстуры шрифта.



 

       glBegin(GL_QUADS);              // Используем четырехугольник, для каждого символа

 

cx и cy будут содержать очень маленькие значения от 0.0f до 1.0f. Оба они будут равны 0 в первой строчке кода ниже, а именно: glTexCoord2f(0.0f,1-0.0f-0.0625f). Помните, что 0.0625 это 1/16-ая нашей текстуры, или ширина/высота одного символа. Координаты текстуры ниже будут координатами левой нижней точки текстуры.



 

Заметьте, мы используем glVertex2i(x, y) вместо glVertex3f(x, y, z). Наш шрифт – это двумерный шрифт, поэтому нам не нужна координата z. Поскольку мы используем плоский экран (Ortho screen – ортографическая или параллельная проекция), нам не надо сдвигаться вглубь экрана. Мы должны сделать, чтобы нарисовать на плоском экране, это задать x и y координаты. Так как наш экран в пикселах от 0 до 639 и от 0 до 479, нам вообще не надо использовать плавающую точку или отрицательные значения :). Используя плоский экран, мы получаем (0, 0) в нижнем левом углу. (640, 480) будет в верхнем правом углу. 0 - левый край по оси x, 639 - правый край экрана по оси x. 0 – верхний край экрана по оси y и 479 – нижний край экрана на оси y. Проще говоря, мы избавились от отрицательных координат. Это тоже удобно для тех, кто не заботится о перспективе и предпочитает работать с пикселами больше, чем с экранными единицами.

 

                    glTexCoord2f(cx,1-cy-0.0625f);  // Точка в текстуре (Левая нижняя)



                    glVertex2i(0,0);                // Координаты вершины (Левая нижняя)

 

Следующая точка на текстуре будет 1/16-ая правее предыдущей точки (точнее ширина одного символа). Поэтому это будет нижняя правая точка текстуры.



 

                    // Точка на текстуре (Правая нижняя)

                    glTexCoord2f(cx+0.0625f,1-cy-0.0625f);

                    glVertex2i(16,0); // Координаты вершины (Правая нижняя)

 

Третья точка текстуры лежит в дальнем правом конце символа, но сдвинута вверх на 1/16-ую текстуры (точнее на высоту одного символа). Это будет верхняя правая точка отдельного символа.



 

                    glTexCoord2f(cx+0.0625f,1-cy);  // Точка текстуры (Верхняя правая)

                    glVertex2i(16,16);              // Координаты вершины (Верхняя правая)

 

Наконец, мы двигаемся влево, чтобы задать нашу последнюю точку в верхнем левом углу символа.



 

                    glTexCoord2f(cx,1-cy);  // Точка текстуры (Верхняя левая)

                    glVertex2i(0,16);       // Координаты вершины (Верхняя левая)

              glEnd();                      // Конец построения четырехугольника (Символа)

 

Наконец, мы перемещаемся на 10 пикселей вправо, двигаясь вправо по текстуре. Если мы не передвинемся, символы будут рисоваться поверх друг друга. Так как наш шрифт очень узкий, мы не будем двигаться на 16 пикселов вправо. Если мы это сделаем, то будет слишком большой пропуск между каждым символом. Двигаясь на 10 пикселей, мы уменьшаем расстояние между символами. 



 

       glTranslated(10,0,0);           // Двигаемся вправо от символа

       glEndList();                    // Заканчиваем создавать список отображения

}                                      // Цикл для создания всех 256 символов

}

 

Следующая секция кода такая же как мы делали в предыдущих уроках, для освобождения списка отображения, перед выходом из программы. Все 256 экранных списков, начиная от base, будут удалены. (Это хорошо!)



 

GLvoid KillFont(GLvoid)                // Удаляем шрифт из памяти

{

       glDeleteLists(base,256);        // Удаляем все 256 списков отображения



}

 

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



 

glPrint() имеет четыре параметра. Первый это координата x на экране (позиция слева на право). Следующая это y координата на экране (сверху вниз... 0 внизу, большие значения наверху). Затем нашу строку string (текст, который мы хотим напечатать), и, наконец, переменную set. Если посмотреть на картинку, которую сделал Giuseppe D'Agata, можно заметить, что там два разных набора символов. Первый набор - обычные символы,  а второй набор - наклонные. Если set = 0, то выбран первый набор. Если set = 1 или больше, то выбран второй набор символов.

 

GLvoid glPrint(GLint x, GLint y, char *string, int set) // Где печатать



{

 

Первое, что мы сделаем - это проверим, что set от 0 до 1. Если set больше 1, то присвоим ей значение 1.



 

       if (set>1)                     // Больше единицы?

       {

              set=1;                  // Сделаем Set равное единице

       }

 

Теперь выберем нашу текстуру со шрифтом. Мы делаем это только, если раньше была выбрана другая текстура, до того как мы решили печатать что-то на экране.



 

       glBindTexture(GL_TEXTURE_2D, texture[0]);       // Выбираем нашу текстуру шрифта

 

Теперь отключим проверку глубины. Причина того, почему я так делаю, в том, что смешивание работает приятнее. Если не отменить проверку глубины, то текст может проходить за каким-нибудь объектом, или смешивание может выглядеть неправильно. Если вы не хотите смешивать текст на экране (из-за смешивания, т.е прозрачности черный фон вокруг символов не виден) можете оставить проверку глубины.



 

       glDisable(GL_DEPTH_TEST);               // Отмена проверки глубины

 

Следующее несколько строк очень важны! Мы выбираем нашу матрицу проекции. Прямо после этого мы используем команду glPushMatrix(). glPushMatrix сохраняет текущую матрицу проекции. Похоже на кнопку "память" на калькуляторе.



 

       glMatrixMode(GL_PROJECTION);            // Выбираем матрицу проекции

       glPushMatrix();                         // Сохраняем матрицу проекции

 

Теперь, когда наша матрица сохранена, мы сбрасываем ее и устанавливаем плоский экран. Первое и третье число (0) задают нижний левый угол экрана. Мы можем сделать левую сторону экрана -640, если захотим, но зачем нам работать с отрицательными числами, если это не нужно. Второе и четвертое число задают верхний правый угол экрана. Неплохо установить эти значения равными текущему разрешению. Глубины нет, поэтому устанавливаем значения z в -1 и 1.



 

       glLoadIdentity();                       // Сбрасываем матрицу проекции

       glOrtho(0,640,0,480,-1,1);              // Устанавливаем плоский экран

 

Теперь выбираем нашу матрицу просмотра модели и сохраняем текущие установки, используя glPushMatrix(). Далее сбрасываем матрицу просмотра модели, так что можно работать, используя ортографическую проекцию.



 

       glMatrixMode(GL_MODELVIEW);             // Выбираем матрицу модели просмотра

       glPushMatrix();                         // Сохраняем матрицу модели просмотра

       glLoadIdentity();                       // Сбрасываем матрицу модели просмотра

 

Сохранив настройки перспективы и установив плоский экран, можно рисовать текст. Начнем с перемещения в позицию на экране, где мы хотим нарисовать текст. Используем glTranslated() вместо glTranslatef(), так как мы работаем с пикселами, поэтому точки с дробными значениями не имеют смысла. В конце концов, нельзя использовать половину пиксела :).



 

       glTranslated(x,y,0);                    // Позиция текста (0,0 - Нижняя левая)

 

Строка ниже выбирает, каким набором символов мы хотим воспользоваться. Если мы хотим использовать второй набор символов, то добавляем 128 к base (128 - половина 256 символов). Добавляя 128, мы пропускаем первые 128 символов.



 

       glListBase(base-32+(128*set));         // Выбираем набор символов (0 или 1)

 

Сейчас, все что осталось - это  нарисовать символы на экране. Делаем это так же как во всех других уроках со шрифтами. Используем glCallLists(). strlen(string) это длина строки (сколько символов мы хотим нарисовать), GL_BYTE означает то, что каждый символ представляется одним байтом (байт это любое значение от 0 до 255). Наконец, string содержит тот текст, который надо напечатать на экране.



 

       glCallLists(strlen(string),GL_BYTE,string); // Рисуем текст на экране

 

Все, что надо теперь сделать, это восстановить перспективу. Мы выбираем матрицу проектирования и используем glPopMatrix(), чтобы восстановить установки, сохраненные с помощью glPushMatrix(). Важно восстановить их в обратном порядке, в том в котором мы их сохраняли.



 

       glMatrixMode(GL_PROJECTION);            // Выбираем матрицу проекции

       glPopMatrix();                          // Восстанавливаем старую матрицу проекции

 

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



 

       glMatrixMode(GL_MODELVIEW);             // Выбираем матрицу просмотра модели

       glPopMatrix();                          // Восстанавливаем старую матрицу проекции

 

Наконец, разрешаем проверку глубины. Если мы не запрещали проверку глубины в коде раньше, то нам не нужна эта строка.



 

       glEnable(GL_DEPTH_TEST);                // Разрешаем тест глубины

}

 

В ReSizeGLScene() ничего менять не надо, так что переходим к InitGL().



 

int InitGL(GLvoid)                             // Все установки для OpenGL здесь

{

 

Переходим к коду построения текстур. Если построить текстуры не удалось по какой-либо причине, то возвращаем значение FALSE. Это позволит нашей программе узнать, что произошла ошибка, и программа изящно завершится.



 

       if (!LoadGLTextures()) // Переходим к загрузке текстуры

       {

              return FALSE;   // Если текстура не загрузилась - возвращаем FALSE

       }

 

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



 

       BuildFont();  // Создаем шрифт

 

Теперь делаем обычную настройку GL. Мы установим черный цвет фона для очистки, зададим значение глубины в 1.0. Выбираем режим проверки глубины вместе со смешиванием. Мы разрешаем сглаженное заполнение и, наконец, разрешаем 2-мерное текстурирование.



 

       glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Очищаем фон черным цветом

       glClearDepth(1.0);                    // Очистка и сброс буфера глубины

       glDepthFunc(GL_LEQUAL);               // Тип теста глубины

       glBlendFunc(GL_SRC_ALPHA,GL_ONE);     // Выбор типа смешивания

 

       glShadeModel(GL_SMOOTH);              // Сглаженное заполнение



       glEnable(GL_TEXTURE_2D);              // 2-мерное текстурирование

 

       return TRUE;                          // Инициализация прошла успешно



}

 

Следующая секция кода создает сцену. Мы рисуем сначала 3-х мерный объект и потом текст, поэтому текст будет поверх 3-х мерного объекта, вместо того, чтобы объект закрывал текст сверху. Причина, по которой я добавил 3-х мерный объект, заключается в том, чтобы показать, что перспективная и ортографическая проекции могут быть использованы одновременно.



 

int DrawGLScene(GLvoid)                             // Здесь мы рисуем все объекты

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очистка экрана и буфера глубины



glLoadIdentity();                                   // Сброс матрицы просмотра модели

 

Мы выбрали нашу bumps.bmp текстуру, и сейчас можно построить маленький 3-х мерный объект. Мы сдвигаемся на 5 экранных единиц вглубь экрана, так чтобы можно было увидеть объект. Мы вращаем объект на 45 градусов вдоль оси z. Это повернет наш четырехугольник на 45 градусов по часовой стрелке, и он будет больше похож на ромб, чем на прямоугольник.



 

glBindTexture(GL_TEXTURE_2D, texture[1]);// Выбираем вторую текстуру

glTranslatef(0.0f,0.0f,-5.0f);           // Сдвигаемся на 5 единиц вглубь экрана

 

       glRotatef(45.0f,0.0f,0.0f,1.0f); // Поворачиваем на 45 градусов (по часовой стрелке)



 

После поворота на 45 градусов, мы вращаем объект вокруг осей x и y, с помощью переменной cnt1*30. Это заставляет наш объект вращаться вокруг своей оси, подобно алмазу.

 

       glRotatef(cnt1*30.0f,1.0f,1.0f,0.0f); // Вращение по X & Y на cnt1 (слева направо)



 

Отменяем смешивание (мы хотим, чтобы 3-х мерный объект был сплошным), и устанавливаем цвет на ярко белый. Затем рисуем один текстурированный четырехугольник.

 

       glDisable(GL_BLEND);            // Отменяем смешивание перед рисованием 3D



       glColor3f(1.0f,1.0f,1.0f);      // Ярко белый

       glBegin(GL_QUADS);              // Рисуем первый текстурированный прямоугольник

       glTexCoord2d(0.0f,0.0f);        // Первая точка на текстуре

 

              glVertex2f(-1.0f, 1.0f); // Первая вершина



              glTexCoord2d(1.0f,0.0f); // Вторая точка на текстуре

              glVertex2f( 1.0f, 1.0f); // Вторая вершина

              glTexCoord2d(1.0f,1.0f); // Третья точка на текстуре

              glVertex2f( 1.0f,-1.0f); // Третья вершина

              glTexCoord2d(0.0f,1.0f); // Четвертая точка на текстуре

              glVertex2f(-1.0f,-1.0f); // Четвертая вершина

 

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



 

Сразу, после того как мы нарисовали первый четырехугольник, мы поворачиваемся на 90 градусов по осям x и y. Затем рисуем другой четырехугольник. Второй четырехугольник проходит сквозь середину первого, в результате получается красивая фигура.

 

// Поворачиваемся по X и Y на 90 градусов (слева на право)



glRotatef(90.0f,1.0f,1.0f,0.0f);

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

              glTexCoord2d(0.0f,0.0f); // Первая точка на текстуре

              glVertex2f(-1.0f, 1.0f); // Первая вершина

              glTexCoord2d(1.0f,0.0f); // Вторая точка на текстуре

              glVertex2f( 1.0f, 1.0f); // Вторая вершина

              glTexCoord2d(1.0f,1.0f); // Третья точка на текстуре

              glVertex2f( 1.0f,-1.0f); // Третья вершина

              glTexCoord2d(0.0f,1.0f); // Четвертая точка на текстуре

              glVertex2f(-1.0f,-1.0f); // Четвертая вершина

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

 

После того как нарисованы четырехугольники, разрешаем смешивание и рисуем текст.



 

       glEnable(GL_BLEND);         // Разрешаем смешивание

       glLoadIdentity();           // Сбрасываем просмотр

 

Мы используем такой же код для раскрашивания, как в предыдущих примерах с текстом. Цвет меняется постепенно по мере движения текста по экрану.



 

       // Изменение цвета основывается на положении текста

       glColor3f(1.0f*float(cos(cnt1)),1.0f*float(sin(cnt2)),

              1.0f-0.5f*float(cos(cnt1+cnt2)));

 

Затем мы рисуем текст. Мы все еще используем glPrint(). Первый параметр - это координата x. Второй - координата y. Третий параметр ("NeHe") - текст, который надо написать на экране, и последний это набор символов (0 - обычный, 1 - наклонный).



 

Как вы могли заметить, мы двигаем текст по экрану используя SIN и COS, используя счетчики cnt1 и cnt2. Если вы не понимаете, что делают SIN и COS, вернитесь и прочитайте тексты предыдущих уроков.

 

       // Печатаем GL текст на экране



       glPrint(int((280+250*cos(cnt1))),int(235+200*sin(cnt2)),"NeHe",0);

       glColor3f(1.0f*float(sin(cnt2)),

              1.0f-0.5f*float(cos(cnt1+cnt2)),1.0f*float(cos(cnt1)));

       // Печатаем GL текст на экране

       glPrint(int((280+230*cos(cnt2))),int(235+200*sin(cnt1)),"OpenGL",1);

 

Мы устанавливаем темно синий цвет и пишем имя автора внизу экрана. Затем мы пишем его имя на экране опять, используя ярко белые символы.



 

       glColor3f(0.0f,0.0f,1.0f);        // Устанавливаем синий цвет

       glPrint(int(240+200*cos((cnt2+cnt1)/5)),

              2,"Giuseppe D'Agata",0);   // Рисуем текст на экране

       glColor3f(1.0f,1.0f,1.0f);        // Устанавливаем белый цвет

       glPrint(int(242+200*cos((cnt2+cnt1)/5)),

              2,"Giuseppe D'Agata",0);   // Рисуем смещенный текст

 

Последнее, что мы сделаем - это добавим обоим счетчикам разные значения. Это заставит текст двигаться и вращаться как 3-х мерный объект.



 

       cnt1+=0.01f;               // Увеличим первый счетчик

       cnt2+=0.0081f;             // Увеличим второй счетчик

       return TRUE;               // Все прошло успешно

}

 

Код в KillGLWindow(), CreateGLWindow() и WndProc() не изменился, поэтому пропустим его.



 

int WINAPI WinMain( HINSTANCE       hInstance,          // Экземпляр

                    HINSTANCE       hPrevInstance,      // Предыдущий экземпляр

                    LPSTR           lpCmdLine,          // Параметры командной строки

                    int             nCmdShow)           // Стиль вывода окна

{

       MSG           msg;          // Структура сообщения



       BOOL       done=FALSE;      // Переменная для выхода из цикла

 

       // Спрашиваем у пользователя, какой режим он предпочитает



       if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?",

              "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO) {

              fullscreen=FALSE;       // Режим окна

       }


 

Сменилось название окна.

 

       // Создаем окно OpenGL



       if (!CreateGLWindow(

              "NeHe & Giuseppe D'Agata's 2D Font Tutorial",640,480,16,fullscreen))

       {

              return 0;           // Окно не создалось - выходим

       }

       while(!done)               // Цикл пока done=FALSE

       {

              if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Пришло сообщение?

              {

                    if (msg.message==WM_QUIT)           // Это сообщение о выходе?

                    {

                           done=TRUE; // Если да, то done=TRUE

                    }

                    else              // Если нет, то обрабатываем сообщение

                    {

                           TranslateMessage(&msg);// Переводим сообщение

                           DispatchMessage(&msg); // Отсылаем сообщение

                    }

              }

              else                    // Нет сообщений

              {

     // Рисуем сцену.  Ждем клавишу ESC или сообщение о выходе из DrawGLScene()

              // Активно?  Было сообщение о выходе?

                    if ((active && !DrawGLScene()) || keys[VK_ESCAPE])

                    {

                           done=TRUE; // ESC или DrawGLScene сообщает о выходе

                    }

                    else              // Не время выходить, обновляем экран

                    {

                           SwapBuffers(hDC); // Меняем экраны (Двойная буферизация)

                    }

              }

       }

       // Закрываем приложение

 

Последнее, что надо сделать, это добавить KillFont() в конец KillGLWindow(), как я показывал раньше. Важно добавить эту строчку. Она очищает память перед выходом из  программы.



 

       if (!UnregisterClass("OpenGL",hInstance)) // Можем удалить регистрацию класса

       {

             MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",

                  MB_OK | MB_ICONINFORMATION);

             hInstance=NULL;       // Устанавливаем hInstance в NULL

       }

       KillFont();                 // Уничтожаем шрифт

}

 

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



 

Я бы хотел поблагодарить Giuseppe D'Agata за оригинальную версию этого урока. Я сильно его изменил и преобразовал в новый базовый код, но без его присланного кода, я, наверное, не смог бы написать этот урок. Его версия кода имела побольше опций, таких как пробелы между символами и т.д., но я дополнил его прикольным 3-х мерным объектом {усмешка}.

 

Я надеюсь, всем понравился этот урок. Если у вас есть вопросы, пишите Giuseppe D'Agata или мне.



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

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

 

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



 

#include          // Заголовочный файл для Windows

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

#include            // Заголовочный файл для библиотеки OpenGL32

#include           // Заголовочный файл для библиотеки GLu32

#include        // Заголовочный файл для библиотеки 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. С помощью морфинга и квадратичных объектов можно сделать достаточно впечатляющие вещи. Анимированный диск - это пример простого морфинга.

Урок 19. Машина моделирования частиц с использованием полосок из треугольников

Добро пожаловать на урок 19. Вы многое узнали, и теперь слегка развлечься. Я познакомлю Вас только с одной новой командой в этом уроке... Полоски из треугольников (triangle strip). Это очень удобно, и поможет ускорить ваши программы, когда надо рисовать множество треугольников.

В этом уроке я обучу Вас, как сделать несложную машину моделирования частиц (Particle Engine). Если Вы поймете, как работает машина моделирования частиц, Вы сможете создавать эффекты огня, дыма, водных фонтанов и так далее, не правда ли хорошее лакомство!

Я должен, однако предупредить Вас! На сегодняшний день я не написал ни одной машины моделирования частиц. Я знал, что 'знаменитая' машина моделирования частиц очень сложный кусок кода. Я делал попытки ранее, но обычно отказывался от этого после того, как я понимал, что я не смогу управлять всеми точками без того, чтобы не сойти с ума.

Вы можете мне не поверить, если я Вам скажу, что этот урок был написан на 100% с нуля. Я не заимствовал других идей, и я не имел никакой дополнительной технической информации. Я начал думать о частицах, и внезапно моя голова, наполнилась идеями (мозг включился?). Вместо того чтобы думать о каждой частице, как о пикселе, который был должен следовать от точки 'A' до точки 'B', и делать это или то, я решил, что будет лучше думать о каждой частице как об индивидуальном объекте, реагирующему на окружающую среду вокруг ее. Я дал каждой частице жизнь, случайное старение, цвет, скорость, гравитационное влияние и другое.

Вскоре я имел готовый проект. Я взглянул на часы, видимо инопланетяне снова забирали меня. Прошло 4 часа! Я помню, что время от времени пил кофе и закрывал глаза, но 4 часа...?

Поэтому, и хотя эта программа, по-моему, мнению грандиозная, и работает точно, так как я бы хотел, но возможно это не самый правильный способ создать машину моделирования частиц. Я не считаю это очень важным, так как машина моделирования частиц работает хорошо, и я могу использовать ее в моих проектах! Если Вы хотите знать, как точно это делается, то Вам надо потратить множество часов, просматривая сеть, в поисках подходящей информации. Только одно предупреждение. Те фрагменты кода, которые Вы найдете, могут оказаться очень загадочными :).

Этот урок использует код урока 1. Есть, однако, много нового кода, поэтому я будут переписывать любой раздел кода, который содержит изменения (это будет проще для понимания).

Используя код урока 1, мы добавим 5 новых строк кода в начало нашей программы. Первая строка (stdio.h) позволит нам читать данные из файлов. Такую же строку, мы добавили и к другим урокам, которые использовали текстуры. Во второй строке задается, сколько мы будем создавать частиц, и отображать на экране. 


Каталог: forum
forum -> Лечение гепатозов
forum -> Система ведения овцеводства в крестьянско-фермерских и личных хозяйствах населения
forum -> Yaesuft-857 (ft-897) – переходник для подключения cat интерфейса к микрофонному разъему трансивера
forum -> Васильев Владимир Юрьевич
forum -> tl-wr1043ND Беспроводной гигабитный маршрутизатор серии n copyright & trademarks
forum -> Цели и задачи Контакта rtf docx
forum -> Назовите не менее трех результатов революции 1905- 1907гг. Приведите не менее трех положений, отражающих значение революции для отечественной истории начала 20 века


Поделитесь с Вашими друзьями:
1   2   3   4   5   6   7   8   9   ...   27


База данных защищена авторским правом ©vossta.ru 2019
обратиться к администрации

    Главная страница