OpenGL -Jednoduché efekty 1 - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



C/C++

OpenGL -Jednoduché efekty 1

open gl

24. ledna 2002, 00.00 | Tak a máme tu další díl seriálu věnovanému OpenGL. Dnes se pokusíme z dosavadních znalostí něco malého vykouzlit. Vyzkoušíme si jednoduché efekty za pomocí blendingu,
jedné textury a hromady polygonů.

Dnes uvidíte, že i malé laciné efektíky mohou vypadat dobře, když se správně naaranžují. Pokusíme se využít jedinou texturu, pro vytvoření jednoduchého efektu. Tak například na obrázku, kde vidíte galaxii (aspoň mi tak připadá). Není to nic jiného než asi 50 polygonů stočených do šroubovice, každý polygon je pokryt texturou hvězdy a díky blendingu je každá hvězda jinak obarvená. Každá má nastavenou průhlednost.

Takže se vám pokusím popsat algoritmus, který vytvoří galaxii z hvězd.

Začneme tak, že pro popis každé hvězdy nám bude sloužit struktura stars.

// struktura popisující každou hvězdičku
typedef struct		
{
int r, g, b; // barva hvězdy
GLfloat dist, // vzdálenost od středu
angle; // úhel otočení
}
stars; stars star[num]; // zhruba 50 hvězd

V této struktuře uložíme informaci o barvě hvězdy, vzdálenost od středu zavinutí a úhel pootočení každé hvězdy. Teď už můžeme popsat každou hvězdu zvlášť. Myslím, že postačí asi 50 hvězd.

Další část programu je naprosto banální a stejná s předchozími díly. Nemusím vám připomínat načtení textury do paměti pod ukazatel texture[0].

Přeskočíme do funkce InitGL, zde nastartujeme výchozí stav, kdy poskládáme všech 50 hvězd za sebe do řádku.

for (loop=0; loop<num; loop++)
{
star[loop].angle=0.0f;
star[loop].dist=(float(loop)/num)*5.0f;
star[loop].r=rand()%256;
star[loop].g=rand()%256;
star[loop].b=rand()%256;
}

Výchozí stav je potřeba k nastartování celého programu, to znamená, že zvolíme náhodně barvu hvězdy r,g,b. Úhel otočení ve výchozím stavu je 0. Další důležitá věc je dist (distance) , vzdálenost každé hvězdy od středu. Teď musíme zajistit aby hvězdy byly průsvitné a toho dosáhneme aktivací blendingu.

glBlendFunc(GL_SRC_ALPHA,GL_ONE);
glEnable(GL_BLEND);

Teď přejdeme k funkci DrawScene. Nyní potřebujeme zajistit, aby jednotlivé hvězdy rotovali kolem středu a navíc se každá hvězda bude přibližovat ke středu. Při každém průchodu smyčkou poslední hvězda zmizí a na první pozici se objeví nová hvězda, co nejdále od středu. Tím jak algoritmus postupně pokračuje vzniká šroubovice.

for (loop=0; loop<num; loop++)
{
glLoadIdentity();
// posune celou scénu do pozadí glTranslatef(0.0f,0.0f,zoom);
// rotuje celou scénu o úhel tilt glRotatef(tilt,1.0f,0.0f,0.0f);
// otočí hvězdu o angle v ose y glRotatef(star[loop].angle,0.0f,1.0f,0.0f);
// posune hvězdu na pozici dist glTranslatef(star[loop].dist,0.0f,0.0f);
// zpětná rotace zajistí, aby jsme se dívali vždy kolmo na hvězdu glRotatef(-star[loop].angle,0.0f,1.0f,0.0f);
glRotatef(-tilt,1.0f,0.0f,0.0f);
 gllRotatef(spin,0.0f,0.0f,1.0f);
//nastaví barvu hvězdy + průhlednost 
 glColor4ub(star[loop].r,star[loop].g,star[loop].b,255);
// polygon s texturou
 glBegin(GL_QUADS);
  glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);
  glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f);
  glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);
  glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
 glEnd();
 spin+=0.01f;
// přepočít pozici hvezdy (uhel - angle, dist - vzdlenost)
 star[loop].angle+=float(loop)/num;
 star[loop].dist-=0.01f;
// pokud je hvězda ve středu, posune se do dist=5
 if (star[loop].dist<0.0f)
 {
  star[loop].dist+=5.0f;
// zvolíme novou barvu r,g,b
  star[loop].r=rand()%256;
  star[loop].g=rand()%256;
  star[loop].b=rand()%256;
  }
 }

Tato smyčka má na starost přepočítávat polohy všech hvězd. Cyklus prochází každou hvězdu zvlášť a přepočítává nové pozice. A jelikož se to špatně vysvětluje slovně pokuste se zorientovat v komentáři k výše uvedenému kódu, je myslím dostatečně okomentován.

Do programu jsem zabudoval možnost měnit náhled na scénu. Pomocí kláves page up a page down můžete přibližovat a vzdalovat scénu. Scéna se natáčí pomocí horní a dolní šipky. A když dobře natočíte scénu a vhodně ji přiblížíte, nevypadá to špatně.

Program je ke stažení ZDE (402 KB).

Oheň

V další části článku se budeme zabývat hořením ohně. Oheň je součástí našeho života a setkáme se s ním na každém kroku: u plynového sporáku, u táboráku, cigaretu si bez ohně také nezapálíte . A tím docházím, k jednomu otřesnému závěru, zkrátka oheň je nezkrotný živel a může napáchat spoustu škody, ale i spoustu dobra.

Já vás naučím, jak založit malý táboráček na obrazovce vašeho počítače, aniž by jste použili sirky a aniž by jste mohli vyhořet.

Již dlouhou dobu jsem se chystal napsat článek o elektronickém ohni, jak ho zapálit (a přitom se nepopálit), ale dlouho jsem váhal, protože na internetu je několik výborných článků, které algoritmus hoření ohně vysvětlují. Asi nejlepší algoritmus hoření ohně popisuje na svých stránkách Radovan Urban.

Přesto si myslím, že má smysl toto téma dále rozvádět, protože zmiňované články se nepokoušeli tvořit oheň v OpenGL. Většina algoritmů tohoto druhu byla psána v assembleru (kvůli rychlosti) a využívala pouze 256 barev.

Jelikož byl algoritmus již tolikrát popsán, připomenu ho jen stručně.

Máme definované dvourozměrné pole, které můžeme pojmenovat barva. Pro toto pole zvolíme vhodnou velikost, například 30*50. Před zapálením ohně :) vynulujeme celé pole barva.
Hoření ohně je neustále se opakující cyklus, ale aby vůbec něco hořelo budeme potřebovat palivo (obdobně jako u skutečného ohně). To nechám na vás, jestli zvolíte dřevo z lesa nebo brikety. Já bych doporučoval spíše 1 pixel. Takže zpět k poli barva. Toto pole si představte jako sloupec plamene. Jedna buňka v poli představuje jeden pixel ohně. A každý tento jeden bod v plameni má svou intenzitu (0 - není oheň, 255 - hořííí). A aby oheň hořel, musíme do něj přikládat palivo, tak do pole barva přiložíme na poslední řádek (libovolně zde umístíme body o intenzitě 255). Nezaplníme však tento řádek úplně, protože potřebujeme určitou nahodilost. V každém kroku hoření vygenerujeme nový řádek s palivem. A teď si musíme něco povědět o samotném hoření ohně (paliva), které již máme připravené. V každém kroku hoření ohně musíme projít celé pole barva od vrchu dolů a přepočítáme každý pixel ohně. Přepočítáme jej podle tzv. průměrných barev. Volíme průměr ze 4 bodů z pole barva.

přepočet každého pixelu (procházíme pixely od vrchu dolů, zleva doprava):

barva(x,y) = (barva(x-1, y+1)+ barva(x, y+1)+barva(x+1, y+1)+barva(x, y+2)/4)

Pokud se chcete podívat na můj pokus, dál jsem vám ho ke stažení ZDE (300 KB). Je dělaný v DelphiX.

Podobných příkladů na toto téma najdete na internetu desítky, avšak většina si vystačí s interpretací ohně přes pixely. Já jsem dostal nápad, jakým způsobem tento bezvadný algoritmus nepatrně vylepšit - předělat do OpenGL.

Oheň v OpenGL

Ještě si vzpomínáte na galaxii, o které jsem psal o něco výše (aby ne). Co takhle využít stejných hvězd k tomu, abychom rozhořeli malý plamínek, jaký vidíte na obrázku.

Místo pole barva, jsem si pole na generování ohně pojmenoval ohen, a ohen1. Protože v příkladu, který se vám snažím představit, jsou pro lepší výsledek použity 2 plameny. Ale zde v textu vám přiblížím pouze jeden, protože u druhého plamene je všechno stejné.

1) Generování paliva

for (int t = 0; t < 20; t++) {
nahod = random(100);
if (nahod < 35)
ohen[48][t] = 255;
else
ohen[48][t] = 0;
}

Tento kód vygeneruje na předposledním řádku v poli ohen náhodně palivo. Podle toho, jakým způsobem generujeme tento řádek, zajistíme u ohně jeho proměnlivost (mihotání plamene).

2) Hoření ohně

for (y = 1; y < 49; y++) {
for (x = 1; x < 15; x++) {
barva = (ohen[y+1][x-1]+ohen[y+1][x]+ohen[y+1][x+1]+ohen[y][x])/4;
 if (barva > 0)
  ohen[y][x] = barva-1;
 }
}

Procházíme jednotlivé řádky našeho myšleného ohně v poli ohen a přepočítáváme hodnoty. Vypočítáváme průměr čtyř bodů a od výsledku odečteme 1 (to sice nemusíme, ale oheň je hezčí). Odečtení 1 snižuje plamen, proto odečítat větší hodnotu nedoporučuji.

3) Vykreslení

glLoadIdentity();
glTranslatef(-2.0f,+4.0f,2*zoom);
glBindTexture(GL_TEXTURE_2D, texture[0]);
for (int cy = 0; cy < 25; cy++) {
for (int cx = 0; cx < 15; cx++) {
if (ohen[cy][cx] < 0.25)
continue; glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f+0.25*cx,-1.0f-0.25*cy, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f+0.25*cx,-1.0f-0.25*cy, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f+0.25*cx, 1.0f-0.25*cy, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f+0.25*cx, 1.0f-0.25*cy, 0.0f);
glEnd();
}
}

A tady je ten slíbený rozdíl oproti běžným ohňům vytvořených z pixelů. Náš oheň se bude skládat z obdélníků, které jsou přes sebe naskládány v řadě za sebou a také pod sebou. Každý obdelník je pokrytý texturou hvězda z minulého příkladu. A pro každý obdélník se definuje také barva podobně jako v předešlém příkladu s tím rozdílem, že si vystačíme s barevnými složkami r, g (nebo jenom r) a tím dosáhneme odstínu žluté barvy. Všechny obdélníky mají nastavenou průhlednost.

Pro mou kartu GeoForce2 nebyl program žádným oříškem s byl plynulý v rozlišení 640*480. Malého zpomalení jsem se dočkal až při 1024*768. Musel jsem proto do programu přidat řádek, který odstraní vykreslování skoro černých plamínků.

if (ohen[cy][cx] < 0.25)
continue;

Program doznal poměrně citelného zrychlení, při 1024*768 už byl naprosto plynulý.

Ještě bych se závěrem zmínil o tom, že celý plamen je asi 200x předpočítaný ve funkci glInit a uložený do display listů. Jednotlivé snímky ohně jsou vyvolávány přes glCallList.

Poznámka:
Čím pošlete oheň dál do pozadí, tím bude rychlejší (přes proměnnou zoom).

Tento oheň si můžete stáhnout ZDE (230 KB).

Doufám, že se vám dnešní uvedení do problematiky efektů líbilo, protože už mám pro vás připravené mnohem lepší lahůdky.

 

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » C/C++  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: