Tutorial Nebel

Aus DGL Wiki
Wechseln zu: Navigation, Suche

DGL Fogging Tutorial

Vorwort

Herzlich Willkommen zu meinem zweiten Tutorial für die DGL! ;) Diesmal werde ich mich einer Technik widmen, die als Fogging bezeichnet wird. Bevor ich allerdings erkläre, wie man diese Technik verwendet, werde ich erstmal ein paar Worte darüber verlieren, worum es beim Fogging geht und wie man davon profitieren kann.

Physik trifft auf norddeutsches Wetter!

Beginnen wir mit einem kleinen Beispiel aus der realen Welt: Blickt man bei diesigem Wetter aus dem Fenster, so fällt auf, dass weit entfernte Objekte im Nebel zu verschwinden scheinen. Je weiter ein Objekt vom Betrachter entfernt ist, desto stärker lässt Kontrast und Farbintensität nach. Grund für diesen Effekt ist, dass Luft nicht vollkommen transparent ist, sondern ein Teil des Lichts an Verunreinigungen (Nebel, Staub, Regen oder Rauch) gebrochen wird. Je dichter der Nebel und je weiter der Weg, den ein Lichtstrahl vom Objekt zum Betrachter zurückzulegen hat, desto weniger Licht kommt ungebrochen und damit in Originalfarbe an.

Was in der Realität gilt, das sollte möglichst auch in der Computergrafik berücksichtigt werden, sofern man einen realistischen Look erreichen will. Aber OpenGL wäre nicht OpenGL, wenn es uns nicht bereits eine Möglichkeit bieten würde, atmosphärische Effekte in Szenen nachzubilden: das Fogging. (von engl. Fog = Nebel)Beim Fogging passiert nichts weiter, als dass die Objekte einer Szene basierend auf dem Abstand zum Betrachter (Z-Buffer) und weiterer Parameter wie Nebel-Dichte und ähnlichem in eine Nebel-Farbe überblendet werden. Geschickt eingesetzt wirkt sich diese Technik nicht nur positiv auf die optische Qualität der Szene aus, sondern kann auch die Rendergeschwindigkeit erhöhen!

Was dicke Suppe mit hohen Frameraten zu tun hat.

Okay, wir wissen nun, was Fogging bedeutet, aber wann macht es Sinn, Fogging wirklich einzusetzen? Im Allgemeinen lohnt es sich vor allem dann, wenn man Szenen mit hohen Sichtweiten hat. Das wären z.B. die Außenlevel in First-Person-Shootern oder Flug-Simulatoren. Allerdings ist darauf zu achten, dass das Szenario atmosphärische Effekte zulässt. Es macht wenig Sinn, in Weltraum-Spielen Fogging zu verwenden - denn mangels Atmosphäre ist etwas nur schwer zu rechtfertigen, was für die atmosphärischen Effekte verantwortlich ist. ;)

Sieht man mal von der Ästhetik und dem Realismus ab, gibt es noch andere, nicht weniger wichtige, Gründe für den Einsatz von Fogging. Denn auch für die aktuellsten Monster-3D-Beschleuniger gilt: Die am schnellsten gezeichneten Polygone sind die, die man nicht sieht, und so werden häufig weit entfernte Objekte einfach bewusst beim Rendering nicht mehr berücksichtigt (Clipping). Nun sehen plötzlich aufploppende Bäume und halbe Häuser aber nicht besonders gut aus - eine unauffällige Möglichkeit, die Sichtweite einzuschränken, kommt da gelegen. Und hier kommt das Fogging ins Spiel: Ein bisschen Nebel über die Landschaft gelegt und schon tauchen unsere Bäume sanft aus dem Nebel auf, statt sich einfach an den Straßenrand zu beamen.

Eine spezielle Form des Fogging ist das Depth Cueing, welches die Tiefenwirkung in Szenen erhöht, wo es eigentlich keine atmosphärischen Effekte gibt. Das Depth Cueing funktioniert im Grunde genommen genau wie das normale Fogging. Die Intensität der Objekte einer Szene nimmt mit der Distanz also ab, nur wird dabei ins Schwarze überblendet.

Nebeltut fogsample.jpg

Mittlerweile dürfte klar geworden sein, was man unter Fogging versteht und wozu man diese Technik einsetzt. Bleibt nur noch zu klären, wie genau man nun den Nebel in die Delphi OpenGL-Anwendung bekommt:

Es wird Zeit für den Hauptgang!

Da Fogging bereits in OpenGL integriert ist, ist die Benutzung recht einfach. Es reicht, die Technik per glEnable(GL_FOG) einzuschalten und ein Reihe von Parametern zu setzen, die das Aussehen des Fog-Effekts steuern. Zum Einstellen der Parameter benötigt man nur einen Befehl, nämlich glFog. Dessen Signatur fordert zwei Parameter. Mit dem ersten bestimmt man, welche Eigenschaft gesetzt werden soll, der zweite Parameter ist der zu setzende Wert.

Will man z.B. die Farbe des Nebels festlegen, so ruft man glFogfv(GL_FOG_COLOR, fogColor) auf. fogColor ist dabei die Adresse eines Arrays, das 4 Fließkommazahlen enthält: Rot, Grün, Blau und Alpha. Dabei ist zu beachten, dass die Werte dieser Zahlen zwischen 0 und 1 liegen müssen. Klingt in der Theorie umständlich, ist aber eigentlich ganz leicht zu programmieren:

Zuerst definieren wir einen neuen Typ, der die Nebelfarbe enthalten soll.

type

TFogColor = record
  Red : single;
  Green : single;
  Blue : single;
  Alpha : single;
end;

nun können wir eine Instanz dieses Typs erstellen, die Farbe festlegen und mit glFog() setzen:

var FogColor : TFogColor;

FogColor.Red := 1; //Volles Rot
FogColor.Green := 0; //Kein Grün
FogColor.Blue := 0; //Kein Blau
FogColor.Alpha := 0; //Kein Alpha

glFogfv(GL_FOG_COLOR, @FogColor);

In Delphi liegen Farben üblicherweise nicht im OpenGL konformen RGB-Format vor, sondern als Konstante vom Typ TColor (clAqua, clBlack etc). Deshalb ist es nützlich, eine Funktion zu schreiben, die eine Farbe von TColor in TFogColor umwandelt:

Function getFogColor(color : TColor) : TFogColor;
begin
  result.Red := getrvalue(colorToRgb(color)) / 255;
  result.Green := getgvalue(colorToRgb(color)) / 255;
  result.Blue := getbvalue(colorToRgb(color)) / 255;
  result.Alpha := 0;
end;

Die Funktion ist schnell erklärt: colorToRGB wandelt einen TColor-Wert in eine RGB-Entsprechung der Farbe um. Dann wird der jeweilige Farbwert genommen und durch 255 geteilt, damit aus der Windows-typischen Byte-Darstellung (0 bis 255) das OpenGl konforme Format mit Fließkommawerten zwischen 0 und 1 wird.

Nachdem wir nun bestimmt haben, welche Farbe der Nebel haben soll, legen wir als nächstes fest, nach welcher internen Gleichung der Nebel berechnet werden soll. Nichts leichter als das! Der Befehl glFogi(GL_FOG_MODE, fogMode) ermöglicht die Wahl zwischen drei verschiedenen Modi: GL_EXP, GL_EXP2 und GL_LINEAR.

Hat man den Fogging-Mode GL_LINEAR gewählt, so verlangt OpenGL die Definition zweier weiterer Parameter. Mit glFogf (GL_FOG_START, fogStart) legt man fest, ab welcher Entfernung eines Objekts vom Betrachter der Nebeleffekt einsetzen soll. Außerdem muss noch eine Entfernung bestimmt werden, ab der ein Objekt komplett im Nebel verschwindet. Dies geschieht durch glFogf (GL_FOG_END, fogEnd); Ist die Entfernung eines Objekts vom Betrachter geringer als fogStart so wird es normal gezeichnet, ist die Entfernung größer als die mit fogEnd definierte maximale Sichtweite so wird es in der Nebelfarbe gezeichnet, aber was passiert dazwischen? Ganz einfach - es wird linear interpoliert. Ein kleines Beispiel zur Veranschaulichung: Als Nebelfarbe haben wir weiß definiert, fogStart liegt bei 10 und fogEnd ist 20 - ein zum Bildschirm paralleles rotes Quad, das in einer Entfernung von 15 gezeichnet wird, erstrahlt in einem hübschen Rosa. Es liegt nämlich genau in der Mitte des Nebel-Bereichs. Die Farbe eines Pixels setzt sich also jeweils zur Hälfte aus dem Rot des Quads und dem Weiß des Nebels zusammen.

Hat man sich bei der Wahl des Modus für GL_EXP oder GL_EXP2 entschieden, so wird die Nebelstärke mit einer exponentiellen Gleichung berechnet. Hierbei ist es vollkommen egal, welche Werte fogEnd bzw. fogStart haben. Stattdessen wird hier die Dichte des Nebels mit einbezogen. Je größer die Dichte des Nebels, desto schneller verschwinden die Objekte im Nebel. Gesetzt wird die Dichte durch glFogf(GL_FOG_DENSITY, fogDensity). Der als fogDensity übergebene Wert sollte dabei zwischen 0 und 1 liegen.

Der wesentliche Unterschied zwischen den exponentiellen Nebel-Gleichungen und der linearen liegt darin, dass die Nebelwirkung bei einem sich vom Betrachter entfernenden Objekt nicht gleichmäßig zunimmt. Stattdessen ist die Zunahme am Anfang sehr stark, je weiter das Objekt jedoch bereits entfernt ist, desto geringer wird sie. So verschwinden Objekte eigentlich nie ganz, auch wenn sie ab einer gewissen Entfernung fast nicht mehr zu sehen sind. Wo diese Entfernung liegt, lässt sich über die bereits erwähnte Dichte des Nebels einstellen. Der Unterschied zwischen GL_EXP und GL_EXP2 ist gering. Bei letzterem wird bei der Berechnung der Exponent quadriert, wodurch die anfängliche Zunahme der Nebelwirkung noch verstärkt wird.

Das Ende naht!

So… eigentlich habe ich alles Wichtige, was es zum einfachen Fogging zu wissen gibt, erwähnt. Natürlich könnte ich jetzt noch ein bisschen technischer werden und die Gleichungen hinschreiben. Aber das spar ich mir einfach mal und verweise auf die OpenGL-SDK oder das Redbook (und vorallem dieses Wiki). Um zu wissen, an welchen Parametern man drehen muss, um den gewünschten Effekt zu erhalten, ist eine anschauliche Erklärung in meinen Augen sinnvoller. Um ein Gefühl dafür zu bekommen, wie die Parameter einander beeinflussen, liegt diesem Tutorial noch ein Sample bei, wo sich alle Parameter zur Laufzeit einstellen lassen.

Mir bleibt jetzt nur noch eins: Das Tutorial mit den Worten „Happy Coding“ zu beenden! Ich hoffe, es hat gefallen und war lehrreich. ;)

Happy Coding,

Lithander

Dateien


Vorhergehendes Tutorial:
-
Nächstes Tutorial:
Tutorial_Partikel1

Schreibt was ihr zu diesem Tutorial denkt ins Feedbackforum von DelphiGL.com.
Lob, Verbesserungsvorschläge, Hinweise und Tutorialwünsche sind stets willkommen.