Jak sprawić, by woda wydawała się ciemniejsza z głębią, jak w Minecraft?


24

W Minecraft, gdy patrzysz na wodę, im głębiej patrzysz, tym ciemniej się robi. Czy ktoś wie, jak zakodować coś takiego?

Minecraft z efektem Minecraft z efektem

podobna gra bez efektu podobna gra bez efektu


18
Czy nie jest to zrobione automatycznie, ponieważ materiał kostki wody jest półprzezroczysty?
pek

Nie wydaje mi się Dla porównania dodałem zdjęcie bez efektu.
Xavier,

2
Może to efekt mieszania dodatków stosowany tylko na kostkach wody? Ponownie powinno to być łatwe, ponieważ materiał jest półprzezroczysty.
pek

1
Możesz także zmienić kolor skrzynek w zależności od głębokości.
Ali1S232,

Odpowiedzi:


51

Istnieją zasadniczo dwa różne podejścia do oświetlania wody w zależności od głębokości:

Oświetlenie wokselowe

Minecraft wykorzystuje oświetlenie oparte na wokselach, które działa poprzez propagowanie światła do sąsiednich kostek, obniżając jasność w zależności od rodzaju bloku. Ciemne oceany są efektem ubocznym tego systemu.

Woda blokuje światło słoneczne i redukuje światło o 3 poziomy na blok (zamiast domyślnego poziomu 1), co oznacza, że ​​jasność oceanu dla każdej odległości od powierzchni wynosi:

0 (surface): 15 (direct sunlight)
1:           12
2:            9
3:            6
4:            3
5 and below:  0 (darkness)

Źródło: Minecraft Wiki - Light

Cieniowanie na podstawie odległości

W grach z tradycyjnym modelem oświetlenia efekt ten można uzyskać, mierząc ilość wody między źródłem światła a dnem oceanu. Światło jest następnie wyblakłe na podstawie tej odległości. Można to zrobić na kilka sposobów:

Obliczanie bezpośrednie

Jeśli masz płaską powierzchnię, możesz łatwo obliczyć odległość, jaką światło pokonuje w wodzie, jeśli przejdziesz powierzchnię normalną z dala od akwenu \ vec {n}i iloczyn skalarny tej normalnej oraz pozycję powierzchni sw module cieniującym geometrię.

Efektywna odległość wody wynosi

\ max (\ left (s - \ vec {n} \ cdot \ vec {p} \ right), 0) \ cdot \ left (1 + \ tan (\ alpha) \ right)

gdzie \ vec {p}jest pozycja wierzchołka i alfakąt między kierunkiem światła pod powierzchnią a powierzchnią wody jest normalny w stosunku do części wód.

O zachodzie słońca alfaosiąga tylko nieco mniej niż 50 °, ponieważ światło załamuje się po wejściu do wody.
Oto post na blogu z dobrym wyjaśnieniem: Aparat cyfrowy: całkowite odbicie wewnętrzne
Kolejny post z bardziej szczegółowymi informacjami: Aparat cyfrowy: prawo załamania Snella

Jeśli używasz mapy wysokości na powierzchni równoległej do wody, \ left (s - \ vec {n} \ cdot \ vec {p} \ right)staje się \ left (s - h \ right). Właściwy współczynnik wynosi 1, jeśli słońce znajduje się bezpośrednio nad powierzchnią wody.
W przypadku światła punktowego należy obliczyć alfadla każdego wierzchołka na podstawie względnej pozycji względem źródła światła.

Przy stałym poziomie wody lub stałym kierunku światła części równania są stałe i nie powinny być obliczane w module cieniującym ze względu na wydajność.

Plusy:

  • Szybki i dokładny

Cons:

  • Działa tylko na płaskich powierzchniach wodnych lub tylko na świetle bezpośrednio z góry, ponieważ zwykle tylko jedna powierzchnia jest brana pod uwagę. (Połączenie chropowatej powierzchni i przechylonego światła może w pewnym stopniu działać przy mapowaniu paralaksy).
  • Bez żrących środków

Mapowanie cieni

Jeśli renderujesz powierzchnię wody na osobną mapę głębokości (widzianą ze źródła światła), możesz użyć tej tekstury głębokości, aby obliczyć odległość, jaką światło przebywa w wodzie przed uderzeniem w powierzchnię.
Aby to zrobić, należy rzutować każdy wierzchołek na rzut źródła światła w module cieniującym wierzchołki i wyszukiwać teksturę w module cieniującym piksele.

Jeśli powierzchnia jest stosunkowo płaska, należy użyć załamanego źródła światła, aby uzyskać lepsze wyniki.

Plusy:

  • Działa ze stosunkowo złożoną geometrią wody, pod warunkiem, że się nie zasłania. *
  • Może być ponownie użyty do prawie każdego rodzaju częściowo przezroczystej objętości.

Cons:

  • Wolniej niż bezpośrednie obliczenia.
  • Potrzebuje dodatkowej pamięci VRAM do mapy głębokości.
  • Nie w 100% dokładne.

* Możesz określić ilość wody przed najbliższą stałą powierzchnią, licząc głębokość z POV światła w następujący sposób:

  1. Renderuj jednolitą geometrię w scenie w normalny sposób. Dla każdego fragmentu dodajesz wartość głębokości do tekstury wynikowej.
  2. Renderuj przednie ściany wody bez aktualizacji bufora głębokości i odejmij głębokości fragmentów od wyniku.
  3. Renderuj tylne ściany w ten sam sposób, ale dodaj głębokość fragmentu do wyniku.

Wynikowa tekstura zawiera teraz ilość wody przed światłem w jasnej przestrzeni widokowej, więc wartość należy przekształcić z powrotem przed użyciem. Ta metoda działa w celu obliczenia światła kierunkowego (minus załamanie światła), ale doprowadzi do nieprawidłowego światła otoczenia, jeśli powierzchnie są bardzo nieregularne i między częściami wody występuje duża ilość powietrza wpływająca na te same fragmenty.
Plusy i minusy są takie same jak w przypadku normalnego mapowania cieni, z tym wyjątkiem, że potrzebujesz jeszcze jednego bufora podczas obliczania głębokości, a wydajność jest gorsza, ponieważ musisz narysować więcej geometrii.

Ray-Tracing

Śledzenie promieni jest zdecydowanie najdokładniejszym, ale także najdroższym rozwiązaniem do renderowania przezroczystych woluminów. Można to zrobić na dwa sposoby: 1. Śledzenie z dna oceanu w kierunku powierzchni i 2. Śledzenie od źródła światła w kierunku wody. Aby obliczyć jasność, dla każdego punktu na podłodze potrzeba wielu promieni.

Plusy:

  • Działa poprawnie z każdą geometrią.
  • Prawidłowe środki kaustyczne!

Cons:

  • Powolny!

Dodatkowe efekty

Podczas renderowania wody należy wziąć pod uwagę jeszcze kilka rzeczy:

Mgła

Światło w wodzie jest ponownie rozpraszane podczas podróży do obserwatora, więc powinieneś mieszać go w jednolitym kolorze.

Jeśli obserwator jest zanurzony , możesz po prostu renderować mgłę na podstawie końcowego wyniku bufora głębokości. Kolor mgły, ale nie jej gęstość, powinien zmieniać się wraz z odległością obserwatora od powierzchni! (Minecraft używa tylko tej części efektu).

Jeśli obserwator patrzy na wodę z góry , musisz obliczyć mgłę na podstawie różnicy głębokości między powierzchnią a geometrią pod wodą. Kolor mgły powinien być nieco ciemniejszy z większymi różnicami głębokości, ale powinien zmieniać się tylko do punktu, w którym mgła jest całkowicie nieprzezroczysta.

Kolor mgły powinien również zależeć od kierunku widoku każdego piksela, więc w obu przypadkach jest nieco ciemniejszy.

Faking Caustics

Jeśli zamiast fałszywej kaustyki użyjesz bezszwowej tekstury 3D zamiast kafelków, możesz uniknąć rozciągania na pionowych powierzchniach. Siła rozproszonego światła w pobliżu powierzchni zmienia się w trzech wymiarach, więc użycie tekstury 2D zwykle powoduje rozciąganie gdzieś na scenie. Możesz modelować zmieniające się kąty światła poprzez rzutowanie pozycji wierzchołków podłogi na inny układ współrzędnych.

Inną możliwością jest obliczenie gęstości światła na podstawie położenia powierzchni w układzie współrzędnych światła, chociaż najprawdopodobniej kosztowałoby to pewną wydajność.

Środki kaustyczne powinny zanikać szybciej niż rozproszone światło o coraz większej głębokości.

Gradient koloru

Kolory są rozproszone w różny sposób, więc kolor światła powinien się zmieniać wraz ze wzrostem głębokości. Zapobiega to również nagłym krawędziom, gdy na przykład plaża przecina powierzchnię wody.

Kąt padania

Z powodu załamania światła światło uderza w dno oceanu znacznie bardziej stromo niż normalnie. Artykuł w Wikipedii o prawie Snella zawiera wzory na kąty i wektory.


6

Wierzę, że efekt oświetlenia nieba w Minecrafcie jest skierowany w dół - rzeczy są zacienione przez to, co jest nad nimi, bez względu na to, gdzie jest słońce. Następnie stosuje się lokalne oświetlenie z pochodniami itp. Z efektem odpadania - im dalej od źródła światła, tym mniej światła dostaje kostka.

Jeśli zostanie to zrobione w ten sposób, każda warstwa wody kumuluje cień pod warstwą pod nią, więc każda z nich będzie stopniowo ciemnieć. Liście drzew zapewniają taki cień, jednak nie kumulują się. Otrzymujesz ten sam odcień pod drzewem, bez względu na to, czy jest to 1 czy 100 kostek liści.

Jednym ze wskazówek, że jest to stosowana metoda, jest to, że woda nie ciemnieje, gdy znajduje się dalej od widza - tylko podczas schodzenia. Tak, efekt mgły pojawia się na odległość, ale nie efekt ciemnej wody.

Podstawową formułą obliczania oświetlenia byłoby coś takiego w pseudokodzie ...

light_on_cube = 1.0
for each cube above target cube, from lowest to highest {
   if cube being examined is tree foliage
      light_on_cube = 0.5
   else if cube being examined is water
      light_on_cube = light_on_cube - 0.1
   else if cube being examined is solid 
      light_on_cube = 0
}

Nie jest to idealne do obliczania oświetlenia pod nawisami lub w jaskiniach, ponieważ przy zwisie byłoby to bardzo ciemne. Można jednak dodać zarówno lokalne źródła światła (pochodnie, pożary itp.), Jak i traktując bloki oświetlone słońcem jako źródła światła. Coś takiego może to zrobić ...

  1. Oblicz światło słoneczne bezpośrednio z góry (za pomocą pseudokodu powyżej) dla każdej kostki.
  2. Jeśli obok sześcianu znajduje się źródło światła, uważaj je za w pełni oświetlone (1.0)
  3. Jeśli sześcian nie otrzymuje słońca bezpośrednio z góry, daj mu trochę światła w zależności od odległości od w pełni oświetlonego sześcianu. Bliżej oznacza więcej światła, dalej oznacza mniej (do zera).

Chodzi o to, że jeśli sześcian zostanie oświetlony przez słońce lub pochodnię, sześcian obok niego również zostanie w jakiś sposób oświetlony. A im dalej jesteś od oświetlonego sześcianu, tym mniej światła będzie. To trochę dziwny sposób na oszacowanie rozproszonego oświetlenia, ale myślę, że to by zadziałało.


1
Tak, jestem prawie pewien, że to jest bilet. Zrobiłem coś podobnego w mojej grze.
MichaelHouse

Nawiasem mówiąc, właśnie dodałem twojego bloga do mojej listy czytelników Google Byte56 - blogi programistów FTW!
Tim Holt,

Och, dlaczego dziękuję. Wciąż poza tematem tego pytania, ale właśnie przeczytałem twojego bloga o klasie profesora Baileya. Byłem w tej klasie w zeszłym roku! Jestem całkiem pewien, że udzieliłeś tej prezentacji w zeszłym roku. Myślałem, że twoje imię jest znajome. Mały świat :)
MichaelHouse

3

Być może nie rozumiem pytania, ale dlaczego nie możesz po prostu zmienić koloru bloków w zależności od ich głębokości?

Jeśli masz głębokość d (w blokach, zaczynając od 0), wówczas rozsądnym równaniem jasności byłoby:

L = (1 m ) e - kd + m

Kod: L = (1.0 - m) * exp(-k * d) + m;

k kontroluje, jak szybko robi się ciemniej (wyżej = szybciej). Rozsądna wartość to 0,5.
m to minimalna wymagana jasność.
L zmienia się od 0 do 1.

Jeśli nie wiesz, jak zmienić kolor bloków w jakimkolwiek graficznym interfejsie API, którego używasz, zadaj to jako osobne pytanie (określając, jakiego interfejsu API używasz i czy używasz shaderów).


Po prostu nie pomyślałem o tym. Właśnie z ciekawości, skąd wzięłaś to równanie?
Xavier,

1
@Xavier: Właśnie to wymyśliłem. e^-kdBit jest tylko gwałtowny rozpad, który jest standardową funkcją dla rzeczy, które stopniowo mają tendencję do zera powyżej pewnej wartości (głębokość). Mnożenie przez (1-m)i dodawanie msłużą jedynie do skalowania i równoważenia rozpadu, tak że kończy się na minimum, male wciąż zaczyna się od 1. en.wikipedia.org/wiki/Exponential_decay
Peter Alexander

Chodzi o to, że bloki o głębszym odcieniu będą widoczne tylko wtedy, gdy bloki mają kolory alfa; w takim przypadku nie ma potrzeby zmiany koloru bloku, alfa utworzy efekt automatycznie.
Jonathan Connell,

@Jathanathan: Nie renderujesz bloków wodnych, renderujesz bloki na dnie morskim z ciemniejącym kolorem, a następnie masz tylko jedną warstwę alfa na powierzchni wody.
Peter Alexander

@Peter Alexander Ok, zakładałem, że w tych grach typu blok nawet woda była zbudowana z bloków.
Jonathan Connell,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.