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.
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(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.
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: Banu Cosmin - Choko
p°elo₧il: Milan Turek (po DrawGLScene()) & Michal Turek - Woq (zbytek)