W tym miejscu powinna być drobna reklama. Proszę, wyłącz blokowanie reklam dla tej strony, to jedyny zarobek ze strony!

Fale - tutorial

Zrzuty ekranu

 shot  shot  shot  shot  shot  

Wymagania

  • Kompilator C++ dla środowiska Windows lub Linux
  • Grafika: Karta z akceleracją 3D i obsługą OpenGL
  • Dźwięk: brak
  • Miejsce na HDD: 2MB
  • Mysz: nie

Wasza ocena

 

Oceń:

Wasza ocena:
ocena 10.0/10: [6 głosów]
 

Opis

 W tym tutorialu chcę pokazać efekt ruchu fal na wodzie, obrazowany przy użyciu OpenGL. Z uwagi na temat tutoriala, pominę wszystkie kwestie OpenGL i założę, że czytający poniższe potrafi się nim już posługiwać. Opisany algorytm pozwoli na uzyskanie powierzchni wody, na której w naturalny sposób poruszają się fale.

Algorytm ruchu fal

Załóżmy, że powierzchnia wody jest złożona z siatki punktów. W najprostszej wersji siatka ta jest prostokątna (dwuwymiarowa). Jeżeli potrzebna będzie powierzchnia o innych kształtach, najprostszym rozwiązaniem będzie wycięcie z prostokąta zbędnych części siatki (nie obliczanie ich ruchu i nie rysowanie na ekranie).
Jak z fizyki powinniśmy wiedzieć, ruch fal to pionowy ruch cząsteczek wody. Idąc tym tokiem rozumowania, zakładamy także, że punkty siatki będą ruszały się jedynie w pionie.
Każdy punkt siatki ma swoje miejsce w pionie (współrzędna Y) oraz prędkość pionową (delta Y). Musimy stworzyć tablicę punktów, w której dla każdego z nich zapiszemy te wartości. Od razu można dodać także dla każdego z punktów współrzędne wektora normalnego, aby uzyskać właściwe odbicie światła.
Zakładamy, że stojąca woda bez ruchu jest wtedy, gdy każdy punkt siatki ma współrzędną Y=0. Jeżeli wartość ta będzie mniejsza niż 0, punkt znajduje się poniżej poziomu wody i musi podnieść się w górę. Analogicznie z punktami ponad poziomem wody - należy poruszyć je w dół. Do ruchu służy oczywiście wartość delta Y, czyli "prędkość" pionowa punktu. Kiedy poruszamy punktem, zmieniamy wartość deltaY w odpowiednią stronę, a przy każdym przejściu pętli programu zmieniamy wartość Y o deltę. Pętla musi przejść przez wszystkie punkty siatki, dlatego wszystkie obliczenia powinny odbywać się w dwóch zagnieżdżonych pętlach (dwa wymiary siatki: X i Z).
Powyższy algorytm to jeszcze nie wszystko. W ten sposób uzyskamy dopiero niezależny ruch każdego punktu siatki, co już wygląda ciekawie, ale ma niewiele wspólnego z powierzchnią wody. Aby uzyskać najciekawszy ruch (np. kółka na wodzie) musimy uzależnić ruch punktów od ich sąsiadów. Możemy liczyć ten ruch uwzględniając 4 sąsiadów obok każdego punktu lub wszystkich 8 sąsiadów (czyli także po ukosie w każdą stronę). Tu wykażemy się delikatną inwencją własną i aby dobrać właściwe zachowanie cieczy, będzie trzeba trochę poeksperymentować. Podczas liczenia ruchu każdego z punktów, do jego prędkości (delta Y) należy dodać średnią prędkość jego sąsiadów - czyli w przypadku 4 sąsiadów dodajemy prędkość każdego z nich do prędkości liczonego punktu, po czym dzielimy sumę przez 5 (5 = delta punktu + 4 delty sąsiadów). Analogicznie dla 8 sąsiadów. Jak zauważymy, ruch może nie odzwierciedlać naszych oczekiwań, dlatego te wartości można pomnożyć lub podzielić przez właściwy współczynnik, dzięki czemu uda nam się zwiększyć fale lub je spłaszczyć, czy też zwiększyć/zmniejszyć ich prędkość rozchodzenia się na boki. Wiąże się to także z wrażeniem innej gęstości cieczy, dzięki czemu możemy uzyskać wodę, błoto, kisiel, lawę, itd.
Im gęstsza siatka, tym fale będą wydawały się mniejsze. Dlatego warto dobrać ją tak, by wielkość fal pasowała do naszych wymagań, mając też na uwadze, że im mniej punktów jest w siatce, tym mniej obliczeń będzie potrzebnych (co odbija się na szybkości działania).

Polecam zajrzenie do przykładowego programu w C++ (do ściągnięcia poniżej), gdzie znajdziesz dokładnie wykonany algorytm według powyższego opisu. Oczywiście po fazie obliczeń, należy narysować siatkę na ekranie. Możemy wykonać to w zupełnie dowolnej technice i wcale nie musi być do tego użyty OpenGL.

Jako ciekawostkę podam też, że ten sam algorytm może posłużyć do liczenia fal w dwóch wymiarach (np. gry 2D), gdzie liczymy tylko jeden wymiar powierzchni, który przez to staje się po prostu linią (coś jak przekrój wody).

Poruszanie falami

Aby zauważyć ciekawy ruch fal, należy jakoś poruszyć wodą. W tym celu najlepiej zmieniać wartość delta Y któregoś z punktów siatki. Przykładowo, aby zasymulować padający deszcz, wybieramy losowo punkty siatki i odejmujemy od wartości delta Y dowolną wartość (np. 0.5). Im więcej odejmiemy, tym mocniejsze uderzenie kropli o wodę zasymulujemy - czyli będzie większe "kółko na wodzie".
W przypadku ruchu np. statku po wodzie, w pozycji statku, analogicznie jak w przypadku kropli deszczu, odejmujemy jakąś wartość od delta Y punktu, gdzie rusza się statek. Siłę ruchu warto uzależnić od prędkości statku.
Reszta zależy od inwencji.

Zniekształcanie tekstury

Tekstura dna, w załączonym przykładzie, porusza się także zgodnie z ruchem fal. Oczywiście rozwiązanie takie nie jest idealne, ale na potrzeby ładnych efektów wystarcza. Nie będę bardzo rozpisywał się na ten temat, gdyż wydaje mi się to bardzo proste.
Najłatwiej ustawić pod powierzchnią wody analogiczną powierzchnię, która będzie dnem. Nakładamy na nią teksturę, a sama powierzchnia musi mieć tyle samo punktów, co powierzchnia wody. Rysując dno poruszamy punktami siatki, biorąc pod uwagę pozycję Y punktu siatki wody w tym samym miejscu. Oczywiście aby dno się nie zniekształcało w pionie, zamieniamy pionowy ruch wody w poziomy ruch tekstury dna. Tym sposobem w miejscu, gdzie na wodzie pojawia się fala, zniekształca się dno, co sprawia łącznie ciekawe wrażenie, jakby woda załamywała światło i widzimy zniekształcone dno przez powierzchnię wody. W przykładzie dodatkowo użyłem przyciemnianie koloru tekstury w miejscu zniekształceń, co dodatkowo potęguje efekt.

Idąc za tym przykładem, bardzo łatwo jest wyobrazić sobie zupełnie innego typu ruch tekstury, nie związany w żaden sposób z falami. Na przykład możemy obliczyć zniekształcenie całej tekstury tak, by robiła wrażenie powiększonej soczewką. Wystarczy obliczyć przesunięcia punktów wokół koła.

Ruch kamery

Właściwie ruch kamery nie jest tematem tego tutoriala, ale zostało to użyte w przykładzie, dlatego wspomnę kilka słów na ten temat.
Dla kamery przeznaczymy tablicę 9 wartości typu Float (zmiennoprzecinkowych) - nazwijmy ją Kamera. Każda wartość odpowiada wartościom, które podajemy w parametrach funkcji gluLookAt (polecam instrukcję do OpenGL, gdzie dokładnie opisana jest ta funkcja). Tworzymy drugą identyczną tablicę (nazwijmy ją Obecna).
Cała idea ruchu polega na tym, że kiedy potrzebujemy zmienić płynnie pozycję kamery, zmieniamy wartości w tablicy Kamera. Jednak dla funkcji gluLookAt używamy w parametrach wartości z tablicy Obecna. Przy każdym przejściu pętli programu zmieniamy wartości tablicy Obecna tak, by zbliżały się do wartości odpowiadających w tablicy Kamera.
Najprostsze obliczanie to wyciąganie średniej pomiędzy wartością w tablicy Kamera, a Obecna. Ponieważ ta technika zbyt szybko przeniesie nam kamerę w docelowe miejsce, obliczamy średnią mnożąc wartość z tablicy Obecna kilkukrotnie, a całość średniej dzielimy o tyle samo razy więcej. Im większą wartość mnożnika wybierzemy, tym wolniejszy ruch kamery uzyskamy. Możemy zobaczyć to na poniższym przykładzie:

Obecna[x][y] = (Obecna[x][y] * szybkosckamery + Kamera[x][y])/(szybkosckamery+1);

Takie obliczenia wykonujemy dla wszystkich 9 wartości w tablicy kamery. Po wprowadzeniu tego rozwiązania do programu, uzyskasz efekt ruchu kamery taki sam, jak w przykładzie z falami na wodzie, który możesz ściągnąć poniżej.

Bardziej rozbudowany przykład na wykorzystanie powyższych algorytmów znajdziesz w programie „Fale”, który także można ściągnąć z tej strony.

Kod źródłowy jest napisany w C++ z wykorzystaniem biblioteki GLUT. Program powinien się kompilować w Windows oraz Linux.

Powodzenia.

Pliki do pobrania

 

Wasze komentarze

 

CL [2012-01-07 20:49:18]

@Seba - To prosta wersja fal, nie ma co się spodziewać mega efektów. Poza tym posiadając ten kod przedstawiony tutaj nie ruszasz od zera i można go dowolnie modyfikować uzyskując super efekty.

Seba [2009-04-24 13:18:10]

dno fajne ale same fale troche nie jak fale

zawier [2008-07-07 00:29:52]

swietna ta woda! jak na mnie zrobila wrazenie. od pol roku bawie sie openglem i milo jest zobaczyc cos na co warto zerknac

łączna ilość komentarzy: 3

Dopisz swój komentarz

  • Twoje imię:
  • Dla zabezpieczenia przepisz ten ciąg znaków: ismyk :
  • treść (max 512 znaków):

Zobacz też podobne

  • Robot - źródła
    Prosty przykład w OpenGL pokazujący animację chodzącego robota po lesie.
 << powrót   Odwiedzin opisu: 174 razy.
Valid XHTML 1.0 Strict
© 1998-2018 Grzegorz Drozd
Strona stworzona dla rozdzielczości minimum 1024x768 i sprawdzona w przeglądarkach FireFox oraz MSIE 6.
Polskie znaki kodowane w ISO-8859-2.