A III. Rész végén tettem egy kis
utalást az animációkra, de példát már hely hiányában nem tudtam rá mutatni.
Nos, ezek után – mint ahogy a cím is sejteti – az animációkkal fogunk foglalkozni.
Sokak legnagyobb örömére, másoknak pedig bánatára, ezt a részt előbbre
vettem, mint a legtöbb hasonló cikkben, ahol a vertexek bemutatása után a
színek, anyagok, fények jönnek. Nekünk ez most a későbbi cikkekre fog
maradni. A dolgot úgy kezdeném, hogy elmondom, milyen lehetőségek állnak
rendelkezésre az animációhoz, utána egy példaprogram, majd pedig annak
magyarázata következik.
Vágjunk is bele! A következő
program a cikk mellékleteként forráskódban és Windows alá lefordítva is
megtalálható. (A továbbiakban az összes példaprogram is hasonlóan.)
A programunk tehát:
#define
GLUT_DISABLE_ATEXIT_HACK
#include
<windows.h>
#include
<GL/gl.h>
#include
<GL/glu.h>
#include
<GL/glut.h>
float x=0;
float y=0;
float xm=0.01;
float ym=0.01;
void SceneIdle(void)
{
if ( (x>=.5)||(x<=-.5) )
{ xm=-xm; }
if ( (y>=.4)||(y<=-.4) )
{ ym=-ym; }
x=x+xm;
y=y+ym;
glutPostRedisplay();
}
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glVertex3f(-0.5f+x, -0.5f+y, 0.0f);
glVertex3f(-0.5f+x,
0.5f+y, 0.0f);
glVertex3f( 0.5f+x,
0.5f+y, 0.0f);
glVertex3f( 0.5f+x, -0.5f+y, 0.0f);
glEnd();
glutSwapBuffers();
}
int main(int argc, char*
argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutCreateWindow("Pelda01");
glutDisplayFunc(RenderScene);
glutIdleFunc(SceneIdle);
glutMainLoop();
return 0;
}
Ez a program a II. Részben bemutatott
első példaprogramunk (Példa 01 – Fehér Négyzet) továbbfejlesztett
változata. Ebben már a korábban megrajzolt négyzetünket pattogtatjuk oda-vissza
az ablak határain belül. Most még megtehetem, hogy az egész programot
bemutatom, de később már csak a megváltozott részeket, illetve azok helyét
fogom itt leírni, hely hiányában. (Egy jó darabig még úgyis ugyanazt a
programot fogjuk továbbfejlesztgetni.)
Hogy hogyan is történik az animáció?
Nos, a GLUT úgynevezett CallBack függvényekkel rendelkezik – pontosabban mi hozzuk
létre őket a számára – melyek bizonyos feltételek mellett, akár
periodikusan is meghívódnak. A legfontosabb CallBack függvény, amit már
használtunk, bár nem neveztünk nevén, a RenderScene(), amely periodikusan
hívódik meg, amikor az OpenGL az aktuális frame (képkocka, de a továbbiakban az
angol – és zsargonban jobban elfogadott nevét használom) tartalmát rendereli.
Amit most használtunk, az SceneIdle() nevű CallBack függvény, amelynek a
RenderScene()-hez hasonlóan nem kell semmilyen paramétert definiálni, a haszna
pedig annyi, hogy meghívódik minden olyan alkalommal, amikor a grafikus
vezérlő dolgozik – pl. éppen renderel – viszont a processzor „ráér”. Itt
elvégezhetők olyan számítások például, mint jelen esetben a négyzet
eltolásának kiszámítása.
Megjegyezném, hogy az M.J. Kilgard
(Silicon Graphics Inc.) által összeállított GLUT API v3 referencia szerint nem
túl ajánlott dolog az SceneIdle() függvényben túl sok számítást végezni, mert
előfordulhat, hogy a ló túloldalára esünk át azáltal, hogy már nem a processzor
fog szabad lenni, hanem más alkalmazások futtatása akad meg, miközben a
videovezérlő már készen áll az új renderfolyamat indításához. (Személyes
tapasztalat: nekem ezt még – ésszerű kereteken belül - szándékosan sem
sikerült így elérnem, tehát nem igazán tudom elmondani, mennyi az a „nem túl
sok” számítás.)
Miután a SceneIdle() függvényt jobban
szemügyre vesszük, láthatjuk, hogy van egy sor a végén, amelyik a
glutPostRedisplay() függvényhívást tartalmazza. Ez a függvény arra szolgál,
hogy meghívásakor felveszi az aktuális ablakot az újrarajzolandó ablakok
listájába. A számításokat végző néhány sort és a módosított glVertex3f()
sorokat nem szándékozom részletezni, az olvasóra bízom a velük való
kísérletezést. A főprogram csak egyetlen sorral bővült az eredetihez
képest, ez pedig:
glutIdleFunc(SceneIdle);
Ebben a sorban beállítjuk, melyik
legyen az a függvény, amely meghívódik az Idle (üresjárati) CallBack esetén.
Ha már felhoztuk a CallBack
függvényeket, nem ártana, ha néhányat bemutatnék közülük, lévén később jó
hasznukat vesszük. Igazából nem túl sok áll belőlük rendelkezésünkre,
viszont azokkal nagyjából mindent meg tudunk csinálni. Láttuk már a
DisplayFunc() ill. IdleFunc() függvényeket, melyekkel a renderelő, illetve
az Idle (üresjárati) CallBack-eket tudtuk lekezelni, illetve kihasználni.
Erről a kettőről tehát itt nem ejtünk szót.
A fennmaradó néhány közül elsőnek
venném a glutTimerFunc() függvényt, amellyel beállíthatunk egy adott
időközönként meghívott függvényt. Ez főleg akkor lesz majd, hasznos,
mikor adott sebességű animációkat akarunk előállítani. Ugyanis az
Idle CallBack-es megoldás nagyban függ a processzor órajelétől és gyorsabb
számítógépen így az animáció (játék) is gyorsabbá válik, ami nem biztos, hogy
jó.
A glutTimerFunc paraméterezése:
glutTimerFunc(unsigned int msecs, void
(*func) (int value), value);
A paraméterek közül a (*func) az
átadott függvény, melynek egyetlen integer paramétere lehet, ez pedig a value.
Ez a híváskor értékül azt kapja, amit a glutTimerFunc()
value-jának adunk meghíváskor. Egyszerre több ilyen időzített hívás is
beállítható, viszont egyiket sem lehet visszavonni. Helyette – érdekes
megoldás! – a value paraméter alapján figyelmen kívül lehet hagyni szükség
esetén az időzített hívást.
Következő CallBack függvény a glutReshapeFunc(void (*func) (int w, int h)) mely
akkor hívódik meg, amikor az aktuális ablak mérete megváltozik. A (h,w) páros
értelemszerűen az új méretet hordozza.
Jó néhány CallBack foglalkozik egér-
és billentyű-események lekezelésével, így ezekre, mivel később nagy
szükség lesz rájuk, a teljes következő cikket szándékozom szentelni.
Erre az alkalomra körülbelül ennyit
terveztem, bízom benne, hogy érthető volt. A GLUT CallBack-ekről
bőséges anyag található a már fent említett Kilgard-féle
referencia-könyvben (angol nyelvű!), amely .PDF változatban jó néhány
hivatalos OpenGL oldalról elérhető.
Továbbra is várom leveleiteket,
kérdéseiteket a témával kapcsolatban!
Merczel
László - da_maniac_@yahoo.com