Lekce 12

Lekce 12 - Display list

Chcete v∞d∞t, jak urychlit vaÜe programy v OpenGL? Jste unaveni z nesmyslnΘho opisovßnφ ji₧ napsanΘho k≤du? Nejde to n∞jak jednoduÜeji? NeÜlo by nap°φklad jednφm p°φkazem vykreslit otexturovanou krychli? Samoz°ejm∞, ₧e jde. Tento tutorißl je urΦen² specißln∞ pro vßs. P°edvytvo°enΘ objekty a jejich vykreslovßnφ jednφm °ßdkem k≤du. Jak snadnΘ...

╪ekn∞me, ₧e programujete hru "Asteroidy". Ka₧d² level zaΦφnß alespo≥ se dv∞ma. No, tak₧e se v klidu posadφte a p°ijdete na to, jak vytvo°it 3d asteroid. Jist∞ bude z polygon∙, jak jinak. T°eba osmist∞nn². Pokud byste cht∞li pracovat elegantn∞, vytvo°φte cyklus a v n∞m m∙₧ete vÜe vykreslovat. SkonΦφte s osmnßcti nebo vφce °ßdky. V klidu. Ale pozor! Pokud tento cyklus prob∞hne vφcekrßt, znateln∞ zpomalφ vykreslovßnφ. Jednou, a₧ budete vytvß°et mnohem komplexn∞jÜφ objekty a scΘny, pochopφte, co mßm na mysli.

Tak₧e, jakΘ je °eÜenφ? Display list, neboli p°edvytvo°enΘ objekty! Tφmto zp∙sobem vytvß°φte vÜe pouze jednou. Namapovat textury, barvy, cokoli, co chcete. A samoz°ejm∞ musφte tento display list pojmenovat. Jeliko₧ vytvß°φme asteroidy nazveme display list "asteroid". Ve chvφli, kdy budete chtφt vykreslit texturovan²/obarven² asteroid na monitor, vÜechno, co ud∞lßte je zavolßnφ funkce glCallList(asteroid). P°edvytvo°en² asteroid se okam₧it∞ zobrazφ. Proto₧e je jednou vytvo°en² v pam∞ti (display listu), OpenGL nemusφ vÜe znovu p°epoΦφtßvat. Odstranili jsme velkΘ zatφ₧enφ procesoru a umo₧nili programu b∞₧et o mnoho rychleji.

P°ipraveni? Vytvo°φme scΘnu sklßdajφcφ se z patnßcti krychlφ. Tyto krychle jsou vytvo°eny z krabice a vφka - celkem dva display listy. Vφko bude vybarveno na tmavÜφ odstφn. K≤d vychßzφ z ÜestΘ lekce. P°epφÜeme v∞tÜinu programu, aby bylo snazÜφ najφt zm∞ny.

#include <windows.h>// HlaviΦkov² soubor pro Windows

#include <stdio.h>// HlaviΦkov² soubor pro standardnφ vstup/v²stup

#include <gl\gl.h>// HlaviΦkov² soubor pro OpenGL32 knihovnu

#include <gl\glu.h>// HlaviΦkov² soubor pro Glu32 knihovnu

#include <gl\glaux.h>// HlaviΦkov² soubor pro Glaux knihovnu

HDC hDC = NULL;// Privßtnφ GDI Device Context

HGLRC hRC = NULL;// Trval² Rendering Context

HWND hWnd = NULL;// Obsahuje Handle naÜeho okna

HINSTANCE hInstance;// Obsahuje instanci aplikace

bool keys[256];// Pole pro uklßdßnφ vstupu z klßvesnice

bool active = TRUE;// Ponese informaci o tom, zda je okno aktivnφ

bool fullscreen = TRUE;// Ponese informaci o tom, zda je program ve fullscreenu

Deklarujeme prom∞nnΘ. Nap°ed mφsto pro texturu. DalÜφ dv∞ prom∞nnΘ budou vystupovat jako pointery na mφsto do pam∞ti RAM, kde jsou ulo₧eny display listy.

GLuint texture[1];// Uklßdß texturu

GLuint box;// Uklßdß display list krabice

GLuint top;// Uklßdß display list vφka

GLuint xloop;// Pozice na ose x

GLuint yloop;// Pozice na ose y

GLfloat xrot;// Rotace na ose x

GLfloat yrot;// Rotace na ose y

Vytvo°φme dv∞ pole barev. Prvnφ uklßdß sv∞tlΘ barvy. Hodnoty ve slo₧en²ch zßvorkßch reprezentujφ ΦervenΘ, zelenΘ a modrΘ slo₧ky. DruhΘ pole urΦuje tmavÜφ barvy, kterΘ pou₧ijeme ke kreslenφ vφka krychlφ. Chceme, aby bylo tmavÜφ ne₧ ostatnφ st∞ny.

static GLfloat boxcol[5][3]=// Pole pro barvy st∞n krychle

{

// Sv∞tlΘ: Φervenß, oran₧ovß, ₧lutß, zelenß, modrß

{1.0f,0.0f,0.0f},{1.0f,0.5f,0.0f},{1.0f,1.0f,0.0f},{0.0f,1.0f,0.0f},{0.0f,1.0f,1.0f}

};

static GLfloat topcol[5][3]=// Pole pro barvy vφka krychle

{

// TmavΘ: Φervenß, oran₧ovß, ₧lutß, zelenß, modrß

{0.5f,0.0f,0.0f},{0.5f,0.25f,0.0f},{0.5f,0.5f,0.0f},{0.0f,0.5f,0.0f},{0.0f,0.5f,0.5f}

};

Nßsledujφcφ funkce generuje display listy.

GLvoid BuildLists(// Generuje display listy)

{

ZaΦneme oznßmenφm OpenGL, ₧e chceme vytvo°it dva listy. glGenList(2) pro n∞ alokuje mφsto v pam∞ti a vrßtφ pointer na prvnφ z nich.

box=glGenLists(2);// 2 listy

Vytvo°φme prvnφ list. U₧ jsme zabrali mφsto pro dva listy a vφme, ₧e box ukazuje na zaΦßtek p°ipravenΘ pam∞ti. Pou₧ijeme p°φkaz glNewList(). Prvnφ parametr box °ekne, ₧e chceme ulo₧it list do pam∞ti, kam ukazuje. Druh² parametr GL_COMPILE °φkß, ₧e chceme p°edvytvo°it list v pam∞ti tak, aby se nemuselo p°i ka₧dΘm vykreslovßnφ znovu vÜechno generovat a p°epoΦφtßvat. GL_COMPILE je stejnΘ jako programovßnφ. Pokud napφÜete program a nahrajete ho do vaÜeho p°ekladaΦe (kompileru), musφte ho zkompilovat v₧dy, kdy₧ ho chcete spustit. Ale pokud bude zkompilovßn do .exe souboru, vÜechno, co se musφ pro spuÜt∞nφ vykonat je kliknout myÜφ na tento .exe soubor a spustit ho. Samoz°ejm∞ bez kompilace. Cokoli OpenGL zkompiluje v display listu je mo₧no pou₧φt bez jakΘkoli dalÜφ pot°eby p°epoΦφtßvßnφ. Urychlφ se vykreslovßnφ.

glNewList(box,GL_COMPILE);// Nov² kompilovan² display list - krabice

glBegin(GL_QUADS);

// Spodnφ st∞na

glNormal3f( 0.0f,-1.0f, 0.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);

// P°ednφ st∞na

glNormal3f( 0.0f, 0.0f, 1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);

// Zadnφ st∞na

glNormal3f( 0.0f, 0.0f,-1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);

// Pravß st∞na

glNormal3f( 1.0f, 0.0f, 0.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);

// Levß st∞na

glNormal3f(-1.0f, 0.0f, 0.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);

glEnd();

glEndList();

P°φkazem glEndList() oznßmφme, ₧e konΦφme vytvß°enφ listu. Cokoli je mezi glNewList() a glEndList() je souΦßstφ display listu a naopak, pokud je n∞co p°ed nebo za u₧ k n∞mu nepat°φ. Abychom zjistili, kam ho ulo₧φme druh² display list, vezmeme hodnotu ji₧ vytvo°enΘho a p°iΦteme k n∞mu jedniΦku (na zaΦßtku funkce jsme °ekli, ₧e d∞lßme 2 display listy, tak₧e je to v po°ßdku).

top=box+1;// Do top vlo₧φme adresu druhΘho display listu

glNewList(top,GL_COMPILE);// Kompilovan² display list - vφko

glBegin(GL_QUADS);

// Hornφ st∞na

glNormal3f( 0.0f, 1.0f, 0.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);

glEnd();

glEndList();

}

Vytvo°ili jsme oba display listy. Nahrßvßnφ textur je stejnΘ, jako v minul²ch lekcφch. Rozhodl jsem se pou₧φt mimapping, proto₧e nemßm rßd, kdy₧ vidφm pixely. Pou₧ijeme obrßzek cube.bmp ulo₧en² v adresß°i data. Najd∞te funkci LoadBMP() a upravte °ßdek se jmΘnem bitmapy.

if (TextureImage[0]=LoadBMP("Data/Cube.bmp"))// Loading textury

V inicializaΦnφ funkci je jen n∞kolik zm∞n. P°idßme °ßdek BuildList(). VÜimn∞te si, ₧e jsme ho umφstili a₧ za LoadGLTextures(). Display list by se zkompiloval bez textur.

int InitGL(GLvoid)// VÜechna nastavenφ OpenGL

{

if (!LoadGLTextures())// Nahraje texturu

{

return FALSE;

}

BuildLists();// Vytvo°φ display listy

glEnable(GL_TEXTURE_2D);// Zapne texturovΘ mapovßnφ

glShadeModel(GL_SMOOTH);// JemnΘ stφnovßnφ

glClearColor(0.0f, 0.0f, 0.0f, 0.5f);// ╚ernΘ pozadφ

glClearDepth(1.0f);// Nastavenφ hloubkovΘho bufferu

glEnable(GL_DEPTH_TEST);// Povolφ hloubkovΘ testovßnφ

glDepthFunc(GL_LEQUAL);// Typ hloubkovΘho testovßnφ

Nßsledujφcφ t°i °ßdky zapφnajφ rychlΘ a ÜpinavΘ osv∞tlenφ (quick and dirty lighting). Light0 je p°eddefinovßno na v∞tÜin∞ video karet, tak₧e zamezφ nep°φjemnostem p°i nastavenφ sv∞tel. Po light0 nastavφme osv∞tlenφ. Pokud vaÜe karta nepodporuje light0, uvidφte Φern² monitor - musφte vypnout sv∞tla. Poslednφ °ßdka p°idßvß barvu do mapovßnφ textur. Nezapneme-li vybarvovßnφ materißlu, textura bude mφt v₧dy originßlnφ barvu. glColor3f(r,g,b) nebude mφt ₧ßdn² efekt (ve vykreslovacφ funkci.

glEnable(GL_LIGHT0);// Zapne implicitnφ sv∞tlo

glEnable(GL_LIGHTING);// Zapne sv∞tla

glEnable(GL_COLOR_MATERIAL);// Zapne vybarvovßnφ materißl∙

Nakonec nastavφme perspektivnφ korekce, aby obraz vypadal lΘpe. Vrßcenφm true oznßmφme programu, ₧e inicializace prob∞hla v po°ßdku.

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// NejlepÜφ perspektivnφ korekce

return TRUE;

}

P°ichßzφ na °adu vykreslovacφ funkce. Jako obyΦejn∞, p°idßme pßr Üφlenostφ s matematikou. Tentokrßt, ale nebudou ₧ßdnΘ siny a kosiny. ZaΦneme vymazßnφm obrazovky a depth bufferu. Potom namapujeme texturu na krychli. Mohl bych tento p°φkaz p°idat do k≤du display listu, Ale te∩ kdykoli mohu vym∞nit aktußlnφ texturu za jinou. Doufßm, ₧e u₧ rozumφte, ₧e cokoli je v display listu, tak se nem∙₧e zm∞nit.

int DrawGLScene(GLvoid)// Vykreslovßnφ

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Sma₧e obrazovku a hloubkov² buffer

glBindTexture(GL_TEXTURE_2D, texture[0]);// V²b∞r textury

Mßme cyklus s °φdφcφ prom∞nnou yloop. Tato smyΦka je pou₧ita k urΦenφ pozice krychlφ na ose y. Vykreslujeme p∞t °ßdk∙, proto k≤d prob∞hne p∞tkrßt.

for (yloop=1;yloop<6;yloop++)// Prochßzφ °ßdky

{

Dßle mßme vno°en² cyklus s prom∞nnou xloop. Je pou₧it² pro pozici krychlφ na ose x. Jejich poΦet zßvisφ na tom, ve kterΘm °ßdku se nachßzejφ. Pokud se nachßzφme v hornφm °ßdku vykreslφme jednu, ve druhΘm dv∞, atd.

for (xloop=0;xloop<yloop;xloop++)// Prochßzφ sloupce

{

glLoadIdentity();// Reset matice

Nßsledujφcφ °ßdek p°esune poΦßtek sou°adnic na dan² bod na obrazovce. Na prvnφ pohled je to trochu matoucφ.
Na ose X: Posuneme se doprava o 1,4 jednotky, tak₧e pyramida je na st°edu obrazovky. Potom nßsobφme prom∞nnou xloop hodnotou 2,8 a p°iΦteme 1,4. (Nßsobφme hodnotou 2,8, tak₧e krychle nejsou jedna nad druhou. 2,8 je p°ibli₧n∞ jejich Üφ°ka, kdy₧ jsou pootoΦeny o 45 stup≥∙.) Nakonec odeΦteme yloop*1,4. To je posune doleva v zßvislosti na tom, ve kterΘ °ad∞ jsme. Pokud bychom je nep°esunuli, se°adφ se na levΘ stran∞. (A nevypadajφ jako pyramida.)
Na ose Y: OdeΦteme prom∞nnou yloop od Üesti jinak by pyramida byla vytvo°ena vzh∙ru nohama. PotΘ nßsobφme v²sledek hodnotou 2,4. Jinak krychle budou jedna na vrcholu druhΘ na ose Y. (2,4 se p°ibli₧n∞ rovnß v²Üce krychle). PotΘ odeΦteme 7, tak₧e pyramida zaΦφnß na spodku obrazovky a je sestavovßna ze zdola nahoru.
Na ose Z: Posuneme 20 jednotek dovnit°. Tak₧e se pyramida vejde akorßt na obrazovku.

// Pozice krychle na obrazovce

glTranslatef(1.4f+(float(xloop)*2.8f)-(float(yloop)*1.4f),((6.0f-float(yloop))*2.4f)-7.0f,-20.0f);

Naklonφme krychle o 45 stup≥∙ k pohledu a odeΦteme 2*yloop. Perspektivnφ m≤d nach²lφ krychle automaticky, tak₧e odeΦφtßme, abychom vykompenzovali naklon∞nφ. Nenφ to nejlepÜφ cesta, ale pracuje to. Potom p°iΦteme xrot. To nßm dßvß mo₧nost ovlßdat ·hel klßvesnicφ. TakΘ pou₧ijeme rotaci o 45 stup≥∙ na ose y. P°iΦteme yroot kv∙li ovlßdßnφ klßvesnicφ.

// Rotace

glRotatef(45.0f-(2.0f*yloop)+xrot,1.0f,0.0f,0.0f);

glRotatef(45.0f+yrot,0.0f,1.0f,0.0f);

Vybereme barvu krabice (sv∞tlou). VÜimn∞te si, ₧e pou₧φvßme glColor3fv(). Tato funkce vybφrß najednou vÜechny t°i hodnoty (Φervenß, zelenß, modrß) najednou a tφm nastavφ barvu. V tomto p°φpad∞ ji najdeme v poli boxcol s indexem yloop-1. Tφm zajistφme rozliÜnou barvu, pro ka₧d² °ßdek pyramidy. Kdybychom pou₧ili xloop-1, dostali bychom stejnΘ barvy pro ka₧d² sloupec.

glColor3fv(boxcol[yloop-1]);// Barva

Po nastavenφ barvy zb²vß jedinΘ - vykreslit krabici. Pro vykreslenφ zavolßme pouze funkci glCallList(box). Parametr °ekne OpenGL, kter² display list mßme na mysli. Krabice bude vybarvenß d°φve vybranou barvou, bude posunutß a taky natoΦenß.

glCallList(box);// Vykreslenφ

Barvu vφka vybφrßme ·pln∞ stejn∞, jako p°ed chvφlφ, akorßt z pole tmavÜφch barev. Potom ho vykreslφme.

glColor3fv(topcol[yloop-1])// Barva;

glCallList(top);// Vykreslenφ

}

}

return TRUE;

}

Poslednφ zbytek zm∞n ud∞lßme ve funkci WinMain(). K≤d p°idßme za p°φkaz SwapBuffers(hDC). Ov∞°φme, zda jsou stisknuty Üipky a podle v²sledku pohybujeme krychlemi.

// Funkce WinMain()

SwapBuffers(hDC);// V²m∞na buffer∙

if (keys[VK_LEFT])// èipka vlevo

{

yrot-=0.2f;

}

if (keys[VK_RIGHT])// èipka vpravo

{

yrot+=0.2f;

}

if (keys[VK_UP])// èipka nahoru

{

xrot-=0.2f;

}

if (keys[VK_DOWN])// èipka dolu

{

xrot+=0.2f;

}

Po doΦtenφ tΘto lekce, byste m∞li rozum∞t, jak display list pracuje, jak ho vytvo°it a jak ho vykreslit. Jsou velk²m p°φnosem. Nejen, ₧e zjednoduÜφ psanφ slo₧it∞jÜφch projekt∙, ale takΘ p°idajφ trochu na rychlosti celΘho programu.

napsal: Jeff Molofee - NeHe
p°elo₧il: Michal Turek - Woq

ZdrojovΘ k≤dy

Lekce 12

<<< Lekce 11 | Lekce 13 >>>