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
{
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.
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.
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.
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.
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.
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>