Lekce 19 - Částicové systémy

Chtěli jste už někdy naprogramovat exploze, vodní fontány, planoucí hvězdy a jiné skvělé efekty, nicméně kódování částicových systémů bylo buď příliš těžké nebo jste vůbec nevěděli, jak na to? V této lekci zjistíte, jak vytvořit jednoduchý, ale dobře vypadající částicový systém. Extra přidáme duhové barvy a ovládání klávesnicí. Také se dozvíte, jak pomocí triangle stripu jednoduše vykreslovat velké množství trojúhelníků.

V této lekci vytvoříme téměř komplexní částicový systém. Jakmile jednou pochopíte, jak pracují zvládnete cokoli.

Předem upozorňuji, že dodneška jsem nikdy nic podobného vytvářel. Vždy jsem si myslel, že ty slavné a "komerční" částicové systémy jsou hodně komplexním kusem kódu.

Možná nebudete věřit, když píši, že tento kód je 100% původní. Neměl jsem před sebou žádné technické dokumentace. Onehdy jsem prostě přemýšlel a náhle se mi v hlavě vygenerovala spousta nápadů. Namísto uvažování o částici jako o pixelu přesunujícím se z bodu A do bodu B a dělajícím to či ono jsem každé přiřadil vlastní objekt (strukturu) reagující na prostředí kolem. Zapouzdřuje život, stárnutí, barvu, rychlost, gravitační závislosti a další vlastnosti.

Takže… ačkoli program, podle mě, vypadá perfektně a pracuje přesně, jak jsem chtěl, možná není tou správnou cestou k vytváření částicových systémů. Osobně jsem se nestaral, jak dobře pracuje, ale ve svých projektech jsem ho mohl bez problémů používat. Jestliže jste typem lidí, "šťouralů", kteří potřebují poznat správnou cestu, zkuste strávit hodiny prohledáváním internetu. Toto bylo varování.

Použijeme kód z lekce 1. Symbolická konstanta definuje počet vytvářených částic. Rainbow zapíná/vypíná cyklování mezi duhovými barvami. Sp a rp předcházejí opakování kódu při delším stisku mezerníku a enteru.

#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

#define MAX_PARTICLES 1000// Počet vytvářených částic

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

bool rainbow = true;// Duhový efekt?

bool sp;// Stisknutý mezerník?

bool rp;// Stisknutý enter?

Následují pomocné proměnné. Slowdown kontroluje rychlost pohybu částic (čím vyšší číslo, tím pomaleji se pohybují). Xspeed a yspeed ovlivňují rychlost na jednotlivých osách. Jsou pouze jedním faktorem implementovaným kvůli ovládání klávesnicí. Zoom používáme pro přesun do/ze scény.

float slowdown=2.0f;// Zpomalení částic

float xspeed;// Základní rychlost na ose x

float yspeed;// Základní rychlost na ose y

float zoom=-40.0f;// Zoom

Loop využíváme především jako proměnnou cyklu, ve kterých inicializujeme a vykreslujeme částice. Col vychází ze slova color a značí barvu. Pomocí časovače delay při zapnutém duhovém módu cyklujeme mezi barvami.

Poslední proměnná je klasická textura. Rozhodl jsem se pro ni, protože vypadají mnohem lépe než jednobarevné body. Také si můžete vytvořit texturu ohně, sněhu, jakéhokoli objektu.

GLuint loop;// Řídící proměnná cyklů

GLuint col;// Vybraná barva

GLuint delay;// Zpoždění pro duhový efekt

GLuint texture[1];// Ukládá texturu

Následuje struktura definující vlastnosti částic. Obsahuje spoustu atributů, takže si je popíšeme. Pokud bude active rovno true částice bude aktivní a false logicky značí neaktivnost. V tomto programu se tato vlastnost nepoužívá, ale někdy jindy by mohla být užitečná. Life a fade definují, jak dlouho a jak jasně bude částice zobrazena. Od života (life) budeme odečítat stárnutí (fade). Na začátku je inicializujeme na random, stejně jako téměř všechny ostatní vlastnosti.

typedef struct// Vytvoří stukturu pro částici

{

bool active;// Aktivní?

float life;// Život

float fade;// Rychlost stárnutí

float r;// Červená složka barvy

float g;// Zelená složka barvy

float b;// Modrá složka barvy

float x;// X Pozice

float y;// Y Pozice

float z;// Z Pozice

float xi;// X směr a rychlost

float yi;// Y směr a rychlost

float zi;// Z směr a rychlost

Následující proměnné určují působení gravitace (každá ve své ose). Kladná xg značí působení doprava, záporná doleva. Směry jsou analogické ke směrům souřadnicových os.

float xg;// X gravitace

float yg;// Y gravitace

float zg;// Z gravitace

} particles;// Struktura částice

Dále deklarujeme pole datového typu particles (naše struktura) o velikosti MAX_PARTICLES a jménu particle.

particles particle[MAX_PARTICLES];// Pole částic

Inicializací pole barev si vytvoříme barevnou paletu. Každá z dvanácti položek obsahuje 3 RGB složky v rozmezí od červené do fialové.

static GLfloat colors[12][3]=// Barevná paleta

{

{1.0f,0.5f,0.5f},{1.0f,0.75f,0.5f},{1.0f,1.0f,0.5f},{0.75f,1.0f,0.5f},

{0.5f,1.0f,0.5f},{0.5f,1.0f,0.75f},{0.5f,1.0f,1.0f},{0.5f,0.75f,1.0f},

{0.5f,0.5f,1.0f},{0.75f,0.5f,1.0f},{1.0f,0.5f,1.0f},{1.0f,0.5f,0.75f}

};

Do inicializačního kódu jsem oproti kódu z první lekce přidal loading textury, nastavení blendingu a zadání počátečních hodnot částic.

int InitGL(GLvoid)// Všechna nastavení OpenGL

{

if (!LoadGLTextures())// Nahraje textury

{

return FALSE;

}

glShadeModel(GL_SMOOTH);// Povolíme jemné stínování

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

glClearDepth(1.0f);// Nastavení hloubkového bufferu

glDisable(GL_DEPTH_TEST);// Vypne hloubkové testování

glEnable(GL_BLEND);// Zapne blending

glBlendFunc(GL_SRC_ALPHA,GL_ONE);// Typ blendingu

glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);// Perspektiva

glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);// Jemnost bodů

glEnable(GL_TEXTURE_2D);// Zapne mapování textur

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

Inicializujeme jednotlivé částice. Začneme aktivováním. Pamatujte si, že naktivní nezobrazujeme a neaktualizujeme. Potom je oživíme. Nebyl jsem si jistý, zda je zhasínání (zprůhledňování) částic závislé na zkracování života, správnou cestou. Nicméně pracuje skvěle, tak co :-) Maximální život 1.0f dává nejjasnější vykreslení (viz. blending).

for (loop=0;loop<MAX_PARTICLES;loop++)// Inicializace částic

{

particle[loop].active=true;// Aktivace

particle[loop].life=1.0f;// Oživení

Na randomovou hodnotu nastavíme rychlost stárnutí a postupného zhasínání. Každým vykreslením se život (life) zkracuje o stárnutí (fade). Hodnotu 0 až 99 vydělíme 1000 a tím získáme velmi malé číslo. Aby rychlost stárnutí nikdy nebyla nulová, přičteme 0,003.

particle[loop].fade=float(rand()%100)/1000.0f+0.003f;// Rychlost stárnutí

Nastavíme barvu částic na některou z výše vytvořené palety. Matematika je jednoduchá: vezmeme řídící proměnnou cyklu a vynásobíme ji podílem počtu barev s celkovým počtem částic. Například při prvním průchodu bude loop = 0, po dosazení a výpočtu získáme 0*(12/1000)=0. Při posledním průchodu (loop = počet částic -1 = 999) vyjde 999*(12/1000)=11,988. Protože předáváme int, výsledek se ořeže na 11, což je poslední barva v paletě.

particle[loop].r=colors[loop*(12/MAX_PARTICLES)][0];// Červená

particle[loop].g=colors[loop*(12/MAX_PARTICLES)][1];// Zelená

particle[loop].b=colors[loop*(12/MAX_PARTICLES)][2];// Modrá

Inicializujeme směr a rychlost pohybu částic. Výpočet provedeme opět randomem, který pro počáteční efekt exploze násobíme deseti. Dostaneme kladná nebo záporná čísla určující směr a rychlost pohybu v jednotlivých osách.

particle[loop].xi=float((rand()%50)-26.0f)*10.0f;// Rychlost a směr pohybu na ose x

particle[loop].yi=float((rand()%50)-25.0f)*10.0f;// Rychlost a směr pohybu na ose y

particle[loop].zi=float((rand()%50)-25.0f)*10.0f;// Rychlost a směr pohybu na ose z

Nakonec nastavíme gravitační působení. Většinou gravitace strhává věci dolů, ale ta naše bude moci působit všemi směry. Na začátku ovšem klasicky dolů (yg = - 0,8).

particle[loop].xg=0.0f;// Gravitace na ose x

particle[loop].yg=-0.8f;// Gravitace na ose y

particle[loop].zg=0.0f;// Gravitace na ose z

}

return TRUE;

}

V další funkci se pokusíme o vykreslování, zajistíme působení gravitace ap. Matici ModelView resetujeme pouze jednou a to na začátku. Pozici částic tedy nebudeme určovat složitými posuny a rotacemi, ale pouze souřadnicemi předávanými funkci glVertex3f().

int DrawGLScene(GLvoid)// Vykreslování

{

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

glLoadIdentity();// Reset matice

for (loop=0;loop<MAX_PARTICLES;loop++)// Cyklus prochází každou částici

{

První věcí je zkontrolování, zda je částice aktivní, pokud ne nebudeme ji aktualizovat ani vykreslovat. Nicméně v tomto programu budou aktivní všechny.

if (particle[loop].active)// Pokud je částice aktivní

{

Následující tři proměnné x,y,z jsou spíše pomocné k zpřehlednění kódu. Všimněte si, že k pozici na ose z přičítáme zoom, čímž můžeme jednoduše měnit hloubku v obrazovce.

float x=particle[loop].x;// x pozice

float y=particle[loop].y;// y pozice

float z=particle[loop].z+zoom;// z pozice + zoom

Dále obarvíme částici její barvou. Jako alfa kanál (průhlednost) s výhodou využijeme život, který nabývá hodnot od 1.0f (plný) do 0.0f (smrt). Postupným stárnutím se tedy stává průhlednější až vybledne docela.

// Barva částice

glColor4f(particle[loop].r,particle[loop].g,particle[loop].b,particle[loop].life);

Máme pozici i barvu, takže přejdeme k vykreslení. Původně jsem chtěl použít otexturovaný čtverec, ale pak jsem se rozhodl pro otexturovaný "triangle strip". Většina grafických karet renderuje trojúhelníky mnohem rychleji než čtverce, protože se čtyřúhelníky často konvertují na dva trojúhelníky. K vykreslení klasickým způsobem bychom potřebovali 6 různých bodů, použitím triangle stripu stačí pouze čtyři. Nejprve tedy požádáme OpenGL o vykreslení triangle stripu.

glBegin(GL_TRIANGLE_STRIP);// Vytvoří obdélník pomocí triangle stripu

Triangle strip vykresluje sérii trojúhelníků užitím bodů V0, V1, V2, potom V2, v1, V3 (všimněte si pořadí), dále V2, V3, V4 atd. Tímto pořadím se zajistí, že se všechny vykreslí se stejnou orientací (viz. pořadí zadávání vrcholů), která je důležitá u některých operací, např. cullingu. Aby se něco vykreslilo musí být zadány alespoň tři body. Pro použití triangle stripu existují dva dobré důvody. První: po inicializaci prvního trojúhelníku stačí pro každý nový trojúhelník jenom jeden bod, který bude skombinován s body toho minulého. Druhý: odstraněním části kódu program poběží rychleji. Zdrojový kód bude kratší a přehlednější. Počet vykreslených trojúhelníku vykreslených na monitor bude o dva menší než počet zadaných bodů.

triangle_strip

glTexCoord2d(1,1); glVertex3f(x+0.5f,y+0.5f,z);// Horní pravý

glTexCoord2d(0,1); glVertex3f(x-0.5f,y+0.5f,z);// Horní levý

glTexCoord2d(1,0); glVertex3f(x+0.5f,y-0.5f,z);// Dolní pravý

glTexCoord2d(0,0); glVertex3f(x-0.5f,y-0.5f,z);// Dolní levý

glEnd();// Ukončí triangle strip

Po vykreslení přichází na řadu aktualizace částice. Matematika může vypadat strašně, ale je krásně jednoduchá. Vezmeme pozici na konkrétní ose a přičteme k ní pohyb na této ose vydělený slowdown krát tisíc. Např. pokud bude částice uprostřed obrazovky (0,0,0), pohyb xi 10 a slowdown 1 přesuneme ji o 10/(1*1000) - do bodu 0.01f na ose x. Pokud bychom inkrementovali slowdown na 2 přesuneme se pouze na 0.005f. Toto je také důvod násobení startovní hodnoty desítkou. Body se po spuštění programu pohybují mnohem rychleji, takže vytvoří dojem exploze.

particle[loop].x+=particle[loop].xi/(slowdown*1000);// Pohyb na ose x

particle[loop].y+=particle[loop].yi/(slowdown*1000);// Pohyb na ose y

particle[loop].z+=particle[loop].zi/(slowdown*1000);// Pohyb na ose z

Po spočítání pohybu aplikujeme gravitační působení. Docílíme toho přičtením "gravitační síly" k rychlosti pohybu. Řekněme, že rychlost pohybu je 10 a gravitace o velikosti 1 působí v opačném směru. Každým překreslením se rychlost pohybu dekrementováním zpomalí. Po deseti překresleních částice změní směr.

particle[loop].xi+=particle[loop].xg;// Gravitační působení na ose x

particle[loop].yi+=particle[loop].yg;// Gravitační působení na ose y

particle[loop].zi+=particle[loop].zg;// Gravitační působení na ose z

Snížíme hodnotu života o stárnutí. Kdybychom toto nedělali nikdy by částice neshořela. Každá má nastavenu jinou rychlost stárnutí, tudíž nezemřou ve stejný časový okamžik.

particle[loop].life-=particle[loop].fade;// Sníží život o stárnutí

V této chvíli musíme otestovat, zda je po zestárnutí stále naživu.

if (particle[loop].life<0.0f)// Pokud zemřela

{

Pokud zemřela "reinkarnujeme" ji nastavením plného života a nové náhodné rychlosti stárnutí.

particle[loop].life=1.0f;// Nový život

particle[loop].fade=float(rand()%100)/1000.0f+0.003f;// Náhodné stárnutí

Resetujeme její pozici na střed obrazovky.

particle[loop].x=0.0f;// Vycentrování doprostřed obrazovky

particle[loop].y=0.0f;// Vycentrování doprostřed obrazovky

particle[loop].z=0.0f;// Vycentrování doprostřed obrazovky

Určíme novou rychlost a vlastně i směr. Všimněte si, že jsem zvětšil maximální a minimální rychlost z 50 na 60 oproti funkci InitGL(), ale tentokrát výsledek nenásobím deseti. Už nechceme žádné exploze, ale pomalejší pohyb. Z důvodu ovládání klávesnicí přičítáme k hodnotě i globální rychlost (xspeed, yspeed).

particle[loop].xi=xspeed+float((rand()%60)-32.0f);// Nová rychlost a směr

particle[loop].yi=yspeed+float((rand()%60)-30.0f);// Nová rychlost a směr

particle[loop].zi=float((rand()%60)-30.0f);// Nová rychlost a směr

Částici přiřadíme také novou barvu. Proměnná col ukládá číslo 0 až 11 (12 barev). Pomocí ní vybíráme červenou, zelenou a modrou intenzitu z palety vytvořené na začátku programu.

particle[loop].r=colors[col][0];// Vybere barvu z palety

particle[loop].g=colors[col][1];// Vybere barvu z palety

particle[loop].b=colors[col][2];// Vybere barvu z palety

}

Následující kód aktualizuje působení gravitace. Stisknutím 8 na klávesnici zvětšíme yg (y gravitaci) a částice bude tažena vzhůru. Tato testování jsou vložena do vykreslování z důvodu zjednodušení. Kdyby bylo umístěno někam jinam museli bychom vytvořit nový cyklus dělající úplně stejnou práci. Podobné postupy poskytují skvělé možnosti. Např. se můžete pokusíte o proud vody větrem vystřikující přímo vzhůru. Přidáním gravitace působící dolů vytvoříte fontánu vody.

// Pokud je stisknuta 8 a y gravitace je menší než 1.5

if (keys[VK_NUMPAD8] && (particle[loop].yg<1.5f)) particle[loop].yg+=0.01f;

// Pokud je stisknuta 2 a y gravitace je menší než -1.5

if (keys[VK_NUMPAD2] && (particle[loop].yg>-1.5f)) particle[loop].yg-=0.01f;

// Pokud je stisknuta 6 a x gravitace je menší než 1.5

if (keys[VK_NUMPAD6] && (particle[loop].xg<1.5f)) particle[loop].xg+=0.01f;

// Pokud je stisknuta 4 a x gravitace je menší než -1.5

if (keys[VK_NUMPAD4] && (particle[loop].xg>-1.5f)) particle[loop].xg-=0.01f;

Pro radost připíšeme malou "vychytávku". Můj bratr si myslel, že úvodní výbuch je skvělý efekt. Stisknutím klávesy TAB se všechny částice resetují do centra obrazovky. Rychlost se vynásobí deseti a tím vytvoří explozi.

if (keys[VK_TAB])// Způsobí výbuch

{

particle[loop].x=0.0f;// Vycentrování na střed obrazovky

particle[loop].y=0.0f;// Vycentrování na střed obrazovky

particle[loop].z=0.0f;// Vycentrování na střed obrazovky

particle[loop].xi=float((rand()%50)-26.0f)*10.0f;// Náhodná rychlost

particle[loop].yi=float((rand()%50)-25.0f)*10.0f;// Náhodná rychlost

particle[loop].zi=float((rand()%50)-25.0f)*10.0f;// Náhodná rychlost

}

}

}

return TRUE;// Všechno OK

}

Funkci WinMain napíši celou, protože je v ní celkem dost změn.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

{

MSG msg;

BOOL done=FALSE;

if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?", MB_YESNO|MB_ICONQUESTION) == IDNO)

{

fullscreen=FALSE;

}

if (!CreateGLWindow("NeHe's Particle Tutorial",640,480,16,fullscreen))

{

return 0;

}

Toto je první důležitá změna. Při rozhodnutí uživatele použít fullscreen změníme slowdown ze 2.0f na 1.0f. Tato úprava není až tak důležitá - lze ji vypustit. Slouží k urychlení fullscreenu - moje grafická karta pracuje v okně trochu rychleji. Nevím proč.

if (fullscreen)

{

slowdown=1.0f;

}

while(!done)

{

if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))

{

if (msg.message==WM_QUIT)

{

done=TRUE;

}

else

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

else

{

if ((active && !DrawGLScene()) || keys[VK_ESCAPE])

{

done=TRUE;

}

else

{

DrawGLScene();

SwapBuffers(hDC);

Ošetříme vstup z klávesnice (+, -, PageUp, PageDown)

if (keys[VK_ADD] && (slowdown>1.0f)) slowdown-=0.01f;// Urychlení částic

if (keys[VK_SUBTRACT] && (slowdown<4.0f)) slowdown+=0.01f;// Zpomalení částic

if (keys[VK_PRIOR])zoom+=0.1f;// Přiblížení pohledu

if (keys[VK_NEXT])zoom-=0.1f;// Oddálení pohledu

V následujících řádcích testujeme stisk enteru, abychom zapnuli cyklování barvami.

if (keys[VK_RETURN] && !rp)// Stisk enteru

{

rp=true;// Nastaví příznak

rainbow = !rainbow;// Zapne/vypne duhový efekt

}

if (!keys[VK_RETURN]) rp=false;// Po uvolnění vypne příznak

Operace při tisku mezerníku mohou být trochu matoucí. Stejně jako při enteru otestujeme, zda je zapnut duhový efekt. Pokud je, podíváme e jestli je hodnota počítadla counter větší než 25. Používá se ke změně barvy celých skupin částic. Pokud by se změnila barva při každém framu všechny částice by se obarvily jinak. Vytvořením zpoždění stihneme obarvit stejnou barvou více částic.

if ((keys[' '] && !sp) || (rainbow && (delay>25)))// Mezerník nebo duhový efekt

{

Pokud je stisknut mezerník vypne se duhový efekt. Kdybychom ho nedeaktivovali, tak by se dokola měnily barvy dokud by nebyl stisknut enter. Dává smysl, že pokud člověk bouchá do mezerníku namísto do enteru, tak chce barvami procházet sám.

if (keys[' '])rainbow=false;// Pokud je stisknut vypne se duhový mód

Pokud je mezerník stisknut nebo je zapnut duhový mód a zpoždění je větší než 25, přiřazením true do sp oznámíme počítači, že byl stisknut. Poté nastavíme delay na nulu, takže se může znovu počítat do 25. Nakonec inkrementujeme barvu na další v paletě.

sp=true;// Oznámí programu, že byl stisknut mezerník

delay=0;// Resetuje zpoždění duhových barev

col++;// Změní barvu částice

Protože máme pouze 12 barev musíme zamezit přetečení pole a následné zhroucení programu.

if (col>11) col=0;// Proti přetečení pole

}

if (!keys[' ']) sp=false;// Uvolnění mezerníku

Definujeme ovládání částic. Na začátku programu jsme deklarovali dvě proměnné rychlosti (xspeed, yspeed). Když částice vyhoří (zemře) přiřadíme jí novou rychlost závisející na těchto proměnných. Můžeme ovlivňovat jejich směr. Řádek dole testuje stisk šipky nahoru. V takovém případě yspeed inkrementujeme. Částice se bude pohybovat nahoru. Max rychlost je omezena na 200, větší už nevypadá dobře. Analogickým principem pracuje i ovládání ostatními šipkami.

if (keys[VK_UP] && (yspeed<200)) yspeed+=1.0f;// Šipka nahoru

if (keys[VK_DOWN] && (yspeed>-200)) yspeed-=1.0f;// Šipka dolů

if (keys[VK_RIGHT] && (xspeed<200)) xspeed+=1.0f;// Šipka doprava

if (keys[VK_LEFT] && (xspeed>-200)) xspeed-=1.0f;// Šipka doleva

Zbývá inkrementovat zpoždění delay, použité pro rychlost změn barev. Ostatní kód znáte z minulých lekcí.

delay++;// Inkrementace zpoždění duhového efektu

if (keys[VK_F1])

{

keys[VK_F1]=FALSE;

KillGLWindow();

fullscreen = !fullscreen;

if (!CreateGLWindow("NeHe's Particle Tutorial",640,480,16,fullscreen))

{

return 0;

}

}

}

}

}

KillGLWindow();

return (msg.wParam);

}

V této lekci jsem se pokoušel o vysvětlení jednoduchého, ale působivého částicového systému. Jeho nejvýhodnější použití spočívá ve vytvoření efektů typu ohně, vody, sněhu, explozí, hvězd a spousty dalších. Jednoduchým modifikováním kódu lze snadno naprogramovat zcela nový efekt.

Děkuji Richardu Nutmanovi za upozornění, že by bylo výhodnější umisťovat částice použitím glVertex3f() namísto resetováním matice a složitými translacemi. Obě metody vypadají stejně, ale jeho verze snižuje zatížení počítače. Program běží rychleji.

napsal: Jeff Molofee - NeHe <nehe (zavináč) connect.ab.ca>
přeložil: Michal Turek - Woq <WOQ (zavináč) seznam.cz>

Zdrojové kódy

Lekce 19

<<< Lekce 18 | Lekce 20 >>>