Lekce 29 - Blitter, nahrávání .RAW textur

V této lekci se naučíte, jak se nahrávají .RAW obrázky a konvertují se do textur. Dozvíte se také o blitteru, grafické metodě přenášení dat, která umožňuje modifikovat textury poté, co už byly nahrány do programu. Můžete jím zkopírovat část jedné textury do druhé, blendingem je smíchat dohromady a také roztahovat. Maličko upravíme program tak, aby v době, kdy není aktivní, vůbec nezatěžoval procesor.

Blitting... v počítačové grafice je toto slovo hodně používané. Označuje se jím zkopírování části jedné textury a vložení do druhé. Pokud programujete ve Win API nebo MFC, jistě jste slyšeli o funkcích BitBlt() nebo StretchBlt(). Přesně toto se pokusíme vytvořit.

Chcete-li napsat funkci, která implementuje blitting, měli byste něco vědět o lineární grafické paměti. Když se podíváte na monitor, vidíte spousty bodů reprezentujících nějaký obrázek, ovládací prvky nebo třeba kurzor myši. Vše je prostě složeno z matice pixelů. Ale jak ví grafická karta nebo BIOS, jak nakreslit bod například na souřadnicích [64; 64]? Jednoduše! Všechno, co je na obrazovce není v matici, ale v lineární paměti (v jednorozměrném poli). Pozici bodu v paměti můžeme získat následující rovnicí:

adresa_v_paměti = (pozice_y * rozlišení_obrazovky_x) + pozice_x

Pokud máme rozlišení obrazovky 640x480, bude bod [64; 64] umístěn na paměťové adrese (64*640) + 64 = 41024. Protože paměť, do které budeme ukládat bitmapy je také lineární, můžeme této vlastnosti využít při přenášení bloků grafických dat. Výslednou adresu ještě budeme násobit barevnou hloubkou obrázku, protože nepoužíváte jedno-bytové pixely (256 barev), ale RGBA obrázky. Pokud jste tento výklad nepochopili, nemá cenu jít dál...

Vytvoříme strukturu TEXTURE_IMAGE, která bude obsahovat informace o nahrávaném obrázku - šířku, výšku, barevnou hloubku. Pointer data bude ukazovat do dynamické paměti, kam nahrajeme ze souboru data obrázku.

typedef struct Texture_Image// Struktura obrázku

{

int width;// Šířka v pixelech

int height;// Výška v pixelech

int format;// Barevná hloubka v bytech na pixel

unsigned char *data;// Data obrázku

} TEXTURE_IMAGE;

Další datový typ je ukazatelem na právě vytvořenou strukturu. Po něm následují dvě proměnné t1 a t2. Do nich budeme nahrávat obrázky, které potom blittingem sloučíme do jednoho a vytvoříme z něj texturu.

typedef TEXTURE_IMAGE *P_TEXTURE_IMAGE;// Datový typ ukazatele na obrázek

P_TEXTURE_IMAGE t1;// Dva obrázky

P_TEXTURE_IMAGE t2;

GLuint texture[1];// Jedna textura

Rot proměnné určují úhel rotace výsledného objektu. Nic nového.

GLfloat xrot;// X rotace

GLfloat yrot;// Y rotace

GLfloat zrot;// Z rotace

Funkcí AllocateTextureBuffer(), alokujeme dynamickou paměť pro obrázek a vrátíme ukazatel. Při neúspěchu se vrací NULL. Funkci předává program celkem tři parametry: šířku, výšku a barevnou hloubku v bytech na pixel.

P_TEXTURE_IMAGE AllocateTextureBuffer(GLint w, GLint h, GLint f)// Alokuje paměť pro obrázek

{

Ukazatel na obrázek ti vrátíme na konci funkce volajícímu kódu. Na začátku ho inicializujeme na NULL. Proměnné c, přiřadíme také NULL. Představuje úložiště nahrávaných dat.

P_TEXTURE_IMAGE ti = NULL;// Ukazatel na strukturu obrázku

unsigned char *c = NULL;// Ukazatel na data obrázku

Pomocí standardní funkce malloc() se pokusíme alokovat dynamickou paměť pro strukturu obrázku. Pokud se operace podaří, program pokračuje dále. Při jakékoli chybě vrátí malloc() NULL. Vypíšeme chybovou zprávu a oznámíme volajícímu kódu neúspěch.

ti = (P_TEXTURE_IMAGE)malloc(sizeof(TEXTURE_IMAGE));// Alokace paměti pro strukturu

if(ti != NULL)// Podařila se alokace paměti?

{

Po úspěšné alokaci paměti vyplníme strukturu atributy obrázku. Barevná hloubka není v obvyklém formátu bit na pixel, ale kvůli jednodušší manipulaci s pamětí v bytech na pixel.

ti->width = w;// Nastaví atribut šířky

ti->height = h;// Nastaví atribut výšky

ti->format = f;// Nastaví atribut barevné hloubky

Stejným způsobem jako pro strukturu alokujeme paměť i pro data obrázku. Její velikost získáme násobením šířky, výšky a barevné hloubky. Při úspěchu nastavíme atribut data struktury na právě získanou dynamickou paměť, neúspěch ošetříme stejně jako minule.

c = (unsigned char *)malloc(w * h * f);// Alokace paměti pro strukturu

if (c != NULL)// Podařila se alokace paměti?

{

ti->data = c;// Nastaví ukazatel na data

}

else// Alokace paměti pro data se nepodařila

{

MessageBox(NULL, "Could Not Allocate Memory For A Texture Buffer", "BUFFER ERROR", MB_OK | MB_ICONINFORMATION);

Překl.: Tady by správně měla funkce vrátit namísto NULL proměnnou ti nebo ještě lépe před opuštěním funkce dealokovat dynamickou paměť struktury ti. Bez vrácení ukazatele nemůžeme z venku paměť uvolnit. Pokud operační systém nepracuje tak, jak má (Toto není narážka na MS Windows :-), čili po skončení neuvolní poskytne zdroje programu, vznikají paměťové úniky.

// Uvolnění paměti struktury (Překl.)

// free(ti);

// ti = NULL;

return NULL;

}

}

else// Alokace paměti pro strukturu se nepodařila

{

MessageBox(NULL,"Could Not Allocate An Image Structure","IMAGE STRUCTURE ERROR",MB_OK | MB_ICONINFORMATION);

return NULL;

}

Pokud dosud nebyly žádné problémy, vrátíme ukazatel na strukturu ti.

return ti;// Vrátí ukazatel na dynamickou paměť

}

Ve funkci DeallocateTexture() děláme pravý opak - uvolňujeme paměť obrázku, na kterou ukazuje předaný parametr t.

void DeallocateTexture(P_TEXTURE_IMAGE t)// Uvolní dynamicky alokovanou paměť obrázku

{

if(t)// Pokud struktura obrázku existuje

{

if(t->data)// Pokud existují data obrázku

{

free(t->data);// Uvolní data obrázku

}

free(t);// Uvolní strukturu obrázku

}

}

Všechno už máme připravené, zbývá jenom nahrát .RAW obrázek. RAW formát je nejjednodušší a nejrychlejší způsob, jak nahrát do programu texturu (samozřejmě kromě funkce auxDIBImageLoad()). Proč je to tak jednoduché? Protože .RAW formát obsahuje pouze samotná data bitmapy bez hlaviček nebo něčeho dalšího. Jediné, co musíme udělat, je otevřít soubor a načíst data tak, jak jsou. Téměř... bohužel tento formát má dvě nevýhody. První je to, že ho neotevřete v některých grafických editorech, o druhé později. Pochopíte sami :-(

Funkci předáváme název souboru a ukazatel na strukturu.

int ReadTextureData(char *filename, P_TEXTURE_IMAGE buffer)// Načte data obrázku

{

Deklarujeme handle souboru, řídící proměnné cyklů a proměnnou done, která indikuje úspěch/neúspěch operace volajícímu kódu. Na začátku jí přiřadíme nulu, protože obrázek ještě není nahraný. Proměnnou stride, která určuje velikost řádku, hned na začátku inicializujeme na hodnotu získanou vynásobením šířky řádku v pixelech s barevnou hloubkou. Pokud bude obrázek široký 256 pixelů a barevná hloubka 4 byty (32 bitů, RGBA), velikost řádku bude celkem 1024 bytů. Pointer p ukazuje do paměti dat obrázku.

FILE *f;// Handle souboru

int i, j, k;// Řídící proměnné cyklů

int done = 0;// Počet načtených bytů ze souboru (návratová hodnota)

int stride = buffer->width * buffer->format;// Velikost řádku

unsigned char *p = NULL;// Ukazatel na aktuální byte paměti

Otevřeme soubor pro čtení v binárním módu.

f = fopen(filename, "rb");// Otevře soubor

if(f != NULL)// Podařilo se ho otevřít?

{

Pokud soubor existuje a šel otevřít, začneme se postupně vnořovat do cyklů. Vše by bylo velice jednoduché, kdyby .RAW formát byl trochu jinak uspořádán. Řádky vedou, jak je obvyklé, zleva doprava, ale jejich pořadí je invertované. To znamená, že první řádek je poslední, druhý předposlední atd. Vnější cyklus tedy nastavíme tak, aby řídící proměnná ukazovala dolů na začátek obrázku. Soubor načítáme od začátku, ale hodnoty ukládáme od konce paměti vzhůru. Výsledkem je převrácení obrázku.

for(i = buffer->height-1; i >= 0 ; i--)// Od zdola nahoru po řádcích

{

Nastavíme ukazatel, kam se právě ukládá, na správný řádek paměti. Jejím začátkem je samozřejmě buffer->data. Sečteme ho s umístěním od začátku i * velikost řádku. Představte si, že buffer->data je stránka v paměti a i * stride představuje offset. Je to úplně stejné. Offsetem se pohybujeme po přidělené stránce. Na začátku je maximální a postupně klesá. Výsledkem je, že v paměti postupujeme vzhůru. Myslím, že je to pochopitelné.

p = buffer->data + (i * stride);// P ukazuje na požadovaný řádek

Druhým cyklem se pohybujeme zleva doprava po pixelech obrázku (ne bytech!).

for (j = 0; j < buffer->width; j++)// Zleva doprava po pixelech

{

Třetí cyklus prochází jednotlivé byty v pixelu. Pokud barevná hloubka (= byty na pixel) bude 4, cyklus projde celkem 3x (od 0 do 2; format-1). Důvodem odečtení jedničky je, že většina .RAW obrázků neobsahuje alfa hodnotu, ale pouze RGB složky. Alfu nastavíme ručně.

Všimněte si také, že každým průchodem inkrementujeme tři proměnné: k, p a done. Řídící proměnná k je jasná. P ukazovalo před vstupem do všech cyklů na začátek posledního řádku v paměti. Postupně ho inkrementujeme až dosáhne úplného konce. Potom ho nastavíme na předposlední řádek atd. Done na konci funkce vrátíme, označuje celkový počet načtených bytů.

for (k = 0; k < buffer->format-1; k++, p++, done++)// Jednotlivé byty v pixelu

{

Funkce fgetc() načte ze souboru f jeden znak a vrátí ho. Tento znak má velikost 1 byte (Už víte proč zrovna unsigned char?). Považujeme ho za složku barvy. Protože se cyklus po třetím průchodu zastaví, načteme a uložíme složky R, G a B.

*p = fgetc(f);// Načte R, G a B složku barvy

}

Po opuštění cyklu přiřadíme alfu a opět inkrementujeme ukazatel, aby se posunul na další byte.

Překl.: Tady se hodí poznamenat, že alfa nemusí být zrovna 255 (neprůhledná), ale můžeme ji nastavit na polovinu (122) a tak vytvořit poloprůhlednou texturu. Nebo si říct, že pixel o určitých složkách RGB bude průhledný. Většinou se vezme černá nebo bílá barva, ale nic nebrání např. načtení levého horního pixelu obrázku a zprůhlednění všech ostatních pixelů se stejným RGB. Nebo postupně, jak načítáme jednotlivé pixely v řádku, snižovat alfu od 255 do 0. Textura bude vlevo neprůhledná a vpravo průhledná - plynulý přechod. S průhledností se dělají hodně kvalitní efekty. Maličké upozornění na konec: Efekty s alfa hodnotou jsou možné nejen u .RAW textur. Nezapomeňte, že už v 6. lekci !!! jsme měli přístup k datům textury. Funkci glTexImage2D() jsme na konci LoadGLTextures() předávali parametr data!

*p = 255;// Alfa neprůhledná (ruční nastavení)

p++;// Ukazatel na další byte

}

}

Poté, co projdeme všechny byty v pixelu, pixely v řádku a řádky v souboru se všechny cykly ukončí. Uf, Konečně! :-) Po ukončení cyklů zavřeme soubor.

fclose(f);// Zavře soubor

}

Pokud byly problémy s otevřením souboru (neexistuje ap.) zobrazíme chybovou zprávu.

else// Soubor se nepodařilo otevřít

{

MessageBox(NULL,"Unable To Open Image File","IMAGE ERROR",MB_OK | MB_ICONINFORMATION);

}

Nakonec vrátíme done. Pokud se soubor nepodařilo otevřít a my nic nenačetli, obsahuje nulu. Pokud bylo vše v pořádku done se rovná počtu načtených bytů.

return done;// Vrátí počet načtených bytů

}

Máme loadovaná data obrázku, takže vytvoříme texturu. Funkci předáváme ukazatel na obrázek. Vygenerujeme texturu, nastavíme ji jako aktuální, zvolíme lineární filtrování pro zvětšení i zmenšení a nakonec vytvoříme mipmapovanou texturu. Vše je úplně stejné jako s knihovnou glaux, ale s tím rozdílem, že jsme si obrázek tentokrát nahráli sami.

void BuildTexture(P_TEXTURE_IMAGE tex)// Vytvoří texturu

{

glGenTextures(1, &texture[0]);// Generuje texturu

glBindTexture(GL_TEXTURE_2D, texture[0]);// Vybere texturu za aktuální

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// Lineární filtrování

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

// Mipmapovaná textura

gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, tex->width, tex->height, GL_RGBA, GL_UNSIGNED_BYTE, tex->data);

}

Funkci Blit(), která implementuje blitting, je předávána spousta parametrů. Co vyjadřují? Vezmeme to hezky popořadě. Src je zdrojovým obrázkem, jehož data vkládáme do cílového obrázku dst. Ostatní parametry vyznačují, která data se zkopírují (obdélník určený čtyřmi src_* čísly), kam se mají do cílového obrázku umístit (dst_*) a jakým způsobem (blending, popř. alfa hodnota).

// Blitting obrázků

void Blit(P_TEXTURE_IMAGE src,// Zdrojový obrázek

P_TEXTURE_IMAGE dst,// Cílový obrázek

int src_xstart,// Levý horní bod kopírované oblasti

int src_ystart,// Levý horní bod kopírované oblasti

int src_width,// Šířka kopírované oblasti

int src_height,// Výška kopírované oblasti

int dst_xstart,// Kam kopírovat (levý horní bod)

int dst_ystart,// Kam kopírovat (levý horní bod)

int blend,// Použít blending?

int alpha)// Hodnota alfy při blendingu

{

Po řídících proměnných cyklů deklarujeme pomocné proměnné s a d, které ukazují do paměti obrázků. Dále ošetříme předávané parametry tak, aby alfa hodnota byla v rozmezí 0 až 255 a blend 0 nebo 1.

int i, j, k;// Řídící proměnné cyklů

unsigned char *s, *d;// Pomocné ukazatele na data zdroje a cíle

if(alpha > 255)// Je alfa mimo rozsah?

alpha = 255;

if(alpha < 0)

alpha = 0;

if(blend < 0)// Je blending mimo rozsah?

blend = 0;

if(blend > 1)

blend = 1;

Překl.: Celé kopírování raději vysvětlím na příkladu, bude snáze pochopitelné. Máme obrázek 256 pixelů široký a chceme zkopírovat např. oblast od 50. do 200. pixelu o určité výšce. Před vstupem do cyklu se přesuneme na první kopírovaný řádek. Potom skočíme na 50. pixel zleva, zkopírujeme 150 pixelů a skočíme na konec řádku přes zbývajících 56 pixelů. Vše opakujeme pro další řádek, dokud nezkopírujeme celý požadovaný obdélník dat zdrojového obrázku do cílového.

Příklad

Nyní nastavíme ukazatele d a s. Cílový ukazatel získáme sečtením adresy, kde začínají data cílového obrázku s offsetem, který je výsledkem násobení y pozice, kam začneme kopírovat, šířkou obrázku v pixelech a barevnou hloubkou obrázku. Tímto získáme řádek, na kterém začínáme kopírovat. Zdrojový ukazatel určíme analogicky.

// Ukazatele na první kopírovaný řádek

d = dst->data + (dst_ystart * dst->width * dst->format);

s = src->data + (src_ystart * src->width * src->format);

Vnější cyklus prochází kopírované řádky od shora dolů.

for (i = 0; i < src_height; i++)// Řádky, ve kterých se kopírují data

{

Už máme ukazatel nastaven na správný řádek, ale ještě musíme přičíst x-ovou pozici, která se opět násobí barevnou hloubkou. Akci provedeme pro zdrojový i cílový ukazatel.

// Posun na první kopírovaný pixel v řádku

s = s + (src_xstart * src->format);

d = d + (dst_xstart * dst->format);

Pointery nyní ukazují na první kopírovaný pixel. Začneme cyklus, který v řádku prochází jednotlivé pixely.

for (j = 0; j < src_width; j++)// Pixely v řádku, které se mají kopírovat

{

Nejvnitřnější cyklus prochází jednotlivé byty v pixelu. Všimněte si, že se také inkrementují pozice ve zdrojovém i cílovém obrázku.

for(k = 0; k < src->format; k++, d++, s++)// Byty v kopírovaném pixelu

{

Přichází nejzajímavější část - vytvoření alfablendingu. Představte si, že máte dva pixely: červený (zdroj) a zelený (cíl). Oba leží na stejných souřadnicích. Pokud je nezprůhledníte, půjde vidět pouze jeden z nich, protože původní pixel bude nahrazen novým. Jak jistě víte, každý pixel se skládá ze tří barevných kanálů RGB. Chceme-li vytvořit alfa blending, musíme nejdříve spočítat opačnou hodnotu alfa kanálu a to tak, že odečteme tuto hodnotu od maxima (255 - alpha). Násobíme jí cílový (zelený) pixel a sečteme ho se zdrojovým (červeným), který jsme násobili neupravenou alfou. Jsme skoro hotovi. Konečnou barvu vypočítáme dělením výsledku maximální hodnotou průhlednosti (255). tuto operaci z důvodu větší rychlosti vykonává bitový posun doprava o osm bitů. A je to! Máme pixel složený z obou předcházejících pixelů. Všimněte si, že se výpočty postupně provádějí se všemi kanály RGBA. Víte, co jsme právě implementovali? OpenGL techniku glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).

if (blend)// Je požadován blending?

{

*d = ((*s * alpha) + (*d * (255-alpha))) >> 8;// Sloučení dvou pixelů do jednoho

}

Pokud nebudeme chtít blending, jednoduše zkopírujeme data ze zdrojové bitmapy do cílové. Žádná matematika, alfa se ignoruje.

else// Bez blendingu

{

*d = *s;// Obyčejné kopírování

}

}

}

Dojdeme-li až na konec kopírované oblasti, zvětšíme ukazatel tak, aby se dostal na konec řádku. Pokud dobře rozumíme ukazatelům a paměťovým operacím, je blitting hračkou.

// Skočí ukazatelem na konec řádku

d = d + (dst->width - (src_width + dst_xstart)) * dst->format;

s = s + (src->width - (src_width + src_xstart)) * src->format;

}

}

Inicializace je tentokrát změněna od základů. Alokujeme paměť pro dva obrázky veliké 256 pixelů, které mají barevnou hloubku 4 byty (RGBA). Poté se je pokusíme nahrát. Pokud něco nevyjde vypíšeme chybovou zprávu a ukončíme program.

int InitGL(GLvoid)// Inicializace

{

t1 = AllocateTextureBuffer(256, 256, 4);// Alokace paměti pro první obrázek

if (ReadTextureData("Data/Monitor.raw", t1) == 0)// Nahraje data obrázku

{

// Nic se nenahrálo

MessageBox(NULL, "Could Not Read 'Monitor.raw' Image Data", "TEXTURE ERROR", MB_OK | MB_ICONINFORMATION);

return FALSE;

}

t2 = AllocateTextureBuffer(256, 256, 4);// Alokace paměti pro druhý obrázek

if (ReadTextureData("Data/GL.raw", t2) == 0)// Nahraje data obrázku

{

// Nic se nenahrálo

MessageBox(NULL, "Could Not Read 'GL.raw' Image Data", "TEXTURE ERROR", MB_OK | MB_ICONINFORMATION);

return FALSE;

}

Pokud jsme se dostali až tak daleko, je bezpečné předpokládat, že můžeme pracovat s daty obrázků, které se pokusíme blittingem sloučit do jednoho. Předáme je funkci - obrázek t2 jako zdrojový, t1 jako cílový. Výsledný obrázek získaný sloučením se uloží do t1. Vytvoříme z něj texturu.

// Blitting obrázků

Blit(t2,// Zdrojový obrázek

t1,// Cílový obrázek

127,// Levý horní bod kopírované oblasti

127,// Levý horní bod kopírované oblasti

128,// Šířka kopírované oblasti

128,// Výška kopírované oblasti

64,// Kam kopírovat (levý horní bod)

64,// Kam kopírovat (levý horní bod)

1,// Použít blending?

128)// Hodnota alfy při blendingu

BuildTexture(t1);// Vytvoří texturu

Překl.: Původně jsem chtěl vložit obrázky, abyste věděli, jak vypadají, ale bohužel ani jeden grafický editor, který mám zrovna doma .RAW formát nepodporuje. V anglickém tutoriálu je zmíněno, že Adobe Photoshop to svede. Ale poradil jsem si... víte jak? OpenGL.

Obrázek t2 Obrázek t1 Obrázek t1 po blittingu

Potom, co je vytvořena textura, můžeme uvolnit paměť obou obrázků.

DeallocateTexture(t1);// Uvolní paměť obrázků

DeallocateTexture(t2);

Následují běžná nastavení OpenGL.

glEnable(GL_TEXTURE_2D);// Zapne texturování

glShadeModel(GL_SMOOTH);// Jemné stínování

glClearColor(0.0f, 0.0f, 0.0f, 0.0f);// Černé pozadí

glClearDepth(1.0);// Povolí mazání depth bufferu

glEnable(GL_DEPTH_TEST);// Zapne testování hloubky

glDepthFunc(GL_LESS);// Typ testování hloubky

return TRUE;

}

DrawGLScene() renderuje obyčejnou krychli - to už určitě znáte.

GLvoid DrawGLScene(GLvoid)// Vykreslování

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Smaže buffery

glLoadIdentity();// Reset matice

glTranslatef(0.0f,0.0f,-10.0f);// Přesun do hloubky

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

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

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

glBindTexture(GL_TEXTURE_2D, texture[0]);// Zvolí texturu

glBegin(GL_QUADS);// Začátek kreslení obdélníků

// Čelní stěna

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

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

// Zadní stěna

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

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

// Horní stěna

glNormal3f(0.0f, 1.0f, 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);

// Dolní stěna

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

// Pravá stěna

glNormal3f(1.0f, 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(-1.0f, 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();// Konec kreslení

xrot += 0.3f;// Zvětší úhly rotace

yrot += 0.2f;

zrot += 0.4f;

}

Maličko upravíme kód WinMain(). Pokud není program aktivní (např. minimalizovaný), zavoláme WaitMessage(). Všechno se zastaví, dokud program neobdrží nějakou zprávu (obyčejně o maximalizaci okna). Ve výsledku dosáhneme toho, že pokud program není aktivní nebude vůbec zatěžovat procesor.

// Funkce WinMain() - v hlavní smyčce programu

if (!active)// Je program neaktivní?

{

WaitMessage();// Čekej na zprávu a zatím nic nedělej

}

Takže to bychom měli. Nyní máte ve svých hrách, enginech, demech nebo jakýchkoli programech všechny dveře otevřené pro vytváření velmi efektních blending efektů. S texturovými buffery můžete vytvářet věci jako například real-time plazmu nebo vodu. Vzájemnou kombinací více obrázků (i několikrát za sebou) je možné dosáhnout téměř fotorealistického terénu. Hodně štěstí.

napsal: Andreas Löffler & Rob Fletcher
přeložil: Michal Turek - Woq <WOQ (zavináč) seznam.cz> & Václav Slováček - Wessan <horizont (zavináč) host.sk>

Zdrojové kódy

Lekce 29

<<< Lekce 28 | Lekce 30 >>>