Міністерство освіти і науки України
Рівненський державний гуманітарний університет
Факультет математики та інформатики
Кафедра інформатики та прикладної математики
КУРСОВА РОБОТА
на тему:
Використання opengl. Моделювання вогню
Виконав:
студент 4 курсу
спеціальності “інформатика”
групи І-41: Антонішин В.В.
Науковий керівник:
Ярощак Сергій Вікторович
Рівне – 2010
ЗМІСТ
ВСТУП
РОЗДІЛ 1. Програмний код та синтаксис OpenGL
1.1 Програмний код OpenGL
1.2 Синтаксис команд OpenGL
РОзділ 2. Конвеєр візуалізації та бібліотеки, що відносяться до OpenGL
2.1 Конвеєр візуалізації OpenGL
2.1.1 Конвеєр
2.1.2 Списки виводу
2.1.3 Обчислювачі
2.1.4 Операції обробки вершин
2.1.5 Збірка примітивів
2.1.6 Операції обробки пікселів
2.1.7 Збірка текстури
2.1.8 Растеризація
2.1.9 Операції обробки фрагментів
2.2 Бібліотеки, що відносяться до OpenGL
2.2.1 Бібліотека OpenGL
2.2.2 Файли, що підключаються
2.2.3 GLUT, інструментарій утиліт бібліотеки OpenGL
РОЗДІЛ 3. АНІМАЦІЯ КОМП'ЮТЕРНОЇ ГРАФІКИ НА ПРИКЛАДІ МОДЕЛЮВАННЯ ВОГНЮ
3.1 Анімація комп'ютерної графіки
3.2 Моделювання вогню
ВИСНОВКИ
ЛІТЕРАТУРА
ВСТУП
Комп'ютерна графіка знайшла широке поширення і застосування в повсякденному житті. Учені використовують комп'ютерну графіку для аналізу результатів моделювання. Інженери і архітектори використовують тривимірну графіку для створення віртуальних моделей. Кінематографісти створюють спецефекти або повністю анімовані фільми («Шрек», «Історія іграшок» та ін.). Останніми роками широке поширення отримали також комп'ютерні ігри, що максимально використовують тривимірну графіку для створення віртуальних світів.
Поширення комп'ютерної графіки супроводили свої труднощі. У 1990-х розробка програмного продукту, здатного працювати на великій кількості графічного устаткування, була зв'язана з великими часовими і фінансовими витратами. Було необхідно окремо створювати модулі для кожного типу графічних адаптерів, що іноді призводило до розмноження однакового програмного коду. Це сильно гальмувало розвиток і поширення комп'ютерної графіки.
Silicon Graphics Incorporated (SGI) спеціалізувалася на створенні високотехнологічного графічного устаткування і програмних засобів. Будучи у той час лідером в тривимірній графіці, SGI бачила проблеми і бар'єри в рості ринку. Тому було прийнято рішення стандартизувати метод доступу до графічної апаратури на рівні програмного інтерфейсу.
Таким чином з'явився програмний інтерфейс OpenGL, який стандартизує доступ до графічної апаратури шляхом зміщення відповідальності за створення апаратного драйвера на виробника графічного пристрою. Це дозволило розробникам програмного забезпечення використовувати вищий рівень абстракції від графічного устаткування, що значно прискорило створення нових програмних продуктів і понизило на них витрати.
У 1992 році компанія SGI очолила OpenGL ARB — групу компаній по розробці специфікації OpenGL. OpenGL еволюціонував з 3D-інтерфейса SGI — IRIS GL. Одним з обмежень IRIS GL було те, що він дозволяв використовувати тільки можливості, підтримувані устаткуванням; якщо можливість не була реалізована апаратно, застосування не могло її використовувати. OpenGL долає цю проблему за рахунок програмної реалізації можливостей, що не надаються апаратно і дозволяє використовувати цей інтерфейс на відносно малопотужних системах.
У 1995 році була випущена бібліотека Direct3D від Microsoft. Незабаром Microsoft, SGI і Hewlett - Packard почали проект під назвою Fahrenheit, який передбачав створення більше універсального програмного інтерфейсу на основі Direct3D і OpenGL. Ідея здавалася багатообіцяючою, покликаною навести лад в області інтерактивної тривимірної графіки, проте, в результаті фінансових труднощів в SGI і відсутності належної індустріальної підтримки, проект був закритий. Правда, пізніше, 16 вересня 2005 року, він все-таки був випущений.
OpenGL орієнтується на наступні два завдання:
Приховати складнощі адаптації різних 3D-прискорювачів, надаючи розробникові єдиний API.
Приховати відмінності в можливостях апаратних платформ, вимагаючи реалізації бракуючої функціональності за допомогою програмної емуляції.
Основним принципом роботи OpenGL є отримання наборів векторних графічних примітивів у вигляді точок, ліній і багатокутників з наступною математичною обробкою отриманих даних і побудовою растрової картинки на екрані і в пам'яті. Векторні трансформації і растеризування виконуються графічним конвеєром , який по суті представляє з себе дискретний автомат. Абсолютна більшість команд OpenGL потрапляють в одну з двох груп : або вони додають графічні примітиви на вхід в конвеєр, або конфігурують конвеєр на різне виконання трансформацій.
OpenGL є низькорівневим процедурним API, що змушує програміста диктувати точну послідовність кроків, щоб побудувати результуючу растрову графіку (імперативний підхід). Це є основною відмінністю від дескрипторних підходів, коли уся сцена передається у вигляді структури даних, які обробляються і будуються на екрані. З одного боку, імперативний підхід вимагає від програміста глибокого знання законів тривимірної графіки і математичних моделей, з іншого боку — дає свободу впровадження різних інновацій.
Метою цієї роботи є вивчити особливості роботи з відкритою графічною бібліотекою OpenGL та навчитися її застосовувати в комп’ютерному моделюванні.
Завдання полягає у написанні програми моделювання горіння вогню з використанням графічної бібліотеки OpenGL.
Предметом дослідження є комп’ютерна графіка, а об’єктом дослідження побудова графічних зображень з використанням відкритої графічної бібліотеки OpenGL.
РОЗДІЛ 1. Програмний код та синтаксис команд OpenGL
1.1 Програмний код OpenGL
Оскільки за допомогою графічної системи OpenGL можна вирішити так багато завдань, OpenGL-програма може бути досить важкою для розуміння. Проте основна структура програми може бути проста: її завдання полягають в тому, щоб ініціалізувати деякі стани, які управляють тим, як бібліотека OpenGL виконує візуалізацію, і визначити об'єкти, які будуть візуалізовані.
Перш ніж приступити до аналізу деякого програмного коду OpenGL, давайте познайомимося з декількома термінами. Візуалізація є процесом, за допомогою якого комп'ютер створює зображення з моделей. Ці моделі, або об'єкти, створюються з геометричних примітивів, - точок, ліній і багатокутників, - які визначаються їх вершинами.
Кінцеве візуалізоване зображення складається з пікселів, що виводяться на екран; піксель є найменшим видимим елементом, який апаратні засоби відображення можуть помістити на екран.
Інформація про пікселі (наприклад, який колір передбачається для цих пікселів) організована в пам'яті у вигляді бітових площин. Бітова площина є областю пам'яті, яка містить один біт інформації для кожного пікселя на екрані; цей біт міг би вказувати, наприклад, на те, що конкретний піксель, як передбачається, є червоним. Бітові площини, у свою чергу, організовані у буфер кадру, який містить усю інформацію, необхідну графічному дисплею для того, щоб управляти кольором і яскравістю усіх пікселів на екрані.
Приклад 1. Фрагмент програмного коду OpenGL
finclude <whateverYouNeed.h>,
main () {
InitializeAWindowPlease();
glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 1.0, 1.0);
glOrtho(0.0, 1.0, 0.0, 1.0, - 1.0, 1.0); glBegin(GL_POLYGON);
glVertex3f(0.25, 0.25, 0.0);
glVertex3f (0.75, 0.25, 0.0);
glVertex3f(0.75, 0.75, 0.0);
glVertex3f(0.25, 0.75, 0.0); glEnd() ; glFlush () ;
UpdateTheWindowAndCheckForEvents(); }
Перший рядок функції main() ініціалізував певне вікно на екрані: функція InitializeAWindowPlease() використовується в даному випадку в якості мітки-"заповнювача" для підпрограм специфічних віконних систем, які в загальному випадку не є викликами OpenGL. Наступні два рядки містять команди OpenGL, які встановлюють чорний колір фону для вікна: функція glCIearCoIor() визначає, який колір фону буде встановлений для вікна, а функція glClear() фактично встановлює колір вікна. Як тільки колір фону встановлений, вікно заливається цим кольором всякий раз, коли викликається функція glClear(). Цей колір фону може бути змінений за допомогою другого виклику функції glClearColor(). Так само функція glColor3f() встановлює, який колір слід використовувати для промальовування об'єктів на екрані - в даному випадку цей колір є білим. Усі об'єкти, що виводяться на екран після цього моменту, використовують цей колір до тих пір, поки він не буде змінений за допомогою наступного виклику команди установки кольору.
Наступна функція OpenGL, використовувана в даній програмі, glOrtho(), визначає систему координат, яку OpenGL приймає для промальовування остаточного зображення, і те, як це зображення відображається на екрані. Виклики, розміщені між функціями glBegin() і glЈnd(), визначають об'єкт, який буде виведений на екран, в даному прикладі це багатокутник з чотирма вершинами. "Кути" багатокутника визначаються за допомогою функції glVertex3f(). Як ви, напевно, вже здогадалися, виходячи зі значень параметрів цієї функції, які є координатами (х, у, z), цей багатокутник є прямокутником, розташованим на площині z(z - 0).
Нарешті, функція gIFlush() гарантує, що команди промальовування фактично виконуються, а не просто зберігаються в деякому буфері, чекаючи додаткових команд OpenGL. Підпрограмма-"заповнювач" UpdateTheWindowAndCheckForEvents() управляє вмістом вікна і починає обробку подій.
1.2 Cинтаксис команд OpenGL
Як ви, ймовірно, могли помітити з прикладу програми, приведеного в попередньому розділі, команди бібліотеки OpenGL використовують префікс gl. Кожне слово, що становить найменування команди, починається із заголовної букви (згадайте, наприклад, функцію glClearColor()). Так само імена констант, визначених у бібліотеці OpenGL, починаються з префікса GL_, записуються заголовними буквами і використовують символи підкреслення, щоб розділити окремі слова (наприклад, GL__COLOR_BUFFER_BIT).
Ви, ймовірно, також змогли помітити деякі символи, які здалися вам сторонніми, вони додаються в кінець найменування деяких команд (наприклад, 3f у функціях glColor3f() і glVertex3f()). Дійсно, частина Color в найменуванні функції glColor3f() достатньо для того, щоб визначити цю команду як команду, що встановлює поточний колір. Проте було визначено декілька таких команд, щоб ви змогли використовувати їх з різними типами параметрів. Зокрема, частина 3 суфікса вказує, що для цієї команди задаються три параметри; інша версія команди Color використовує чотири параметри. Частина f суфікса вказує на те, що параметри цієї команди є числами з плаваючою крапкою. Наявність різних форматів дозволяє бібліотеці OpenGL приймати дані користувача в його власному форматі даних.
Деякі команди бібліотеки OpenGL допускають використання 8 різних типів даних в якості своїх параметрів. Букви, використовувані в якості суфіксів для того, щоб визначити ці типи даних для реалізації ISO З бібліотеки OpenGL, представлені в Таблиці 1.2; там же приведені відповідні визначення типів у бібліотеці OpenGL. Конкретна реалізація бібліотеки OpenGL, яку ви використовуєте, може не співпадати в точності з приведеною схемою; наприклад, реалізації для мов програмування C++ або Ada, не вимагають цього.
Таблиця 1.2. Суфікси найменування команд і типи параметрів
Тип даних | Тип, що відповідає типу даних мови програмування С | Визначення типів даних бібліотеки OpenGL |
8-розрядне ціле | signed char | GLbyte |
16-розрядне ціле | short | GLshort |
32-розрядне ціле | Int або long | GLint, GLsizei |
32-розрядне число з плаваючою крапкою | float | GLfloat, GLclampf |
64-розрядне число з плаваючою крапкою | double | GLdouble, GLclampd |
8-розрядне беззнакове ціле | unsigned char | GLubyte, GLboolean |
16-розрядне беззнакове ціле | unsigned short | GLushort |
32-розрядне беззнакове ціле |
unsignedint або unsigned long |
GLuint, GLenum, GLbitfield |
Таким чином, дві команди glVertex2i (1,3); glVertex2f (1.0, 3.0); є еквівалентними, за винятком того, що перша з них визначає координати вершини як 32-розрядні цілі числа, а друга визначають їх як числа з плаваючою крапкою з одинарною точністю.
Найменування деяких команд бібліотеки OpenGL можуть мати завершальний символ v, який вказує на те, що ця команда приймає покажчик на вектор (чи масив) значень, а не набір індивідуальних параметрів. Багато команд мають як векторні, так і невекторні версії, але деякі команди приймають тільки індивідуальні параметри, тоді як інші команди вимагають, щоб, принаймні, деякі з їх параметрів були визначені як вектори. Наступні рядки показують, як можна було б використовувати векторну і невекторну версію команди, яка встановлює поточний колір :
glColor3f (1.0, 0.0, 0.0);
gLfloat color_array [] = {1.0, 0.0, 0.0); glColor3fv (color_array);
Нарешті, бібліотека OpenGL визначає тип даних GLvoid. Цей тип даних найчастіше використовується для тих команд бібліотеки OpenGL, які приймаються як параметри покажчики на масиви значень.
РОЗДІЛ 2. Конвеєр візуалізації та бібліотеки, що відносяться до OpenGL
2.1 Конвеєр візуалізації OpenGL
2.1.1 Конвеєр
Більшість реалізацій бібліотеки OpenGL мають однаковий порядок операцій, певну послідовність стадій обробки, яка називається конвеєром візуалізації OpenGL. Цей порядок функціонування не є суворим правилом реалізації бібліотеки OpenGL, проте він є надійним керівництвом для передбачення результату роботи OpenGL.
Рис. 2.1.1. Конвеєр візуалізації
Наступна діаграма демонструє концепцію складального конвеєра Генрі Форда, яку бібліотека OpenGL використовує для обробки даних. Геометричні дані (вершини, прямі і багатокутники) проходять через послідовність блоків, до числа яких входять обчислювачі і операції обробки вершин, тоді як піксельні дані (пікселі, зображення і растрові образи) для певної частини процесу візуалізації обробляються по-іншому. Обидва типи даних піддаються одним і тим же завершальним операціям (операції растеризування і пофрагментной обробки), перш ніж підсумкові піксельні дані записуються у буфер кадру.
2.1.2 Списки виводу
Усі дані, незалежно від того, чи описують вони геометрію або пікселі, можуть бути збережені в списку виводу для поточного або пізнішого використання. (Альтернативою збереженню даних в списку виводу являється негайна обробка даних, вона також відома під назвою безпосередній режим роботи.) Після того, як список виводу був створений, збережені дані посилаються з цього списку виводу точно так, якби вони були послані прикладною програмою у безпосередньому режимі роботи.
2.1.3 Обчислювачі
Усі геометричні примітиви описуються вершинами. Параметричні криві і поверхні можуть бути спочатку описані контрольними точками і поліноміальними функціями, які називаються базовими функціями. Обчислювачі надають метод отримання вершин, використовуваний для представлення поверхні по контрольних точках. Таким методом є поліноміальне відображення, яке може формувати нормаль до поверхні, координати текстури, кольори і значення просторових координат по контрольних точках.
2.1.4 Операції обробки вершин
Для даних по вершинах наступною є стадія "операцій обробки вершин", яка перетворює вершини в примітиви. Деякі типи даних по вершинах (наприклад, просторові координати) перетворюються в матриці чисел з плаваючою точкою розміром 4x4. Просторові координати проектуються з положення в тривимірному просторі в положення на вашому екрані. Якщо дозволено використання розширених функціональних можливостей, то ця стадія обробки даних ще більше насичена. Якщо використовується накладення текстур, то на цій стадії можуть бути згенеровані і перетворені координати текстури. Якщо дозволено освітлення, то тут виконуються обчислення параметрів освітлення, для чого використовуються перетворені вершини, нормаль до поверхні, положення джерела освітлення, властивості матеріалу і інша інформація освітлення, необхідна для набуття значення кольору.
2.1.5 Збірка примітивів
Операція відсікання, основна частина збірки примітивів, є видаленням частин геометрії, яка виходить за межі напівпростору, визначеного деякою площиною. При відсіканні точок просто пропускаються або відкидаються вершини; при відсіканні ліній або багатокутників можуть додаватися додаткові вершини залежно від того, як відсікається лінія або багатокутник.
В деяких випадках цей процес супроводжується перспективним діленням, яке примушує видалені геометричні об'єкти здаватися меншими, ніж ближчі об'єкти. Після цього застосовуються операції отримання вікна перегляду і глибини. Якщо відсікання дозволене, і цей примітив є багатокутником, тоді він може бути відкинутий в процесі виконання тесту на відсікання. Залежно від способу побудови багатокутник може бути виведений на екран у вигляді точок або у вигляді ліній.
Результатом виконання цієї стадії є закінчені геометричні примітиви, які є перетвореними і відсіченими вершинами і пов'язаними з ними значеннями кольору, глибини і іноді координатами текстур, а також вказівки для виконання стадії растеризування.
2.1.6 Операції обробки пікселів
Тоді як геометричні дані слідують одним шляхом по конвеєру візуалізації OpenGL, піксельні дані слідують іншим маршрутом. Пікселі з певного масиву в системній пам'яті спочатку розпаковуються з якого-небудь одного з безлічі форматів в належну кількість компонентів. Потім ці дані масштабуються, зміщуються і обробляються за допомогою карти елементів відображення. Після цього результати фіксуються і або записуються в область пам'яті, виділеній під текстури, або передаються на стадію растеризування.
Якщо піксельні дані зчитуються з буфера кадру, то виконуються операції по передачі пікселя (масштабування, зміщення, відображення і фіксація). Потім отримані результати упаковуються у відповідний формат і повертаються в деякий масив системної пам'яті.
Існують спеціальні операції копіювання пікселів для копіювання даних з буфера кадру в інші частини буфера кадру або в область пам'яті, виділену для текстур. Виконується однопрохідна реалізація операцій при передачі пікселя, а потім дані записуються в область пам'яті, виділену для текстур або назад у буфер кадру.
2.1.7 Збірка текстури
OpenGL-програми можуть накладати зображення текстури на геометричні об'єкти для того, щоб зробити їх вид реалістичнішим. Якщо використовується декілька зображень текстури, то дуже розумно буде помістити їх в об'єкти текстури для спрощення перемикання між ними.
Деякі реалізації бібліотеки OpenGL можуть мати спеціальні ресурси для прискореного виконання операцій над текстурами. Це може бути реалізовано як спеціалізована, високопродуктивна область пам'яті, виділена для текстур. Якщо така пам'ять доступна, об'єкти текстури можуть бути впорядковані по пріоритетах для полегшення управління цим обмеженим і цінним ресурсом.
2.1.8 Растеризація
Растеризація є перетворенням як геометричних, так і піксельних даних у фрагменти. Кожен квадратний фрагмент відповідає певному пікселю у буфері кадру. Штрихування ліній і багатокутників, ширина лінії, розмір точки, модель зафарбовування і обчислення покриття, необхідні для підтримки згладжування, враховуються як вершини, які з'єднуються в лінії, або як внутрішні пікселі, розраховані для зафарбованого багатокутника. Значення кольору і глибини призначаються для кожного квадратного фрагмента.
2.1.9 Операції обробки фрагментів
Перш ніж значення збережуться у буфері кадру, виконується ряд операцій, внаслідок чого фрагменти можуть бути змінені або навіть відкинуті. Усі ці операції можуть бути включені або відключені.
Першою операцією, з якою можна зіткнутися, є накладення текстур. Ця операція полягає в тому, що тексель (елемент текстури) генерується з пам'яті текстур для кожного фрагмента і застосовується до конкретного фрагмента. Після цього можуть застосовуватися обчислення туману, які супроводжуються тестом ножиць, альфа-тестом, тестом трафарету і тестом буфера глибини (тест буфера глибини є видаленням невидимих поверхонь). Невдале завершення включеного теста може припинити тривалу обробку квадрата фрагмента. Потім можуть бути виконані операції змішування кольорів, псевдозмішування (розмивання) кольорів для передачі півтонів, логічної обробки і маскування за допомогою бітової маски. Нарешті, повністю оброблений фрагмент виводиться у відповідний буфер, де він остаточно перетворюється на піксель і досягає свого кінцевого місця розташування.
2.2 Бібліотеки, що відносяться до OpenGL
2.2.1 Бібліотека OpenGL
Бібліотека OpenGL надає потужний, але примітивний набір команд візуалізації, і усі зображення більш високого рівня мають бути створені з використанням саме цих команд. Окрім цього OpenGL-програми повинні використовувати основні механізми системи управління вікнами. Існує декілька бібліотек, які дозволяють спростити рішення ваших завдань в області програмування. До числа цих бібліотек входять наступні:
Бібліотека утиліт OpenGL (GLU - OpenGL Utility Library) містить декілька підпрограм, які використовують OpenGL-команди низького рівня для виконання таких завдання, як установка матриць для певного орієнтування і проектування перегляду, виконання тесселяції багатокутників(розбиття довільного багатокутника на опуклі багатокутники) і візуалізацію поверхні. Ця бібліотека надається в якості складової частини кожної реалізації бібліотеки OpenGL. Складові частини біблиотеки GLU описуються в Довіднику по OpenGL {OpenGL Reference Manual).
Для кожної віконної системи існує бібліотека, яка розширює функціональні можливості цієї віконної системи, щоб забезпечити підтримку візуалізації OpenGL. Для обчислювальних машин, які використовують оболонку X Window System, в якості доповнення до бібліотеки OpenGL надається розширення бібліотеки OpenGL для оболонки X Window System(GLX - OpenGL Extension to the X Window System). Підпрограми GLX використовують префікс glX. Для операційних систем Microsoft Windows 95/98/NT інтерфейс операційної системи Windows до бібліотеки OpenGL забезпечується підпрограмами бібліотеки WGL. Усі підпрограми WGL використовують префікс wgl.
Для операційної системи OS/2 корпорації IBM використовуються PGL-інтерфейс адміністратора представлень (Presentation Manager) до бібліотеки OpenGL, і його підпрограми використовують префікс pgl. Для комп'ютерів фірми Apple інтерфейсом для графічних систем, що підтримують бібліотеку OpenGL, являється AGL, і підпрограми AGL використовують префікс agl.
Інструментарій утиліт бібліотеки OpenGL (GLUT - Graphics Library UtilityToolkit) є незалежним від віконної системи інструментарієм, написаним Марком Килгардом (Mark Kilgard) для того, щоб приховати складність програмного інтерфейсу прикладних програм (API - ApplicationProgramming Interface) різних віконних систем. Підпрограми GLUT використовують префікс glut.
FSG (Fahrenheit Scene Graph) є об'єктно-орієнтованим набором інструментальних засобів, заснованим на бібліотеці OpenGL і надаючим об'єкти і методи для створення інтерактивних тривимірних графічних прикладних програм. FSG написаний на мові програмування C++, містить заздалегідь підготовлені об'єкти і вбудовану модель обробки подій при взаємодії з користувачем, компоненти прикладних програм високого рівня для створення і редагування тривимірних сцен і можливості для обміну даними в інших графічних форматах. FSG не залежний від OpenGL.
2.2.2 Файли, що підключаються
Для усіх OpenGL-програм ви можете підключити заголовний файл gl.h в кожен файл проекту. Майже усі OpenGL-програми використовують GLU, вищезгадану Бібліотеку утиліт OpenGL, яка вимагає включення заголовного файлу glu.h. Отже майже кожен початковий файл OpenGL-програми починається з наступних рядків:
#include <gi/gl.h>
#include <gl/glu.h>
Операційна система Microsoft Windows вимагає, щоб заголовний файл windows.h був включений до підключення заголовних файлів gl.h або glu.h, оскільки деякі макрокоманди, визначені в Microsoft Windows, версіях заголовних файлів gl.h і glu.h, визначаються в заголовному файлі windows.h.
Якщо ви звертаєтеся безпосередньо до бібліотеки віконного інтерфейсу, яка забезпечує підтримку OpenGL, наприклад, до бібліотеки GLX, AGL, PGL або WGL, то необхідно включити додаткові заголовні файли. Наприклад, якщо ви викликаєте бібліотеку GLX, то, можливо, потрібно буде додати до вашого програмного коду рядки, приведені нижче :
#include <X11/Xlib.h>
#include <GL/glx.h>
У операційній системі Microsoft Windows для підключення підпрограм WGL слід додати до вашого програмного коду наступний рядок:
#include <windows.h>
(і рядок
#include <GL/glaux.h>
якщо вам потрібні розширені можливості OpenGL).
Якщо ви використовуєте бібліотеку GLUT для управління завданнями віконного менеджера, то необхідно буде включити наступний рядок:
#include <GL/glut.h>
Більшість OpenGL-програм також використовують системні виклики для стандартної бібліотеки мови програмування С, тому зазвичай слід включати заголовні файли, не пов'язані з обробкою графіки, такі як:
#include <stdlib.h>
#include <stdio.h>
2.2.3 GLUT, інструментарій утиліт бібліотеки OpenGL
Інструментарій бібліотек
Як ви вже знаєте, бібліотека OpenGL містить команди візуалізації, проте вона розроблялася так, щоб бути незалежною від будь-якої конкретної віконної або операційної системи. Отже, ця бібліотека не містить ніяких команд для відкриття вікон або зчитування подій від клавіатури або миші. На жаль, неможливо написати закінчену графічну програму, не відкриваючи, принаймні, одне вікно, а найцікавіші програми вимагають певного об'єму обробки даних, що вводяться користувачем, або інших послуг від віконної або операційної системи. У багатьох випадках закінчені програми дають найцікавіші приклади, тому справжня програма використовує бібліотеку GLUT для того, щоб спростити процедури відкриття вікон, виявлення введення даних користувачем і так далі. Якщо на вашій системі є реалізація бібліотеки OpenGL і інструментарію GLUT, то приклади повинні працювати без змін при роботі з вашими бібліотеками OpenGL і GLUT.
Крім того, оскільки склад команд малювання у бібліотеці OpenGL обмежений тільки командами, які генерують прості геометричні примітиви (точки, лінії і багатокутники), бібліотека GLUT включає декілька підпрограм, що створюють складніші тривимірні об'єкти, такі як сфера, тор і чайник. Таким чином, можна отримати для перегляду досить цікаві кадри виведення програми. (Зверніть увагу на те, що бібліотека утиліт OpenGL, GLU, також має у своєму складі підпрограми побудови двовимірних поверхонь, які створюють деякі тривимірні об'єкти, такі ж, як і ті, які створює інструментарій GLUT, у тому числі сферу, циліндр або конус.)
Інструментарій GLUT може бути не достатнім для повнофункціональних OpenGL-програм, але він може виявитися хорошою відправною точкою для вивчення OpenGL. Інша частина розділу коротко описує невелику підмножину підпрограм бібліотеки GLUT так, щоб ви змогли розібратися з прикладами програмування.
Управління вікнами
П'ять підпрограм інструментарію GLUT виконують завдання, необхідні для того, щоб ініціалізувати вікно.
Підпрограма glutlnit(int *argc, char **argv) ініціалізує бібліотеку GLUT і опрацьовує будь-які аргументи командного рядка (для оболонки X WindowSystem це могли б бути такі опції, як - display і - geometry). Підпрограма glutlnit() має бути викликана перед будь-якою іншою підпрограмою бібліотеки GLUT.
Підпрограма glutInitDisplayMode(unsigned int mode) визначає, яку колірну модель слід використовувати: режим RGBA або режим індексації кольору. Можна також визначити, чи хочете ви працювати з буфером кадру вікна з одинарною або з подвійною буферизацією. (Якщо ви працюєте в режимі індексації кольору, то ви, можливо, захочете завантажити деякі кольори в таблицю компонентів кольору; для того, щоб зробити це, скористайтеся командою glutSetColor().) Нарешті, можна використовувати цю підпрограму для того, щоб вказати, що ви хочете зв'язати з цим вікном буфери глибини, трафарету і буфер-накопичувач. Наприклад, якщо ви хочете використовувати вікно з подвійною буферизацією, колірною моделлю RGBA і буфером глибини, то для цього можна викликати дану підпрограму з наступними параметрами: g\utlnitmsv\&yMote{GLUT_DOUBLE\ GLUT^RGB \ GLUTJ) EPTH).
Підпрограма glutInitWindowPosition(int х, int у) визначає місце розташування лівого верхнього кута створюваного вікна на екрані монітора.
Підпрограма glutInitWindowSize(int width, int size) визначає розмір створюваного вікна в пікселях.
Підпрограма int glutCreateWindow(char *string) створює вікно з контекстомOpenGL. Ця підпрограма повертає унікальний ідентифікатор для нового вікна. Майте на увазі : до тих пір, поки викликається підпрограма glutMainLoopO, це вікно ще не відображається на екрані.
Функція зворотного виклику відображення
Підпрограма gIutDisplayFunc(void(void)) є першою і найбільш важливою функцією зворотного виклику по події, з якою належить зіткнутися. Всякий раз, коли бібліотека GLUT визначає, що вміст цього вікна має бути відновлений, виконується функція зворотного виклику, зареєстрована підпрограмою glutDisplayFunc(). Тому ви повинні помістити усі підпрограми, які потрібні для перемальовування сцени, в цю функцію зворотного виклику відображення.
Якщо ваша програма змінює вміст вікна, то іноді ви повинні викликати підпрограму glutPostRedisplay(void), яка змушує підпрограму glutMainLoopO викликати зареєстровану функцію зворотного виклику відображення при наступній слушній нагоді.
Виконання програми
Саме останнє, що ви повинні зробити, це викликати підпрограму glutMainLoop(void). При цьому відображаються усі вікна, які були створені, і в цих вікнах тепер працює візуалізація. Починається обробка подій, і викликається зареєстрована функція зворотного виклику відображення. Увійшовши одного разу до цього циклу, з нього не виходять ніколи!
Для того, щоб зробити ефективність програми максимальної, усі операції, які повинні викликатися одноразово (установка кольору фону і системи координат), тепер включені до складу процедури, названої init(). Операції, необхідні для візуалізації (і, можливо, для повторної візуалізації) сцени, включені до складу процедури display(), яка є зареєстрованою функцією зворотного виклику відображення бібліотеки GLUT.
Приклад 2. Проста програма OpenGL, що використовує інструментарій GLUT : hello.c
#include <GL/glut.h>
#include <stdlib.h>
void display(void)
/* Очистити усі пікселі */
glClear(GL_COLOR_BUFFER_BIT);
/* намалювати білий багатокутник (прямокутник) з кутами,
розташованими в точках з координатами (0.25, 0.25, 0.0)
і (0.75, 0.75, 0.0) */
glColor3f(1.0, 1.0, 1.0); glBegin(GL_POLYGON);
glVertex3f(0.25, 0.25, 0.0);
glVertex3f(0.75, 0.25, 0.0);
glVertex3f(0.75, 0.75, 0.0);
glVertex3f(0.25, 0.75, 0.0);glEnd() ;
/* He чекати! Запустити обробку тих, що буферизують
* підпрограм OpenGL*/
glFlushO ; }
void init(void){
/* Вибрати колір очищення (колір фону) */
glClearColor (0.0, 0.0, 0.0, 0.0);
/* Ініціалізувати значення, що переглядаються, */
glMatrixMode(GL_PROJECTION);
glLoadldentity();
glOrtho(0.0, 1.0, 0.0, 1.0, - 1.0, 1.0); }
/* Оголосити початковий розмір вікна, його положення на екрані і режим відображення (одинарна буферизація і режим RGBA).
Відкрити вікно із словом "hello" в рядку заголовка. Викликати підпрограми ініціалізації. Зареєструвати функцію зворотного виклику для відображення графіки. Увійти до основного циклу і обробляти події.*/
int main(int argc, char** argv){
glutInit(Sargc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(250, 250);
glutInitWindowPosition(100, 100);
glutCreateWindow("hello");
init();
glutDisplayFunc(display);
glutMainLoop();
return 0; /* Мова програмування С, згідно ISO, вимагає, щоб функція main повертала значення типу int. */ }
Обробка подій введення даних користувачем
Для того, щоб зареєструвати зворотний виклик команд, які викликаються у тому випадку, коли відбуваються вказані події, можна скористатися наступними підпрограмами.
Підпрограма glutReshapeFunc(void(int w, int h)) вказує на те, яка саме дія має бути виконана при зміні розміру вікна.
Підпрограми glutKeyboardFunc(void(unsigned char key, int x, int у)) і glutMouseFunc (void(int button, int state, int x, int у)) дозволяють зв'язувати певну клавішу клавіатури або кнопку миші з підпрограмою, яка викликається, коли ця клавіша або кнопка миші натискається або відпускається користувачем.
Підпрограма glutMotionFunc(void(int x, int у)) реєструє деяку підпрограму для зворотного виклику при переміщенні миші з натиснутою кнопкою.
Управління фоновим процесом
Можна визначити деяку функцію, яка має бути виконана за допомогою підпрограми glutIdleFunc(void(void)) у тому випадку, якщо не очікуються ніякі інші події, наприклад, коли цикл обробки подій перейшов би в стан простою. Ця підпрограма в якості свого єдиного параметра приймає покажчик на цю функцію. Для того, щоб відключити виконання цієї функції, передайте їй значення NULL (нуль).
Малювання тривимірних об'єктів
Бібліотека GLUT включає декілька підпрограм для малювання перерахованих нижче тривимірних об'єктів: Конус, Ікосаедр, Чайник, Куб, Октаедр, Тетраедр, Додекаедр, Сфера, Тор.
Ви можете намалювати ці об'єкти у вигляді каркасних моделей або у вигляді суцільних зафарбованих об'єктів з певними нормалями до поверхонь. Наприклад, підпрограми для куба і сфери мають наступний синтаксис:
void glutWireCube(GLdouble size);
void glutSolidCube(GLdouble size);
void glutWireSphere(GLdouble radius, GLint slices, GLint stacks);
void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks);
Усі ці моделі малюються центрованими відносно початку світової системи координат.
РОЗДІЛ 3. Анімація комп'ютерної графіки на прикладі моделювання вогню
3.1 Анімація комп'ютерної графіки
Одна з найбільш захоплюючих речей, яку ви можете зробити в області комп'ютерної графіки, - це малювання зображень, що рухаються. Незалежно від того, чи являєтеся ви інженером, що намагається побачити усі сторони механічного вузла, що розробляється, пілотом, що вивчає з використанням моделювання процес пілотування літака, або ж просто пристрасним любителем комп'ютерних ігор, очевидно, що анімація є важливою складовою частиною комп'ютерної графіки.
У кінотеатрі ілюзія руху досягається за рахунок використання послідовності зображень і проектування їх на екран з частотою 24 кадри в секунду. Кожен кадр послідовно переміщається в положення позаду об'єктиву, затвор відкривається, і цей кадр відображається на екрані. Затвор на мить закривається, тоді як плівка простягається до наступного кадру, потім на екрані відображається цей наступний кадр, і так далі. Хоча кожну секунду ви спостерігаєте на екрані 24 різні кадру, ваш мозок змішує усі ці кадри в "безперервну" анімацію. (Старі фільми Чарлі Чаплина знімалися з частотою 16 кадрів в секунду і при відтворенні фігури рухалися помітними різкими поштовхами.) Екран в комп'ютерній графіці зазвичай оновлюється (перемальовував зображення) приблизно від 60 до 76 разів в секунду, а іноді прикладні програми забезпечують навіть приблизно 120 оновлень в секунду. Очевидно, що анімація з частотою 60 кадрів в секунду виглядає "гладшими", ніж при частоті 30 кадрів в секунду, а 120 кадрів в секунду помітно краще, ніж 60 кадрів в секунду. Проте частоти регенерації, що перевищують 120 кадрів в секунду, можуть бути за межами точки зменшення повторної появи, залежно від меж сприйняття.
Головна причина того, що технологія проектування кінофільму працює, полягає в тому, що кожен кадр є закінченим у момент його відображення на екрані. Припустимо, що ви намагаєтеся зробити комп'ютерну анімацію зі свого кінофільму, що складається з одного мільйона кадрів, за допомогою програми, подібної до приведеного нижче фрагмента псевдокоду :
відкрити вікно();
for (i = 0; i < 1000000; i++){
очистити вікно();
намалювати_кадр (i);
почекати_доки_не_закінчиться_інтервал_в_1_24__частку_секунди(); )
Якщо ви додасте час, який потрібно вашій обчислювальній системі для того, щоб очистити екран і намалювати типовий кадр, то приведена вище програма показує результати, що усе більш тривожать, залежно від того, наскільки близько підходить час, потрібний їй для очищення екрану і промальовування кадру до 1/ 24 частці секунди. Припустимо, що процедура малювання в цій програмі майже повністю займає 1/24 частку секунди. Елементи, намальовані на самому початку, видимі впродовж повної 1/24 частки секунди і представляють суцільне зображення на екрані; елементи, намальовані у кінці даного інтервалу, негайно очищаються, як тільки програма запускається для малювання наступного кадру. Вони є у кращому разі деякою подібністю примарного зображення, оскільки велику частину інтервалу в 1/24 секунди ваше око розглядає очищений фон замість тих елементів, які, до нещастя для них, були намальовані останніми. Проблема в даному випадку полягає в тому, що приведена вище програма не відображає повністю намальовані кадри; замість цього ви спостерігаєте процес малювання в його розвитку.
Більшість реалізацій бібліотеки OpenGL забезпечують подвійну буферизацію - апаратну або програмну, яка надає два готові буфери з кольоровими зображеннями. Зображення з одного буфера відображається на екрані, тоді як в іншому буфері малюється нове зображення. Коли малювання чергового кадру завершується, ці два буфери міняються місцями, і той буфер, що містив зображення, що відображалося, тепер використовується для малювання, і навпаки. Це схоже на роботу кінопроектора, плівка в якому містить всього два кадри і склеєна в петлю; тоді як один проектується на екран, кіномеханік відчайдушно стирає і перемальовував невидимий глядачеві кадр. Якщо кіномеханік працює досить швидко, то глядач не помічає відмінностей між таким "кінопроектором" і реальною системою, в якій усі кадри вже намальовані, і кінопроектор просто відображає їх один за іншим. При використанні подвійної буферизації кожен кадр відображається тільки тоді, коли його малювання завершене; глядач ніколи не побачить частково намальованого кадру.
Псевдокод зміненої версії приведеної вище програми, яка відображає плавно анімовану графіку, використовуючи при цьому подвійну буферизацію, міг би виглядати таким чином:
відкрити_вікно_в_режимі_подвійної_буфериэації(); for (i = 0; i < 1000000; i++){
очистити_вікно();
намалювати_кадр(i);
поміняти_буфери_місцями() ; }
3.2 Моделювання вогню
Крім того, частота оновлення відеоінформації, що відображається, є постійною величиною, яка може мати деякі несподівані наслідки з точки зору продуктивності. Наприклад, при періоді оновлення інформації, що відображається на моніторі, рівної 1/60 часток секунди і при постійній швидкості передачі кадрів ви можете працювати зі швидкостями 60 fps, 30 fps, 20 fps, 15 fps, 12 fps і т. д. (60/1, 60/2, 60/3, 60/4, 60/5, і т. д.). Це означає, що якщо ви пишете прикладну програму і поступово додаєте до неї нові функціональні можливості (припустимо, що ця програма - імітатор польоту, і ви додаєте наземний пейзаж), то спочатку кожна нова деталь, що додається, не робитиме ніякого ефекту на сумарну продуктивність - ви все одно отримуєте швидкість передачі кадрів, рівну 60 fps. Потім, коли ви додаєте ще одну нову деталь, система вже не може намалювати усе це впродовж 1/60 частки секунди, і анімація різко сповільнюється - з 60 fps до 30 fps, оскільки вона пропускає перший можливий момент зміни буферів. Аналогічна ситуація відбувається, коли час малювання одного кадру стає більше, ніж 1/30 частка секунди - швидкість передачі кадрів анімації стрибком зменшується від 30 fps до 20 fps.
В даному випадку програма для моделювання горіння вогню була написана на основі системи часток. Ці частки моделюють різні шари - "домени" іонізованого повітря і їх випадковий рух в просторі. В ході руху ці шари-частки світяться, поступово втрачаючи яскравість. У цій програмі використовується лінійна інтерполяція загасання яскравості шарів. Напрям і величина початкової швидкості вибирається випадково в деякому діапазоні. Так само випадково вибирається і так звана швидкість гасіння частки - швидкість, з якою задана частка втрачає яскравість свого світіння. Вважається, що початкова величина яскравості частки дорівнює одиниці. Для імітування архімедівської сили, що піднімає шари повітря, використовується "антигравітація", тобто умовно задається середній напрям прискорення руху. Для розрахунку руху часток використовується метод Ейлера в простому своєму варіанті. Цього вистачає, оскільки рух окремої частки в даному випадку неважливий - нам необхідно створити випадковий розподіл руху часток для відтворення хаотичної динаміки досліджуваного явища. Ну і нарешті не менш візуально важливим параметром є колір вогню. Світловий спектр вогню можна вважати схожим із спектром Сонця. У програмі використовується лише чотири кольори з червоної області спектру по черзі змінюючі один-одного. Ось їх значення в системі RGB : (255,128,128) (255,128,64) (255,161,102) (255,128,51). Проте кількість використовуваних кольорів нескладна істотно збільшити.
При запуску програми в пам'яті комп'ютера створюється масив, що містить поточний стан кожної з часток. У нім також міститься така величина, як "прожите життя" частки. При русі окрім яскравості зменшується і життя частки. Коли спочатку рівна одиниці життя обнуляється, частка відроджується в основі полум'я з новими параметрами. Таким чином кількість задіяних часток постійна (у цій програмі їх 2000), а частки проживають усі нові і нові "життя". Як уже згадувалося, поведінка полум'я цілком залежить від того з якими параметрами відроджуються частки. Ось ці параметри: по-перше, початкове положення часток (x, y, z -координат, де z=const=3, а x і y вибираються згідно з розподілом, згідно з яким вірогідність появи частки в центральній частині (квадраті 1х1) квадрата 2х2 з центром в нулі втричі вище чим по околиці), далі початкові швидкості часток(x, y, z -координати вибираються випадково зі змінюваних інтервалів) і швидкість гасіння частки (також в межах заданого інтервалу). Наступна діаграма ілюструє усе вищесказане.
Рис. 3.2 Циклічна схема переміщення часток
Далі приводиться код самої програми:
#include <windows.h>// Заголовочний файл Windows
#include <math.h>
#include <stdio.h>// Заголовочний файл вводу/виводу
#include <gl\gl.h>// Заголовочний файл бібліотеки OpenGL32
#include <gl\glu.h>// Заголовочний файл бібліотеки GLu32
#include <gl\glaux.h>// Заголовочний файл бібліотеки The Glaux
#defineMAX_PARTICLES1000
#define MAX_COLORS 4// Кількість кольорів полум’я
#define BOTTOM-3.0f
HDChDC=NULL;
HGLRChRC=NULL;
HWNDhWnd=NULL;
HINSTANCEhInstance;
boolkeys[256];
boolactive=TRUE;
boolfullscreen=FALSE;
boolrainbow=TRUE;
floatslowdown=6.0f;// Сповільнення часток
floatxspeed;
floatyspeed;
floatzoom=-20.0f;
GLuintloop;// Параметр циклу
GLuintcol;// Поточний колір
GLuintdelay;// Затримка ефекту веселки
GLuinttexture[1];// Пам’ять для текстури часток
GLfloatr;
typedef struct// Оголошення змінних
{
boolactive;
floatlife;
floatfade;
floatr;
floatg;
floatb;
floatx;
floaty;
floatz;
floatxi;
floatyi;
floatzi;
floatxg;
floatyg;
floatzg;
}
particles;// Структура часток
particles particle[MAX_PARTICLES];// Масив часток
static GLfloat colors[MAX_COLORS][4]=// Кольори веселки
{
{1.0f,0.5f,0.5f},{1.0f,0.5f,0.24f},{1.0f,0.63f,0.4f},{1.0f,0.2f,0.5f}
};
LRESULTCALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
AUX_RGBImageRec *LoadBMP(char *Filename)// Завантаження Bitmap зображення
{
FILE *File=NULL;// Дескриптор файлу
if (!Filename)// Переконатися у наданні імені файлу
{
return NULL;
}
File=fopen(Filename,"r");// Перевірити чи існує файл
if (File)
{
fclose(File);
return auxDIBImageLoad(Filename)
}
return NULL; // Якщо завантаження не вдалося, то повертається NULL
}
int LoadGLTextures()
{
int Status=FALSE;// Індикатор стану
AUX_RGBImageRec *TextureImage[1];// Створити простір пам’яті длятекстур
memset(TextureImage,0,sizeof(void *)*1);// Встановити покажчик на NULL
if (TextureImage[0]=LoadBMP("Data/Particle.bmp"))// Завантажити текстури
{
Status=TRUE;glGenTextures(1, &texture[0]);// Створення однієї текстури
glBindTexture(GL_TEXTURE_2D, texture[0]);
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[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
if (TextureImage[0])
{
if (TextureImage[0]->data) // Якщо текстури не існує
{
free(TextureImage[0]->data); // Звільнити пам’ять
}
free(TextureImage[0]);
}
return Status;
}
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Перевизначити розмір вікна
{
if (height==0)
{
height=1;// Висота рівна одиниці
}
glViewport(0,0,width,height);// Перезавантажити поточну область перегляду
glMatrixMode(GL_PROJECTION);// Вибір матриці проекту
glLoadIdentity();// Перезавантаження матриці проекту
// Обчислити коефіцієнт стискування вікна
gluPerspective(20.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int InitGL(GLvoid)// Налаштування OpenGL
{
if (!LoadGLTextures())
return FALSE;
srand((DWORD)GetTickCount);
glShadeModel(GL_SMOOTH);
glClearColor(0.0f,0.0f,0.0f,0.0f);// Чорний фон
glClearDepth(1.0f);// Буфер глибини
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);// Змішування
glBlendFunc(GL_SRC_ALPHA,GL_ONE);// Тип змішування
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,texture[0]);// Вибір текстури
for (loop=0;loop<MAX_PARTICLES;loop++)// Ініціалізація текстур
{
particle[loop].active=TRUE;// Make All The Particles Active
particle[loop].life=1.0f;// Give All The Particles Full Life
particle[loop].fade=float(rand()%100)/100.0f+0.3f;
particle[loop].r=colors[loop*(MAX_COLORS/MAX_PARTICLES)][0];// Вибір червоного кольору
particle[loop].g=colors[loop*(MAX_COLORS/MAX_PARTICLES)][1];// Вибір червоного кольору
particle[loop].b=colors[loop*(MAX_COLORS/MAX_PARTICLES)][2];// Вибір червоного кольору
particle[loop].xi=float((rand()%50)-26.0f)*10.0f;// Випадкова Швидкість На Осі X
particle[loop].yi=float((rand()%100)-25.0f)*10.0f;// Випадкова Швидкість На Осі Y
particle[loop].zi=float((rand()%50)-25.0f)*10.0f;;// Випадкова Швидкість На Осі Z
particle[loop].xg=0.0f;particle[loop].yg=-0.08f; particle[loop].zg=0.0f;
particle[loop].y=BOTTOM;
}
return TRUE;// Ініціалізація
}
int DrawGLScene(GLvoid)// Малювання
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очищення екрану і буфера глибини
glLoadIdentity();
for (loop=0;loop<MAX_PARTICLES;loop++)// Цикл через усі частки
{
if (particle[loop].active)// Якщо частки активні
{
float x=particle[loop].x;// Захопити позицію Xfloat y=particle[loop].y;// Захопити позицію Y
float z=particle[loop].z+zoom;// Захопити позицію Z
int coin;
glColor4f(particle[loop].r,particle[loop].g,particle[loop].b,particle[loop].life)
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2d(1,1); glVertex3f(x+0.5f,y+0.5f,z);
glTexCoord2d(0,1); glVertex3f(x-0.5f,y+0.5f,z);
glTexCoord2d(1,0); glVertex3f(x+0.5f,y-0.5f,z);
glTexCoord2d(0,0); glVertex3f(x-0.5f,y-0.5f,z);
glEnd();
particle[loop].x+=particle[loop].xi/(slowdown*1000);
particle[loop].y+=particle[loop].yi/(slowdown*1000);
particle[loop].z+=particle[loop].zi/(slowdown*1000);
particle[loop].xi+=particle[loop].xg;
particle[loop].yi+=particle[loop].yg;
particle[loop].zi+=particle[loop].zg;
particle[loop].life-=particle[loop].fade;// Скорочення терміну життя часток
if (particle[loop].life<0.0f)// Якщо частка перегорає
{
particle[loop].life=1.0f;// Дати нове життя
particle[loop].fade=float(rand()%100)/1000.0f+0.01f; // Випадкове значення
particle[loop].y=BOTTOM;
coin=rand();
if (coin<(RAND_MAX/8)) particle[loop].x=float(rand())/(RAND_MAX)-2.0f;
else
if (coin<(RAND_MAX/4)) particle[loop].x=float(rand())/(RAND_MAX)+1.0f;
else particle[loop].x=2*float(rand())/(RAND_MAX)-1.0f;
coin=rand();
if (coin<(RAND_MAX/8)) particle[loop].z=float(rand())/(RAND_MAX)-2.0f;
else
if (coin<(RAND_MAX/4)) particle[loop].z=float(rand())/(RAND_MAX)+1.0f;
else particle[loop].z=2*float(rand())/(RAND_MAX)-1.0f;
particle[loop].xi=xspeed+float((rand()%60)-32.0f);
particle[loop].yi=yspeed+float((rand()%700)-200.0f);
particle[loop].zi=float((rand()%60)-30.0f);
col=rand()*(MAX_COLORS+1)/RAND_MAX;
if (col>3) col=0;
particle[loop].r=colors[col][0];// Вибір червоного кольору
particle[loop].g=colors[col][1];// Вибір зеленого кольору
particle[loop].b=colors[col][2];// Вибір синього кольору
}
if (keys[VK_NUMPAD8] && (particle[loop].yg<1.5f)) particle[loop].yg+=0.01f;
if (keys[VK_NUMPAD2] && (particle[loop].yg>-1.5f)) particle[loop].yg-=0.01f;
if (keys[VK_NUMPAD6] && (particle[loop].xg<1.5f)) particle[loop].xg+=0.01f;
if (keys[VK_NUMPAD4] && (particle[loop].xg>-1.5f)) particle[loop].xg-=0.01f;
}
}
return TRUE;
}
GLvoid KillGLWindow(GLvoid)
{
if (fullscreen)
{
ChangeDisplaySettings(NULL,0);
ShowCursor(TRUE);// Відображення курсора
}
if (hRC)
{
if (!wglMakeCurrent(NULL,NULL)){
MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(hRC))
{
MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
hRC=NULL;
}
if (hDC && !ReleaseDC(hWnd,hDC))
{
MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hDC=NULL;
}
if (hWnd && !DestroyWindow(hWnd))// Ми млжемо знищити вікно?
{
MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hWnd=NULL;// Set hWnd To NULL
}
if (!UnregisterClass("OpenGL",hInstance))// Ми можемо не зареєструвати клас
{
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hInstance=NULL;// Set hInstance To NULL
}
}
/*Наступний код створює вікно OpenGL:
*title- заголовок вікна
* width- довжина вікна
*height- висота вікна
*bits- кількість бітів для відображення кольору (8/16/24/32)
*fullscreenflag- використання повноекранного режиму*/
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuintPixelFormat;// Уртимання результату після пошуку відповідності
WNDCLASSwc;// Клас структури вікна
DWORDdwExStyle;// Розширений Стиль вікна
DWORDdwStyle;// Стиль вікна
RECTWindowRect;
WindowRect.left=(long)0;
WindowRect.right=(long)width;
WindowRect.top=(long)0;
WindowRect.bottom=(long)height;
fullscreen=fullscreenflag;// Повноекранний режим
hInstance= GetModuleHandle(NULL);
wc.style= CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Перемалювання вікна
wc.lpfnWndProc= (WNDPROC) WndProc;
wc.cbClsExtra= 0;// Жодних додаткових вікон
wc.cbWndExtra= 0;// Жодних додаткових вікон
wc.hInstance= hInstance;// Встановтти зразок
wc.hIcon= LoadIcon(NULL, IDI_WINLOGO); // Завантаження стандартного значка
wc.hCursor= LoadCursor(NULL, IDC_ARROW); // Завантаження координит курсора
wc.hbrBackground= NULL;wc.lpszMenuName= NULL;wc.lpszClassName= "OpenGL";// Вказання ім’я класу
if (!RegisterClass(&wc))// Зареєструвати клас вікна
{
MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;}
if (fullscreen)
{
DEVMODE dmScreenSettings;// Режим пристрою
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));// Перевірка очищення пам’яті
dmScreenSettings.dmSize=sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth= width;// Довжина вікна
dmScreenSettings.dmPelsHeight= height;// Висота вікна
dmScreenSettings.dmBitsPerPel= bits;// Кількість бітів
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{
// При збої
if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE;// Присвоїти повноекранному режиму значення = FALSE
}
else
{
// Виштовхнути дані зі стека.
MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
return FALSE;
}
}
}
if (fullscreen)
{
dwExStyle=WS_EX_APPWINDOW;dwStyle=WS_POPUP;ShowCursor(FALSE);}
else
{
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwStyle=WS_OVERLAPPEDWINDOW;
}
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
// Створення вікна
if (!(hWnd=CreateWindowEx(dwExStyle,"OpenGL",// Ім’я класу
title,// Заголовок вікна
dwStyle |// Певний стиль вікна
WS_CLIPSIBLINGS |// Необхідний стиль вікна
WS_CLIPCHILDREN,// Необхідний стиль вікна
0, 0,// Позиція вікна
WindowRect.right-WindowRect.left,// Розрахунок довжини
WindowRect.bottom-WindowRect.top,// Розрахунок висоти
NULL,
NULL,// Без меню
hInstance,// Зразок
NULL)))// Не передавати нічого для WM_CREATE
{
KillGLWindow();// Оновлення екрану
MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
staticPIXELFORMATDESCRIPTOR pfd=
{
sizeof(PIXELFORMATDESCRIPTOR), // Розмір пікселів
1,// Номер версії
PFD_DRAW_TO_WINDOW |// Вікно повинно підтримувати формат
PFD_SUPPORT_OPENGL |// OpenGL повинен підтримувати формат
PFD_DOUBLEBUFFER,// Підтримка буферу
PFD_TYPE_RGBA,// Підтримка формату RGBA
bits,// Глибина кольору
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
16,// 16-бітний Z-буфер
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0// Проігнорувати маски слою
};
if (!(hDC=GetDC(hWnd)))
{
KillGLWindow();// Оновлення екрану
MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))
{
KillGLWindow();// Оновлення екрану
MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd))
{
KillGLWindow();// Оновлення екрану
MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
if (!(hRC=wglCreateContext(hDC)))
{
KillGLWindow();// Оновлення екрану
MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
if(!wglMakeCurrent(hDC,hRC))
{
KillGLWindow();// Оновлення екрану
MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
ShowWindow(hWnd,SW_SHOW);// Показ вікна
SetForegroundWindow(hWnd);
SetFocus(hWnd);
ReSizeGLScene(width, height);
if (!InitGL())// Ініціалізація вікна
{
KillGLWindow();// Оновлення екрану
MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWNDhWnd,// обробка вікна
UINTuMsg, // Повідлмлення вікна
WPARAMwParam,LPARAMlParam){
switch (uMsg)// Перевірка повідомлень
{
case WM_ACTIVATE:// Спостерігати за повідомленнями
{
if (!HIWORD(wParam))
{
active=TRUE;// Програма активна
}
else// Або
{
active=FALSE;// Програма більше не активна
}
return 0;// Повернення до циклу повідомлень
}
case WM_SYSCOMMAND:// Перевірка системних команд
{
switch (wParam)// Перевірка системних викликів
{
case SC_SCREENSAVE:
case SC_MONITORPOWER:
return 0;
}
break;// Вихід
}
case WM_CLOSE:// Чи отримали повідомлення?
{
PostQuitMessage(0);// Відправити повідомлення
return 0;
}
case WM_KEYDOWN: // Чи натиснена клавіша?
{
keys[wParam] = TRUE;
return 0;
}
case WM_KEYUP:// Чи клавіша відпущена?
{
keys[wParam] = FALSE;
return 0;
}
case WM_SIZE:// Зміна розміра вікна OpenGL
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width, HiWord=Height
return 0;
}
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
int WINAPI WinMain(HINSTANCEhInstance, // Зразок
HINSTANCEhPrevInstance,// Попередній Instance
LPSTRlpCmdLine, // Командний рядок Parameters
intnCmdShow) // Вікно стану
{
MSGmsg;// Структури повідомлень вікна
BOOLdone=FALSE;// Вихід з циклу
// Створення вікна OpenGL
if (!CreateGLWindow("Fire Simulation Based On Particles",640,480,16,fullscreen))
{
return 0;// Вихід, якщо вікно не вдалося створити
}
while(!done)// Цикл працює поки=FALSE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))//
{
if (msg.message==WM_QUIT) // Чи є нові повідомлення?
{
done=TRUE;
}
else
{
TranslateMessage(&msg);// Перекласти повідомлення
DispatchMessage(&msg);// Відіслати повідомлення
}
}
else// Немає повідомлень
{
// Малювання сцени DrawGLScene()
if ((active && !DrawGLScene()) || keys[VK_ESCAPE]){
done=TRUE;// ESC для виходу
}
else
{
SwapBuffers(hDC);// Зміна буфера
if (keys[VK_ADD] && (slowdown>0.0f)) slowdown-=0.1f;// Швидкість руху часток вверх
if (keys[VK_SUBTRACT] && (slowdown<50.0f)) slowdown+=0.1f;// Швидкість руху часток вниз
if (keys[VK_PRIOR])zoom+=0.1f;// Приближення
if (keys[VK_NEXT])zoom-=0.1f;// Віддалення
if (keys[VK_UP] && (yspeed<2000)) yspeed+=1.0f;
if (keys[VK_DOWN] && (yspeed>-2000)) yspeed-=1.0f;
if (keys[VK_RIGHT] && (xspeed<2000)) xspeed+=1.0f;
if (keys[VK_LEFT] && (xspeed>-2000)) xspeed-=1.0f;
delay++; // Збільшити кольри
if (keys[VK_F1])// Якщо натиснено F1?
{
keys[VK_F1]=FALSE;
KillGLWindow();// Знащити поточне вікно
fullscreen=!fullscreen;// Відновлення вікна
if (!CreateGLWindow("Fire Simulation Based On Particles",1024,768,16,fullscreen))
{
return 0;// Quit If Window Was Not Created
}
}
}
}
}
// Вимикання
KillGLWindow();// Знищення вікна
return (msg.wParam);// Вихід з програми
}
Таким чином, ґрунтуючись на нескладній моделі поведінки вогню, мені вдалося ефективно реалізувати реалістичну імітацію оптичних процесів що виникають при горінні в повітряній атмосфері.
ВИСНОВКИ
В процесі написання курсової роботи була розглянута графічна бібліотека OpenGL з метою використання її в комп'ютерному моделюванні. Був розглянутий синтаксис команд та програмний код команд, а також бібліотеки, що відносяться до OpenGL.
Основним завданням цієї курсової роботи було максимально реалістично змоделювання горіння вогню.
Для вирішення завдання була написана програма на мові С з використанням технології OpenGL. Вона реалізує поставлене завдання на основі системи часток. Це найбільш простий і ефективний підхід, що показує непоганий результат і високу продуктивність, не накладаючи високі вимоги на відеоадаптер.
ЛІТЕРАТУРА
Эйнджел Э. Интерактивная компьютерная графика. Вводний курс на базе OpenGL / Э. Эйнджел. 2001. – 590 с.
Тихомиров Ю. Программирование трехмерной графики. Серия '' Мастер''. – СПб.: BHV-Санкт-Петербург. 2000. – 256 с.
Майер Р. В. Компьютерное моделирование физических явлений / Р. В. Майер. – Глазов: ГГПИ, 2009. – 111 с.
Мейсон Ву. OpenGL. Официальное руководство программиста / Ву Мейсон, Нейдер Д., Девис Т., Шрайнер Д. 2002. – 592 с.
Роджерс Д. Алгоритмические основы машинной графики / Д. Роджерс. М.: Мир. 1989. – 512 с.