Lekce 26

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

ZdrojovΘ k≤dy

Lekce 26

<<< Lekce 25 | Lekce 27 >>>