Wie kann ich Wasser mit der Tiefe dunkler erscheinen lassen, wie in Minecraft?


24

In Minecraft wird es dunkler, wenn Sie Wasser betrachten, je tiefer Sie es sehen. Weiß jemand, wie man so etwas codiert?

Minecraft mit Wirkung Minecraft mit Wirkung

ähnliches Spiel ohne Wirkung ähnliches Spiel ohne Wirkung


18
Wird das nicht automatisch gemacht, da das Material des Wasserwürfels halbtransparent ist?
pek

Ich glaube nicht. Ich habe zum Vergleich ein Bild ohne den Effekt hinzugefügt.
Xavier

2
Vielleicht ist es ein additiver Mischeffekt, der nur auf die Wasserwürfel angewendet wird? Auch dies sollte einfach sein, da das Material halbtransparent ist.
pek

1
Sie können die Farbe der Kästchen auch entsprechend der Tiefe ändern.
Ali1S232

Antworten:


51

Grundsätzlich gibt es zwei verschiedene Ansätze, um Wasser je nach Tiefe zu beleuchten:

Voxel-Beleuchtung

Minecraft verwendet eine voxelbasierte Beleuchtung, die das Licht auf benachbarte Würfel ausbreitet und die Helligkeit abhängig vom Blocktyp verringert. Dunkle Ozeane sind ein Nebeneffekt dieses Systems.

Wasser blockiert das Sonnenlicht und reduziert das Licht um 3 Stufen pro Block (anstelle der Standardstufe 1). Dies bedeutet, dass die Helligkeit in einem Ozean für jede Entfernung von der Oberfläche wie folgt ist:

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

Quelle: Minecraft Wiki - Licht

Entfernungsbasiertes Abschatten

In Spielen mit einem herkömmlichen Beleuchtungsmodell kann dieser Effekt durch Messen der Wassermenge zwischen der Lichtquelle und dem Meeresboden erzeugt werden. Das Licht wird dann basierend auf dieser Entfernung ausgeblendet. Hierfür gibt es einige Methoden:

Direkte Berechnung

Wenn Sie eine flache Oberfläche haben, können Sie leicht die Entfernung berechnen, die das Licht im Wasser zurücklegt, wenn Sie die Oberflächennormale vom Gewässer entfernt \ vec {n}und das Skalarprodukt dieser Normale und eine Oberflächenposition sin den Geometrie-Shader eingeben.

Die effektive Wasserentfernung beträgt

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

wo \ vec {p}ist die Position des Scheitelpunkts und Alphaist der Winkel zwischen der Lichtrichtung unter der Oberfläche und der Wasseroberfläche normal zum Gewässer.

Bei Sonnenuntergang werden Alphanur etwas weniger als 50 ° erreicht, da das Licht beim Betreten des Wassers gebrochen wird.
Hier ist ein Blog-Beitrag mit einer guten Erklärung: Die Digitalkamera: Total Internal Reflection
Ein weiterer Beitrag mit weiteren Details: Die Digitalkamera: Snells Brechungsgesetz

Wenn Sie eine Höhenkarte auf einer Oberfläche parallel zum Wasser verwenden, \ left (s - \ vec {n} \ cdot \ vec {p} \ right)wird \ left (s - h \ right). Der richtige Faktor ist 1, wenn sich die Sonne direkt über der Wasseroberfläche befindet.
Bei einem Punktlicht müssen Sie Alphafür jeden Scheitelpunkt die relative Position zur Lichtquelle berechnen .

Bei einem festgelegten Wasserstand oder einer festgelegten Lichtrichtung sind Teile der Gleichung konstant und sollten aus Leistungsgründen nicht im Shader berechnet werden.

Vorteile:

  • Schnell und genau

Nachteile:

  • Funktioniert nur für flache Wasseroberflächen oder nur für Licht von direkt oben, da normalerweise nur eine Oberfläche berücksichtigt wird. (Die Kombination aus rauer Oberfläche und geneigtem Licht könnte bei der Parallaxen-Zuordnung in gewissem Maße funktionieren.)
  • Keine Ätzmittel

Shadow Mapping

Wenn Sie die Wasseroberfläche in einer separaten Tiefenkarte rendern (von der Lichtquelle aus gesehen), können Sie diese Tiefenstruktur verwenden, um die Entfernung zu berechnen, die das Licht im Wasser zurücklegt, bevor es auf die Oberfläche trifft.
Dazu projizieren Sie jeden Scheitelpunkt in die Ansichtsprojektion der Lichtquelle im Scheitelpunkt-Shader und führen die Textur-Suche im Pixel-Shader durch.

Wenn die Oberfläche relativ flach ist, sollten Sie für bessere Ergebnisse einen gebrochenen Lichtursprung verwenden.

Vorteile:

  • Funktioniert mit relativ komplexer Wassergeometrie, solange sie sich nicht selbst verschließt. *
  • Kann für fast jede Art von teilweise transparentem Volumen wiederverwendet werden.

Nachteile:

  • Langsamer als die direkte Berechnung.
  • Benötigt zusätzliches VRAM für die Tiefenkarte.
  • Nicht 100% genau.

* Sie können die Wassermenge vor der nächsten festen Oberfläche bestimmen, indem Sie die Tiefe des POV des Lichts wie folgt zählen:

  1. Rendern Sie die Volumengeometrie in Ihrer Szene wie gewohnt. Für jedes Fragment fügen Sie der Ergebnisstruktur den Tiefenwert hinzu.
  2. Rendern Sie die Frontflächen des Wassers, ohne den Tiefenpuffer zu aktualisieren, und subtrahieren Sie die Tiefen der Fragmente vom Ergebnis.
  3. Rendern Sie die Rückseiten auf die gleiche Weise, fügen Sie dem Ergebnis jedoch die Fragmenttiefe hinzu.

Die Ergebnistextur enthält jetzt die Wassermenge vor dem Licht im Lichtsichtraum, sodass der Wert vor der Verwendung zurücktransformiert werden muss. Diese Methode berechnet das gerichtete Licht (minus Brechung), führt jedoch zu falschem Umgebungslicht, wenn die Oberflächen sehr unregelmäßig sind und sich eine große Menge Luft zwischen den Gewässern befindet, die dieselben Fragmente beeinflussen.
Die Vor- und Nachteile sind die gleichen wie bei der normalen Schattenzuordnung, außer dass Sie beim Berechnen der Tiefe einen Puffer mehr benötigen und die Leistung schlechter ist, weil Sie mehr Geometrie zeichnen müssen.

Ray-Tracing

Raytracing ist bei weitem die genaueste, aber auch die teuerste Lösung für das Rendern transparenter Volumes. Dazu gibt es zwei Möglichkeiten: 1. Verfolgung vom Meeresboden zur Oberfläche und 2. Verfolgung von der Lichtquelle zum Wasser. Für jeden Punkt auf dem Boden werden mehrere Strahlen benötigt, um die Helligkeit zu berechnen.

Vorteile:

  • Funktioniert korrekt mit jeder Geometrie.
  • Korrekte Kaustik!

Nachteile:

  • Schleppend!

Zusätzliche Effekte

Beim Rendern von Wasser sind noch einige Dinge zu beachten:

Nebel

Das Licht im Wasser wird auf dem Weg zum Betrachter wieder gestreut, daher sollten Sie es zu einer festen Farbe mischen.

Wenn der Beobachter eingetaucht ist , können Sie den Nebel nur basierend auf dem Endergebnis des Tiefenpuffers rendern. Die Farbe des Nebels, aber nicht seine Dichte, sollte sich mit dem Abstand des Beobachters von der Oberfläche ändern! (Minecraft verwendet nur diesen Teil des Effekts.)

Wenn der Beobachter von oben auf das Wasser schaut , müssen Sie den Nebel anhand des Tiefenunterschieds zwischen der Oberfläche und der Geometrie unter Wasser berechnen. Die Nebelfarbe sollte mit größeren Tiefenunterschieden etwas dunkler werden, sich aber nur bis zu dem Punkt ändern, an dem der Nebel vollständig undurchsichtig ist.

Die Farbe des Nebels sollte auch von der Blickrichtung der einzelnen Pixel abhängen, sodass sie in beiden Fällen etwas dunkler ist, wenn Sie nach unten schauen.

Fälschende Caustics

Wenn Sie eine 3D-Textur mit nahtlosen Kacheln anstelle eines Aufklebers für gefälschte Kaustiken verwenden, können Sie vermeiden, dass vertikale Oberflächen gestreckt werden. Die Stärke des gestreuten Lichts in der Nähe der Oberfläche variiert in drei Dimensionen, sodass die Verwendung einer 2D-Textur normalerweise zu einer Dehnung an einer beliebigen Stelle in der Szene führt. Sie können sich ändernde Lichtwinkel modellieren, indem Sie die Eckpunkte des Bodens in ein anderes Koordinatensystem projizieren.

Eine andere Möglichkeit besteht darin, die Lichtdichte basierend auf der Oberflächenposition im Koordinatensystem des Lichts zu berechnen, obwohl dies höchstwahrscheinlich einige Leistung kosten würde.

Die Kaustik sollte mit zunehmender Tiefe schneller verblassen als das diffuse Licht.

Farbverlauf

Die Farben werden unterschiedlich gestreut, sodass sich die Lichtfarbe mit zunehmender Tiefe ändern sollte. Dies verhindert auch abrupte Kanten, an denen beispielsweise ein Strand die Wasseroberfläche schneidet.

Einfallswinkel

Aufgrund der Lichtbrechung trifft das Licht viel steiler auf den Meeresboden als es normalerweise der Fall wäre. Der Wikipedia-Artikel über das Snell-Gesetz enthält Formeln für Winkel und Vektoren.


6

Ich glaube, dass der Himmelslichteffekt in Minecraft direkt nach unten gerichtet ist - die Dinge werden durch das, was sich über ihnen befindet, abgeschattet, egal wo die Sonne ist. Dann wird lokales Licht von Fackeln usw. mit einem Dropoff-Effekt angewendet - je weiter ein Würfel von der Lichtquelle entfernt ist, desto weniger Licht erhält er.

Auf diese Weise würde jede Wasserschicht die darunter liegende Schicht kumulativ beschatten, sodass jede Schicht zunehmend dunkler wird. Baumlaub spendet Schatten, ist aber nicht kumulativ. Sie erhalten den gleichen Schatten unter einem Baum, ob es 1 oder 100 Laubwürfel sind.

Ein Hinweis darauf, dass dies die verwendete Methode ist, ist, dass Wasser nicht dunkler wird, wenn es weiter vom Betrachter entfernt ist - nur wenn Sie nach unten gehen. Ja, der Nebeleffekt setzt in der Ferne ein, aber nicht der Wasserdunkeleffekt.

Die Grundformel für die Berechnung der Beleuchtung wäre also so etwas in Pseudocode ...

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
}

Dies ist nicht perfekt für die Berechnung der Beleuchtung unter Überhängen oder in Höhlen, da es mit dieser Methode unter einem Überhang stockfinster wäre. Man könnte jedoch sowohl lokale Lichtquellen (Fackeln, Feuer usw.) hinzufügen als auch sonnenbeschienene Blöcke als Lichtquellen behandeln. So etwas könnte es tun ...

  1. Berechnen Sie für jeden Würfel das Licht der Sonne direkt von oben (über den obigen Pseudocode).
  2. Wenn sich neben einem Würfel eine Lichtquelle befindet, betrachten Sie ihn als voll beleuchtet (1.0)
  3. Wenn ein Würfel keine Sonne direkt von oben empfängt, geben Sie ihm Licht, je nachdem, wie weit er von einem voll beleuchteten Würfel entfernt ist. Näher bedeutet mehr Licht, weiter entfernt weniger (bis Null).

Die Idee dabei ist, dass, wenn ein Würfel von der Sonne oder einer Fackel beleuchtet wird, der Würfel daneben auch auf irgendeine Weise beleuchtet wird. Und je weiter Sie von diesem beleuchteten Würfel entfernt sind, desto weniger Licht wird es geben. Es ist eine Art Trick, diffuses Licht einzuschätzen, aber ich denke, (?) Es würde funktionieren.


1
Ja, ich bin mir ziemlich sicher, dass das die Eintrittskarte ist. Ich habe in meinem Spiel etwas Ähnliches gemacht.
MichaelHouse

Übrigens habe ich gerade deinen Blog zu meiner Google Reader Liste hinzugefügt. Byte56 - Entwicklerblogs FTW!
Tim Holt

Oh, warum danke ich dir? Ich habe diese Frage noch nicht beantwortet, aber ich habe gerade Ihren Blog über Professor Baileys Klasse gelesen. Ich war letztes Jahr in dieser Klasse! Ich bin mir ziemlich sicher, dass Sie diese Präsentation auch letztes Jahr gehalten haben. Ich dachte, dein Name kommt mir bekannt vor. Kleine Welt :)
MichaelHouse

3

Vielleicht verstehe ich die Frage falsch, aber warum können Sie die Farbe der Blöcke nicht einfach in Abhängigkeit von ihrer Tiefe ändern?

Wenn Sie die Tiefe d haben (in Blöcken, beginnend mit 0), wäre eine sinnvolle Gleichung für die Helligkeit:

L = (1 - m ) e - kd + m

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

k steuert, wie schnell es dunkler wird (höher = schneller). Ein sinnvoller Wert wäre 0,5.
m ist die gewünschte Mindesthelligkeit.
L variiert von 0 bis 1.

Wenn Sie nicht wissen, wie Sie die Farbe der Blöcke in der von Ihnen verwendeten Grafik-API ändern können, stellen Sie dies bitte als separate Frage (unter Angabe der von Ihnen verwendeten API und ob Sie Shader verwenden oder nicht).


Daran habe ich einfach nicht gedacht. Nur aus Neugier, woher hast du diese Gleichung?
Xavier

1
@ Xavier: Ich habe es gerade erfunden. Das e^-kdBit ist nur ein exponentieller Abfall, eine Standardfunktion für Dinge, die über einen bestimmten Wert (Tiefe) allmählich gegen Null tendieren. Die Multiplikation mit (1-m)und Addition von mskalieren und versetzen den Zerfall so, dass er bei einem Minimum von endet, maber immer noch bei beginnt 1. en.wikipedia.org/wiki/Exponential_decay
Peter Alexander

Die Blöcke mit einem tieferen Farbton sind nur sichtbar, wenn die Blöcke Alphafarben haben. In diesem Fall muss die Blockfarbe nicht geändert werden. Das Alpha erzeugt den Effekt automatisch.
Jonathan Connell

@Jonathan: Sie rendern die Wasserblöcke nicht, sondern rendern die Blöcke auf dem Meeresboden mit dunkler werdender Farbe und haben dann nur eine einzige Alpha-Ebene auf der Wasseroberfläche.
Peter Alexander

@Peter Alexander Ok, ich nahm an, dass in diesen Block-Spielen sogar das Wasser aus Blöcken bestand.
Jonathan Connell
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.