Для того, щоб зробити ефективність програми максимальної, усі операції, які повинні викликатися одноразово (установка кольору фону і системи координат), тепер включені до складу процедури, названої 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). Проте кількість використовуваних кольорів нескладна істотно збільшити.