Autor Wątek: Rendering chmur  (Przeczytany 3797 razy)

Offline limonki

  • Użytkownik

# Maj 05, 2012, 14:40:30
Witam wszystkich forumowiczów ;)

Ostatnio przez głowę przewinęła mi się kwestia związana z renderowaniem chmur. W pierwszej kolejności co wpadło mi do głowy, to stworzenie ileś tam metrów nad głową gracza prostego plane'a, którego połączyłbym z szumem Perlina. Jednak po chwili zastanowienia się nad tą techniką, stwierdziłem, że to nie jest to co potrzebuję, że chmury będą wyglądać dziwnie i strasznie przestarzale (przekonał mnie do tego znaleziony artykuł na stronie Intela ale już nie pamiętam jaki). Po dłuższym szukaniu w Googlach natknąłem się na zagadnienie, które zwie się Volume Rendering'iem. Znalazłem nawet kilka filmików na YT, gdzie stosowano tą technikę do robienia chmur real-time. Wyglądały naprawdę świetnie kosztem FPS'ów. Duża ilość zdjęć medycznych i innych dziwnych tworów znalezionych pod tym zagadnieniem przeraziła mnie, doszedł do tego jeszcze brak znalezienia w internecie żadnego sensownego tutka pod GLSL. Zrezygnowałem. Iskra nadziei, że zdążę z moim projektem na czas, poszła kiedy przeszukiwałem warsztatową otchłań. Znalazłem tam zakopany temat, w którym było coś wspomniane o chmurach robionych megaparticlesami. Nie jestem jednak pewny czy aby na pewno jest to dobra technika. Przypuśćmy, że na jedną chmurkę przypadnie 10 cząsteczek, co daje 20 trójkątów, gdzie chciałbym renderować załóżmy 1000 chmur (o różnych wielkościach). Łącznie mamy 20 tysiaków trójkątów. Prawie tyle co heightmap'a 256 * 256 (255 * 255 = 65025, 65025 / 3 = 21675). Spadek wydajności na mojej karcie gwarantowany.

Co sadzicie o tym Wy? Jakie techniki polecacie? Może źle chcę to ugryźć lub coś przeoczyłem? Może nie będzie aż tak źle z tą wydajnością przy megaparticlesach? Pewnie przesadziłem z tą ilością chmur...

Pozdrawiam,
limonki

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Vipa

  • Redaktor

# Maj 05, 2012, 16:41:31
Podstawowy papier do renderingu chmur masz tutaj:
http://ofb.net/~niniane/clouds-jgt.pdf
Cytuj
Przypuśćmy, że na jedną chmurkę przypadnie 10 cząsteczek, co daje 20 trójkątów, gdzie chciałbym renderować załóżmy 1000 chmur (o różnych wielkościach). Łącznie mamy 20 tysiaków trójkątów.
Ogólnie level of detail.
Jeżeli dobierzesz fajne teksturki (jest na przykład bardzo dużo brushy do photoshopa czy paint shop pro) to nie musisz zużywać 10 cząsteczek tylko 5 czy 6. Im ich mniej tym muszą być jednak większe i przy przechodzeniu przez nie obracają się do kamery całkiem widocznie.
Polecam wyliczać po nawet 100 cząsteczek na chmurę, ale po prostu im jest ona dalej tym mniej ich wyświetlać zwiększając je przy tym. Zależy ile czasu chcesz zużyć na generowanie zachmurzenia i ile masz pamięci. Oczywiście też od tego czy chmury są ważnym elementem gry. Jeżeli to symulator lotniczy to polecam właśnie zainwestować bardziej w chmury niż obiekty na ziemi.

Sam shader nie musi być skomplikowany. Potrzebować będziesz paru danych do wyliczeń:
- stopień zachmurzenia - mniej wyświetlanych chmur a dalej mniej cząsteczek na chmurę,
- pora dnia - oświetlenie chmur jest ważne, możesz to uprościć tworząc teksturę:

i zachowując proporcje pory dnia - 0 do 1 - odczytać kolor w shaderze.
- wysokość chmury - im wyżej tym bardziej rozrzedzona i jaśniejsza
- pozycja cząsteczki w chmurze - prosta zależność, im dalej (lokalnie, w obrębie chmury) od słońca tym ciemniejsza. Przy większych cząsteczkach możesz ustalać jasność dla każdego vertexa cząsteczki.

Osobnym tematem są point sprites. Przyspieszają, ale obracają się zawsze tak jak ekran. Różnica na zdjęciu poniżej:


Jeżeli chcesz się zagłębić dalej w temat, to już będziesz potrzebował całego atlasu tekstur (chmury są naprawdę różne) i generować chmurki w zależności od wysokości nad poziomem morza, uwzględniać ich ruch od ukształtowania terenu, rozrzedzanie, wpływ wiatru, generowanie realtime itd.

Te wszystkie techniki z normalmapą plane'a, noisami i innymi kombinacjami polecam wyrzucić do kosza. Próbowałem chyba dziesiątek różnych i wszystkie albo nie dają zamierzonych rezultatów albo są tylko niewiele szybsze kosztem czasu implementacji.

Offline limonki

  • Użytkownik

# Maj 05, 2012, 17:12:31
Podstawowy papier do renderingu chmur masz tutaj:
http://ofb.net/~niniane/clouds-jgt.pdf

Dzięki, przyda się. Poczytam sobie w wolnym czasie ;)

Ogólnie level of detail.

Tak. W sumie to bardzo dobry pomysł. Nie wpadł mi wcześniej do głowy.

Jeżeli dobierzesz fajne teksturki (jest na przykład bardzo dużo brushy do photoshopa czy paint shop pro) to nie musisz zużywać 10 cząsteczek tylko 5 czy 6.

Jeśli ogólnie chodzi o tekstury to miałem zamiar tworzyć je proceduralnie. Przypuśćmy robię 1000 chmur i generuję sobie 10 czy tam nawet 20 tekstur (256*256), przypisując każdej cząsteczce losową tworząc w ten sposób unikalne chmury. Co do czytania ich z pliku jako atlas tekstur to w ostateczności dodam sobie taką funkcję jeśli starczy mi czasu ;)

Im ich mniej tym muszą być jednak większe i przy przechodzeniu przez nie obracają się do kamery całkiem widocznie.

Co do obrotu to tego właśnie się obawiam, że będą artefakty. Myślę, że im więcej cząsteczek i im będą mniejsze aż tak bardzo tego nie będzie widać.

Polecam wyliczać po nawet 100 cząsteczek na chmurę, ale po prostu im jest ona dalej tym mniej ich wyświetlać zwiększając je przy tym. Zależy ile czasu chcesz zużyć na generowanie zachmurzenia i ile masz pamięci. Oczywiście też od tego czy chmury są ważnym elementem gry. Jeżeli to symulator lotniczy to polecam właśnie zainwestować bardziej w chmury niż obiekty na ziemi.

Jeśli chodzi o ilość cząsteczek i ilość przeznaczonej pamięci RAM na ich przechowywanie to raczej nie problem. U siebie mam 3,5 GB a na sprzęcie gdzie będę to najprawdopodobniej uruchamiać jest jeśli się nie mylę to 3 GB (laptop z Vistą co mnie nie cieszy). Bardziej boję się o VRAM, że zakatuje go ilością tekstur. Chmury chmurami ale dochodzą również inne tekstury terenu itp. itd. Tym bardziej, że na tym laptopie jest karta graficzna zintegrowana (podejrzewam, że nie ogarnie OGL'a 2.1). Czas zużyty na generowanie zachmurzenia...Hmmm...Pewnie nie zbyt dużo, jednak będzie to dość ważny element środowiska.

Osobnym tematem są point sprites. Przyspieszają, ale obracają się zawsze tak jak ekran. Różnica na zdjęciu poniżej:


Kiedyś na warsztacie znalazłem jakiś artykuł odnoście point sprite'ów i chciałem właśnie je zaimplementować. Efekt jest tragiczny po prostu...Nawet się nie skalują wraz z zwiększeniem się odległości, dlatego to już dawno przekreśliłem i robię sobie cząsteczki na trójkątach.

A odnośnie Volume Rendering'u? Pewnie nie jest to zbytnio opłacalne dla chmurek? Chociaż bodajże w Cry Engine 3 są volumetryczne chmury.

Offline Vipa

  • Redaktor

# Maj 05, 2012, 17:30:15
Point sprite'y możesz samemu skalować w shaderze. Ale nie wiem czy jest sens w ogóle tym się przejmować. W efektach gdzie tych cząsteczek ma być masa lub potrzeba jest bardzo ostrej optymalizacji można, ale do chmur nie polecam.

Właśnie te chmury w Cry Engine 3 osobiście mi się nie podobają. Mało przypominają chmury. Tak samo w wielu innych produkcjach, przykład:

Takie połowiczne rozwiązanie pomiędzy pełnym volume a zwykłymi teksturami wydaje mi się najbardziej sensowne. No chyba, że potrzeba jest efektów tego typu, jak na 1 obrazku z tego linka:
http://joomla.renderwiki.com/joomla/index.php?option=com_content&view=article&id=68&Itemid=72
Ale i tutaj ta sama metoda wyżej terenu będzie wyglądała do kitu. Pewnie najsensowniejszym rozwiązaniem było by użycie paru metod, ale to bardziej chyba do konkretnej symulacji tego zjawiska.

Offline limonki

  • Użytkownik

# Maj 05, 2012, 17:45:57
Aż takich efektów mi nie potrzeba ;) Jednak chciałbym, żeby wszystko wyglądało możliwie jak najbardziej realistycznie. Point sprite'y...Nie. Szkoda nawet marnować czas na pisanie shaderów, tym bardziej, że komputery są coraz szybsze i nie ma sensu się z tym babrać. Kiedyś i owszem ale teraz ;) Volume rendering sobie podaruję na chwilę obecną. Myślę, że ta technika to raczej do jakiś symulacji niż do gier. Particlesy są w zupełności wystarczające. Mam tylko nadzieję, że mój komputer się nie zadławi ;)

Dzięki za nakierowanie mnie na temat.

Pozdrawiam,
limonki

Offline Vipa

  • Redaktor

# Maj 05, 2012, 17:52:15
Spoko, tu masz efekt końcowy kombinowania z volumem i cząsteczkami:
http://warsztat.gd/screen/11442/spodobalo_mi_sie
Rysowane quady są w sumie stosunkowo duże, około 20 na jedna chmurę. Cieniowane shaderem tak jak pisałem wyżej. Dodatkowo godrays także z chmur, lekki dof i dosłownie parę zależności pomiędzy cząsteczkami w obrębie jednej chmury. Ważne są dość ostre krawędzie chmur. Te polecam zrobić całkowicie ostre i potem stopniowo blurować podglądając rezultat. Oczywiście bardziej ostre muszą być górne krawędzie.

Offline limonki

  • Użytkownik

# Maj 05, 2012, 17:59:59
Tak jak porównałem sobie teraz CE3 a ten screen to bardziej jestem przekonany do techniki tej, którą poleciłeś. Zrobione mniejszym nakładem pracy i mocy obliczeniowej, a efekty końcowe bardzo ładne. Zanim do godrays'ów dojdę to jeszcze trochę minie :) Lubię sobie dodawać różne efekty graficzne stopniowo

Offline limonki

  • Użytkownik

# Maj 06, 2012, 18:14:49

Witam ponownie,

mam problem z moimi cząsteczkami. Nie z ich implementacją, a z twardymi krawędziami podczas kolizji z innymi obiektami sceny. Udałem się do Google i znalazłem technikę, która zwie się Soft Particles. Wszystkie artykuły w sieci opisują ją z wykorzystaniem bufora głębokości. Ja podczas renderowania cząsteczek wyłączam zapis właśnie do tego bufora, żeby nie wykonywać sortowania.

Moje pytania brzmią następująco:

1. Czy są inne techniki ale bez wykorzystania bufora głębokości?

2. Jeśli nie ma żadnych innych metod, to skąd mam wziąć w takim razie głębokość cząsteczki? Np. w poniższym pseudo kodzie:

fade = saturate((scene_depth – particle_depth) * scale);
scene_depth to zapisany w postaci tekstury bufor głębokości bez cząsteczek.
Odnośnie particle_depth to analogicznie jak wyżej tylko, że z zapisanymi cząsteczkami? A może jednak tylko bufor głębokości cząsteczek?

3. I czy bufor głębokości mogę zapisać tak samo jak w przypadku shadow mappingu czy musi być jakoś specjalnie zapisany?

Jeśli ktoś byłby w stanie mi to wyjaśnić, to będę wdzięczny.

Pozdrawiam,
limonki

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Maj 06, 2012, 18:39:12
Cytuj
Te wszystkie techniki z normalmapą plane'a, noisami i innymi kombinacjami polecam wyrzucić do kosza. Próbowałem chyba dziesiątek różnych i wszystkie albo nie dają zamierzonych rezultatów albo są tylko niewiele szybsze kosztem czasu implementacji.
Zależy co chcesz osiągnąć. Takie chmury to raczej będzie ciężko na particlach zrobić. :)

Offline Interceptor

  • Użytkownik

# Maj 06, 2012, 19:11:00
Ja podczas renderowania cząsteczek wyłączam zapis właśnie do tego bufora, żeby nie wykonywać sortowania. Sortowanie nie kosztuje duzo dlatego warto je stosowac, mozna dzieki temu uzyskac o niebo lepszy efekt czasek.

Z tego co pamietam bez sortowania mozesz uzywacz czastek ale uzywajac wylacznie blendingu addatywnego.

Jeśli nie ma żadnych innych metod, to skąd mam wziąć w takim razie głębokość cząsteczki? Np. w poniższym pseudo kodzie:

Renderujesz do nowego rt same czastki z wlaczonym depth testem, bez texturowania czastek, ale z wlaczonym billboardem.

I czy bufor głębokości mogę zapisać tak samo jak w przypadku shadow mappingu czy musi być jakoś specjalnie zapisany?

Tak samo.

Offline limonki

  • Użytkownik

# Maj 06, 2012, 19:30:44
Ehhh...Czyli sortowanie. Będę musiał to trochę przebudować ale skoro ma być lepszy efekt to czemu by nie :)

Renderujesz do nowego rt same czastki z wlaczonym depth testem, bez texturowania czastek, ale z wlaczonym billboardem.

Czyli tak:
1. Render sceny defaultowo + zapis bufora głębokości do tekstury.
2. Podczas renderu cząsteczek przełączam się na nowego render target'a (renderuje same quady czy tam trianglesy bez teksturek) +  zapis bufora głębokości cząsteczek do tekstury.

I tutaj chciałbym jeszcze zadać jedno pytanie:

Akumulować wszystkie wartości bufora głębokości i zapisywać raz do depth tekstury i wysyłać raz do shadera, czy może dla każdej cząsteczki osobno?

3. I na końcu znowu na default.

Dobrze to zrozumiałem?
« Ostatnia zmiana: Maj 06, 2012, 19:35:32 wysłana przez limonki »

Offline Interceptor

  • Użytkownik

# Maj 06, 2012, 19:55:21
Dokladnie tak jak piszesz.

Depth wszystkich czastek zapisujesz do 1 textury.

Offline miloszmaki

  • Użytkownik

# Maj 06, 2012, 20:46:49

fade = saturate((scene_depth – particle_depth) * scale);
scene_depth to zapisany w postaci tekstury bufor głębokości bez cząsteczek.
Odnośnie particle_depth to analogicznie jak wyżej tylko, że z zapisanymi cząsteczkami? A może jednak tylko bufor głębokości cząsteczek?

A nie wystarczy po prostu policzyć particle_depth w VS/PS dla każdej cząsteczki podczas jej renderowania?

Offline Oti

  • Użytkownik

# Maj 06, 2012, 21:10:11
A nie wystarczy po prostu policzyć particle_depth w VS/PS dla każdej cząsteczki podczas jej renderowania?
Wystarczy. :)

Ja u siebie( http://warsztat.gd/screen/11304/particle_system ) korzystam z tej techniki, o której mówisz-w jednym obiegu renderuję SCENĘ do depthbuffera(jest to dokładnie ten sam shader i ustawienia co przy generowaniu shadow map), a w drugim przesyłam do shadera i scene_depth mam z tekstury, a particle_depth to po prostu współrzędna z pozycji wierzchołka w przestrzeni ekranu.

Offline limonki

  • Użytkownik

# Maj 06, 2012, 23:47:38
Ok. Sprawa ma się następująco:

1. Renderuję bufor głębokości sceny do tekstury (powinien być cały biały?).
2. Wysyłam go do shader'a soft particles'ów.
3. Liczę głębokość particles'a w vertex shaderze w następujący sposób:

vec4 vsPos = gl_ModelViewMatrix * gl_Vertex;
depth = -vsPos.z / MAX_DISTANCE;


Gdzie MAX_DISTANCE = 1000.0.

Do tego momentu wszystko działa idealnie. Depth jest liczony dla particles'a.

4. W fragment shaderze:

uniform sampler2D tex;
uniform sampler2D depthtex;

varying float depth;

void main()
{
     vec2 coord = gl_FragCoord.xy / vec2(1024, 768);
     vec4 depthMapDepth = -texture2D(depthtex, coord).z;
     vec4 color = texture2D(tex, gl_TexCoord[0].st);

     float scale = 0.2;
     float fade = clamp((depthMapDepth.x - depth) * scale, 0.0, 1.0);

      gl_FragColor = vec4(vec3(color),color.a * fade);

    //gl_FragColor = vec4(depth, depth, depth, 1);
}

Kod fragment shader'a wziąłem z jakiejś stronki i przerobiłem go pod siebie. I tu jest problem. Bo żadna cząsteczka nie chce się pojawić na ekranie. Jeśli usunę wszystko i zostawię tylko linijkę gl_FragColor = vec4(depth, depth, depth, 1); to widzę jak ładnie liczy się depth dla cząsteczek.

Co tu jest nie tak (ew. obstawiłbym depth teksturę, która jest cała (?) biała)? Powinno śmigać a tu beret.