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. Вона реалізує поставлене завдання на основі системи часток. Це найбільш простий і ефективний підхід, що показує непоганий результат і високу продуктивність, не накладаючи високі вимоги на відеоадаптер.
ЛІТЕРАТУРА
1. Эйнджел Э. Интерактивная компьютерная графика. Вводний курс на базе OpenGL / Э. Эйнджел. 2001. – 590 с.
2. Тихомиров Ю. Программирование трехмерной графики. Серия '' Мастер''. – СПб.: BHV-Санкт-Петербург. 2000. – 256 с.
3. Майер Р. В. Компьютерное моделирование физических явлений / Р. В. Майер. – Глазов: ГГПИ, 2009. – 111 с.
4. Мейсон Ву. OpenGL. Официальное руководство программиста / Ву Мейсон, Нейдер Д., Девис Т., Шрайнер Д. 2002. – 592 с.
5. Роджерс Д. Алгоритмические основы машинной графики / Д. Роджерс. М.: Мир. 1989. – 512 с.
0 комментариев