Bump/Normal Mapping und Shader-Performance verstehen

Der Herr der Hügel (Teil 2)
Kommentare

Windows Developer

Der Artikel „Der Herr der Hügel“ von Tam Hanna ist erstmalig erschienen im Windows Developer 7.2012
Normalisieren!
Damit sind wir auch schon beim Kern, also der eigentlichen Berechnung

Windows Developer

Der Artikel „Der Herr der Hügel“ von Tam Hanna ist erstmalig erschienen im Windows Developer 7.2012

Normalisieren!

Damit sind wir auch schon beim Kern, also der eigentlichen Berechnung angekommen. Er besteht aus nur fünf Codezeilen, die es aber in sich haben. Die erste Zeile ist dabei eher einfach: Sie nutzt einen zweiten Sampler, der die Farbwerte aus der Normal Map puhlt. Da Farbwerte normalerweise im Bereich von 0 bis 1 sitzen, bringen wir sie durch das Abziehen von 0,5 und die folgende Multiplikation mit 2 in den Bereich -1 bis 1. Im zweiten Teile der Routine ermitteln wir den neuen Vektor der Oberfläche. Dazu schnappen wir uns den Normal-Vektor des ursprünglichen Punkts und addieren zwei weitere Vektoren dazu. Die beiden hinzuzufügenden Vektoren beschaffen wir uns, indem wir die Binormale und die Tangente des Punkts mit Skalierungswerten verwursten. Abbildung 3 zeigt die Position der drei Vektoren im Raum. Man erkennt klar, dass man damit den Punkt fast beliebig schieben kann. Theoretisch könnte man den hier nicht verwendeten Blau-Kanal zum Verschieben in und aus der Richtung der Normale verwenden – das passiert hier aus Platzgründen nicht.

Abb. 3: Die beiden Vektoren erlauben das Verschieben im Raum
Abb. 3: Die beiden Vektoren erlauben das Verschieben im Raum

In Zeile Nummer 3 normalisieren wir den neuen Vektor des Objekts. Die Normalisierung ist eine auf den ersten Blick verwirrende Operation, die sich erst nach einiger Überlegung vollständig erschließt. Für Shader-Programmierer genügt erfreulicherweise ein simpleres, mentales Modell, das wir nun vorstellen wollen. Im Normalfall gibt ein Vektor zwei Informationen an: erstens den Abstand des Punkts zum Ursprung und zweitens die Richtung des Punkts. Normalisiert man einen Vektor, so entfernt man die Abstandsinformationen. Der resultierende Vektor gibt nur mehr die Richtung an. Sind zwei Punkte verschieden weit vom Ursprung entfernt, liegen aber in derselben Richtung, haben sie zwei verschiedene Vektoren. Normalisiert man diese, bekommt man zweimal denselben Wert heraus. In der vierten Zeile ermitteln wir, wie schon im Diffuse Lighting Shader, das Dot Product aus dem normalisierten Vektor der Lichtquelle und dem Vektor des neuen, also durch die Normal Map veränderten Pixels. Das Resultat davon ist eine Zahl, die den Grad der Übereinstimmung angibt. Weitere Informationen dazu finden sich in Teil 5. Zu guter Letzt clampen wir den Wert der Variable auf Null.

Was ist wie schnell?

Im Lauf der letzten Teile der Serie haben wir eine Vielzahl von Grafikeffekten kennengelernt. Mit ihnen können Sie sehr realistische Spielgrafiken erstellen; die fehlenden Shader-Effekte, wie Spiegelungen, finden sich im Internet. Offen blieb bis dato allerdings die Frage nach dem Rechenleistungsbedarf. Insbesondere beim Entwickeln von Spielen für Konsolen und/oder Smartphones – ihre Hardware ist bekanntlich schwer aufrüstbar – ist die Rechenleistung des Systems oft der beschränkende Faktor. In diesem Fall ist es sehr hilfreich, die Kosten der einzelnen Effekte genauer ermitteln zu können. Das wollen wir im nächsten Schritt mithilfe eines neuen Testprogramms tun. Es rendert auf Zuruf dieselbe Szene unter Verwendung jeweils anderer Routinen und Datenstrukturen.

Wenn wir die Beispiele der vorhergehenden Teile der Serie betrachten, bemerken wir, dass je nach Anwendungsszenario verschiedene Rendering Pipelines und verschiedene Modelle zum Einsatz kamen. In unserem Benchmark-Programm laden wir in LoadContent einfach alle benötigten Daten; in der Routine Draw können wir später Unterscheidungen treffen. Normalerweise synchronisiert die Xbox das Neuzeichnen des Bildschirminhalts mit dem Ausführen der Berechnungen. Auf diese Art wird das Berechnen unnötiger, da weniger anzeigbar. Frames werden eliminiert. In einem Spiel macht das aufgrund der eingesparten Energie Sinn. In einem Benchmark ist das hingegen kontraproduktiv, weshalb wir das Graphics-Objekt unseres Beispiels anpassen (Listing 4).

Listing 4

public Game1()
{
    graphics = new GraphicsDeviceManager(this);
    graphics.SynchronizeWithVerticalRetrace = false;
    this.IsFixedTimeStep = false;
    Content.RootDirectory = "Content";
    graphics.PreferredBackBufferHeight = 1080;
    graphics.PreferredBackBufferWidth = 1900;
}  

Interessant ist hier neben den Zeilen SynchronizeWithVerticalRetrace und IsFixedTimeStep auch das Festlegen der bevorzugten Höhe und Tiefe des Grafikfensters. Der Autor erbittet hier absichtlich HD, um die Konsole maximal auszulasten – wider Erwarten ist der Unterschied zwischen der kleineren und der größeren HD-Auflösung in unseren Tests minimal. Im ersten Testmodus verwendet die Xbox 360 ein Modell mit Materialfarben, das mittels BasicEffect und EnableDefaultLighting auf den Bildschirm gerendert wird. In Abbildung 4 sehen wir das Resultat dieser Rendering-Methode auf einer Xbox 360S. Die insgesamt 49 zu rendernden Würfel erscheinen mit einer Frame-Rate von rund 390 Frames pro Sekunde (in Spielerkreisen wird das oft als fps abgekürzt).

Abb. 4: Der BasicEffect erledigt Lichtberechnungen mit 389 fps
Abb. 4: Der BasicEffect erledigt Lichtberechnungen mit 389 fps

Am überraschendsten ist das Ergebnis, wenn man texturierte Kugeln ohne Berücksichtigung der Belichtung mit dem BasicEffect rendert. Die Xbox 360 des Autors kam in diesem Rendering-Modus auf Frame-Raten von rund 930 fps (Abb. 5). Aktiviert man hingegen die Beleuchtung, so pendeln sich die Frame-Raten im Bereich von rund 400 Frames, also immer noch etwas schneller als im Fall des normalen Würfels, ein (Abb. 6). Damit haben wir die mit dem BasicEffect realisierbaren Effekte (und das am Windows Phone 7 Darstellbare) hinter uns gelassen. Der total primitive Shader aus Teil 5 unserer Serie liefert – will man ihn ob der in Abbildung 7 gezeigten, hässlichen Darstellung wirklich verwenden – rund 490 Frames pro Sekunde.

Abb. 5: Texturen wandern - BasicEffect sei Dank - mit 938fps auf den Schirm
Abb. 5: Texturen wandern – BasicEffect sei Dank – mit 938fps auf den Schirm
Abb. 6: Textur und Beleuchtung schafft der BasicEffect mit rund 395 fps
Abb. 6: Textur und Beleuchtung schafft der BasicEffect mit rund 395 fps
Abb. 7: Der primitivste Shader arbeitet mit weniger als 500 fps; BasicEffect ist offensichtlich schneller
Abb. 7: Der primitivste Shader arbeitet mit weniger als 500 fps; BasicEffect ist offensichtlich schneller

Unser relativ grob arbeitender Diffuse Lighting Shader rechnet mit rund 480 Frames pro Sekunde – das Aktivieren von Diffuse Lighting braucht offensichtlich nicht besonders viel Performance (Abb. 8). Verwenden wir statt Diffuse Lighting das Look-up einer eher kleinen Textur, so reduziert sich die Frame-Rate auf rund 465 fps (Abb. 9).

Abb. 8: Ein Diffuse Lighting Shader frisst nicht allzu viel Performance
Abb. 8: Ein Diffuse Lighting Shader frisst nicht allzu viel Performance
Abb. 9: Ein Textur-Shader arbeitet langsamer als der BasicEffect
Abb. 9: Ein Textur-Shader arbeitet langsamer als der BasicEffect

Paradoxerweise erhöht sich die Leistung der Xbox 360 in Tests des Autors, wenn man zusätzlich zum Textur-Lookup auch Bump Mapping aktiviert. Da die hier abgedruckten Screenshots (Abb. 10) immer nur einen Momentanzustand angeben, ist der langfristige Trend der fps nicht sichtbar.

Abb. 10: Bump Mapping, im Prinzip ja nur eine Abart von Diffuse Lighting, braucht kaum Extraleistung
Abb. 10: Bump Mapping, im Prinzip ja nur eine Abart von Diffuse Lighting, braucht kaum Extraleistung

Noch etwas sollte man beim Ermitteln der Performance beachten: Hängt man den Debugger an seine Xbox 360 (startet das Programm also aus Visual Studio heraus über die XNA-Creators-Club-Applikation), so läuft er um gut 20 Prozent langsamer, als wenn man ihn aus dem Programmstarter der Xbox 360 heraus startet.

Aktiviert man über TOOLS | SETTINGS | EXPERT SETTINGS den Konfigurationsmanager, so kann man über BUILD | CONFIGURATION MANAGER den Kompilationsmodus von DEBUG auf RELEASE umstellen. Allerdings bringt das nur eine minimale Performancesteigerung. Führt man seine Tests stets im Debug-Modus durch, startet dabei das Programm aber aus dem Launcher heraus, ist man auf der sicheren Seite. Zum Vergleich sei auf Tabelle 1 verwiesen. Sie vergleicht die in den Abbildungen gezeigten Performancedaten mit denen, die bei einem direkten Start aus dem Launcher heraus erreicht werden.

Tabelle 1: Das Deaktivieren des Debuggers beschleunigt die Programmausführung

Shader-Typ fps bei Debugger fps bei Launch
BasicEffect, DefaultLighting 390 492
BasicEffect, Textur 940 1165
BasicEffect, Textur + Licht 395 502
Primitiver Shader 490 704
Ambient/Diffuse 481 671
Textur 465 661
Normal Map 463 651
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -