╚ernΘ okraje obrßzk∙ jsme dosud o°ezßvali blendingem. AΦkoli je tato metoda efektivnφ, ne v₧dy transparentnφ objekty vypadajφ dob°e. Modelovß situace: vytvß°φme hru a pot°ebujeme celistv² text nebo zak°iven² ovlßdacφ panel, ale p°i blendingu scΘna prosvφtß. NejlepÜφm °eÜenφm je maskovßnφ obrßzk∙.
Bitmapov² formßt obrßzku je podporovßn ka₧d²m poΦφtaΦem a ka₧d²m operaΦnφm systΘmem. Nejen, ₧e se s nimi snadno pracuje, ale velmi snadno se nahrßvajφ a konvertujφ na textury. K o°ezßnφ Φern²ch okraj∙ textu a obrßzk∙ jsme s v²hodou pou₧φvali blending, ale ne v₧dy v²sledek vypadal dob°e. P°i spritovΘ animaci ve h°e nechcete, aby postavou prosvφtalo pozadφ. Podobn∞ i text by m∞l b²t pevn² a snadno Φiteln². V takov²ch situacφch se s v²hodou vyu₧φvß maskovßnφ. Mß dv∞ fßze. V prvnφ do scΘny umφstφme Φernobφlou texturu, ve druhΘ na stejnΘ mφsto vykreslφme hlavnφ texturu. Pou₧it² typ blendingu zajistφ, ₧e tam, kde se v masce (prvnφ obrßzek) vyskytovala bφlß barva z∙stane p∙vodnφ scΘna. Textura se nepr∙hledn∞ vykreslφ na Φernou barvu.
#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
Masking uklßdß p°φznak zapnutΘho/vypnutΘho maskovßnφ a podle scene se rozhodujeme, zda vykreslujeme prvnφ nebo druhou verzi scΘny. Loop je °φdφcφ prom∞nnß cykl∙, roll pou₧ijeme pro rolovßnφ textur a rotaci objektu p°i zapnutΘ druhΘ scΘn∞.
bool masking=TRUE;// Maskovßnφ on/off
bool mp;// Stisknuto M?
bool sp;// Stisknut mezernφk?
bool scene;// Kterß scΘna se mß kreslit
GLuint texture[5];// Uklßdß 5 textur
GLuint loop;// ╪φdφcφ prom∞nnß cykl∙
GLfloat roll;// Rolovßnφ textur
Generovßnφ textur je ve svΘm principu ·pln∞ stejnΘ jako ve vÜech minul²ch lekcφch, ale velmi p°ehledn∞ demonstruje nahrßvßnφ vφce textur najednou. TΘm∞° v₧dy jsme pou₧φvali pouze jednu. Deklarujeme pole ukazatel∙ na p∞t bitmap, vynulujeme je a nahrajeme do nich obrßzky, kterΘ vzßp∞tφ zm∞nφme na textury.
int LoadGLTextures()// Nahraje bitmapu a konvertuje na texturu
{
int Status=FALSE;
AUX_RGBImageRec *TextureImage[5];// Alokuje mφsto pro bitmapy
memset(TextureImage,0,sizeof(void *)*5);
if ((TextureImage[0]=LoadBMP("Data/logo.bmp")) &&// Logo
(TextureImage[1]=LoadBMP("Data/mask1.bmp")) &&// Prvnφ maska
(TextureImage[2]=LoadBMP("Data/image1.bmp")) &&// Prvnφ obrßzek
(TextureImage[3]=LoadBMP("Data/mask2.bmp")) &&// Druhß maska
(TextureImage[4]=LoadBMP("Data/image2.bmp")))// Druh² obrßzek
{
Status=TRUE;
glGenTextures(5, &texture[0]);
for (loop=0; loop<5; loop++)// Generuje jednotlivΘ textury
{
glBindTexture(GL_TEXTURE_2D, texture[loop]);
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);
}
}
for (loop=0; loop<5; loop++)
{
if (TextureImage[loop])
{
if (TextureImage[loop]->data)
{
free(TextureImage[loop]->data);
}
free(TextureImage[loop]);
}
}
return Status;
}
Z inicializace z∙stala doslova kostra.
int InitGL(GLvoid)// VÜechno nastavenφ OpenGL
{
if (!LoadGLTextures())// Nahraje textury
{
return FALSE;
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);// ╚ernΘ pozadφ
glClearDepth(1.0);// Povolφ mazßnφ Depth Bufferu
glEnable(GL_DEPTH_TEST);// Zapne hloubkovΘ testovßnφ
glShadeModel(GL_SMOOTH);// JemnΘ stφnovßnφ
glEnable(GL_TEXTURE_2D);// Zapne mapovßnφ textur
return TRUE;
}
P°i vykreslovßnφ zaΦneme jako obyΦejn∞ mazßnφm buffer∙, resetem matice a translacφ do obrazovky.
int DrawGLScene(GLvoid)// Vykreslovßnφ
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Sma₧e obrazovku a hloubkov² buffer
glLoadIdentity();// Reset matice
glTranslatef(0.0f,0.0f,-2.0f);// P°esun do obrazovky
Zvolφme texturu loga a namapujeme ji na obdΘlnφk. Koordinßty vypadajφ n∞jak divn∞. Namφsto obvykl²ch hodnot 0 a₧ 1 tentokrßt zadßme Φφsla 0 a 3. P°edßnφm trojky oznßmφme, ₧e chceme namapovat texturu na polygon t°ikrßt. Pro vysv∞tlenφ m∞ napadß vlastnost vedle sebe p°i umφst∞nφ malΘho obrßzku na plochu OS. Trojku zadßvßme do Üφ°ky i do v²Üky, tudφ₧ se na polygon rovnom∞rn∞ namapuje celkem dev∞t stejn²ch obrßzk∙. Ke koordinßt∙m takΘ p°iΦφtßme (defakto odeΦφtßme) prom∞nnou roll, kterou na konci funkce inkrementujeme. Vznikß dojem, ₧e vykreslovanß hladina scΘny roluje, ale v programu se vlastn∞ m∞nφ pouze texturovΘ koordinßty. Rolovßnφ m∙₧e b²t pou₧ito pro r∙znΘ efekty. Nap°φklad pohybujφcφ se mraky nebo text lΘtajφcφ po objektu.
glBindTexture(GL_TEXTURE_2D, texture[0]);// V²b∞r textury loga
glBegin(GL_QUADS);// Kreslenφ obdΘlnφk∙
glTexCoord2f(0.0f, -roll+0.0f); glVertex3f(-1.1f,-1.1f, 0.0f);
glTexCoord2f(3.0f, -roll+0.0f); glVertex3f( 1.1f,-1.1f, 0.0f);
glTexCoord2f(3.0f, -roll+3.0f); glVertex3f( 1.1f, 1.1f, 0.0f);
glTexCoord2f(0.0f, -roll+3.0f); glVertex3f(-1.1f, 1.1f, 0.0f);
glEnd();// Konec kreslenφ
Zapneme blending. Aby efekt pracoval musφme vypnout testovßnφ hloubky. Kdyby se nevypnulo nejv∞tÜφ pravd∞podobnostφ by nic nebylo vid∞t.
glEnable(GL_BLEND);// Zapne blending
glDisable(GL_DEPTH_TEST);// Vypne testovßnφ hloubky
Podle hodnoty prom∞nnΘ se rozhodneme, zda budeme obrßzek maskovat nebo pou₧ijeme mnohokrßt vyzkouÜen² blending. Maska je Φernobφlß kopie textury, kterou chceme vykreslit. BφlΘ oblasti masky budou pr∙hlednΘ, ΦernΘ nebudou. Pod bφl²mi sekcemi z∙stane scΘna nezm∞n∞na.
if (masking)// Je zapnutΘ maskovßnφ?
{
glBlendFunc(GL_DST_COLOR,GL_ZERO);// Blending barvy obrazu pomocφ nuly (Φernß)
}
Pokud bude scene true vykreslφme duhou, jinak prvnφ scΘnu.
if (scene)// Vykreslujeme druhou scΘnu?
{
Nechceme objekty p°φliÜ velkΘ, tak₧e se p°esuneme hloub∞ji do obrazovky. Provedeme rotaci na ose z o 0░ a₧ 360░ podle prom∞nnΘ roll.
glTranslatef(0.0f,0.0f,-1.0f);// P°esun o jednotku do obrazovky
glRotatef(roll*360,0.0f,0.0f,1.0f);// Rotace na ose z
Pokud je zapnutΘ maskovßnφ, vykreslφme nejd°φve masku a potom objekt. P°i vypnutΘm pouze objekt.
if (masking)// Je zapnutΘ maskovßnφ?
{
Nastavenφ blendingu pro masku jsme provedli d°φve. Zvolφme texturu masky a namapujeme ji na obdΘlnφk. Po vykreslenφ se na scΘn∞ objevφ Φernß mφsta odpovφdajφcφ masce.
glBindTexture(GL_TEXTURE_2D, texture[3]);// V²b∞r textury druhΘ masky
glBegin(GL_QUADS);// ZaΦßtek kreslenφ obdΘlnφk∙
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f,-1.1f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f,-1.1f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f);
glEnd();// Konec kreslenφ
}
Znovu zm∞nφme m≤d blendingu. Oznßmφme tφm, ₧e chceme vykreslit vÜechny Φßsti barevnΘ textury, kterΘ NEJSOU ΦernΘ. Proto₧e je obrßzek barevnou kopiφ masky, tak se vykreslφ jen mφsta nad Φern²mi Φßstmi masky. Proto₧e je maska Φernß, nic ze scΘny nebude prosvφtat skrz textury. Vznikne dojem pevn∞ vypadajφcφho obrßzku. Zvolφme barevnou texturu. PotΘ ji vykreslφme se stejn²mi sou°adnicemi bod∙ v prostoru a stejn²mi texturov²mi koordinßty jako masku. Kdybychom masku nevykreslily, obrßzek by se zkopφroval do scΘny, ale dφky blendingu by byl pr∙hledn². Objekty za nφm by prosvφtaly.
glBlendFunc(GL_ONE, GL_ONE);// Pro druh² barevn² obrßzek
glBindTexture(GL_TEXTURE_2D, texture[4]);// Zvolφ druh² obrßzek
glBegin(GL_QUADS);// ZaΦßtek kreslenφ obdΘlnφk∙
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f,-1.1f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f,-1.1f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f);
glEnd();// Konec kreslenφ
}
P°i hodnot∞ FALSE ulo₧enΘ ve scene se vykreslφ prvnφ scΘna. Op∞t v∞tvφme program podle maskovßnφ. P°i zapnutΘm vykreslφme masku pro scΘnu jedna. Textura roluje zprava doleva (roll p°iΦφtßme k horizontßlnφm koordinßt∙m). Chceme, aby textura zaplnila celou scΘnu, tak₧e neprovßdφme translaci do obrazovky.
else// Vykreslenφ prvnφ scΘny
{
if (masking)// Je zapnutΘ maskovßnφ?
{
glBindTexture(GL_TEXTURE_2D, texture[1]);// V²b∞r textury prvnφ masky
glBegin(GL_QUADS);// ZaΦßtek kreslenφ obdΘlnφk∙
glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f,-1.1f, 0.0f);
glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f,-1.1f, 0.0f);
glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f, 1.1f, 0.0f);
glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f, 1.1f, 0.0f);
glEnd();// Konec kreslenφ
}
Blending nastavφme stejn∞ jako minule. Vybereme texturu scΘny jedna a vykreslφme ji na stejnΘ mφsto jako masku.
glBlendFunc(GL_ONE, GL_ONE);// Pro prvnφ barevn² obrßzek
glBindTexture(GL_TEXTURE_2D, texture[2]);// Zvolφ prvnφ obrßzek
glBegin(GL_QUADS);// ZaΦßtek kreslenφ obdΘlnφk∙
glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f,-1.1f, 0.0f);
glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f,-1.1f, 0.0f);
glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f, 1.1f, 0.0f);
glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f, 1.1f, 0.0f);
glEnd();// Konec kreslenφ
}
Zapneme testovßnφ hloubky a vypneme blending. V malΘm programu je to v∞c celkem zbyteΦnß, ale u rozsßhlejÜφch projekt∙ n∞kdy nevφte, co zrovna mßte zapnutΘ nebo vypnutΘ. Tyto chyby se obtφ₧n∞ hledajφ a kradou Φas. Po urΦitΘ dob∞ ztrßcφte orientaci, k≤d se stßvß slo₧it∞jÜφm - preventivnφ opat°enφ.
glEnable(GL_DEPTH_TEST);// Zapne testovßnφ hloubky
glDisable(GL_BLEND);// Vypne blending
Aby se scΘna dynamicky pohybovala musφme inkrementovat roll.
roll+=0.002f;// Inkrementace roll
if (roll>1.0f)// Je v∞tÜφ ne₧ jedna?
{
roll-=1.0f;// OdeΦte jedna
}
return TRUE;
}
OÜet°φme vstup z klßvesnice. Po stisku mezernφku zm∞nφme vykreslovanou scΘnu.
// Funkce WinMain()
if (keys[' '] && !sp)// Mezernφk - zm∞na scΘny
{
sp=TRUE;
scene=!scene;
}
if (!keys[' '])// Uvoln∞nφ mezernφku
{
sp=FALSE;
}
Stiskem klßvesy M zapneme, pop°. vypneme maskovßnφ.
if (keys['M'] && !mp)// Klßvesa M - zapne/vypne maskovßnφ
{
mp=TRUE;
masking=!masking;
}
if (!keys['M'])// Uvoln∞nφ klßvesy M
{
mp=FALSE;
}
Vytvo°enφ masky nenφ p°φliÜ t∞₧kΘ. Pokud mßte originßlnφ obrßzek ji₧ nakreslen², otev°ete ho v n∞jakΘm grafickΘm editoru a transformujte ho do ÜedΘ palety barev. Po tΘto operaci zvyÜte kontrast, tak₧e se ÜedΘ pixely ztmavφ na ΦernΘ. Zkuste takΘ snφ₧it jas ap. Je d∙le₧itΘ, aby bφlß byla opravdu bφlß a Φernß Φist∞ Φernß. Mßte-li pochyby p°eve∩te obrßzek do ΦernobφlΘho re₧imu (2 barvy). Pokud by v masce z∙staly ÜedΘ pixely byly by pr∙hlednΘ. Je takΘ d∙le₧itΘ, aby barevn² obrßzek m∞l ΦernΘ pozadφ a masku bφlou. Otestujte si barvy masky kapßtkem (v∞tÜinou b²vajφ chyby na rozhranφ). Bφlß je v RGB 255 255 255 (FF FF FF), Φernß 0 0 0.
Lze zjistit barvu pixel∙ p°i nahrßvßnφ bitmapy. Chcete-li pixel pr∙hledn² m∙₧ete mu p°i°adit alfu rovnou nule. VÜem ostatnφm barvßm 255. Tato metoda takΘ pracuje spolehliv∞, ale vy₧aduje extra k≤d tΘto lekce. ( Chci poukßzat, ₧e k v²sledku existuje vφce cest - vÜechny mohou b²t sprßvnΘ.
NauΦili jsme se, jak vykreslit Φßst textury bez pou₧itφ alfa kanßlu. Klasick² blending, kter² znßme, nevypadal nejlΘpe a textury s alfa kanßlem pot°ebujφ obrßzky, kterΘ alfa kanßl podporujφ. Bitmapy jsou vhodnΘ p°edevÜφm dφky snadnΘ prßci, ale majφ ji₧ zmφn∞nΘ omezenφ. Tento program ukßzal, jak obejφt nedostatky bitmapov²ch obrßzk∙ a vykreslovßnφ jednΘ textury vφcekrßt na jeden obdΘlnφk. VÜe jsme rozÜφ°ili rolovßnφm textur po scΘn∞.
D∞kuji Robu Santovi za ukßzkov² k≤d, ve kterΘm mi poprvΘ p°edstavil trik mapovßnφ dvou textur. NicmΘn∞ ani tato cesta nenφ ·pln∞ dokonalß. Aby efekt pracoval, pot°ebujete dva pr∙chody - dvakrßt vykreslujete jeden objekt. Z toho plyne, ₧e vykreslovßnφ tφmto zp∙sobem je dvakrßt pomalejÜφ. NicmΘn∞... co se dß d∞lat?
napsal: Jeff Molofee - NeHe
p°elo₧il: Michal Turek - Woq