Wizualny OpenGL

Efekty pogodowe to niezbędny element grafiki w dzisiejszych grach komputerowych. Przedstawiamy prosty sposób generowania efektu padającego deszczu za pomocą Microsoft Visual C++ i biblioteki OpenGL.

Efekty pogodowe to niezbędny element grafiki w dzisiejszych grach komputerowych. Przedstawiamy prosty sposób generowania efektu padającego deszczu za pomocą Microsoft Visual C++ i biblioteki OpenGL.

Efekty pogodowe, stosowane w większości gier FPP, zwiększają realizm grafiki.

Efekty pogodowe, stosowane w większości gier FPP, zwiększają realizm grafiki.

Zjawiska pogodowe znakomicie zwiększają realizm grafiki w grach FPP. Deszcz, śnieżyca, efekty działania wiatru, błyskawice i mgły potrafią zmienić na przykład nudny, pusty teren w pełne realizmu pustkowia gry RPG. Do scenerii wygenerowanej na podstawie poprzednich warsztatów dodamy efekt deszczu. Można wyobrazić sobie sposób modelowania padającego deszczu jako ogromną liczbę małych kul, poruszających się z góry na dół obszaru gry. Oczywiście takie rozwiązanie problemu jest całkowicie nieefektywne. Upraszczając nieco, możemy zamiast kul użyć po prostu odcinków symulujących ślad ruchu kropli. Nie trzeba także rysować wszystkich kropli deszczu pojawiających się na obszarze gry. W zupełności wystarczy narysować je wyłącznie w najbliższym otoczeniu obserwatora-gracza. Deszcz na dalszym planie symulowany będzie przez efekt mgły, o którym pisaliśmy w poprzedniej części warsztatu.

Program, który obecnie przedstawiamy, został przygotowany w środowisku Microsoft Visual C++ 6.0. Aby zrozumieć treść cytowanych dalej fragmentów kodu źródłowego, trzeba przynajmniej pobieżnie znać język C++ oraz mieć podstawowe wiadomości o OpenGL, nabyte np. w poprzednich warsztatach naszego cyklu.

Zastanówmy się, jakie właściwości ma każda kropla deszczu. Z pewnością należy określić jej prędkość, położenie i kolor. Dodatkowo należy określić czas życia danej kropli, tzn. jak długo będzie widoczna w obszarze gry, oraz maksymalną liczbę wszystkich kropli. Ten ostatni parametr jest niezbędny, żeby nadmiernie nie obciążać systemu generowaniem deszczu. System generowania i rysowania kropli powinien brać także pod uwagę efekt działania wiatru oraz losowe zaburzenia ruchu każdej kropli. W tymi miejscu należy zwrócić uwagę na pewien istotny szczegół. Krople deszczu, które pojawią się w grze, nie muszą się zachowywać zgodnie z prawami fizyki, a tylko sprawiać wrażenie, że zachowują się naturalnie. Dokładne wymodelowanie prawdziwego zachowania kropel wody poddanych działaniu grawitacji i wiatru byłoby bardzo kosztowne obliczeniowo, a efekt końcowy nie różniłby się znacznie od naszego, bardzo uproszczonego, systemu deszczu.

Obiekty złożone z wielu poruszających się drobnych elementów, takie jak deszcz, śnieg, dym, eksplozje, nazywane są często efektami cząsteczkowymi (particle effects), a narzędzia służące do ich tworzenia znaleźć można w każdym szanującym się silniku graficznym. Większość efektów cząsteczkowych oparta jest na podobnym pomyśle: generowaniu wielu małych obiektów, mających charakterystyczne cechy (prędkość, czas życia, masa, kolor itd.). Efekt cząsteczkowy ma też punkt startowy - tzw. źródło emisji. Określone są również ograniczenia ruchu cząstek - kropli deszczu, płatków śniegu, iskier. W naszym przypadku ten tzw. obszar emisji został określony przez promień i wysokość.

Efekt deszczu w działaniu - strugi zachowują się naturalnie, ale warto poprawić ich wygląd.

Efekt deszczu w działaniu - strugi zachowują się naturalnie, ale warto poprawić ich wygląd.

Nasz przykładowy efekt cząsteczkowy - deszcz - można łatwo przerobić tak, żeby służył na przykład do wytworzenia efektu padającego śniegu. Wtedy zamiast linii powinny być rysowane małe czworokąty pokryte teksturą płatka śniegu, a ruch cząsteczek należałoby nieco zmodyfikować, symulując np. wirowanie płatków śniegu na wietrze.

Przeanalizowanie wstępnego modelu "deszczu" pozwala uprościć nieco potrzebną strukturę danych: wszystkie krople mają ten sam kolor i maksymalny czas życia, a różnią się punktem startowym i prędkością. Każda kropla otrzymuje stałą prędkość pionową, która następnie zostaje nieznacznie losowo zaburzona (w realnym świecie krople deszczu również nie poruszają się dokładnie równolegle). Uwzględniając te założenia, zdefiniujmy klasę, która posłuży do narysowania realistycznego opadu deszczu (listing 1).

Listing 1

struct drop; // struktura przechowująca

{ // dane o pojedynczej kropli

vector3d v; // prędkość kropli

vector3d p; // położenie kropli

vector3d prevp; // poprzednie położenie kropli

float t; // czas życia kropli

};

class CRain

{

public:

// konstruktor klasy generujący deszcz o zadanej

// maks. liczbie kropli, położeniu początkowym

// [x0,y0,z0], czasie życia timemax, promieniu maxr,

// wysokości maxh i prędkości pionowej kropli vstart

CRain(int maxn, float x0, float y0, float z0,

float timemax, float maxr, float maxh, float vstart);

~CRain();

void Render(); // rysuje opad deszczu

// Update uaktualnia dane o deszczu po upływie

// czasu t dla wektora wiatru wind

// i procentowego stopnia zaburzenia d

void Update(float t, vector3d wind, float d);

void Generate(int n); // tworzy n nowych kropli deszczu

void StopAll(); // zatrzymuje emisję deszczu i usuwa

// wszystkie krople

// MoveTo symuluje przesunięcie

// obserwatora do punktu [x,y,z]

void MoveTo(float x, float y, float z);

protected:

drop *drops; // lista "kropli" deszczu

int maxnr; // maksymalna liczba kropli

int nr; // aktualna liczba kropli

float r; // promień obszaru emisji

float h; // wysokość obszaru emisji

float tmax; // maksymalny czas życia kropli

vector3d start; // położenie punktu startowego

// emisji deszczu

// CreateDrop tworzy nową kroplę w losowym punkcie

void CreateDrop();

};

Przyjrzyjmy się bliżej funkcji tworzącej pojedynczą kroplę deszczu. Kropla taka umieszczana jest w losowym punkcie obszaru emisji, określonego przez punkt startowy start, promień r i wysokość h. Zwróćmy uwagę na sposób obliczania prędkości początkowej kropli. Bez uwzględniania losowego zaburzenia ruchu wektor prędkości kropli miałby postać [0, -v0, 0] (kropla poruszałaby się ruchem jednostajnym pionowo w dół). Generując zaburzenia ruchu, bierzemy pod uwagę współczynnik zaburzenia distortion oraz czynnik losowy - obliczony za pomocą funkcji rand() (listing 2).

Listing 2

void CRain::CreateDrop()

{

if(nr<maxnr)

{

// początkowa prędkość kropli, zaburzona losowo

drops[nr].v.x = distortion*((float)rand()/(float)(RAND_MAX+1)-0.5);

drops[nr].v.y = -v0 + distortion*((float)rand()/(float)(RAND_MAX+1)-0.5);

drops[nr].v.z = distortion*((float)rand()/(float)(RAND_MAX+1)-0.5);

// początkowe, losowe położenie kropli

drops[nr].p.x = start.x+((float)rand()/(float)(RAND_MAX+1.0)-1.0)*r;

drops[nr].p.z = start.z+((float)rand()/(float)(RAND_MAX_1.0)-1.0)*r;

drops[nr].p.y = start.y-(float)rand()/(float)(RAND_MAX_1.0)*h;

// poprzednie położenie kropli =

// = startowe położenie kropli

drops[nr].prevp = drops[nr].p;

drops[nr].t = 0; // czas życia kropli = 0

nr++; // uaktualnij liczbę kropli

}

}


Zobacz również