Lekce 7

Lekce 7 - TexturovΘ filtry, osv∞tlenφ, ovlßdßnφ pomocφ klßvesnice

V tomto dφlu se pokusφm vysv∞tlit pou₧itφ t°φ odliÜn²ch texturov²ch filtr∙. Dßle pak pohybu objekt∙ pomocφ klßvesnice a nakonec aplikaci jednoduch²ch sv∞tel v OpenGL. Nebude se jako obvykle navazovat na k≤d z p°edchozφho dφlu, ale zaΦne se p∞kn∞ od zaΦßtku.

#include <windows.h>// HlaviΦkov² soubor pro Windows

#include <stdio.h>// HlaviΦkov² soubor pro standartdnφ 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

P°idßme t°i booleovskΘ prom∞nnΘ. Prom∞nnß light sleduje zda je sv∞tlo zapnutΘ. Prom∞nnΘ lp a fp nßm indikujφ stisk klßvesy 'L' nebo 'F'. ProΦ je pot°ebujeme se dozvφme dßle. Te∩ staΦφ v∞d∞t, ₧e zabra≥ujφ opakovßnφ obslu₧nΘho k≤du p°i delÜφm dr₧enφ.

bool light;// Sv∞tlo ON/OFF

bool lp;// Stisknuto L?

bool fp;// Stisknuto F?

GLfloat xrot;// X Rotace

GLfloat yrot;// Y Rotace

GLfloat xspeed;// Rychlost x rotace

GLfloat yspeed;// Rychlost y rotace

GLfloat z=-5.0f;// Hloubka v obrazovce

Nßsledujφ pole pro specifikaci sv∞tla. Pou₧ijeme dva odliÜnΘ typy. Prvnφ bude okolnφ (ambient). Okolnφ sv∞tlo nevychßzφ z jednoho bodu, ale jsou jφm nasvφceny vÜechny objekty ve scΘn∞. Druh²m typem bude p°φmΘ (diffuse). P°φmΘ sv∞tlo vychßzφ z n∞jakΘho zdroje a odrß₧φ se o povrch. Povrchy objektu, na kterΘ sv∞tlo dopadß p°φmo, budou velmi jasnΘ a oblasti mßlo osv∞tlenΘ budou temnΘ. To vytvß°φ p∞knΘ stφnovΘ efekty po stranßch krabice. Sv∞tlo se vytvß°φ stejn²m zp∙sobem jako barvy. Je-li prvnφ Φφslo 1.0f a dalÜφ dv∞ 0.0f, dostßvßme jasnou Φervenou. Poslednφ hodnotou je alfa kanßl. Ten tentokrßt nechßme 1.0f. ╚ervenß, zelenß a modrß nastavenΘ na stejnou hodnotu v₧dy vytvo°φ stφn z ΦernΘ (0.0f) do bφlΘ (1.0f). Bez okolnφho sv∞tla by mφsta bez p°φmΘho sv∞tla byla p°φliÜ tmavß.

GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f };// Okolnφ sv∞tlo

V dalÜφm °ßdku jsou hodnoty pro p°φmΘ sv∞tlo. Proto₧e, jsou vÜechny hodnoty 1.0f, bude to nejjasn∞jÜφ sv∞tlo jakΘ m∙₧eme zφskat. P∞kn∞ osvφtφ krabici.

GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f };// P°φmΘ sv∞tlo

Nakonec nastavφme pozici sv∞tla. Proto₧e chceme aby sv∞tlo svφtilo na bednu zp°edu, nesmφme pohnout sv∞tlem na ose x a y. T°etφ parametr nßm zaruΦφ, ₧e bedna bude osv∞tlena zep°edu. Sv∞tlo bude zß°it sm∞rem k divßkovi. Zdroj sv∞tla neuvidφme, proto₧e je p°ed monitorem, ale uvidφme jeho odraz od bedny. Poslednφ Φφslo definujeme na 1.0f. UrΦuje koordinßty pozice sv∞telnΘho zdroje. Vφce v dalÜφ lekci.

GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };// Pozice sv∞tla

Prom∞nnß filter bude pou₧ita p°i zobrazenφ textury. Prvnφ textura je vytvß°ena pou₧itφm GL_NEAREST. Druhß textura bude GL_LINEAR - filtrovßnφ pro ·pln∞ hladk² obrßzek. T°etφ textura pou₧φvß mipmapingu, kter² tvo°φ hodn∞ dobr² povrch. Prom∞nnß filter tedy bude nab²vat hodnot 0, 1 a 2. GLuint texture[3] ukazuje na t°i textury.

GLuint filter;// Specifikuje pou₧φvan² texturov² filtr

GLuint texture[3];// Uklßdß t°i textury

Nahrajeme bitmapu a vytvo°φme z nφ t°i r∙znΘ textury. Tato lekce pou₧φvß glaux knihovny k nahrßvßnφ bitmap. Vφm ₧e Delphi a VC++ majφ tuto knihovnu. Co ostatnφ jazyky, nevφm. K tomu u₧ moc °φkat nebudu, °ßdky jsou okomentovanΘ a kompletnφ vysv∞tlenφ je v 6 lekci. Nahraje a vytvo°φ textury z bitmap.

int LoadGLTextures()// Loading bitmapy a konverze na texturu

{

int Status=FALSE;// Indikuje chyby

AUX_RGBImageRec *TextureImage[1];// Uklßdß bitmapu

memset(TextureImage,0,sizeof(void *)*1);// Vynuluje pam∞¥

Nynφ nahrajeme bitmapu. Kdy₧ vÜe prob∞hne, data obrßzku budou ulo₧ena v TextureImage[0], status se nastavφ na true a zaΦneme sestavovat texturu.

if (TextureImage[0]=LoadBMP("Data/Crate.bmp"))// Nahraje bitmapu a kontroluje vzniklΘ chyby

{

Status=TRUE;// VÜe je bez problΘm∙

Data bitmapy jsou nahrßna do TextureImage[0]. Pou₧ijeme je k vytvo°enφ t°φ textur. Nßsledujφcφ °ßdek oznßmφ, ₧e chceme sestavit 3 textury a chceme je mφt v ulo₧eny v texture[0], texture[1] a texture[2].

glGenTextures(3, &texture[0]);// Generuje t°i textury

V ÜestΘ lekci jsme pou₧ili lineßrnφ filtrovßnφ, kterΘ vy₧aduje hodn∞ v²konu, ale vypadß velice p∞kn∞. Pro prvnφ texturu pou₧ijeme GL_NEAREST. Spot°ebuje mßlo v²konu, ale v²sledek je relativn∞ Üpatn². Kdy₧ ve h°e vidφte ΦtvereΦkovanou texturu, pou₧φvß toto filtrovßnφ, nicmΘn∞ dob°e funguje i na slabÜφch poΦφtaΦφch. VÜimn∞te si ₧e jsme pou₧ili GL_NEAREST pro MIN i MAG. M∙₧eme smφchat GL_NEAREST s GL_LINEAR a textury budou vypadat sluÜn∞, ale zßrove≥ nevy₧adujφ vysok² v²kon. MIN_FILTER se u₧φvß p°i zmenÜovßnφ, MAG_FILTER p°i zv∞tÜovßnφ.

// Vytvo°φ nelineßrn∞ filtrovanou texturu

glBindTexture(GL_TEXTURE_2D, texture[0]);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

DalÜφ texturu vytvo°φme stejn∞ jako v lekci 6. Lineßrn∞ filtrovanß. Jedin² rozdφl spoΦφvß v pou₧itφ texture[1] mφsto texture[0], proto₧e se jednß o druhou texturu.

// Vytvo°φ lineßrn∞ filtrovanou texturu

glBindTexture(GL_TEXTURE_2D, texture[1]);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

Mipmaping jeÜt∞ neznßte. Pou₧φvß se p°i malΘm obrßzku, kdy mnoho detail∙ mizφ z obrazovky. Takto vytvo°en² povrch vypadß z blφzka dost Üpatn∞. Kdy₧ chcete sestavit mipmapovanou texturu, sestavφ se vφce textur odliÜnΘ velikosti a vysokΘ kvality. Kdy₧ kreslφte takovou texturu na obrazovku vybere se nejlΘpe vypadajφcφ textura. Nakreslφ na obrazovku mφsto toho, aby zm∞nilo rozliÜenφ p∙vodnφho obrßzku, kterΘ je p°φΦinou ztrßty detail∙. V ÜestΘ lekci jsem se zmφnil o stanoven²ch limitech Üφ°ky a v²Üky - 64, 128, 256 atd. Pro mipmapovanou texturu m∙₧eme pou₧φt jakoukoli Üφ°ku a v²Üku bitmapy. Automaticky se zm∞nφ velikost. Proto₧e toto je textura Φφslo 3, pou₧ijeme texture[2]. Nynφ mßme v texture[0] texturu bez filtru, texture[1] pou₧φvß lineßrnφ filtrovßnφ a texture[2] pou₧φvß mipmaping.

// Vytvo°φ mipmapovanou texturu

glBindTexture(GL_TEXTURE_2D, texture[2]);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);

gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

}

M∙₧eme uvolnit vÜechnu pam∞¥ zapln∞nou daty bitmapy. Otestujeme zda se data nachßzφ v TextureImage[0]. Kdy₧ tam budou, tak je sma₧eme. Nakonec uvolnφme strukturu obrßzku.

if (TextureImage[0])// Pokud obrßzek existuje

{

if (TextureImage[0]->data)// Pokud existujφ data obrßzku

{

free(TextureImage[0]->data);// Uvolnφ pam∞¥ obrßzku

}

free(TextureImage[0]);// Uvolnφ strukturu obrßzku

}

return Status;// Oznßmφ p°φpadnΘ chyby

}

Nejd∙le₧it∞jÜφ Φßst inicializace spoΦφvß v pou₧itφ sv∞tel.

int InitGL(GLvoid)// VÜechno nastavenφ OpenGL

{

if (!LoadGLTextures())// Nahraje texturu

{

return FALSE;

}

glEnable(GL_TEXTURE_2D);// Zapne mapovßnφ textur

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

glEnable(GL_DEPTH_TEST);// Zapne hloubkovΘ testovßnφ

glDepthFunc(GL_LEQUAL);// Typ hloubkovΘho testovßnφ

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// NejlepÜφ perspektivnφ korekce

Nastavφme sv∞tla - konkrΘtn∞ light1. Na zaΦßtku tΘto lekce jsme definovali okolnφ sv∞tlo do LightAmbient. Pou₧ijeme hodnoty nastavenΘ v poli.

glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);// Nastavenφ okolnφho sv∞tla

Hodnoty p°φmΘho sv∞tla jsou v LightDiffuse.

glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);// Nastavenφ p°φmΘho sv∞tla

Nynφ nastavφme pozici sv∞tla. Ta je ulo₧ena v LightPosition.

glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);// Nastavenφ pozice sv∞tla

Nakonec zapneme sv∞tlo jedna. Sv∞tlo je nastavenΘ, umφst∞nΘ a zapnutΘ, jakmile zavolßme glEnable(GL_LIGHTING) rozsvφtφ se.

glEnable(GL_LIGHT1);// Zapne sv∞tlo

return TRUE;// Inicializace prob∞hla v po°ßdku

}

Vykreslφme krychli s texturami. Kdy₧ nepochopφte co n∞kterΘ °ßdky d∞lajφ, podφvejte se do lekce 6.

int DrawGLScene(GLvoid)// Vykreslovßnφ

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Vyma₧e obrazovku a hloubkov² buffer

glLoadIdentity();// Reset matice

glTranslatef(0.0f,0.0f,z);

glRotatef(xrot,1.0f,0.0f,0.0f);

glRotatef(yrot,0.0f,1.0f,0.0f);

DalÜφ °ßdek je podobn² °ßdku v lekci 6, ale namφsto texture[0] tu mßme texture[filter]. Kdy₧ stiskneme klßvesu F, hodnota ve filter se zv²Üφ. Bude-li v∞tÜφ ne₧ 2, nastavφme zase 0. P°i startu programu bude filter nastaven na 0. Prom∞nnou filter tedy urΦujeme, kterou ze t°φ textur mßme pou₧φt.

glBindTexture(GL_TEXTURE_2D, texture[filter]);// Zvolφ texturu

P°i pou₧itφ sv∞tel musφme definovat normßlu povrchu. Je to Φßra vychßzejφcφ ze st°edu polygonu v 90 stup≥ovΘm ·hlu. ╪ekne jak²m sm∞rem je Φelo polygonu. Kdy₧ ji neurΦφte, stane se hodn∞ divn²ch v∞cφ. Povrchy kterΘ by m∞ly svφtit se nerozsvφtφ, Üpatnß strana polygonu svφtit bude, atd. Normßla po₧aduje bod vychßzejφcφ z polygonu. Pohled na p°ednφ povrch ukazuje ₧e normßla je kladnß na ose z. To znamenß ₧e normßla ukazuje k divßkovi. Na zadnφ stran∞ normßla jde od divßka, do obrazovky. Kdy₧ bude kostka otoΦenß o 180 stup≥∙ v na ose x nebo y, p°ednφ povrch bude ukazovat do obrazovky a zadnφ uvidφ divßk. Bez ohledu na to kter² povrch je vid∞t divßkem, normßla tohoto povrchu jde sm∞rem k n∞mu. Kdy₧ se tak stane, povrch bude osv∞tlen. U dalÜφch bod∙ normßly sm∞rem k sv∞tlu bude povrch takΘ sv∞tl². Kdy₧ se posunete do st°edu kostky, bude tmav². Normßla je bod ven, nikoli dovnit°, proto nenφ sv∞tlo uvnit° a tak to mß b²t.

glBegin(GL_QUADS);

// P°ednφ st∞na

glNormal3f( 0.0f, 0.0f, 1.0f);// Normßla

glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);

// Zadnφ st∞na

glNormal3f( 0.0f, 0.0f,-1.0f);// Normßla

glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);

// Hornφ st∞na

glNormal3f( 0.0f, 1.0f, 0.0f);// Normßla

glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);

// Spodnφ st∞na

glNormal3f( 0.0f,-1.0f, 0.0f);// Normßla

glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);

// Pravß st∞na

glNormal3f( 1.0f, 0.0f, 0.0f);// Normßla

glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);

// Levß st∞na

glNormal3f(-1.0f, 0.0f, 0.0f);// Normßla

glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);

glEnd();

xrot+=xspeed;

yrot+=yspeed;

return TRUE;

}

Posuneme se dol∙ k WinMain(). P°idßme k≤d k zapnutφ/vypnutφ sv∞tla, otßΦenφ, v²b∞r filtru a posun kostky do/z obrazovky. T∞sn∞ u konce WinMain() uvidφte p°φkaz SwapBuffers(hDC). Ihned za tento °ßdek p°idßme k≤d.

Nßsledujφcφ k≤d zjiÜ¥uje, zda je stisknuta klßvesa L. Je-li stisknuta ale lp nenφ false, klßvesa jeÜt∞ nebyla uvoln∞na.

// Funkce WinMain()

SwapBuffers(hDC);// Prohozenφ buffer∙

if (keys['L'] && !lp)// Klßvesa L - sv∞tlo

{

Kdy₧ bude lp false, L nebylo stisknuto, nebo bylo uvoln∞no. Tento trik je pou₧it pro p°φpad, kdy je klßvesa dr₧ena dΘle a my chceme, aby se k≤d vykonal pouze jednou. P°i prvnφm pr∙chodu se lp nastavφ na true a prom∞nnß light se invertuje. P°i dalÜφm pr∙chodu je u₧ lp true a k≤d se neprovede a₧ do uvoln∞nφ klßvesy, kterΘ nastavφ lp zase na false. Kdyby zde toto nebylo, sv∞tlo by p°i stisku akorßt blikalo.

lp=TRUE;

light=!light;

Nynφ se podφvßme na prom∞nnou light. Kdy₧ bude false, vypneme sv∞tlo, kdy₧ ne zapneme ho.

if (!light)

{

glDisable(GL_LIGHTING);// Vypne sv∞tlo

}

else

{

glEnable(GL_LIGHTING);// Zapne sv∞tlo

}

}

Nßsleduje nastavenφ prom∞nnΘ lp na false p°i uvoln∞nφ klßvesy L.

if (!keys['L'])

{

lp=FALSE;

}

Nynφ oÜet°φme stisk F. Kdy₧ se stiskne, dojde ke zv²Üenφ filter. Pokud bude v∞tÜφ ne₧ 2, nastavφme ho zp∞t na 0. K oÜet°enφ delÜφho stisku klßvesy pou₧ijeme stejn² zp∙sob jako u sv∞tla.

if (keys['F'] && !fp)// Klßvesa F - zm∞na texturovΘho filtru

{

fp=TRUE;

filter+=1;

if (filter>2)

{

filter=0;

}

}

if (!keys['F'])// Uvoln∞nφ F

{

fp=FALSE;

}

Otestujφ stisk klßvesy Page Up. Kdy₧ bude stisknuto, snφ₧φme prom∞nnou z. To zp∙sobφ vzdalovßnφ kostky v p°φkazu glTranslatef(0.0f,0.0f,z).

if (keys[VK_PRIOR])// Klßvesa Page Up - zv²Üφ zano°enφ do obrazovky

{

z-=0.02f;

}

Otestujφ stisk klßvesy Page Down. Kdy₧ bude stisknuta, zv²Üφme prom∞nnou z. To zp∙sobφ p°ibli₧ovßnφ kostky v p°φkazu glTranslatef(0.0f,0.0f,z).

if (keys[VK_NEXT])// Klßvesa Page Down - snφ₧φ zano°enφ do obrazovky

{

z+=0.02f;

}

Dßle zkontrolujeme kurzorovΘ klßvesy. Bude-li stisknuto vlevo/vpravo, prom∞nnß xspeed se bude zvyÜovat/sni₧ovat. Bude-li stisknuto nahoru/dol∙, prom∞nnß yspeed se bude zvyÜovat/sni₧ovat. Jestli si vzpomφnßte, v²Üe jsem psal, ₧e vysokΘ hodnoty zp∙sobφ rychlou rotaci. Dlouh² stisk n∞jakΘ klßvesy zp∙sobφ prßv∞ rychlou rotaci kostky.

if (keys[VK_UP])// èipka nahoru

{

xspeed-=0.01f;

}

if (keys[VK_DOWN])// èipka dolu

{

xspeed+=0.01f;

}

if (keys[VK_RIGHT])// èipka vpravo

{

yspeed+=0.01f;

}

if (keys[VK_LEFT])// èipka vlevo

{

yspeed-=0.01f;

}

Nynφ byste m∞li v∞d∞t jak vytvo°it vysoce kvalitnφ, realisticky vypadajφcφ, texturovan² objekt. TakΘ jsme se n∞co dozv∞d∞li o t°ech r∙zn²ch filtrech. Stiskem urΦit²ch klßves m∙₧ete pohybovat objektem na obrazovce, a nakonec vφme jak aplikovat jednoduchΘ sv∞tlo. Zkuste experimentovat s jeho pozicφ a barvou.

napsal: Jeff Molofee - NeHe
p°elo₧il: Ji°φ Rajsk² - RAJSOFT junior

ZdrojovΘ k≤dy

Lekce 7

<<< Lekce 6 | Lekce 8 >>>