Programujeme grafický engine VI.

opengl_logo.jpg V tej­to čas­ti se­riá­lu bu­de­me po­kra­čo­vať v opi­se fun­kcio­na­li­ty mo­du­lov ap­li­ká­cie En­gi­ne v1.0. Za­me­ria­me sa na zvyš­né mo­du­ly, med­zi kto­ré pa­tria Octtree, Fron­tend, Bac­kend, Ava­tar, Inter­fa­ce a En­gi­ne. Tým za­vŕši­me opis mo­du­lov ap­li­ká­cie a za­čne­me sa ve­no­vať jej zlep­šo­va­niu.

Mo­du­ly ap­li­ká­cie En­gi­ne v1.0 – po­kra­čo­va­nie
V nas­le­du­jú­cich blo­koch si po­vie­me pár slov o zvyš­ných mo­du­loch ap­li­ká­cie En­gi­ne v1.0. Mo­du­ly opi­so­va­né v tej­to čas­ti se­riá­lu vy­ko­ná­va­jú väč­ši­nu úlo, a pre­to sa im bu­de­me ve­no­vať po­drob­nej­šie.

Mo­dul Octtree (trie­da mOcttree, sú­bo­ry com­mon/mOcttree.cpp, com­mon/mOcttree.h)
Z člán­kov pre­doš­lé­ho se­riá­lu vie­me, že ok­tá­no­vý strom (angl. oct-tree) je úda­jo­vá štruk­tú­ra, kto­rá slú­ži na urýc­hle­nie vý­be­ru tých ob­jek­tov, kto­ré tre­ba spra­co­vať. Ok­tá­no­vý strom mož­no ok­rem iné­ho po­užiť aj na vý­ber ob­jek­tov, kto­ré sú vi­di­teľ­né na ob­ra­zov­ke, na vý­po­čet ko­lí­zie s vy­bra­ný­mi ob­jek­tmi a na nie­koľ­ko ďal­ších, zväč­ša „se­lek­tív­nych“ úloh. Ob­jek­tom sa v tom­to po­ní­ma­ní spra­vid­la ro­zu­mie tro­ju­hol­ník (angl. triang­le). Ok­tá­no­vý strom im­ple­men­to­va­ný v ap­li­ká­cii En­gi­ne v1.0 je vy­tvo­re­ný po­mo­cou re­kur­zie, pri­čom list toh­to stro­mu je rep­re­zen­to­va­ný nas­le­du­jú­cou úda­jo­vou štruk­tú­rou:

_tree_no­de

Ok­tá­no­vý strom ap­li­ká­cie En­gi­ne v1.0 po­zos­tá­va z lis­tov rep­re­zen­to­va­ných tzv. AABB (angl. axis-alig­ned boun­ding box) koc­ka­mi, resp. hra­nol­mi. Na zjed­no­du­še­nie bu­de­me vždy ho­vo­riť o koc­kách, aj keď ich ste­ny ne­mu­sia mať rov­na­ké roz­me­ry. Kaž­dá z týc­hto ko­ciek po­zos­tá­va zo 6 stien, rov­no­bež­ných s jed­not­li­vý­mi osa­mi sú­rad­ni­co­vej sús­ta­vy. Ko­reň stro­mu ob­sa­hu­je všet­ky ob­jek­ty scé­ny (ce­lý te­rén). Po­dlis­ty stro­mu sú vy­tvo­re­né na zá­kla­de de­le­nia ko­re­ňa stro­mu (koc­ky) na 8 čas­tí. Tie­to čas­ti roz­de­lia pô­vod­nú „ro­di­čov­skú“ koc­ku na 8 men­ších ko­ciek. Na vy­tvo­re­nie ok­tá­no­vé­ho stro­mu po­uží­va­me fun­kciu:
int mOcttree::Octtree_crea­te(mTriang­le *T,GLuint N)

Zob­ra­ze­nie jed­not­li­vých lis­tov (ko­ciek) ok­tá­no­vé­ho stro­mu reali­zu­je fun­kcia:
void mOcttree::Octtree_view(void)

Jed­not­li­vé lis­ty ok­tá­no­vé­ho stro­mu zru­ší­me po­mo­cou nas­le­du­jú­cej fun­kcie:
int mOcttree::Octtree_des­troy(void)

Naj­výz­nam­nej­šia je tá­to fun­kcia mo­du­lu Octtree, o kto­rej si po­vie­me viac:
void mOcttree::Octtree_fi­nish(mTriang­le *T,mFrus­tum *F,GLfloat AX,GLfloat AY,GLfloat AZ)

Na obr. 1 mô­že­me vi­dieť, aká je úlo­ha fun­kcie Octtree_fi­nish(). Tá­to fun­kcia vy­be­rá tro­ju­hol­ní­ky zo štruk­tú­ry ok­tá­no­vé­ho stro­mu, te­da z je­ho lis­tov (_tree_no­de), ap­li­ku­je test Frus­tum Culling, kto­rým sa vy­be­rú tie lis­ty stro­mu, kto­ré sú v da­nom oka­mi­hu vi­di­teľ­né, a od­ka­zy (angl. po­in­ters) na tro­ju­hol­ní­ky ulo­že­né vo vy­bra­ných lis­toch ok­tá­no­vé­ho stro­mu umies­tňu­je do po­ľa act_tri mo­du­lu Fron­tend (kon­krét­ne trie­dy mTriang­le).

Aký vý­znam má pl­ne­nie ta­kej­to úlo­hy?
Po vy­ko­na­ní fun­kcie Octtree_fi­nish() sa bu­dú v po­li act_tri nac­hád­zať iba tie tro­ju­hol­ní­ky, kto­ré bo­li vy­bra­né na zá­kla­de po­rov­na­nia po­lo­hy lis­tov ok­tá­no­vé­ho stro­mu s po­hľa­dom (angl. Frus­tum) po­zo­ro­va­te­ľa. Po vy­ko­na­ní nie­koľ­kých tes­tov sa tak vy­lú­či veľ­ké množ­stvo tro­ju­hol­ní­kov, kto­ré net­re­ba ďa­lej spra­cú­vať. Tá­to me­tó­da te­da slú­ži naj­mä na urýc­hle­nie po­rov­ná­va­cích tes­tov. Pred­stav­me si, že by sme na­mies­to pár tes­tov mu­se­li otes­to­vať po­lo­hu kaž­dé­ho tro­ju­hol­ní­ka de­fi­no­va­né­ho v scé­ne. Scé­ny mo­der­ných po­čí­ta­čo­vých hier ob­sa­hu­jú stá­ti­sí­ce tro­ju­hol­ní­kov. Nie je mož­né a ani efek­tív­ne tes­to­vať kaž­dý jed­not­li­vý tro­ju­hol­ník. Prá­ve pre­to sme tro­ju­hol­ní­ky te­ré­nu umies­tni­li do štruk­tú­ry ok­tá­no­vé­ho stro­mu, kto­rá nám umož­ní po nie­koľ­kých tes­toch vy­lú­čiť veľ­ké množ­stvo „ne­vi­di­teľ­ných“ tro­ju­hol­ní­kov.

engine obr1.bmp
Obr. 1 Úlo­ha fun­kcie Octtree_fi­nish()

Mo­dul Fron­tend (trie­da mFron­tend, sú­bo­ry mFron­tend.cpp, mFron­tend.h)
Dos­ta­li sme sa k naj­dô­le­ži­tej­šie­mu mo­du­lu ce­lej ap­li­ká­cie. Mo­dul Fron­tend de­fi­nu­je úda­jo­vú štruk­tú­ru tro­ju­hol­ní­ka, kto­rá je spra­cú­va­ná prak­tic­ky v kaž­dom mo­du­le ap­li­ká­cie. Naj­skôr sa do nej ulo­žia tro­ju­hol­ní­ky na­čí­ta­né zo zdro­jo­vých sú­bo­rov, po­tom sa vy­ko­na­jú pat­rič­né tes­ty s tý­mi­to tro­ju­hol­ník­mi a na­ko­niec sa od­ka­zy na tro­ju­hol­ní­ky ur­če­né na zob­ra­ze­nie umies­tnia do vop­red prip­ra­ve­né­ho po­ľa act_tri, kto­ré je ta­kis­to sú­čas­ťou mo­du­lu Fron­tend. Všet­ky spo­mí­na­né čin­nos­ti sa vy­ko­ná­va­jú v rám­ci trie­dy mTriang­le. Pri­da­nie no­vé­ho tro­ju­hol­ní­ka do zoz­na­mu všet­kých tro­ju­hol­ní­kov vy­ko­ná­va­me fun­kciou:

int mTriang­le::tri_add(tVe­c3 V1,tVe­c3 V2,tVe­c3 V3,
                       tVe­c3 N1,tVe­c3 N2,tVe­c3 N3,
                       tVec2 T1,tVec2 T2,tVec2 T3,
                       tVec4 C,
                       GLuint TEX1,GLuint TEX2)

Príp­ra­vu po­ľa na ulo­že­nie od­ka­zov na tzv. ak­tív­ne tro­ju­hol­ní­ky má na sta­ros­ti nas­le­du­jú­ca fun­kcia:
int mTriang­le::tri_fi­nish(GLuint N)

Ak­tív­ne sú tie tro­ju­hol­ní­ky, kto­ré preš­li tes­ta­mi v pre­doš­lých fá­zach vy­ko­ná­va­nia prog­ra­mo­vé­ho kó­du. Všet­ky ak­tív­ne tro­ju­hol­ní­ky však ne­mu­sia byť na­ko­niec zob­ra­ze­né. Dô­vo­dom je nás­led­né po­uži­tie Frus­tum Cullin­gu na tie­to tro­ju­hol­ní­ky. Vy­svet­lí­me to pri opi­se fun­kcie act_tree_draw(). Vy­kres­le­nie všet­kých tro­ju­hol­ní­kov ulo­že­ných v zoz­na­me tro­ju­hol­ní­kov vy­ko­ná­va fun­kcia
int mTriang­le::tri_draw(mFrus­tum *F)

Vy­kres­le­nie tro­ju­hol­ní­kov ulo­že­ných v po­li ak­tív­nych tro­ju­hol­ní­kov reali­zu­je­me po­mo­cou fun­kcie
int mTriang­le::act_tri_draw(mFrus­tum *F)

engine obr2.bmp
Obr. 2 Úlo­ha fun­kcie act_tree_draw()

Na obr. 2 je blo­ko­vý di­ag­ram vy­ko­ná­va­nia fun­kcie act_tri_draw(). Fun­kcia po­stup­ne vy­be­rá tro­ju­hol­ní­ky ulo­že­né v po­li od­ka­zov na ak­tív­ne tro­ju­hol­ní­ky, tes­tu­je ich na vi­di­teľ­nosť po­mo­cou Frus­tum Culling a tro­ju­hol­ní­ky, kto­ré ús­peš­ne spl­nia test, „za­sie­la“ na vy­kres­le­nie na ob­ra­zov­ke. V tom­to oka­mi­hu po­zná­me ce­lú ces­tu, po kto­rej sa tro­ju­hol­ník pre­mies­tňu­je od oka­mi­hu, keď sa na­čí­ta zo zdro­jo­vé­ho sú­bo­ru, až po je­ho vy­kres­le­nie na ob­ra­zov­ke po­čí­ta­ča.

Mô­že­me si to zhr­núť:
1. Tro­ju­hol­ník na­čí­ta­ný zo sú­bo­ru 3ds je ulo­že­ný do zoz­na­mu, na kto­ré­ho pr­vý pr­vok uka­zu­je pre­men­ná first_tri – mo­dul Bac­kend, fun­kcia extract_terrain().
2. Od­ka­zy na tro­ju­hol­ní­ky ulo­že­né v zoz­na­me first_tri sú umies­tne­né do lis­tov ok­tá­no­vé­ho stro­mu po­dľa to­ho, aká je ich po­lo­ha v scé­ne – mo­dul Octtree, fun­kcia Octtree_crea­te().
3. Po­mo­cou Frus­tum Culling sa vy­be­rú tie lis­ty ok­tá­no­vé­ho stro­mu s ulo­že­ný­mi od­kaz­mi na tro­ju­hol­ní­ky, kto­ré sa v da­nom oka­mi­hu nac­hád­za­jú v po­hľa­de, a tie­to od­ka­zy sa umies­tnia do po­ľa act_tri – mo­dul Octtree, fun­kcia Octtree_fi­nish().
4. Po­mo­cou Frus­tum Culling sa vy­be­rú tro­ju­hol­ní­ky ulo­že­né v po­li act_tri, kto­ré sa v da­nom oka­mi­hu nac­hád­za­jú v po­hľa­de, a tie sa vy­kre­slia na ob­ra­zov­ke – mo­dul Fron­tend (trie­da mTriang­le), fun­kcia act_tri_draw().

Mo­dul Bac­kend (trie­da mBac­kend, sú­bo­ry mBac­kend.cpp, mBac­kend.h)
Ten­to mo­dul je pred­nos­tne ur­če­ný na na­čí­ta­va­nie vstup­ných sú­bo­rov. V prí­pa­de, že ide o sú­bor for­má­tu 3ds, kon­krét­ne na­čí­ta­nie vy­ko­ná mo­dul 3ds. Do ap­li­ká­cie bu­de­me po­stup­ne pri­dá­vať schop­nosť na­čí­tať ďal­šie sú­bo­ry so vstup­ný­mi údaj­mi, ku kto­rým prip­ra­ví­me adek­vát­ne mo­du­ly.

Mo­dul Bac­kend pl­ní fun­kciu in­teg­rá­to­ra všet­kých mo­du­lov zod­po­ved­ných za na­čí­ta­va­nie vstup­ných úda­jov. V ap­li­ká­cii En­gi­ne v1.0 je za­tiaľ im­ple­men­to­va­né na­čí­ta­nie 3ds sú­bo­rov, kon­krét­ne ide o na­čí­ta­nie te­ré­nu, na­čí­ta­nie tzv. fik­tív­nych, resp. po­moc­ných (angl. dum­my) ob­jek­tov a na­čí­ta­nie ob­jek­tov umies­tňo­va­ných do scé­ny (angl. en­ti­ties). Tie­to úlo­hy vy­ko­ná­va­jú nas­le­du­jú­ce fun­kcie:

int mOb­ject::extract_terrain(m3ds *OBJ,mTriang­le *T,GLuint DTEX,GLuint OL)
int mOb­ject::extract_dum­my(m3ds *OBJ)
int mOb­ject::extract_en­ti­ties(m3ds *OBJ,mTriang­le *T)

Vráť­me sa k na­čí­ta­niu ob­jek­tu pred­sta­vu­jú­ce­ho te­rén. Ten­to špe­ciál­ny prí­pad sme dopl­ni­li o op­ti­ma­li­zá­ciu te­ré­nu. Vý­stu­pom väč­ši­ny gra­fic­kých edi­to­rov, kto­ré sú schop­né vy­tvo­riť ob­jekt pred­sta­vu­jú­ci te­rén, sú to­tiž zväč­ša pra­vi­del­né sie­te tro­ju­hol­ní­kov, kto­ré spra­vid­la mož­no op­ti­ma­li­zo­vať. Od­strá­nia sa tak ploc­hy, kto­ré sú sí­ce rep­re­zen­to­va­né via­ce­rý­mi tro­ju­hol­ník­mi, ale po ap­li­ká­cii správ­ne­ho ap­roximač­né­ho al­go­rit­mu mož­no via­ce­ré tro­ju­hol­ní­ky od­strá­niť, resp. zos­ku­piť. Op­ti­ma­li­zá­cia te­ré­nov je veľ­mi zlo­ži­tý pro­ces, o kto­rom sa vo sve­te ve­ľa na­pí­sa­lo. My sme v na­šej ap­li­ká­cii po­uži­li re­la­tív­ne jed­no­duc­hý prís­tup, kto­rým sa v pra­vi­del­ných od­stu­poch zos­ku­pia tie sku­pi­ny tro­ju­hol­ní­kov, kto­ré tvo­ria „tak­mer“ ro­vin­né ploc­hy. Tú­to úlo­hu pl­ní nas­le­du­jú­ca fun­kcia:

int mOb­ject::mat128x128_op­ti­mi­ze(GLuint OL) 

Po­kiaľ by sme chce­li zjed­no­du­še­ne opí­sať tie čas­ti kó­du, kto­ré spra­cú­va­jú ob­jekt te­ré­nu, mô­že­me skon­šta­to­vať, že ten­to ob­jekt na­čí­ta­ný po­mo­cou mo­du­lu 3ds je op­ti­ma­li­zo­va­ný v mo­du­le Bac­kend a je­ho tro­ju­hol­ní­ky sú umies­tne­né do úda­jo­vej štruk­tú­ry ok­tá­no­vé­ho stro­mu. Ide te­da o dvo­jú­rov­ňo­vú op­ti­ma­li­zá­ciu. V prí­pa­de, že by sme ma­li zá­ujem expe­ri­men­to­vať s jed­not­li­vý­mi úrov­ňa­mi op­ti­ma­li­zá­cie, mô­že­me napr. za­ká­zať vy­ko­na­nie fun­kcie na op­ti­ma­li­zá­ciu ob­jek­tu te­ré­nu, resp. mô­že­me vy­ra­diť z čin­nos­ti po­uži­tie ok­tá­no­vé­ho stro­mu.

Na obr. 2 mô­že­me vi­dieť sie­ťo­vý mo­del te­ré­nu, kto­rý vzni­kol po je­ho op­ti­ma­li­zá­cii. Zno­vu po­do­tý­kam, že nej­de o vy­so­ko kva­lit­ný op­ti­ma­li­zač­ný al­go­rit­mus. Na na­še cie­le však úpl­ne po­sta­ču­je a ďal­šou op­ti­ma­li­zá­ciou te­ré­nu sa ne­mu­sí­me za­obe­rať. Zos­tá­va iba dopl­niť, že po­uži­tým op­ti­ma­li­zač­ným al­go­rit­mom sa z pô­vod­né­ho sie­ťo­vé­ho mo­de­lu te­ré­nu od­strá­ni­la viac ako po­lo­vi­ca tro­ju­hol­ní­kov, čo môž­me po­va­žo­vať za ús­pech.

engine obr3.bmp
Obr. 3 Op­ti­ma­li­zá­cia sie­ťo­vé­ho mo­de­lu te­ré­nu

Mo­dul Ava­tar (trie­da mA­va­tar, sú­bo­ry mA­va­tar.cpp, mA­va­tar.h)
Mo­dul Ava­tar ne­bu­de­me na tom­to mies­te po­drob­ne opi­so­vať. Uve­die­me iba uži­toč­nú in­for­má­ciu, že v mo­du­le Ava­tar doc­hád­za k spra­co­va­niu po­žia­da­viek po­zo­ro­va­te­ľa na po­hyb (angl. mo­ve), resp. ro­tá­ciu (angl. ro­ta­te) v scé­ne. Ten­to mo­dul bu­de­me v bu­dúc­nos­ti ve­ľak­rát up­ra­vo­vať a dopĺňať, pre­to­že prá­ve tu sa bu­de im­ple­men­to­vať de­tek­cia ko­lí­zií (angl. Colli­sion De­tec­tion).

Mo­dul Inter­fa­ce (trie­da mIn­ter­fa­ce, sú­bo­ry mIn­ter­fa­ce.cpp, mIn­ter­fa­ce.h)
Ako sme už uvied­li, mo­dul Inter­fa­ce je uzol med­zi všet­ký­mi os­tat­ný­mi mo­dul­mi ap­li­ká­cie. Ok­rem tej­to úlo­hy prip­ra­vu­je sta­vo­vý stroj OpenGL a ob­sa­hu­je fun­kciu, v kto­rej doc­hád­za k prep­nu­tiu zob­ra­zo­va­cích zá­sob­ní­kov (angl. Fra­me­buf­fer), čím dôj­de k spra­co­va­niu všet­kých za­da­ných prí­ka­zov OpenGL a vy­kres­le­niu scé­ny. Tie­to úlo­hy pl­nia nas­le­du­jú­ce dve fun­kcie:

int mIn­ter­fa­ce::ogl_Ini­tia­li­ze(void)
int mIn­ter­fa­ce::ogl_Draw(void)

Mo­dul En­gi­ne (trie­da mEn­gi­ne, sú­bo­ry mEn­gi­ne.cpp, mEn­gi­ne.h)
Po­sled­ný z opi­so­va­ných mo­du­lov ap­li­ká­cie En­gi­ne v1.0 je En­gi­ne. Ten­to mo­dul nepl­ní ni­ja­kú dô­le­ži­tú fun­kciu, je však pr­vým mies­tom, na kto­rom sa za­čne vy­ko­ná­vať kód na­šej ap­li­ká­cie. Ob­sa­hu­je hlav­nú vy­ko­ná­va­ciu fun­kciu

int main(int argc,char **argv) 

a fun­kcie na na­čí­ta­va­nie vstu­pov z klá­ves­ni­ce a my­ši:

void APP_key­board(void)
void APP_mou­se(int but­ton,int sta­te,int x,int y)

V mo­du­le En­gi­ne ta­kis­to doc­hád­za k ini­cia­li­zá­cii SDL a vy­tvo­re­niu ok­na ap­li­ká­cie (angl. SDL+OpenGL Win­dow). O vy­ko­na­nie všet­kých os­tat­ných úloh sa sta­rá mo­dul Inter­fa­ce, kto­rý in­teg­ru­je os­tat­né mo­du­ly ap­li­ká­cie.

Na­bu­dú­ce...
V bu­dú­cej čas­ti se­riá­lu roz­ší­ri­me fun­kcio­na­li­tu na­šej ap­li­ká­cie o de­tek­ciu ko­lí­zií a pros­tre­die v oko­lí po­zo­ro­va­te­ľa (scé­nu) dopl­ní­me o niek­to­ré jed­no­duc­hé ob­jek­ty. V kaž­dej nas­le­du­jú­cej čas­ti se­riá­lu bu­de­me do scé­ny pri­dá­vať no­vé ob­jek­ty. Bu­de­me sa sna­žiť up­ra­vo­vať oko­lie po­zo­ro­va­te­ľa tak, aby mal čo naj­reál­nej­ší do­jem z pries­to­ru, v kto­rom sa nac­hád­za.

Ďal­šie čas­ti >>

Zdroj: Infoware 4/2008



Ohodnoťte článok:
   
 

24 hodín

týždeň

mesiac

Najnovšie články

Prog­ra­mu­je­me gra­fic­ký en­gi­ne XXXVII.
V tejto a takisto v nasledujúcej časti seriálu budeme riešiť synchronizáciu streľby s pohybom postavy. Pôvodne sme sa tejto oblasti venovali oddelene od blokov súvisiacich so zobrazením postavy. čítať »
 
Prog­ra­mu­je­me gra­fic­ký en­gi­ne XXXVI.
V tejto časti seriálu sa budeme venovať tomu, ako možno do grafických (herných) enginov implementovať objekty reprezentujúce rebríky (ladders). čítať »
 
Prog­ra­mu­je­me gra­fic­ký en­gi­ne XXXV.
V tejto časti seriálu podrobnejšie rozoberieme jednotlivé časti programového kódu súvisiace s implementáciou streľby. čítať »
 
Prog­ra­mu­je­me gra­fic­ký en­gi­ne XXXIV.
V tejto časti seriálu vám v stručnosti predstavíme tzv. systémy častíc (particle systems). Kvalitne navrhnutými systémami častíc dokážeme veľmi rýchlo a elegantne zvýšiť dynamiku grafických aplikácií. čítať »
 
Prog­ra­mu­je­me gra­fic­ký en­gi­ne XXXIII.
V predchádzajúcej časti seriálu sme dokončili kapitolu, v ktorej sme sa venovali simulácii fyziky. čítať »
 
Prog­ra­mu­je­me gra­fic­ký en­gi­ne XXXII.
Touto časťou seriálu ukončíme tému implementácie fyzikálnych zákonov v rámci grafických enginov. Všetky vedomosti, ktorými v tomto okamihu disponujeme čítať »
 
Prog­ra­mu­je­me gra­fic­ký en­gi­ne XXXI.
Týmto článkom sa pomaly dostávame k záveru celku, v ktorom sme sa zaoberali simuláciou fyziky. Zostáva nám opísať princíp detekcie kolízií a uviesť spôsob reakcie simulačného systému čítať »
 
Prog­ra­mu­je­me gra­fic­ký en­gi­ne XXX.
V tomto pokračovaní seriálu si doplníme teoretické vedomosti potrebné na implementáciu fyzikálnych zákonov v grafických a herných enginoch. čítať »
 
 
 
  Zdieľaj cez Facebook Zdieľaj cez Google+ Zdieľaj cez Twitter Zdieľaj cez LinkedIn Správy z RSS Správy na smartfóne Správy cez newsletter