Lekce 18 - Kvadriky

Představuje se vám báječný svět kvadriků. Jedním řádkem kódu snadno vytváříte komplexní objekty typu koule, disku, válce ap. Pomocí matematiky a trochy plánování lze snadno morphovat jeden do druhého.

Quadratic (neznám český ekvivalent slova, takže zůstanu u původní verze) je jednoduchou cestou k vykreslení komplexních objektů. Na pozadí pracují na několika cyklech for a troše trigonometrie. Rozvineme kód z lekce 7, přidáme pár proměnných a aby byla také nějaká změna, použijeme jinou texturu

#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

bool light;// Světlo ON/OFF

bool lp;// L stisknuté?

bool fp;// F stisknuté?

bool sp;// Stisknutý mezerník?

int part1;// Začátek disku

int part2;// Konec disku

int p1=0;// Přírůstek 1

int p2=1;// Přírůstek 2

GLfloat xrot;// X rotace

GLfloat yrot;// Y rotace

GLfloat xspeed;// Rychlost x rotace

GLfloat yspeed;// Rychlost y rotace

GLfloat z=-5.0f;// Hloubka v obrazovce

GLUquadricObj *quadratic;// Bude ukládat kvadrik

GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f };// Okolní světlo

GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f };// Přímé světlo

GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };// Pozice světla

GLuint filter;// Typ filtru

GLuint texture[3];// Místo pro 3 textury

GLuint object=0;// Určuje aktuálně vykreslovaný objekt

Přesuneme se do funkce InitGL(), kde inicializujeme kvadrik. Na konec, ale před return, přidáme následující kód této lekce. ( V prvním řádku vytvoříme nový kvadrik (funkce na něj vrátí ukazatel, při chybě nulu). Aby světlo vypadalo opravdu perfektně nastavíme normálové vektory na GLU_SMOOTH (další možné hodnoty GLU_NONE a GLU_FLAT). Nakonec zapneme texturové mapování. Je celkem "neohrabané", protože nemůžeme naplánovat, co kam namapujeme - všechno se generuje automaticky.

quadratic=gluNewQuadric();// Vrátí ukazatel na nový kvadrik

gluQuadricNormals(quadratic, GLU_SMOOTH);// Vygeneruje normálové vektory (hladké)

gluQuadricTexture(quadratic, GL_TRUE);// Vygeneruje texturové koordináty

Rozhodl jsem se, že původní krychli z lekce 7 nesmažu, ale že ji zde ponechám. Měli byste si uvědomit, že stejně jako mapujeme textury na námi vytvořený objekt, tak se úplně stejně mapují na kvadriky.

GLvoid glDrawCube()// Vykreslí krychli

{

glBegin(GL_QUADS);

// 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);

// Vrchní 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);

// 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);

// 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();

}

Ve funkci DrawGLScene() se program větví podle druhu objektu, který chceme kreslit (kužel, válec, koule...). Do všech funkcí zajišťujících vykreslování (kromě naší krychle) se přidává parametr "quadratic".

int DrawGLScene(GLvoid)

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Smaže obrazovku a hloubkový buffer

glLoadIdentity();// Reset matice

glTranslatef(0.0f,0.0f,z);

glRotatef(xrot,1.0f,0.0f,0.0f);

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

glBindTexture(GL_TEXTURE_2D, texture[filter]);// Vybere texturu

switch(object)// Vybere, co se bude kreslit

{

Krychle

case 0:

glDrawCube();// Krychle

break;

Dalším objektem bude válec. Prvním parametrem je spodní poloměr. Druhý určuje horní poloměr. Předáním rozdílných hodnot se vykreslí jiný tvar (zužující trubka, popř. kužel). Třetí parametr specifikuje výšku/délku (vzdálenost základen). Čtvrtá hodnota značí množství polygonů "kolem" osy Z a pátá počet polygonů "na" ose Z. Například použitím 5 místo první 32 nevykreslíte válec, ale hranatou trubku, jejíž podstava je tvořena pravidelným pětiúhelníkem. Naopak rozdíl při záměně druhé 32 snad ani nepoznáte. Čím je těchto polygonů více, tím se zvětší kvalita (počet detailů) výstupu. Musím ale podtrhnout, že se program zpomalí. Snažte se vždy najít nějakou rozumnou hodnotu.

Válec

case 1:

glTranslatef(0.0f,0.0f,-1.5f);// Vycentrování válce

gluCylinder(quadratic,1.0f,1.0f,3.0f,32,32);// Válec

break;

Třetím vytvářeným objektem bude disk tvaru CD. První parametr určuje vnitřní poloměr - pokud zadáte nulu vykreslí se celistvý (bez středového kruhu). Druhu hodnotou je vnější poloměr (zadá-li se o málo větší než vnitřní vytvoříte prsten). Dejte si pozor, abyste nezadali vnější menší než vnitřní. Nespadne vám sice program, ale nic neuvidíte. Třetím parametrem je počet plátků, jako když se krájí pizza. Čím jich bude více, tím budou okraje méně zubaté (napři. zadáním 5 vykreslíte pravidelný pětiúhelník). Posledním předávané číslo značí počet kružnic - analogie spirále na CD nebo gramofonové desce. Opět nemá moc velký vliv na kvalitu.

Disk

case 2:

gluDisk(quadratic,0.5f,1.5f,32,32);// Disk ve tvaru CD

break;

Následuje objekt, o kterém přemýšlíte v dlouhých bezesných nocích... koule. Stačí jedna funkce. Nejdůležitějším parametrem je poloměr - netřeba vysvětlovat. Pokud byste ale chtěli jít ještě dál, změňte před vykreslením měřítko jednotlivých os (glScalef(x,y,z)). Vytvoříte zaoblený tvar, který mi v první chvíli připomínal ozdobu na stromeček (šiška - zploštělá koule). Popř. zkuste zmenšit první 32 na 5. Vytvoříte hranatou (krychloidní :-o) kouli. Jak to popsat... kdybyste ji přes střed rozdělili rovinou, řezem bude pětiúhelník, ale druhým řezem kolmým na první bude stále koule.

Koule

case 3:

// glScalef(1.0f,0.5f,1.0f);// Překl.: Změna měřítka

gluSphere(quadratic,1.3f,32,32);// Koule

// glScalef(1.0f,2.0f,1.0f);// Překl.: Obnovení měřítka

break;

Už jsem trochu nakousl u válce, že kužel se vytváří téměř stejně. Předáte jeden poloměr rovný nule, tudíž se na jednom konci objeví špička.

Kužel

case 4:

glTranslatef(0.0f,0.0f,-1.5f);// Vycentrování kužele

gluCylinder(quadratic,1.0f,0.0f,3.0f,32,32);// Kužel

break;

Šestý tvar vytvoříme příkazem gluPartialDisk(). Tento disk bude skoro stejný jako disk výše, nicméně další dva parametry funkce zajistí, že se nebude vykreslovat celý. Parametr part1 specifikuje počáteční úhel, od kterého chceme kreslit a asi si domyslíte, že ten druhý určuje úhel, za kterým se už nic nevykreslí. Je vztažen k tomu prvnímu, takže pokud první nastavíme na 30 a druhý na 90 přestane se kreslit na 30° + 90° = 120°. My se rovnou pokusíme o "level 2" - zkusíme přidat jednoduchou animaci, kdy se disk bude překreslovat (po směru hodinových ručiček). Nejdříve zvyšujeme přírůstkový úhel. Jakmile dosáhne 360° (jeden oběh), začneme zvyšovat počáteční úhel - opět do 360° atd.

Částečný disk

case 5:

part1+=p1;// Inkrementace počátečního úhlu

part2+=p2;// Inkrementace přírůstkového úhlu

if(part1>359)// 360°

{

p1=0;// Zastaví zvětšování počátečního úhlu (part1+=0;)

part1=0;// Vynulování počátečního úhlu

p2=1;// Začne zvětšovat přírůstkový úhel

part2=0;// Vynulování přírůstkového úhlu

}

if(part2>359)// 360°

{

p1=1;// Začne zvětšovat počáteční úhel

p2=0;// Přestane zvětšovat přírůstkový úhel

}

gluPartialDisk(quadratic,0.5f,1.5f,32,32,part1,part2-part1);// Neúplný disk

break;

};

xrot+=xspeed;// Inkrementace rotace

yrot+=yspeed;// Inkrementace rotace

return TRUE;

}

Přidáme ovládání klávesnicí - pokud stisknete mezerník objekt se změní na následující v pořadí.

// Funkce WinMain()

if(keys[' '] && !sp)// Stisknutý mezerník?

{

sp=TRUE;

object++;// Cyklování objekty

if(object>5)// Ošetření přetečení

object=0;

}

if(!keys[' '])// Uvolnění mezerníku?

{

sp=FALSE;

}

Takže to je vše. Měli byste umět v OpenGL vykreslovat jakýkoli kvadrik. Pomocí morphingu a kvadriků se dá dosáhnout zajímavých efektů. Příkladem budiž námi animovaný disk.

napsal: GB Schmick - TipTup
přeložil: Michal Turek - Woq <WOQ (zavináč) seznam.cz>

Zdrojové kódy

Lekce 18

<<< Lekce 17 | Lekce 19 >>>