Lekce 23 - Mapování textur na kulové kvadriky

Tento tutoriál je napsán na bázi lekce 18. V lekci 15 (Mapování textur na fonty) jsem psal o automatickém mapování textur. Vysvětlil jsem jak můžeme poprosit OpenGL o automatické generování texturových koordinátů, ale protože lekce 15 byla celkem skromná, rozhodl jsem se přidat mnohem více detailů o této technice.

Mapování kulového prostředí (Sphere Environment Mapping) je rychlá cesta pro přidání zrcadlení na kovové nebo zrcadlové objekty. Třebaže není tak přesná jako skutečné zrcadlení nebo jako krychlová mapa prostředí (cube environment map) je o hodně rychlejší. Jako základ použijeme kód z lekce 18, ale nepoužijeme žádnou z předchozích textur. Použijeme jednu kulovou mapu (sphere map) a jeden obrázek pro pozadí.

Než začneme... Red Book definuje kulovou mapu jako obraz scény na kovové kouli z nekonečné vzdálenosti a nekonečného ohniskového bodu. To je idální a ve skutečném životě nemožné. Nejlepší způsob, bez použití čoček rybího oka (fish eye lens), který jsem našel je použití programu Adobe Photoshop:

Nejdříve budeme potřebovat obrázek prostředí, které chceme namapovat na kouli. Otevřeme obrázek v Adobe Photoshopu a vybereme celý obrázek. Zkopírujeme obrázek a vytvoříme nový obrázek PSD (Photoshop formát). Nový obrázek by měl být stejné velikosti jako obrázek který jsme právě zkopírovali. Vložíme kopii původního obrázku do nového. Důvodem proč děláme kopii je, že tak může Photoshop aplikovat své filtry. Namísto kopírování obrázku můžeme vybrat mód z lokálního menu (na kliknutí pravého tlačítka myši) a zvolit mód RGB. Poté budou dostupné všechny filtry.

Dále potřebujeme změnit velikost obrázku tak že bude mocninou dvou. Pamatujte, že abyste mohli použít obrázek jako texturu musí mít rozměry 128x128, 256x256 atd. V menu image tedy vybereme image size, odškrtneme constraint proportions (zachovat poměr stran) a změníme velikost obrázku na platnou velikost textury. Pokud má váš obrázek velikost 100x90 je lepší vytvořit texturu o velikosti 128x128 než 64x64. Vytvářením menšího obrázku ztratíte hodně detailů.

Jako poslední vybereme menu filter (filtry) a v něm distort (zdeformovat) a použijte spherize modifier (modifikátor koule). Můžeme vidět, že střed obrázku je nafouklý jako balón. V normálním kulovém mapování by byla vnější plocha černá, ale to nemá skutečný vliv. Uložíme obrázek jako BMP a jsme připraveni k programování.

Textura pozadí Pozadí zdeformované koulí

V této lekci nebudeme přidávat žádné nové globální proměnné, ale pouze upravíme index pole pro uložení šesti textur.

GLuint texture[6];// Šest textur

Dále modifikujeme funkci LoadGLTextures(), abychom mohli nahrát 2 textury a aplikovat 3 filtry. Jednoduše dvakrát projdeme cyklem a v každém průchodu vytvoříme 3 textury pokaždé s jiným filtrovacím módem. Skoro celý tento kód je nový nebo modifikovaný.

int LoadGLTextures()// Loading bitmap a konverze na textury

{

int Status=FALSE;// Indikuje chyby

AUX_RGBImageRec *TextureImage[2];// Ukládá dvě bitmapy

memset(TextureImage,0,sizeof(void *)*2);// Vynuluje paměť

// Nahraje bitmapy a kontroluje vzniklé chyby

if ((TextureImage[0]=LoadBMP("Data/BG.bmp")) &&// Textura pozadí

(TextureImage[1]=LoadBMP("Data/Reflect.bmp")))// Textura kulové mapy (sphere map)

{

Status=TRUE;// Vše je bez problémů

glGenTextures(6, &texture[0]);// Generuje šest textur

for (int loop=0; loop<=1; loop++)

{

// Vytvoří nelineárně filtrovanou texturu

glBindTexture(GL_TEXTURE_2D, texture[loop]);// Textury 0 a 1

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[loop]->sizeX, TextureImage[loop]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);

// Vytvoří lineárně filtrovanou texturu

glBindTexture(GL_TEXTURE_2D, texture[loop+2]);// Textury 2 a 3

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[loop]->sizeX, TextureImage[loop]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);

// Vytvoří mipmapovanou texturu

glBindTexture(GL_TEXTURE_2D, texture[loop+4]);// Textury 2 a 3

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[loop]->sizeX, TextureImage[loop]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);

}

for (loop=0; loop<=1; loop++)

{

if (TextureImage[loop])// Pokud obrázek existuje

{

if (TextureImage[loop]->data)// Pokud existují data obrázku

{

free(TextureImage[loop]->data);// Uvolní paměť obrázku

}

free(TextureImage[loop]);// Uvolní strukturu obrázku

}

}

}

return Status;// Oznámí případné chyby

}

Trochu upravíme kód kreslení krychle. Namísto použití hodnot 1.0 a -1.0 pro normály, použijeme 0.5 a -0.5. Změnou hodnot normál můžeme měnit velikost odrazové mapy dovnitř a ven. Pokud je hodnota normály velká, odražený obrázek je větší a mohl by se zobrazovat čtverečkovaně. Snížením hodnoty normál na 0.5 a -0.5 je obrázek trochu zmenšen, takže obrázek odrážený na krychli nevypadá tak čtverečkovaně. Nastavením příliš malých hodnot získáme nežádoucí výsledky.

GLvoid glDrawCube()

{

glBegin(GL_QUADS);

// Přední stěna

glNormal3f( 0.0f, 0.0f, 0.5f);

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,-0.5f);

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);

// Vrchní stěna

glNormal3f( 0.0f, 0.5f, 0.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);

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

// Spodní stěna

glNormal3f( 0.0f,-0.5f, 0.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);

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

// Pravá stěna

glNormal3f( 0.5f, 0.0f, 0.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);

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

// Levá stěna

glNormal3f(-0.5f, 0.0f, 0.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);

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

glEnd();

}

Do InitGL přidáme volání dvou nových funkcí. Tyto dvě volání nastaví mód generování textur na S a T pro kulové mapování (sphere mapping). Texturové koordináty S, T, R a Q souvisí s koordináty objektu x, y, z a w. Pokud používáte jednorozměrnou (1D) texturu, použijete souřadnici S. Pokud použijete dvourozměrnou texturu použijete souřadnice S a T.

Takže následující kód říká OpenGL jak automaticky generovat S a T koordináty na kulově mapovaném (sphere-mapping) vzorci. Koordináty R a Q jsou obvykle ignorovány. Koordinát Q může být použit pro pokročilé techniky mapování textur a koordinát R může být užitečný až bude do OpenGL přidáno mapování 3D textur. Ale pro teď budeme koordináty R a Q ignorovat. Koordinát S běží horizontálně přes čelo našeho polygonu a T zase vertikálně.

// Funkce InitGL()

// Nastavení módu generování textur pro S koordináty pro kulové mapování

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

// Nastavení módu generování textur pro T koordináty pro kulové mapování

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

Máme téměř hotovo. Vše co musíme ještě udělat je nastavit vykreslování. Odstranil jsem několik typů quadratiků, protože nepracovali dobře s mapováním prostředí (environment mapping). Zaprvé potřebujeme povolit generování textur. Potom vybereme odrazovou texturu (kulovou mapu - sphere map) a vykreslíme náš objekt. Před vykreslením pozadí vypneme kulové mapování. Všimněte si, že příkaz glBindTexture() může vypadat docela složitě. Vše co děláme je výběr filtru pro kreslení naší kulové mapy nebo obrázku pozadí.

int DrawGLScene(GLvoid)// Všechno kreslení

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Smaže obrazovku a hloubkový buffer

glLoadIdentity();// Reset matice

glTranslatef(0.0f,0.0f,z);

glEnable(GL_TEXTURE_GEN_S);// Povolí generování texturových koordinátů S

glEnable(GL_TEXTURE_GEN_T);// Povolí generování texturových koordinátů T

glBindTexture(GL_TEXTURE_2D, texture[filter+(filter+1)]); // Zvolí texturu kulové mapy

glPushMatrix();

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

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

switch(object)// Vybere, co se bude kreslit

{

case 0:

glDrawCube();// Krychle

break;

case 1:

glTranslatef(0.0f,0.0f,-1.5f);// Vycentrování

gluCylinder(quadratic,1.0f,1.0f,3.0f,32,32);// Válec

break;

case 2:

gluSphere(quadratic,1.3f,32,32);// Koule

break;

case 3:

glTranslatef(0.0f,0.0f,-1.5f);// Vycentrování

gluCylinder(quadratic,1.0f,0.0f,3.0f,32,32);// Kužel

break;

}

glPopMatrix();

glDisable(GL_TEXTURE_GEN_S);// Vypne automatické generování koordinátů S

glDisable(GL_TEXTURE_GEN_T);// Vypne automatické generování koordinátů T

glBindTexture(GL_TEXTURE_2D, texture[filter*2]);// Zvolí texturu pozadí

glPushMatrix();

glTranslatef(0.0f, 0.0f, -24.0f);

glBegin(GL_QUADS);

glNormal3f( 0.0f, 0.0f, 1.0f);

glTexCoord2f(0.0f, 0.0f); glVertex3f(-13.3f, -10.0f, 10.0f);

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

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

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

glEnd();

glPopMatrix();

xrot+=xspeed;

yrot+=yspeed;

return TRUE;

}

Poslední věc, kterou v této lekci uděláme je upravení kódu kontrolujícího stisk mezerníku - odstranili jsme disky.

if (keys[' '] && !sp)

{

sp=TRUE;

object++;

if(object>3)

object=0;

}

A máme hotovo. Umíme vytvářet skutečně působivé efekty s použitím zrcadlení okolí na objektu - například téměř přesného odrazu pokoje. Původně jsem chtěl také ukázat, jak vytvářet krychlové mapování prostředí, ale moje aktuální videokarta ho nepodporuje. Možná za měsíc nebo tak nějak, až si koupím GeForce2 :-]. Mapování okolí jsem se naučil sám (hlavně proto, že jsem o tom nemohl najít téměř žádné informace), takže pokud je v tomto tutoriálu něco nepřesné, pošlete mi email nebo uvědomte NeHe-ho. Díky a hodně štěstí.

napsal: GB Schmick - TipTup
přeložil: Milan Turek <nalim.kerut (zavináč) email.cz>

Zdrojové kódy

Lekce 23

<<< Lekce 22 | Lekce 24 >>>