Lekce 27

Lekce 27 - Stφny

P°edstavuje se vßm velmi komplexnφ tutorißl na vrhßnφ stφn∙. Efekt je doslova neuv∞°iteln². Stφny se roztahujφ, oh²bajφ a zahalujφ i ostatnφ objekty ve scΘn∞. Realisticky se pokroutφ na st∞nßch nebo podlaze. Se vÜφm lze pomocφ klßvesnice pohybovat ve 3D prostoru. Pokud jeÜt∞ nejste se stencil bufferem a matematikou jako jedna rodina, nemßte nejmenÜφ Üanci.

Tento tutorißl mß trochu jin² p°φstup - sumarizuje vÜechny vaÜe znalosti o OpenGL a p°idßvß spoustu dalÜφch. Ka₧dopßdn∞ byste m∞li stoprocentn∞ chßpat nastavovßnφ a prßci se stencil bufferem. Pokud mßte pocit, ₧e v n∞Φem existujφ mezery, zkuste se vrßtit ke Φtenφ d°φv∞jÜφch lekcφ. Mimo jinΘ byste takΘ m∞li mφt alespo≥ malΘ znalosti o analytickΘ geometrii (vektory, rovnice p°φmek a rovin, nßsobenφ matic...) - urΦit∞ m∞jte po ruce n∞jakou knihu. Jß osobn∞ pou₧φvßm zßpisky z matematiky prvnφho semestru na univerzit∞. V₧dy jsem v∞d∞l, ₧e se n∞kdy budou hodit.

Nynφ u₧ ale ke k≤du. Aby byl program p°ehledn², definujeme n∞kolik struktur. Prvnφ z nich, sPoint, vyjad°uje bod nebo vektor v prostoru. Uklßdß jeho x, y, z sou°adnice.

struct sPoint// Sou°adnice bodu nebo vektoru

{

float x, y, z;

};

Struktura sPlaneEq uklßdß hodnoty a, b, c, d obecnΘ rovnice roviny, kterß je definovßna vzorcem ax + by + cz + d = 0.

struct sPlaneEq// Rovnice roviny

{

float a, b, c, d;// Ve tvaru ax + by + cz + d = 0

};

Struktura sPlane obsahuje vÜechny informace pot°ebnΘ k popsßnφ troj·helnφku, kter² vrhß stφn. Instance t∞chto struktur budou reprezentovat facy (Φelo, st∞na - nebudu p°eklßdat, proto₧e je tento termφn hodn∞ pou₧φvan² i v ΦeÜtin∞) troj·helnφk∙. Facem se rozumφ st∞na troj·helnφku, kterß je p°ivrßcenß nebo odvrßcenß od pozorovatele. Jeden troj·helnφk mß v₧dy dva facy.

Pole p[3] definuje t°i indexy v poli vertex∙ objektu, kterΘ dohromady tvo°φ tento troj·helnφk. DruhΘ trojrozm∞rnΘ pole, normals[3], zastupuje normßlov² vektor ka₧dΘho rohu. T°etφ pole specifikuje indexy sousednφch fac∙. PlaneEq urΦuje rovnici roviny, ve kterΘ le₧φ tento face a parametr visible oznamuje, jestli je face p°ivrßcen² (viditeln²) ke zdroji sv∞tla nebo ne.

struct sPlane// Popisuje jeden face objektu

{

unsigned int p[3];// Indexy 3 vertex∙ v objektu, kterΘ vytvß°ejφ tento face

sPoint normals[3];// NormßlovΘ vektory ka₧dΘho vertexu

unsigned int neigh[3];// Indexy sousednφch fac∙

sPlaneEq PlaneEq;// Rovnice roviny facu

bool visible;// Je face viditeln² (p°ivrßcen² ke sv∞tlu)?

};

Poslednφ struktura, glObject, je mezi prßv∞ definovan²mi strukturami na nejvyÜÜφ ·rovni. Prom∞nnΘ nPoints a nPlanes urΦujφ poΦet prvk∙, kterΘ pou₧φvßme v polφch points a planes.

struct glObject// Struktura objektu

{

GLuint nPoints;// PoΦet vertex∙

sPoint points[100];// Pole vertex∙

GLuint nPlanes;// PoΦet fac∙

sPlane planes[200];// Pole fac∙

};

GLvector4f a GLmatrix16f jsou pomocnΘ datovΘ typy, kterΘ definujeme pro snadn∞jÜφ p°edßvßnφ parametr∙ funkci VMatMult(). Vφce pozd∞ji.

typedef float GLvector4f[4];// Nov² datov² typ

typedef float GLmatrix16f[16];// Nov² datov² typ

Nadefinujeme prom∞nnΘ. Obj je objektem, kter² vrhß stφn. Pole ObjPos[] definuje jeho polohu, roty jsou ·hlem natoΦenφ na osßch x, y a speedy jsou rychlosti otßΦenφ.

glObject obj;// Objekt, kter² vrhß stφn

float ObjPos[] = { -2.0f, -2.0f, -5.0f };// Pozice objektu

GLfloat xrot = 0, xspeed = 0;// X rotace a x rychlost rotace objektu

GLfloat yrot = 0, yspeed = 0;// Y rotace a y rychlost rotace objektu

Nßsledujφcφ Φty°i pole definujφ sv∞tlo a dalÜφ Φty°i pole materißl. Pou₧ijeme je p°edevÜφm v InitGL() p°i inicializaci scΘny.

float LightPos[] = { 0.0f, 5.0f,-4.0f, 1.0f };// Pozice sv∞tla

float LightAmb[] = { 0.2f, 0.2f, 0.2f, 1.0f };// Ambient sv∞tlo

float LightDif[] = { 0.6f, 0.6f, 0.6f, 1.0f };// Diffuse sv∞tlo

float LightSpc[] = { -0.2f, -0.2f, -0.2f, 1.0f };// Specular sv∞tlo

float MatAmb[] = { 0.4f, 0.4f, 0.4f, 1.0f };// Materißl - Ambient hodnoty (prost°edφ, atmosfΘra)

float MatDif[] = { 0.2f, 0.6f, 0.9f, 1.0f };// Materißl - Diffuse hodnoty (rozptylovßnφ sv∞tla)

float MatSpc[] = { 0.0f, 0.0f, 0.0f, 1.0f };// Materißl - Specular hodnoty (zrcadlivost)

float MatShn[] = { 0.0f };// Materißl - Shininess hodnoty (lesk)

Poslednφ dv∞ prom∞nnΘ jsou pro kouli, na kterou dopadß stφn objektu.

GLUquadricObj *q;// Quadratic pro kreslenφ koule

float SpherePos[] = { -4.0f, -5.0f, -6.0f };// Pozice koule

Struktura datovΘho souboru, kter² pou₧φvßme pro definici objektu, nenφ a₧ tak slo₧itß, jak na prvnφ pohled vypadß. Soubor se d∞lφ do dvou Φßstφ: jedna Φßst pro vertexy a druhß pro facy. Prvnφ Φφslo prvnφ Φßsti urΦuje poΦet vertex∙ a po n∞m nßsledujφ jejich definice. Druhß Φßst zaΦφnß specifikacφ poΦtu fac∙. Na ka₧dΘm dalÜφm °ßdku je celkem dvanßct Φφsel. Prvnφ t°i p°edstavujφ indexy do pole vertex∙ (ka₧d² face mß t°i vrcholy) a zbyl²ch dev∞t hodnot urΦuje t°i normßlovΘ vektory (pro ka₧d² vrchol jeden). To je vÜe. Abych nezapomn∞l v adresß°i Data m∙₧ete najφt jeÜt∞ t°i podobnΘ soubory.

24

-2 0.2 -0.2

2 0.2 -0.2

2 0.2 0.2

-2 0.2 0.2

-2 -0.2 -0.2

2 -0.2 -0.2

2 -0.2 0.2

-2 -0.2 0.2

-0.2 2 -0.2

0.2 2 -0.2

0.2 2 0.2

0.2 2 0.2

-0.2 -2 -0.2

0.2 -2 -0.2

0.2 -2 0.2

-0.2 -2 0.2

-0.2 0.2 -2

0.2 0.2 -2

0.2 0.2 2

-0.2 0.2 2

-0.2 -0.2 -2

0.2 -0.2 -2

0.2 -0.2 2

-0.2 -0.2 2

36

1 3 2 0 1 0 0 1 0 0 1 0

1 4 3 0 1 0 0 1 0 0 1 0

5 6 7 0 -1 0 0 -1 0 0 -1 0

5 7 8 0 -1 0 0 -1 0 0 -1 0

5 4 1 -1 0 0 -1 0 0 -1 0 0

5 8 4 -1 0 0 -1 0 0 -1 0 0

3 6 2 1 0 0 1 0 0 1 0 0

3 7 6 1 0 0 1 0 0 1 0 0

5 1 2 0 0 -1 0 0 -1 0 0 -1

5 2 6 0 0 -1 0 0 -1 0 0 -1

3 4 8 0 0 1 0 0 1 0 0 1

3 8 7 0 0 1 0 0 1 0 0 1

9 11 10 0 1 0 0 1 0 0 1 0

9 12 11 0 1 0 0 1 0 0 1 0

13 14 15 0 -1 0 0 -1 0 0 -1 0

13 15 16 0 -1 0 0 -1 0 0 -1 0

13 12 9 -1 0 0 -1 0 0 -1 0 0

13 16 12 -1 0 0 -1 0 0 -1 0 0

11 14 10 1 0 0 1 0 0 1 0 0

11 15 14 1 0 0 1 0 0 1 0 0

13 9 10 0 0 -1 0 0 -1 0 0 -1

13 10 14 0 0 -1 0 0 -1 0 0 -1

11 12 16 0 0 1 0 0 1 0 0 1

11 16 15 0 0 1 0 0 1 0 0 1

17 19 18 0 1 0 0 1 0 0 1 0

17 20 19 0 1 0 0 1 0 0 1 0

21 22 23 0 -1 0 0 -1 0 0 -1 0

21 23 24 0 -1 0 0 -1 0 0 -1 0

21 20 17 -1 0 0 -1 0 0 -1 0 0

21 24 20 -1 0 0 -1 0 0 -1 0 0

19 22 18 1 0 0 1 0 0 1 0 0

19 23 22 1 0 0 1 0 0 1 0 0

21 17 18 0 0 -1 0 0 -1 0 0 -1

21 18 22 0 0 -1 0 0 -1 0 0 -1

19 20 24 0 0 1 0 0 1 0 0 1

19 24 23 0 0 1 0 0 1 0 0 1

Prßv∞ p°edstaven² soubor nahrßvß funkce ReadObject(). Pro pochopenφ podstaty by m∞ly staΦit komentß°e.

inline int ReadObject(char *st, glObject *o)// Nahraje objekt

{

FILE *file;// Handle souboru

unsigned int i;// ╪φdφcφ prom∞nnß cykl∙

file = fopen(st, "r");// Otev°e soubor pro Φtenφ

if (!file)// Poda°ilo se ho otev°φt?

return FALSE;// Pokud ne - konec funkce

fscanf(file, "%d", &(o->nPoints));// NaΦtenφ poΦtu vertex∙

for (i = 1; i <= o->nPoints; i++)// NaΦφtß vertexy

{

fscanf(file, "%f", &(o->points[i].x));// JednotlivΘ x, y, z slo₧ky

fscanf(file, "%f", &(o->points[i].y));

fscanf(file, "%f", &(o->points[i].z));

}

fscanf(file, "%d", &(o->nPlanes));// NaΦtenφ poΦtu fac∙

for (i = 0; i < o->nPlanes; i++)// NaΦφtß facy

{

fscanf(file, "%d", &(o->planes[i].p[0]));// NaΦtenφ index∙ vertex∙

fscanf(file, "%d", &(o->planes[i].p[1]));

fscanf(file, "%d", &(o->planes[i].p[2]));

fscanf(file, "%f", &(o->planes[i].normals[0].x));// NormßlovΘ vektory prvnφho vertexu

fscanf(file, "%f", &(o->planes[i].normals[0].y));

fscanf(file, "%f", &(o->planes[i].normals[0].z));

fscanf(file, "%f", &(o->planes[i].normals[1].x));// NormßlovΘ vektory druhΘho vertexu

fscanf(file, "%f", &(o->planes[i].normals[1].y));

fscanf(file, "%f", &(o->planes[i].normals[1].z));

fscanf(file, "%f", &(o->planes[i].normals[2].x));// NormßlovΘ vektory t°etφho vertexu

fscanf(file, "%f", &(o->planes[i].normals[2].y));

fscanf(file, "%f", &(o->planes[i].normals[2].z));

}

return TRUE;// VÜe v po°ßdku

}

Dφky funkci SetConnectivity() zaΦφnajφ b²t v∞ci zajφmavΘ :-) Hledßme v nφ ke ka₧dΘmu facu t°i sousednφ facy, se kter²mi mß spoleΦnou hranu. Proto₧e je zdrojov² k≤d, abych tak °ekl, trochu h∙°e pochopiteln², p°idßvßm i pseudo k≤d, kter² by mohl situaci maliΦko objasnit.

ZaΦßtek funkce

{

Postupn∞ se prochßzφ ka₧d² face (A) v objektu

{

V ka₧dΘm pr∙chodu se znovu prochßzφ vÜechny facy (B) objektu (zjiÜ¥uje se sousedstvφ A s B)

{

Dßle se projdou vÜechny hrany facu A

{

Pokud aktußlnφ hrana jeÜt∞ nemß p°i°azenΘho souseda

{

Projdou se vÜechny hrany facu B

{

Provedou se v²poΦty, kter²mi se zjistφ, jestli je okraj A stejn² jako okraj B

Pokud ano

{

Nastavφ se soused v A

Nastavφ se soused v B

}

}

}

}

}

}

}

Konec funkce

U₧ chßpete?

inline void SetConnectivity(glObject *o)// Nastavenφ soused∙ jednotliv²ch fac∙

{

unsigned int p1i, p2i, p1j, p2j;// PomocnΘ prom∞nnΘ

unsigned int P1i, P2i, P1j, P2j;// PomocnΘ prom∞nnΘ

unsigned int i, j, ki, kj;// ╪φdφcφ prom∞nnΘ cykl∙

for(i = 0; i < o->nPlanes-1; i++)// Ka₧d² face objektu (A)

{

for(j = i+1; j < o->nPlanes; j++)// Ka₧d² face objektu (B)

{

for(ki = 0; ki < 3; ki++)// Ka₧d² okraj facu (A)

{

if(!o->planes[i].neigh[ki])// Okraj jeÜt∞ nemß souseda?

{

for(kj = 0; kj < 3; kj++)// Ka₧d² okraj facu (B)

{

Nalezenφm dvou vertex∙, kterΘ oznaΦujφ konce hrany a jejich porovnßnφm m∙₧eme zjistit, jestli majφ spoleΦn² okraj. ╚ßst (kj+1) % 3 oznaΦuje vertex umφst∞n² vedle toho, o kterΘm uva₧ujeme. Ov∞°φme, jestli jsou vertexy stejnΘ. Proto₧e m∙₧e b²t jejich po°adφ rozdφlnΘ musφme testovat ob∞ mo₧nosti.

// V²poΦty pro zjiÜt∞nφ sousedstvφ

p1i = ki;

p1j = kj;

p2i = (ki+1) % 3;

p2j = (kj+1) % 3;

p1i = o->planes[i].p[p1i];

p2i = o->planes[i].p[p2i];

p1j = o->planes[j].p[p1j];

p2j = o->planes[j].p[p2j];

P1i = ((p1i+p2i) - abs(p1i-p2i)) / 2;

P2i = ((p1i+p2i) + abs(p1i-p2i)) / 2;

P1j = ((p1j+p2j) - abs(p1j-p2j)) / 2;

P2j = ((p1j+p2j) + abs(p1j-p2j)) / 2;

if((P1i == P1j) && (P2i == P2j))// Jsou sousedΘ?

{

o->planes[i].neigh[ki] = j+1;

o->planes[j].neigh[kj] = i+1;

}

}

}

}

}

}

}

Abychom se mohli alespo≥ trochu nadechnout :-) vypφÜi k≤d funkce DrawGLObject(), kter² je na prvnφ pohled maliΦko jednoduÜÜφ. Jak u₧ z nßzvu vypl²vß, vykresluje objekt.

void DrawGLObject(glObject o)// Vykreslenφ objektu

{

unsigned int i, j;// ╪φdφcφ prom∞nnΘ cykl∙

glBegin(GL_TRIANGLES);// Kreslenφ troj·helnφk∙

for (i = 0; i < o.nPlanes; i++)// Projde vÜechny facy

{

for (j = 0; j < 3; j++)// Troj·helnφk mß t°i rohy

{

// Normßlov² vektor a umφst∞nφ bodu

glNormal3f(o.planes[i].normals[j].x, o.planes[i].normals[j].y, o.planes[i].normals[j].z);

glVertex3f(o.points[o.planes[i].p[j]].x, o.points[o.planes[i].p[j]].y, o.points[o.planes[i].p[j]].z);

}

}

glEnd();

}

V²poΦet rovnice roviny vypadß pro ne-matematika sice hodn∞ slo₧it∞, ale je to pouze implementace matematickΘho vzorce, kter² se, kdy₧ je pot°eba, najde v tabulkßch nebo knφ₧ce.

P°ekl.: MaliΦkß chybiΦka. Pole v[] mß rozsah Φty°i prvky, ale pou₧φvajφ se jenom t°i. Index 0 se nikdy nepou₧ije.

inline void CalcPlane(glObject o, sPlane *plane)// Rovnice roviny ze t°φ bod∙

{

sPoint v[4];// PomocnΘ hodnoty

int i;// ╪φdφcφ prom∞nnß cykl∙

for (i = 0; i < 3; i++)// Pro zkrßcenφ zßpisu

{

v[i+1].x = o.points[plane->p[i]].x;// Ulo₧φ hodnoty do pomocn²ch prom∞nn²ch

v[i+1].y = o.points[plane->p[i]].y;

v[i+1].z = o.points[plane->p[i]].z;

}

plane->PlaneEq.a = v[1].y*(v[2].z-v[3].z) + v[2].y*(v[3].z-v[1].z) + v[3].y*(v[1].z-v[2].z);

plane->PlaneEq.b = v[1].z*(v[2].x-v[3].x) + v[2].z*(v[3].x-v[1].x) + v[3].z*(v[1].x-v[2].x);

plane->PlaneEq.c = v[1].x*(v[2].y-v[3].y) + v[2].x*(v[3].y-v[1].y) + v[3].x*(v[1].y-v[2].y);

plane->PlaneEq.d = -( v[1].x*(v[2].y*v[3].z - v[3].y*v[2].z) + v[2].x*(v[3].y*v[1].z - v[1].y*v[3].z) + v[3].x*(v[1].y*v[2].z - v[2].y*v[1].z) );

}

Funkce, kterΘ jsme prßv∞ napsali se volajφ ve funkci InitGLObjects(). Neexistuje-li po₧adovan² soubor, vrßtφme false. Pokud ale existuje, funkcφ ReadObject() ho nahrajeme do pam∞ti, pomocφ SetConnectivity() najdeme sousedφcφ facy a potom se v cyklu spoΦφtßme rovnici roviny ka₧dΘho facu.

int InitGLObjects()// Inicializuje objekty

{

if (!ReadObject("Data/Object2.txt", &obj))// Nahraje objekt

{

return FALSE;// P°i chyb∞ konec

}

SetConnectivity(&obj);// Pospojuje facy (najde sousedy)

for (unsigned int i = 0; i < obj.nPlanes; i++)// Prochßzφ facy

CalcPlane(obj, &(obj.planes[i]));// SpoΦφtß rovnici roviny facu

return TRUE;// VÜe v po°ßdku

}

Nynφ p°ichßzφ funkce, kterß renderuje stφn. Na zaΦßtku nastavφme vÜechny pot°ebnΘ parametry OpenGL a potΘ, ne na obrazovku, ale do stencil bufferu, vyrenderujeme stφn. Dßle vykreslφme vep°edu p°ed scΘnu velk² Üed² obdΘlnφk. Tam, kde byl stencil buffer modifikovßn se zobrazφ ÜedΘ plochy - stφn.

void CastShadow(glObject *o, float *lp)// Vr₧enφ stφnu

{

unsigned int i, j, k, jj;// PomocnΘ

unsigned int p1, p2;// Dva body okraje vertexu, kterΘ vrhajφ stφn

sPoint v1, v2;// Vektor mezi sv∞tlem a p°edchozφmi body

Nejprve urΦφme, kterΘ povrchy jsou p°ivrßcenΘ ke sv∞tlu a to tak, ₧e zjistφme, kterß strana facu je osv∞tlenß. Provedeme to velice jednoduÜe: mßme rovnici roviny (ax + by + cz + d = 0) i polohu sv∞tla, tak₧e dosadφme x, y, z koordinßty sv∞tla do rovnice. Nezajφmß nßs hodnota, ale znamΘnko v²sledku. Pokud bude v²sledek v∞tÜφ ne₧ nula, mφ°φ normßlov² vektor roviny na stranu ke sv∞tlu a rovina je osv∞tlenß. P°i zßpornΘm Φφsle mφ°φ vektor od sv∞tla, rovina je od n∞j odvrßcenß. VyÜel-li by v²sledek nula, bude sv∞tlo le₧et v rovin∞ facu, ale tφm se nebudeme zab²vat.

float side;// Pomocnß prom∞nnß

for (i = 0; i < o->nPlanes; i++)// Projde vÜechny facy objektu

{

// Rozhodne jestli je face p°ivrßcen² nebo odvrßcen² od sv∞tla

side = o->planes[i].PlaneEq.a * lp[0] + o->planes[i].PlaneEq.b * lp[1] + o->planes[i].PlaneEq.c * lp[2] + o->planes[i].PlaneEq.d * lp[3];

if (side > 0)// Je p°ivrßcen²?

{

o->planes[i].visible = TRUE;

}

else// Nenφ

{

o->planes[i].visible = FALSE;

}

}

Nastavφme parametry OpenGL, kterΘ jsou nutnΘ pro vr₧enφ stφnu. Vypneme sv∞tla, proto₧e nebudeme renderovat do color bufferu (v²stup na obrazovku), ale pouze do stencil bufferu. Ze stejnΘho d∙vodu zakß₧eme pomocφ glColorMask() vykreslovßnφ na obrazovku. AΦkoli je testovßnφ hloubky stßle zapnutΘ, nechceme, aby stφny byly v depth bufferu reprezentovßny pevn²mi objekty. Jako prevenci tedy nastavφme masku hloubky na GL_FALSE. Nakonec nastavφme stencil buffer tak, aby na mφsta v n∞m oznaΦenß mohly b²t vykresleny stφny.

glDisable(GL_LIGHTING);// Vypne sv∞tla

glDepthMask(GL_FALSE);// Vypne zßpis do depth bufferu

glDepthFunc(GL_LEQUAL);// Funkce depth bufferu

glEnable(GL_STENCIL_TEST);// Zapne stencilovΘ testy

glColorMask(0, 0, 0, 0);// Nekreslit na obrazovky

glStencilFunc(GL_ALWAYS, 1, 0xffffffff);// Funkce stencilu

Proto₧e mßme zapnutΘ o°ezßvßnφ zadnφch stran troj·helnφk∙ (viz. InitGL()), specifikujeme, kterΘ strany jsou p°ednφ. TakΘ nastavφme stencil buffer tak, aby se v n∞m p°i kreslenφ zv∞tÜovaly hodnoty.

glFrontFace(GL_CCW);// ╚elnφ st∞na proti sm∞ru hodinov²ch ruΦiΦek

glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);// ZvyÜovßnφ hodnoty stencilu

V cyklu projdeme ka₧d² face a pokud je oznaΦen jako viditeln² (p°ivrßcen² ke sv∞tlu), zkontrolujeme vÜechny jeho okraje. Pokud vedle n∞j nenφ ₧ßdn² sousednφ face nebo sice mß souseda, kter² ale nenφ viditeln², naÜli jsme okraj objektu, kter² vrhß stφn. Pokud se nad t∞mito dv∞ma podmφnkami zamyslφte, zjistφte, ₧e jsou pravdivΘ. Zφskali jsme prvnφ dv∞ sou°adnice Φty°·helnφku, kter² je st∞nou stφnu. V tomto p°φpad∞ si p°edstavte stφn jako oblast, kter² je ohraniΦena na jednΘ stran∞ objektem brßnφcφm pr∙chodu sv∞teln²ch paprsk∙, z druhΘ strany promφtacφ rovinou (st∞na mφstnosti) a na okrajφch Φty°·helnφky, kterΘ se prßv∞ sna₧φme vykreslit. U₧ je to trochu jasn∞jÜφ?

for (i = 0; i < o->nPlanes; i++)// Ka₧d² face objektu

{

if (o->planes[i].visible)// Je p°ivrßcen² ke sv∞tlu

{

for (j = 0; j < 3; j++)// Ka₧d² okraj facu

{

k = o->planes[i].neigh[j];// Index souseda (pomocn²)

Nynφ zjistφme, jestli je vedle aktußlnφho okraje face, kter² bu∩ nenφ viditeln² nebo v∙bec neexistuje (nemß souseda). Pokud podmφnka platφ, naÜli jsme okraj objektu, kter² vrhß stφn.

// Pokud nemß souseda, kter² je p°ivrßcen² ke sv∞tlu

if ((!k) || (!o->planes[k-1].visible))

{

Rohy hrany prßv∞ ov∞°ovanΘho troj·helnφku udßvajφ prvnφ dva body stφnu. DalÜφ dva zφskßme spoΦφtßnφm sm∞rovΘho vektoru, kter² vychßzφ ze sv∞tla, prochßzφ bodem p1 pop°. p2 a dφky nßsobenφ stem pokraΦuje ve stejnΘm sm∞ru n∞kam do hlubin scΘny. Nßsobenφ stem bychom si mohli p°edstavit jako m∞°φtko pro prodlou₧enφ vektoru a tudφ₧ i polygonu, aby dosßhl a₧ k promφtacφ rovin∞ a neskonΦil n∞kde p°ed nφ.

Kreslenφ stφnu hrubou silou pou₧itΘ zde, nenφ zrovna nejvhodn∞jÜφ, proto₧e mß velmi velkΘ nßroky na grafickou kartu. Nekreslφme toti₧ pouze k promφtacφ rovin∞, ale a₧ za ni k≤d tΘto lekce. ( * 100). Pro v∞tÜφ ·Φinnost by bylo vhodnΘ modifikovat tento algoritmus tak, aby se polygony stφnu o°ezaly objektem, na kter² dopadß. Tento postup by ovÜem byl mnohem nßroΦn∞jÜφ na vymyÜlenφ a asi by byl problematick² sßm o sob∞.

// NaÜli jsme okraj objektu, kter² vrhß stφn - nakreslφme polygon

p1 = o->planes[i].p[j];// Prvnφ bod okraje

jj = (j+1) % 3;// Pro zφskßnφ druhΘho okraje

p2 = o->planes[i].p[jj];// Druh² bod okraje

// DΘlka vektoru

v1.x = (o->points[p1].x - lp[0]) * 100;

v1.y = (o->points[p1].y - lp[1]) * 100;

v1.z = (o->points[p1].z - lp[2]) * 100;

v2.x = (o->points[p2].x - lp[0]) * 100;

v2.y = (o->points[p2].y - lp[1]) * 100;

v2.z = (o->points[p2].z - lp[2]) * 100;

Zbytek u₧ je celkem snadn². Mßme dva body s dΘlkou a tak vykreslφme Φty°·helnφk - jeden z mnoha okraj∙ stφnu.

glBegin(GL_TRIANGLE_STRIP);// Nakreslφ okrajov² polygon stφnu

glVertex3f(o->points[p1].x, o->points[p1].y, o->points[p1].z);

glVertex3f(o->points[p1].x + v1.x, o->points[p1].y + v1.y, o->points[p1].z + v1.z);

glVertex3f(o->points[p2].x, o->points[p2].y, o->points[p2].z);

glVertex3f(o->points[p2].x + v2.x, o->points[p2].y + v2.y, o->points[p2].z + v2.z);

glEnd();

V cyklech z∙staneme tak dlouho, dokud nenajdeme a nevykreslφme vÜechny okraje stφnu.

}

}

}

}

NejjednoduÜÜφ a nejpochopiteln∞jÜφ vysv∞tlenφ toho, proΦ vykreslujeme to samΘ jeÜt∞ jednou, je obrßzek - stφny budou pouze tam, kde b²t majφ. P°i vykreslovßnφ se nynφ budou hodnoty ve stencil bufferu sni₧ovat. TakΘ si vÜimn∞te, ₧e funkcφ glFrontFace() budeme o°ezßvat opaΦnΘ strany troj·helnφk∙.

Bez druhΘho kreslenφ Se druh²m kreslenφm

glFrontFace(GL_CW);// ╚elnφ st∞na po sm∞ru hodinov²ch ruΦiΦek

glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);// Sni₧ovßnφ hodnoty stencilu

for (i=0; i < o->nPlanes; i++)// Ka₧d² face objektu

{

if (o->planes[i].visible)// Je p°ivrßcen² ke sv∞tlu

{

for (j = 0; j < 3; j++)// Ka₧d² okraj facu

{

k = o->planes[i].neigh[j];// Index souseda (pomocn²)

// Pokud nemß souseda, kter² je p°ivrßcen² ke sv∞tlu

if ((!k) || (!o->planes[k-1].visible))

{

// NaÜli jsme okraj objektu, kter² vrhß stφn - nakreslφme polygon

p1 = o->planes[i].p[j];// Prvnφ bod okraje

jj = (j+1) % 3;// Pro zφskßnφ druhΘho okraje

p2 = o->planes[i].p[jj];// Druh² bod okraje

// DΘlka vektoru

v1.x = (o->points[p1].x - lp[0])*100;

v1.y = (o->points[p1].y - lp[1])*100;

v1.z = (o->points[p1].z - lp[2])*100;

v2.x = (o->points[p2].x - lp[0])*100;

v2.y = (o->points[p2].y - lp[1])*100;

v2.z = (o->points[p2].z - lp[2])*100;

glBegin(GL_TRIANGLE_STRIP);// Nakreslφ okrajov² polygon stφnu

glVertex3f(o->points[p1].x, o->points[p1].y, o->points[p1].z);

glVertex3f(o->points[p1].x + v1.x, o->points[p1].y + v1.y, o->points[p1].z + v1.z);

glVertex3f(o->points[p2].x, o->points[p2].y, o->points[p2].z);

glVertex3f(o->points[p2].x + v2.x, o->points[p2].y + v2.y, o->points[p2].z + v2.z);

glEnd();

}

}

}

}

A₧ te∩ opravdu zobrazφme na scΘnu stφny. Na ·rovni roviny obrazovky vykreslφme velk², Üed², polopr∙hledn² obdΘlnφk. Zobrazφ se pouze ty pixely, kterΘ byly prßv∞ oznaΦeny ve stencil bufferu (na pozici stφnu). ╚φm bude obdΘlnφk tmavÜφ, tφm tmavÜφ bude i stφn. M∙₧ete zkusit jinou pr∙hlednost nebo dokonce i barvu. Jak by se vßm lφbil Φerven², zelen² nebo modr² stφn? Äßdn² problΘm!

glFrontFace(GL_CCW);// ╚elnφ st∞na proti sm∞ru hodinov²ch ruΦiΦek

glColorMask(1, 1, 1, 1);// Vykreslovat na obrazovku

// Vykreslenφ obdΘlnφku p°es celou scΘnu

glColor4f(0.0f, 0.0f, 0.0f, 0.4f);// ╚ernß, 40% pr∙hlednß

glEnable(GL_BLEND);// Zapne blending

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);// Typ blendingu

glStencilFunc(GL_NOTEQUAL, 0, 0xffffffff);// Nastavenφ stencilu

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// Nem∞nit hodnotu stencilu

glPushMatrix();// Ulo₧φ matici

glLoadIdentity();// Reset matice

glBegin(GL_TRIANGLE_STRIP);// ╚ern² obdΘlnφk

glVertex3f(-0.1f, 0.1f,-0.10f);

glVertex3f(-0.1f,-0.1f,-0.10f);

glVertex3f( 0.1f, 0.1f,-0.10f);

glVertex3f( 0.1f,-0.1f,-0.10f);

glEnd();

glPopMatrix();// Obnovφ matici

Nakonec obnovφme zm∞n∞nΘ parametry OpenGL na v²chozφ hodnoty.

// Obnovφ zm∞n∞nΘ parametry OpenGL

glDisable(GL_BLEND);

glDepthFunc(GL_LEQUAL);

glDepthMask(GL_TRUE);

glEnable(GL_LIGHTING);

glDisable(GL_STENCIL_TEST);

glShadeModel(GL_SMOOTH);

}

DrawGLScene(), ostatn∞ jako v₧dycky, zajiÜ¥uje vÜechno vykreslovßnφ. Prom∞nnß Minv bude reprezentovat OpenGL matici, wlp budou lokßlnφ koordinßty a lp pomocnß pozice sv∞tla.

int DrawGLScene(GLvoid)// Hlavnφ vykreslovacφ funkce

{

GLmatrix16f Minv;// OpenGL matice

GLvector4f wlp, lp;// Relativnφ pozice sv∞tla

Sma₧eme obrazovkov², hloubkov² i stencil buffer. Resetujeme matici a p°esuneme se o dvacet jednotek do obrazovky. Umφstφme sv∞tlo, provedeme translaci na pozici koule a pomocφ quadraticu ji vykreslφme.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);// Sma₧e buffery

glLoadIdentity();// Reset matice

glTranslatef(0.0f, 0.0f, -20.0f);// P°esun 20 jednotek do hloubky

glLightfv(GL_LIGHT1, GL_POSITION, LightPos);// Umφst∞nφ sv∞tla

glTranslatef(SpherePos[0], SpherePos[1], SpherePos[2]);// Umφst∞nφ koule

gluSphere(q, 1.5f, 32, 16);// Vykreslenφ koule

SpoΦφtßme relativnφ pozici sv∞tla vzhledem k lokßlnφmu sou°adnicovΘmu systΘmu objektu, kter² vrhß stφn. Do prom∞nnΘ Min ulo₧φme transformaΦnφ matici objektu, ale obrßcenou (vÜe se zßporn²mi Φφsly a zadßvanΘ opaΦn²m po°adφm), tak₧e se stane invertovanou transformaΦnφ maticφ. Z lp vytvo°φme kopii pozice sv∞tla a potΘ ho vynßsobφme prßv∞ zφskanou OpenGL maticφ. JednoduÜe °eΦeno: na konci bude lp pozicφ sv∞tla v sou°adnicovΘm systΘmu objektu.

glLoadIdentity();// Reset matice

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

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

glGetFloatv(GL_MODELVIEW_MATRIX, Minv);// Ulo₧enφ ModelView matice do Minv

lp[0] = LightPos[0];// Ulo₧enφ pozice sv∞tla

lp[1] = LightPos[1];

lp[2] = LightPos[2];

lp[3] = LightPos[3];

VMatMult(Minv, lp);// Vynßsobenφ pozice sv∞tla OpenGL maticφ

glTranslatef(-ObjPos[0], -ObjPos[1], -ObjPos[2]);// Posun zßporn∞ o pozici objektu

glGetFloatv(GL_MODELVIEW_MATRIX, Minv);// Ulo₧enφ ModelView matice do Minv

wlp[0] = 0.0f;// Globßlnφ koordinßty na nulu

wlp[1] = 0.0f;

wlp[2] = 0.0f;

wlp[3] = 1.0f;

VMatMult(Minv, wlp);// Originßlnφ globßlnφ sou°adnicov² systΘm relativn∞ k lokßlnφmu

lp[0] += wlp[0];// Pozice sv∞tla je relativnφ k lokßlnφmu sou°adnicovΘmu systΘmu objektu

lp[1] += wlp[1];

lp[2] += wlp[2];

Vykreslφme mφstnost s objektem a potom zavolßme funkci CastShadow(), kterß vykreslφ stφn objektu. P°edßvßme jφ referenci na objekt spolu s pozicφ sv∞tla, kterß je nynφ ve stejnΘm sou°adnicovΘm systΘmu jako objekt.

glLoadIdentity();// Reset matice

glTranslatef(0.0f, 0.0f, -20.0f);// P°esun 20 jednotek do hloubky

DrawGLRoom();// Vykreslenφ mφstnosti

glTranslatef(ObjPos[0], ObjPos[1], ObjPos[2]);// Umφst∞nφ objektu

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

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

DrawGLObject(obj);// Vykreslenφ objektu

CastShadow(&obj, lp);// Vr₧enφ stφnu zalo₧enΘ na siluet∞

Abychom po spuÜt∞nφ dema vid∞li, kde se prßv∞ nachßzφ sv∞tlo, vykreslφme na jeho pozici mal² oran₧ov² kruh (respektive kouli).

glColor4f(0.7f, 0.4f, 0.0f, 1.0f);// Oran₧ovß barva

glDisable(GL_LIGHTING);// Vypne sv∞tlo

glDepthMask(GL_FALSE);// Vypne masku hloubky

glTranslatef(lp[0], lp[1], lp[2]);// Translace na pozici sv∞tla

// Po°ßd jsme v lokßlnφm sou°adnicovΘm systΘmu objektu

gluSphere(q, 0.2f, 16, 8);// Vykreslenφ malΘ koule (reprezentuje sv∞tlo)

glEnable(GL_LIGHTING);// Zapne sv∞tlo

glDepthMask(GL_TRUE);// Zapne masku hloubky

Aktualizujeme rotaci objektu a ukonΦφme funkci.

xrot += xspeed;// Zv∞tÜenφ ·hlu rotace objektu

yrot += yspeed;

glFlush();

return TRUE;// VÜechno v po°ßdku

}

Dßle napφÜeme specißlnφ funkci DrawGLRoom(), kterß vykreslφ mφstnost. Je jφ obyΦejnß krychle.

void DrawGLRoom()// Vykreslφ mφstnost (krychli)

{

glBegin(GL_QUADS);// ZaΦßtek kreslenφ obdΘlnφk∙

// Podlaha

glNormal3f(0.0f, 1.0f, 0.0f);// Normßla sm∞°uje nahoru

glVertex3f(-10.0f,-10.0f,-20.0f);// Lev² zadnφ

glVertex3f(-10.0f,-10.0f, 20.0f);// Lev² p°ednφ

glVertex3f( 10.0f,-10.0f, 20.0f);// Prav² p°ednφ

glVertex3f( 10.0f,-10.0f,-20.0f);// Prav² zadnφ

// Strop

glNormal3f(0.0f,-1.0f, 0.0f);// Normßla sm∞°uje dol∙

glVertex3f(-10.0f, 10.0f, 20.0f);// Lev² p°ednφ

glVertex3f(-10.0f, 10.0f,-20.0f);// Lev² zadnφ

glVertex3f( 10.0f, 10.0f,-20.0f);// Prav² zadnφ

glVertex3f( 10.0f, 10.0f, 20.0f);// Prav² p°ednφ

// ╚elnφ st∞na

glNormal3f(0.0f, 0.0f, 1.0f);// Normßla sm∞°uje do hloubky

glVertex3f(-10.0f, 10.0f,-20.0f);// Lev² hornφ

glVertex3f(-10.0f,-10.0f,-20.0f);// Lev² dolnφ

glVertex3f( 10.0f,-10.0f,-20.0f);// Prav² dolnφ

glVertex3f( 10.0f, 10.0f,-20.0f);// Prav² hornφ

// Zadnφ st∞na

glNormal3f(0.0f, 0.0f,-1.0f);// Normßla sm∞°uje k obrazovce

glVertex3f( 10.0f, 10.0f, 20.0f);// Prav² hornφ

glVertex3f( 10.0f,-10.0f, 20.0f);// Prav² spodnφ

glVertex3f(-10.0f,-10.0f, 20.0f);// Lev² spodnφ

glVertex3f(-10.0f, 10.0f, 20.0f);// Lev² zadnφ

// Levß st∞na

glNormal3f(1.0f, 0.0f, 0.0f);// Normßla sm∞°uje doprava

glVertex3f(-10.0f, 10.0f, 20.0f);// P°ednφ hornφ

glVertex3f(-10.0f,-10.0f, 20.0f);// P°ednφ dolnφ

glVertex3f(-10.0f,-10.0f,-20.0f);// Zadnφ dolnφ

glVertex3f(-10.0f, 10.0f,-20.0f);// Zadnφ hornφ

// Pravß st∞na

glNormal3f(-1.0f, 0.0f, 0.0f);// Normßla sm∞°uje doleva

glVertex3f( 10.0f, 10.0f,-20.0f);// Zadnφ hornφ

glVertex3f( 10.0f,-10.0f,-20.0f);// Zadnφ dolnφ

glVertex3f( 10.0f,-10.0f, 20.0f);// P°ednφ dolnφ

glVertex3f( 10.0f, 10.0f, 20.0f);// P°ednφ hornφ

glEnd();// Konec kreslenφ

}

P°edtφm ne₧ zapomenu... v DrawGLScene() jsme pou₧ili funkci VMatMult(), kterß nßsobφ vektor maticφ. Op∞t se jednß o implementaci vzorce z knφ₧ky o matematice.

void VMatMult(GLmatrix16f M, GLvector4f v)

{

GLfloat res[4];// Uklßdß v²sledky

res[0] = M[ 0]*v[0] + M[ 4]*v[1] + M[ 8]*v[2] + M[12]*v[3];

res[1] = M[ 1]*v[0] + M[ 5]*v[1] + M[ 9]*v[2] + M[13]*v[3];

res[2] = M[ 2]*v[0] + M[ 6]*v[1] + M[10]*v[2] + M[14]*v[3];

res[3] = M[ 3]*v[0] + M[ 7]*v[1] + M[11]*v[2] + M[15]*v[3];

v[0] = res[0];// V²sledek ulo₧φ zp∞t do v

v[1] = res[1];

v[2] = res[2];

v[3] = res[3];// Homogennφ sou°adnice

}

V Inicializaci OpenGL nejsou tΘm∞° ₧ßdnΘ novinky. Na zaΦßtku nahrajeme a inicializujeme objekt, kter² vrhß stφn, potom nastavφme obvyklΘ parametry a sv∞tla.

int InitGL(GLvoid)// Nastavenφ OpenGL

{

if (!InitGLObjects())// Nahraje objekt

return FALSE;

glShadeModel(GL_SMOOTH);// JemnΘ stφnovßnφ

glClearColor(0.0f, 0.0f, 0.0f, 0.5f);// ╚ernΘ pozadφ

glClearDepth(1.0f);// Nastavenφ hloubkovΘho bufferu

glClearStencil(0);// Nastavenφ 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

glLightfv(GL_LIGHT1, GL_POSITION, LightPos);// Pozice sv∞tla

glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmb);// Ambient sv∞tlo

glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDif);// Diffuse sv∞tlo

glLightfv(GL_LIGHT1, GL_SPECULAR, LightSpc);// Specular sv∞tlo

glEnable(GL_LIGHT1);// Zapne sv∞tlo 1

glEnable(GL_LIGHTING);// Zapne sv∞tla

Materißly, kterΘ urΦujφ jak vypadajφ polygony p°i dopadu sv∞tla, jsou, myslφm, novinkou. Nemusφme vepisovat ₧ßdnΘ hodnoty, proto₧e p°edßvanΘ pole jsou definovßny na zaΦßtku tutorißlu. Materißly mimo jinΘ urΦujφ i barvu povrchu, tak₧e p°i zapnutΘm sv∞tle nebude mφt zm∞na barvy pomocφ glColor() ₧ßdn² vliv (P°ekl. To jsem zjistil ·pln∞ nßhodou. Nevφm, jestli je to pravda obecn∞, ale minimßln∞ v tomto demu ano.).

glMaterialfv(GL_FRONT, GL_AMBIENT, MatAmb);// Prost°edφ, atmosfΘra

glMaterialfv(GL_FRONT, GL_DIFFUSE, MatDif);// Rozptylovßnφ sv∞tla

glMaterialfv(GL_FRONT, GL_SPECULAR, MatSpc);// Zrcadlivost

glMaterialfv(GL_FRONT, GL_SHININESS, MatShn);// Lesk

Abychom alespo≥ trochu zrychlili vykreslovßnφ, zapneme culling, tak₧e se zadnφ strany troj·helnφk∙ nebudou vykreslovat. Kterß strana je odvrßcenß se urΦφ podle po°adφ zadßvßnφ vrchol∙ polygon∙ (po/proti sm∞ru hodinov²ch ruΦiΦek).

glCullFace(GL_BACK);// O°ezßvßnφ zadnφch stran

glEnable(GL_CULL_FACE);// Zapne o°ezßvßnφ

Budeme vykreslovat i n∞jakΘ koule, tak₧e vytvo°φme a inicializujeme quadratic.

q = gluNewQuadric();// Nov² quadratic

gluQuadricNormals(q, GL_SMOOTH);// Generovßnφ normßlov²ch vektor∙ pro sv∞tlo

gluQuadricTexture(q, GL_FALSE);// Nepot°ebujeme texturovacφ koordinßty

return TRUE;// V po°ßdku

}

Poslednφ funkcφ tohoto tutorißlu je ProcessKeyboard(). Stejn∞ jako vykreslovßnφ, tak i ona, se volß v ka₧dΘm pr∙chodu hlavnφ smyΦky programu. OÜet°uje u₧ivatelskΘ p°φkazy p°i stisku klßves. Jak se program zachovß, popisujφ komentß°e.

void ProcessKeyboard()// OÜet°enφ klßvesnice

{

// Rotace objektu

if (keys[VK_LEFT]) yspeed -= 0.1f;// èipka vlevo - sni₧uje y rychlost

if (keys[VK_RIGHT]) yspeed += 0.1f;// èipka vpravo - zvyÜuje y rychlost

if (keys[VK_UP]) xspeed -= 0.1f;// èipka nahoru - sni₧uje x rychlost

if (keys[VK_DOWN]) xspeed += 0.1f;// èipka dol∙ - zvyÜuje x rychlost

// Pozice objektu

if (keys[VK_NUMPAD6]) ObjPos[0] += 0.05f;// '6' - pohybuje objektem doprava

if (keys[VK_NUMPAD4]) ObjPos[0] -= 0.05f;// '4' - pohybuje objektem doleva

if (keys[VK_NUMPAD8]) ObjPos[1] += 0.05f;// '8' - pohybuje objektem nahoru

if (keys[VK_NUMPAD5]) ObjPos[1] -= 0.05f;// '5' - pohybuje objektem dol∙

if (keys[VK_NUMPAD9]) ObjPos[2] += 0.05f;// '9' - p°ibli₧uje objekt

if (keys[VK_NUMPAD7]) ObjPos[2] -= 0.05f;// '7' oddaluje objekt

// Pozice sv∞tla

if (keys['L']) LightPos[0] += 0.05f;// 'L' - pohybuje sv∞tlem doprava

if (keys['J']) LightPos[0] -= 0.05f;// 'J' - pohybuje sv∞tlem doleva

if (keys['I']) LightPos[1] += 0.05f;// 'I' - pohybuje sv∞tlem nahoru

if (keys['K']) LightPos[1] -= 0.05f;// 'K' - pohybuje sv∞tlem dol∙

if (keys['O']) LightPos[2] += 0.05f;// 'O' - p°ibli₧uje sv∞tlo

if (keys['U']) LightPos[2] -= 0.05f;// 'U' - oddaluje sv∞tlo

// Pozice koule

if (keys['D']) SpherePos[0] += 0.05f;// 'D' - pohybuje koulφ doprava

if (keys['A']) SpherePos[0] -= 0.05f;// 'A' - pohybuje koulφ doleva

if (keys['W']) SpherePos[1] += 0.05f;// 'W' - pohybuje koulφ nahoru

if (keys['S']) SpherePos[1] -= 0.05f;// 'S'- pohybuje koulφ dol∙

if (keys['E']) SpherePos[2] += 0.05f;// 'E' - p°ibli₧uje kouli

if (keys['Q']) SpherePos[2] -= 0.05f;// 'Q' - oddaluje kouli

}

N∞kolik poznßmek ohledn∞ tutorißlu

Na prvnφ pohled vypadß demo hyperefektn∞ :-), ale mß takΘ svΘ mouchy. Tak nap°φklad koule nezastavuje projekci stφnu na st∞nu. V reßlnΘm prost°edφ by takΘ vrhala stφn, tak₧e by se nic moc nestalo. NicmΘn∞ je zde pouze na ukßzku toho, co se se stφnem stane na zak°ivenΘm povrchu.

Pokud program b∞₧φ extrΘmn∞ pomalu, zkuste p°epnout do fullscreenu nebo zm∞nit barevnou hloubku na 32 bit∙. Arseny L. napsal: "Pokud mßte problΘmy s TNT2 v okennφm m≤du, ujist∞te se, ₧e nemßte nastavenu 16bitovou barevnou hloubku. V tomto barevnΘm m≤du je stencil buffer emulovan², co₧ ve v²sledku znamenß mal² v²kon. V 32bitovΘm m≤du je vÜe bez problΘm∙."

napsal: Banu Cosmin - Choko & Brett Porter
p°elo₧il: Michal Turek - Woq

ZdrojovΘ k≤dy

Lekce 27

<<< Lekce 26 | Lekce 28 >>>