Lekce 26 - Odrazy a jejich ořezávání za použití stencil bufferu

Tutoriál demonstruje extrémně realistické odrazy za použití stencil bufferu a jejich ořezávání, aby "nevystoupily" ze zrcadla. Je mnohem více pokrokový než předchozí lekce, takže před začátkem čtení doporučuji menší opakování. Odrazy objektů nebudou vidět nad zrcadlem nebo na druhé straně zdi a budou mít barevný nádech zrcadla - skutečné odrazy.

Důležité: Protože grafické karty Voodoo 1, 2 a některé jiné nepodporují stencil buffer, nebude na nich tento tutoriál fungovat. Pokud si nejste jistí, že vaše karta stencil buffer podporuje, stáhněte si zdrojový kód a zkuste jej spustit. Kromě toho budete také potřebovat procesor a grafickou kartu se slušným výkonem. Na mé GeForce 1 občas vidím malé zpomalení. Demo běží nejlépe v 32 bitových barvách.

První část kódu je celkem standardní.

#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

Nastavíme pole pro definici osvětlení. Okolní světlo bude 70% bílé. Difúzní světlo nastavuje rozptyl osvětlení (množství světla rovnoměrně odrážené na plochách objektů). V tomto případě odrážíme plnou intenzitou. Poslední je pozice. Pokud bychom ho mohli spatřit, plulo by v pravém horním rohu monitoru.

// Parametry světla

static GLfloat LightAmb[] = {0.7f, 0.7f, 0.7f, 1.0f};// Okolní

static GLfloat LightDif[] = {1.0f, 1.0f, 1.0f, 1.0f};// Rozptýlené

static GLfloat LightPos[] = {4.0f, 4.0f, 6.0f, 1.0f};// Pozice

Ukazatel q je pro quadratic koule (plážový míč). Xrot a yrot ukládají hodnoty natočení míče, xrotspeed a yrotspeed definují rychlost rotace. Zoom používáme pro přibližování a oddalování scény a height je výška balónu nad podlahou. Pole texture[] už standardně ukládá textury.

GLUquadricObj *q;// Quadratic pro kreslení koule (míče)

GLfloat xrot = 0.0f;// X rotace

GLfloat yrot = 0.0f;// Y rotace

GLfloat xrotspeed = 0.0f;// Rychlost x rotace

GLfloat yrotspeed = 0.0f;// Rychlost y rotace

GLfloat zoom = -7.0f;// Hloubka v obrazovce

GLfloat height = 2.0f;// Výška míče nad scénou

GLuint texture[3];// 3 textury

Vytváření lineárně filtrovaných textur z bitmap je standardní, v předchozích lekcích jsme jej používali velice často, takže ho sem nebudu opisovat. Na obrázcích vidíte texturu míče, podlahy a světla odráženého od míče.

Textura míče Textura podlahy Textura světla odráženého od míče

Inicializace OpenGL.

int InitGL(GLvoid)// Nastavení OpenGL

{

if (!LoadGLTextures())// Loading textur

{

return FALSE;// Ukončí program

}

glShadeModel(GL_SMOOTH);// Vyhlazené stínování

glClearColor(0.2f, 0.5f, 1.0f, 1.0f);// Světle modré pozadí

glClearDepth(1.0f);// Nastavení hloubkového bufferu

Příkaz glClearStencil() definuje chování funkce glClear() při mazání stencil bufferu. V tomto případě ho budeme vyplňovat nulami.

glClearStencil(0);// Nastavení mazání stencil bufferu

glEnable(GL_DEPTH_TEST);// Povolí testování hloubky

glDepthFunc(GL_LEQUAL);// Typ testování hloubky

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// Perspektivní korekce

glEnable(GL_TEXTURE_2D);// Mapování textur

Nastavíme světla. Pro okolní použijeme hodnoty z pole LightAmb[], rozptylové světlo definujeme pomocí LightDif[] a pozici z LightPos[]. Nakonec povolíme světla. Pokud bychom dále v kódu chtěli vypnout všechna světla, použili bychom glDisable(GL_LIGHTING), ale při vypínání jenom jednoho postačí pouze glDisable(GL_LIGHT(0až7)). GL_LIGHTING v parametru zakazuje globálně všechna světla.

glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmb);// Okolní

glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDif);// Rozptylové

glLightfv(GL_LIGHT0, GL_POSITION, LightPos);// Pozice

glEnable(GL_LIGHT0);// Povolí světlo 0

glEnable(GL_LIGHTING);// Povolí světla

Dále vytvoříme a nastavíme objekt quadraticu. Vygenerujeme mu normály pro světlo a texturové koordináty, jinak by měl ploché stínování a nešly by na něj namapovat textury.

q = gluNewQuadric();// Nový quadratic

gluQuadricNormals(q, GL_SMOOTH);// Normály pro světlo

gluQuadricTexture(q, GL_TRUE);// Texturové koordináty

Nastavíme mapování textur na vykreslované objekty a to tak, aby při natáčení míče byla viditelná stále stejná část textury. Zatím ho nezapínáme.

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);// Automatické mapování textur

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);// Automatické mapování textur

return TRUE;// Inicializace v pořádku

}

Následující funkci budeme volat pro vykreslení plážového míče. Bude jím quadraticová koule s nalepenou texturou. Nastavíme barvu na bílou, aby se textura nezabarvovala, poté zvolíme texturu a vykreslíme kouli o poloměru 0.35 jednotek, s 32 rovnoběžkami a 16 poledníky.

void DrawObject()// Vykreslí plážový míč

{

glColor3f(1.0f, 1.0f, 1.0f);// Bílá barva

glBindTexture(GL_TEXTURE_2D, texture[1]);// Zvolí texturu míče

gluSphere(q, 0.35f, 32, 16);// Nakreslí kouli

Po vykreslení první koule vybereme texturu světla, nastavíme opět bílou barvu, ale tentokrát s 40% alfou. Povolíme blending, nastavíme jeho funkci založenou na zdrojové alfa hodnotě, zapneme kulové mapování textur a nakreslíme stejnou kouli jako před chvílí. Výsledkem je simulované odrážení světla od míče, ale vlastně se jedná jen o světlé body namapované na plážový míč. Protože je povoleno kulové mapování, textura je vždy natočena k pozorovateli stejnou částí bez ohledu na natočení míče. Je také zapnutý blending takže nová textura nepřebije starou (jednoduchá forma multitexturingu).

glBindTexture(GL_TEXTURE_2D, texture[2]);// Zvolí texturu světla

glColor4f(1.0f, 1.0f, 1.0f, 0.4f);// Bílá barva s 40% alfou

glEnable(GL_BLEND);// Zapne blending

glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Mód blendingu

glEnable(GL_TEXTURE_GEN_S);// Zapne kulové mapování

glEnable(GL_TEXTURE_GEN_T);// Zapne kulové mapování

gluSphere(q, 0.35f, 32, 16);// Stejná koule jako před chvílí

Vypneme kulové mapování a blending.

glDisable(GL_TEXTURE_GEN_S);// Vypne kulové mapování

glDisable(GL_TEXTURE_GEN_T);// Vypne kulové mapování

glDisable(GL_BLEND);// Vepne blending

}

Následující funkce kreslí podlahu, nad kterou se míč vznáší. Vybereme texturu podlahy a na ose z vykreslíme čtverec s jednoduchou texturou.

void DrawFloor()// Vykreslí podlahu

{

glBindTexture(GL_TEXTURE_2D, texture[0]);// Zvolí texturu podlahy

glBegin(GL_QUADS);// Kreslení obdélníků

glNormal3f(0.0, 1.0, 0.0);// Normálová vektor míří vzhůru

glTexCoord2f(0.0f, 1.0f);// Levý dolní bod textury

glVertex3f(-2.0, 0.0, 2.0);// Levý dolní bod podlahy

glTexCoord2f(0.0f, 0.0f);// Levý horní bod textury

glVertex3f(-2.0, 0.0,-2.0);// Levý horní bod podlahy

glTexCoord2f(1.0f, 0.0f);// Pravý horní bod textury

glVertex3f( 2.0, 0.0,-2.0);// Pravý horní bod podlahy

glTexCoord2f(1.0f, 1.0f);// Pravý dolní bod textury

glVertex3f( 2.0, 0.0, 2.0);// Pravý dolní bod podlahy

glEnd();// Konec kreslení

}

Na tomto místě zkombinujeme všechny objekty a obrázky tak, abychom vytvořili výslednou scénu. Začneme mazáním obrazovky (GL_COLOR_BUFFER_BIT) na výchozí modrou barvu, hloubkového bufferu (GL_DEPTH_BUFFER_BIT) a stencil bufferu (GL_STENCIL_BUFFER_BIT). Při čištění stencil bufferu ho vyplňujeme nulami.

int DrawGLScene(GLvoid)// Vykreslí výslednou scénu

{

// Smaže obrazovku, hloubkový buffer a stencil buffer

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

Nadefinujeme rovnici ořezávací plochy (clipping plane equation). Bude použita při vykreslení odraženého míče. Hodnota na ose y je záporná, to znamená, že uvidíme pixely jen pokud jsou kresleny pod podlahou nebo na záporné části osy y. Při použití této rovnice se nezobrazí nic, co vykreslíme nad podlahou (odraz nemůže vystoupit ze zrcadla). Více později.

// Rovnice ořezávací plochy

double eqr[] = { 0.0f, -1.0f, 0.0f, 0.0f };// Použito pro odražený objekt

Všemu, co bylo doposud probráno v této lekci byste měli rozumět. Teď přijde něco "maličko" horšího. Potřebujeme nakreslit odraz míče a to tak, aby se na obrazovce zobrazoval jenom na těch pixelech, kde je podlaha. K tomu využijeme stencil buffer. Pomocí funkce glClear() jsme ho vyplnili samými nulami. Různými nastaveními, které si vysvětlíme dále, docílíme toho, že se podlaha sice nezobrazí na obrazovce, ale na místech, kde se měla vykreslit se stencil buffer nastaví do jedničky. Pro pochopení si představte, že je to obrazovka v paměti, jejíž pixely jsou rovny jedničce, pokud se na nich objekt vykresluje a nule (nezměněný) pokud ne. Na místa, kde je stencil buffer v jedničce vykreslíme plochý odraz míče, ale ne do stencil bufferu - viditelně na obrazovku. Odraz vlastně můžeme vykreslit i kdekoli jinde, ale pouze tady bude vidět. Nakonec klasickým způsobem vykreslíme všechno ostatní. To je asi všechno, co byste měli o stencil bufferu prozatím vědět.

Nyní už konkrétně ke kódu. Resetujeme matici modelview a potom přesuneme scénu o šest jednotek dolů a o zoom do hloubky. Nejlepší vysvětlení pro translaci dolů bude na příkladě. Vezměte si list papíru a umístěte jej rovnoběžně se zemí do úrovně očí. Neuvidíte nic víc než tenkou linku. Posunete-li jím o maličko dolů, spatříte celou plochu, protože se na něj budete dívat více ze shora namísto přímo na okraj. Rozšířil se zorný úhel.

glLoadIdentity();// Reset matice

glTranslatef(0.0f, -0.6f, zoom);// Zoom a vyvýšení kamery nad podlahu

Novým příkazem definujeme barevnou masku pro vykreslované barvy. Funkci se předávají čtyři parametry reprezentující červenou, zelenou, modrou a alfu. Pokud například červenou složku nastavíme na jedna (GL_TRUE) a všechny ostatní na nulu (GL_FALSE), tak se bude moci zobrazit pouze červená barva. V opačném případě (0,1,1,1) se budou zobrazovat všechny barvy mimo červenou. Asi tušíte, že jsou barvy implicitně nastaveny tak, aby se všechny zobrazovaly. No, a protože v tuto chvíli nechceme nic zobrazovat zakážeme všechny barvy.

glColorMask(1,1,1,1); glColorMask(1,0,0,0); glColorMask(0,1,1,1);

glColorMask(0,0,0,0);// Nastaví masku barev, aby se nic nezobrazilo

Začínáme pracovat se stencil bufferem. Napřed potřebujeme získat obraz podlahy vyjádřený jedničkami (viz. výše). Začneme zapnutím stencilového testování (stencil testing). Jakmile je povoleno jsme schopni modifikovat stencil buffer.

glEnable(GL_STENCIL_TEST);// Zapne stencil buffer pro paměťový obraz podlahy

Následující příkaz je možná těžko pochopitelný, ale určitě se velice těžko vysvětluje. Funkce glStencilFunc(GL_ALWAYS,1,1) oznamuje OpenGL, jaký typ testu chceme použít na každý pixel při jeho vykreslování. GL_ALWAYS zaručí, že test proběhne vždy. Druhý parametr je referenční hodnotou a třetí parametr je maska. U každého pixelu se hodnota masky ANDuje s referenční hodnotou a výsledek se uloží do stencil bufferu. V našem případě se do něj umístí pokaždé jednička (reference & maska = 1 & 1 = 1). Nyní víme, že na souřadnicích pixelu na obrazovce, kde by se vykreslil objekt, bude ve stencil bufferu jednička.

Pozn.: Stencilové testy jsou vykonávány na pixelech pokaždé, když se objekt vykresluje na scénu. Referenční hodnota ANDovaná s hodnotou masky se testuje proti aktuální hodnotě ve stencil bufferu ANDované s hodnotou masky.

glStencilFunc(GL_ALWAYS, 1, 1);// Pokaždé proběhne, reference, maska

GlStencilOp() zpracuje tři rozdílné požadavky založené na stencilových funkcích, které jsme se rozhodli použít. První parametr říká OpenGL, co má udělat pokud test neuspěje. Protože je nastaven na GL_KEEP nechá hodnotu stencil bufferu tak, jak právě je. Nicméně test uspěje vždy, protože máme funkci nastavenu na GL_ALWAYS. Druhý parametr určuje co dělat, pokud stencil test proběhne, ale hloubkový test bude neúspěšný. Tato situace by nastala například, když by se objekt vykreslil za jiným objektem a hloubkový test by nepovolil jeho vykreslení. Opět může být ignorován, protože hned následujícím příkazem hloubkové testy vypínáme. Třetí parametr je pro nás důležitý. Definuje, co se má vykonat, pokud test uspěje (uspěje vždycky). V našem případě OpenGL nahradí nulu ve stencil bufferu na jedničku (referenční hodnota ANDovaná s maskou = 1).

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);// Vykreslením nastavíme konkrétní bit ve stencil bufferu na 1

Po nastavení stencilových testů vypneme hloubkové testy a zavoláme funkci pro vykreslení podlahy.

glDisable(GL_DEPTH_TEST);// Vypne testování hloubky

DrawFloor();// Vykreslí podlahu (do stencil bufferu ne na scénu)

Takže teď máme ve stencil bufferu neviditelnou masku podlahy. Tak dlouho, jak bude stencilové testování zapnuté, budeme moci zobrazovat pixely pouze tam, kde je stencil buffer v jedničce (tam kde byla vykreslena podlaha). Zapneme hloubkové testování a nastavíme masku barev zpět do jedniček. To znamená, že se od teď vše vykreslované opravdu zobrazí.

glEnable(GL_DEPTH_TEST);// Zapne testování hloubky

glColorMask(1, 1, 1, 1);// Povolí zobrazování barev

Namísto užití GL_ALWAYS pro stencilovou funkci, použijeme GL_EQUAL. Reference i maska zůstávají v jedničce. Pro stencilové operace nastavíme všechny parametry na GL_KEEP. Vykreslované pixely se zobrazí na obrazovku POUZE tehdy, když je na jejich souřadnicích hodnota stencilu v jedničce (reference ANDovaná s maskou (1), které jsou rovny (GL_EQUAL) hodnotě stencil bufferu ANDované s maskou (také 1)). GL_KEEP zajistí, že se hodnoty ve stencil bufferu nebudou modifikovat.

glStencilFunc(GL_EQUAL, 1, 1);// Zobrazí se pouze pixely na jedničkách ve stencil bufferu (podlaha)

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// Neměnit obsah stencil bufferu

Zapneme ořezávací plochu zrcadla, která je definována rovnicí uloženou v poli eqr[]. Umožňuje, aby byl odraz objektu vykreslen pouze směrem dolů od podlahy (v podlaze). Touto cestou nebude moci odraz míče vystoupit do "reálného světa". Pokud nechápete, co je tímto míněno zakomentářujte v kódu řádek glEnable(GL_CLIP_PLANE0), zkompilujte program a zkuste projít reálným míčem skrz podlahu. Pokud clipping nebude zapnutý uvidíte, jak při vstupu míče do podlahy jeho odraz vystoupí nahoru nad podlahu. Vše vidíte na obrázku. Mimochodem, všimněte si, že vystoupivší obraz je pořád vidět jen tam, kde je ve stencil bufferu obraz podlahy.

Clipping zapnutý Clipping vypnutý

Po zapnutí ořezávací plochy 0 (obyčejně jich může být 0 až 5) jí předáme parametry rovnice uložené v eqr[].

glEnable(GL_CLIP_PLANE0);// Zapne ořezávací testy pro odraz

glClipPlane(GL_CLIP_PLANE0, eqr);// Rovnice ořezávací roviny

Zálohujeme aktuální stav matice, aby ji změny trvale neovlivnily. Zadáním mínus jedničky do glScalef() obrátíme směr osy y. Do této chvíle procházela zezdola nahoru, nyní naopak. Stejný efekt by měla rotace o 180°. Vše je teď invertované jako v zrcadle. Pokud něco vykreslíme nahoře, zobrazí se to dole (zrcadlo je vodorovně ne svisle), rotujeme-li po směru, objekt se otočí proti směru hodinových ručiček a podobně. Tento stav se může zrušit buď opětovným voláním glScalef(), které provede opětovnou inverzi nebo POPnutím matice.

glPushMatrix();// Záloha matice

glScalef(1.0f, -1.0f, 1.0f);// Zrcadlení směru osy y

Nadefinujeme pozici světla podle pole LightPos[]. Na reálný míč svítí z pravé horní strany, ale protože se i poloha světla zrcadlí, tak na odraz bude zářit zezdola.

glLightfv(GL_LIGHT0, GL_POSITION, LightPos);// Umístění světla

Přesuneme se na ose y nahoru nebo dolů v závislosti na proměnné height. Opět je translace zrcadlena, takže pokud se přesuneme o pět jednotek nad podlahu budeme vlastně o pět jednotek pod podlahou. Stejným způsobem pracují i rotace. Nakonec nakreslíme objekt plážového míče a POPneme matici. Tím zrušíme všechny změny od volání glPushMatrix().

glTranslatef(0.0f, height, 0.0f);// Umístění míče

glRotatef(xrot, 1.0f, 0.0f, 0.0f);// Rotace na ose x

glRotatef(yrot, 0.0f, 1.0f, 0.0f);// Rotace na ose y

DrawObject();// Vykreslí míč (odraz)

glPopMatrix();// Obnoví matici

Vypneme ořezávací testy, takže se budou zobrazovat i objekty nad podlahou. Také vypneme stencil testy, abychom mohli vykreslovat i jinam než na pixely, které byly modifikovány podlahou.

glDisable(GL_CLIP_PLANE0);// Vypne ořezávací rovinu

glDisable(GL_STENCIL_TEST);// Už nebudeme potřebovat stencil testy

Připravíme program na vykreslení podlahy. Opět umístíme světlo, ale tak, aby už jeho pozice nebyla zrcadlena. Osa y je sice už v pořádku, ale světlo je stále vpravo dole.

glLightfv(GL_LIGHT0, GL_POSITION, LightPos);// Umístění světla

Zapneme blending, vypneme světla (globálně) a nastavíme 80% průhlednost bez změny barev textur (bílá nepřidává barevný nádech). Mód blendingu je nastaven pomocí glBlendFunc(). Poté vykreslíme částečně průhlednou podlahu. Asi nechápete, proč jsme napřed kreslili odraz a až poté zrcadlo. Je to proto, že chceme, aby byl odraz míče smíchán s barvami podlahy. Pokud se díváte do modrého zrcadla, tak také očekáváte trochu namodralý odraz. Vykreslení míče napřed způsobí zabarvení podlahou. Efekt je více reálný.

glEnable(GL_BLEND);// Zapne blending, jinak by se odraz míče nezobrazil

glDisable(GL_LIGHTING);// Kvůli blendingu vypneme světla

glColor4f(1.0f, 1.0f, 1.0f, 0.8f);// Bílá barva s 80% průhledností

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);// Funkce na bázi alfy zdroje a jedna mínus alfy cíle

DrawFloor();// Vykreslí podlahu

A konečně vykreslíme reálný míč. Napřed ale zapneme světla (pozice už je nastavená). Kdybychom nevypnuli blending, míč by při průchodu podlahou vypadal jako odraz. To nechceme.

glEnable(GL_LIGHTING);// Zapne světla

glDisable(GL_BLEND);// Vypne blending

Tento míč už narozdíl od jeho odrazu neořezáváme. Kdybychom používali clipping, nezobrazil by se pod podlahou. Docílili bychom toho definováním hodnoty +1.0f na ose y u rovnice ořezávací roviny. Pro toto demo není žádný důvod, abychom míč nemohli vidět pod podlahou. Všechny translace i rotace zůstávají stejné jako minule s tím rozdílem, že nyní už jde osa y klasickým směrem. Když posuneme reálný míč dolů, odraz jde nahoru a naopak.

glTranslatef(0.0f, height, 0.0f);// Umístění míče

glRotatef(xrot, 1.0f, 0.0f, 0.0f);// Rotace na ose x

glRotatef(yrot, 0.0f, 1.0f, 0.0f);// Rotace na ose y

DrawObject();// Vykreslí míč

Zvětšíme hodnoty natočení míče a jeho odrazu o rychlost rotací. Před návratem z funkce zavoláme glFlush(), které počká na ukončení renderingu. Prevence mihotání na pomalejších grafických kartách.

xrot += xrotspeed;// Zvětší natočení

yrot += yrotspeed;// Zvětší natočení

glFlush();// Vyprázdní pipeline

return TRUE;// Všechno v pořádku

}

Následující funkce testuje stisk kláves. Voláme ji periodicky v hlavní smyčce WinMain(). Šipkami ovládáme rychlost rotace míče, klávesy A a Z přibližují/oddalují scénu, Page Up s Page Down umožňují změnit výšku plážového míče nad podlahou. Klávesa ESC plní stále svoji funkci, ale její umístění zůstalo ve WinMain().

void ProcessKeyboard()// Ovládání klávesnicí

{

if (keys[VK_RIGHT]) yrotspeed += 0.08f;// Šipka vpravo zvýší rychlost y rotace

if (keys[VK_LEFT]) yrotspeed -= 0.08f;// Šipka vlevo sníží rychlost y rotace

if (keys[VK_DOWN]) xrotspeed += 0.08f;// Šipka dolů zvýší rychlost x rotace

if (keys[VK_UP]) xrotspeed -= 0.08f;// Šipka nahoru sníží rychlost x rotace

if (keys['A']) zoom +=0.05f;// A přiblíží scénu

if (keys['Z']) zoom -=0.05f;// Z oddálí scénu

if (keys[VK_PRIOR]) height += 0.03f;// Page Up zvětší vzdálenost míče nad podlahou

if (keys[VK_NEXT]) height -= 0.03f;// Page Down zmenší vzdálenost míče nad podlahou

}

V CreateGLWindow() je úplně miniaturní změna, nicméně by bez ní program nefungoval. Ve struktuře PIXELFORMATDESCRIPTOR pfd nastavíme číslo, které vyjadřuje počet bitů stencil bufferu. Ve všech minulých lekcích jsme ho nepotřebovali, takže mu byla přiřazena nula. Při použití stencil bufferu MUSÍ být počet jeho bitů větší nebo roven jedné! Nám stačí jeden bit.

// Uprostřed funkce CreateGLWindow()

static PIXELFORMATDESCRIPTOR pfd=// Oznamuje Windows jak chceme vše nastavit

{

sizeof(PIXELFORMATDESCRIPTOR),// Velikost struktury

1,// Číslo verze

PFD_DRAW_TO_WINDOW |// Podpora okna

PFD_SUPPORT_OPENGL |// Podpora OpenGL

PFD_DOUBLEBUFFER,// Podpora double bufferingu

PFD_TYPE_RGBA,// RGBA formát

bits,// Barevná hloubka

0, 0, 0, 0, 0, 0,// Bity barev ignorovány

0,// Žádný alfa buffer

0,// Ignorován shift bit

0,// Žádný akumulační buffer

0, 0, 0, 0,// Akumulační bity ignorovány

16,// 16 bitový z-buffer

1,// Stencil buffer (DŮLEŽITÉ)

0,// Žádný auxiliary buffer

PFD_MAIN_PLANE,// Hlavní vykreslovací vrstva

0,// Rezervováno

0, 0, 0// Maska vrstvy ignorována

};

Jak jsem se zmínil výše, test stisknutí kláves už nebudeme vykonávat přímo ve WinMain(), ale ve funkci ProcessKeyboard(), kterou voláme hned po vykreslení scény.

// Funkce WinMain()

DrawGLScene();// Vykreslí scénu

SwapBuffers(hDC);// Prohodí buffery

ProcessKeyboard();// Vstup z klávesnice

Doufám, že jste si užili tuto lekci. Vím, že probírané téma nebylo zrovna nejjednodušší, ale co se dá dělat? Byl to jeden z nejtěžších tutoriálů, jak jsem kdy napsal. Pro mě je celkem snadné pochopit, co který řádek dělá a který příkaz se musí použít, aby vznikl požadovaný efekt. Ale sedněte si k počítači a pokuste se to vysvětlit lidem, kteří neví, co to je stencil buffer a možná o něm dokonce v životě neslyšeli (Překl.: Můj případ). Osobně si myslím, že i když mu napoprvé neporozumíte, po druhém přečtení by mělo být vše jasné...

napsal: Jeff Molofee - NeHe <nehe (zavináč) connect.ab.ca>
přeložil: Michal Turek - Woq <WOQ (zavináč) seznam.cz> & Milan Turek <nalim.kerut (zavináč) email.cz>

Zdrojové kódy

Lekce 26

<<< Lekce 25 | Lekce 27 >>>