Nebel

Autor: Lars Hachmeister

Da wir aus Performancegründen immer nur einen gewissen Teil unserer Welt gleichzeitig laden können gibt es immer einen “Rand der Welt“ an dem diese abrupt aufhört. Des weiteren erscheinen an diesem Rand auch plötzlich neu geladene Chunks aus dem nichts. Diese Effekte sind nahezu unvermeidlich, können aber durch den Einsatz von Nebel deutlich weniger auffällig gestaltet werden.

Dazu werden Objekte, in Abhängigkeit von ihrer Distanz zum Spieler, bzw. der Kamera, unterschiedlich stark von der Farbe des Nebels beeinflusst. Objekte sind in diesem Fall das Gelände (die Hexagonalen Säulen) und die Pflanzen. Aus diesem Grund findet die Berechnung des Nebels in den Shadern der Chunks (chunk_std.frag) und der Pflanzen (plants.tes und plants.frag) statt. Die Berechnung unterscheidet sich, bis auf die Benennung einiger, weniger Variablen nicht voneinander, daher stammen die Code-Beispiele nur aus den Shadern der Pflanzen.

Zunächst wird der Vektor vom Spieler zum Objekt berechnet. Für Chunks geschieht dies direkt im Fragment-Shader, für Pflanzen wird er im Tesselation-Shader berechnet und als Out-Variable an den Fragment-Shader übergeben. Die Berechnung erfolgt dabei ganz einfach über die Differenz zwischen der Position der Kamera und der Position des Objektes. Bei der Berechnung wird die Z-Koordinate (die Höhe) ignoriert, dies hat zwei Gründe. Zum einen, da die Welt nur in der xy-Ebene unendlich weit generiert wird, ist nach oben zum Himmel oder nach unten zum Boden kein verstecken der zu beginn genannten Effekte nötig. Des weiteren, da die Position der Säulen an ihrem Fuß gespeichert ist, würden, wenn man die Z-Koordinate in die Berechnung einbezieht auch für viele nahe Säulen große Distanzen entstehen, was dazu führen würde das auch sie stark vom Nebel beeinflusst würden. Im Vergleich dazu würden Säulen deren Fuß sich über einer Höhle, und somit näher am Spieler, befindet deutlich schwächer vom Nebel beeinflusst.

    pos = worldPos - camera_pos;
    pos = vec3(pos.x, pos.y, 0);

Im Fragment-Shader wird dann, nach der Berechnung der eigentlichen Farbe, zunächst eine Variable Distance in Abhängigkeit der Länge des zuvor erhaltenen Vektors berechnet. Diese bestimmt im weiteren Verlauf wie stark der Einfluss des Nebel ist.

float distance = (length(pos) / 130) * (length(pos) / 130);
if (distance > 1) {
  distance = 1;
}

Hierbei wird die maximale Nebel-Stärke nach einer Distanz von 130 erreicht. Durch das Quadrieren tritt der Effekt deutlich schwächer an nahen Objekten auf, ohne einen plötzlichen Anfang ab einer gewissen Distanz zu haben. Dies wird weiter durch die HDR-Farben unterstützt, durch die der Nebel an nahen Objekten praktisch nicht zu erkennen ist. Werte über 1 müssen dabei abgefangen werden, da die zum Interpolieren verwendete OpenGL-Funktion mix für Werte über 1 ungewollte Ergebnisse zurückgibt. Daraus könnten, in diesem Fall, sogar negative Farbwerte entstehen, da sich die 'mix' Funktion wie folgt berechnet:

mix(Farbe1, Farbe2, Anteil) = Farbe1 * (1-Anteil) + Farbe2 * Anteil

Da die meisten Objekte (Farbe1) deutlich höhere HDR-Werte haben als der Nebel (Farbe2) würde dies für Werte größer 1 (Anteil) zu negativen Farbwerten führen. Durch das Abfangen sind so alle Objekte ab der Maximaldistanz gleichmäßig stark vom Nebel beeinflusst. Dies hat insbesondere den Effekt, dass alle neu geladenen Chunks, da sie hinter der Maximaldistanz liegen, zunächst nur mit maximaler Nebelstärke erscheinen.

Die Variable fog_time wird dafür genutzt, um die Farbe des Nebels von der Tageszeit abhängig zu machen. Dies dient dazu, dass der Nebel sich der Himmelsfarbe anpasst, um weniger hervorzustechen. Dabei muss er von einem dunkel Blau bei Nacht in einen weiß/grau-Ton bei Tag übergehen. Dies geschieht als einfache Approximation in Abhängigkeit der Z-Koordinate des (bereits zuvor normalisierten) Sonnenlichtvektors. Insbesondere ist dabei auch zu beachtet, dass die Farbwerte am Tag deutlich höher sein müssen, da der Nebel sonst aufgrund der hohen HDR-Werte anderer Objekte sehr dunkle Farben annimmt. Es reicht dabei aus, am Tag alle Farbwerte gleichmäßig zu erhöhen, da in den deutlich höheren HDR-Werten der um 0.05 erhöhte Blau-Wert nicht auffällt, und so ein grau/weiß entsteht.

float fog_time= -(sun_dir.z / 3) * 30;
if (fog_time < 0) {
  fog_time = 0;
}

Hier müssen wieder Werte unter 0 abgefangen werden, um unerwünschte Werte zu vermeiden und dafür zu sorgen, dass die Nebelfarbe im Verlauf der Nacht konstant bleibt.

Vergleich: Nebel bei Tag/Nacht

Nebel bei Tag



Nebel bei Nacht

Schlussendlich wird die zuvor angesprochene mix Funktion angewandt um linear zwischen der Ursprungsfarbe und der Nebelfarbe zu interpolieren, in Abhängigkeit von Distance. Distance wird dabei durch 1.5 geteilt, um so mindestens ein Drittel der ursprünglichen Farbe zu erhalten.

tmp_color = mix(tmp_color, fog_color, distance/1.5);

Zusammenfassend ist zu sagen, dass der von uns verwendete Nebel keines Falls den Anspruch hat physikalisch korrekt zu sein. Er dient lediglich als eine einfache Methode die Grenzen der geladenen Welt dem Spieler gegenüber zu verschleiern. Ein physikalisch korrekter Nebel müsste sehr viel mehr Faktoren beachten so zum Beispiel den genauen Einstrahlungswinkel der Sonne, das Wetter, Schatten, oder die Sonnenfarbe. Des weiteren müsste er auch Einfluss auf den Himmel nehmen, in unseren Fall also entweder auch in den Skydome- und Sonnen-Shadern beachtet werden oder insgesamt als eigene Struktur implementiert werden.

results matching ""

    No results matching ""