ZFX
ZFX Neu
Home
Community
Neueste Posts
Chat
FAQ
IOTW
Tutorials
Bücher
zfxCON
ZFXCE
Mathlib
ASSIMP
NES
Wir über uns
Impressum
Regeln
Suchen
Mitgliederliste
Membername:
Passwort:
Besucher:
4420807
Jetzt (Chat):
35 (0)
Mitglieder:
5239
Themen:
24223
Nachrichten:
234554
Neuestes Mitglied:
-insane-
ZFX - KurzartikelDruckversion

Einführung in Vertex- und Pixel-Shader
Der Kurzartikel von Stefan Zerbst

© Copyright 2002 [whole tutorial] by Stefan Zerbst
Die Texte der Tutorials unterliegen dem Copyright und drfen ohne schriftliche Genehmigung vom Author weder komplett noch auszugsweise vervielfltigt, auf einer anderen Homepage verwendet oder in sonst einer Form verffentlicht werden. Die zur Verfgung gestellten Quelltexte hingegen stehen zur freien Weiterverwendung in Software Projekten zur Verfgung.
Die Quelltexte dieses Tutorials werden auf einer "as is" Basis bereit gestellt. Der Autor bernimmt keinerlei Garantie fr die Lauffhigkeit der Quelltexte. Fr eventuelle Schden die sich aus der Anwendung der Quelltexte ergeben wird keinerlei Haftung bernommen.


Einleitung

Dieses Tutorial soll dazu dienen, die grundlegenden Begrifflichkeiten Vertex-Shader und Pixel-Shader zu klren. In diesem Tutorial geht es noch nicht darum, eine Implementierung aufzuzeigen. Dies wird Bestandteil spterer Tutorials sein. An dieser Stelle interessiert uns daher erst mal, womit wir es eigentlich bei den Shadern zu tun haben. Diese Shader wurden in Hardware erstmals durch die nVidia GeForce3 Grafikkarten eingefhrt, und durch die DirectGraphics 8 API und entsprechende OpenGL Extensions untersttzt. In OpenGL Terminologie heissen diese Shader allerdings Programs, also beispielsweise Vertex-Program anstelle von Vertex-Shader. Im folgenden bleiben wir jedoch bei der DirectX Terminologie.


Shader in der 3D Pipeline

Um zu verstehen, was Shader sind, und wie sie arbeiten, mssen wir einen Blick auf die 3D-Pipeline werfen, wie sie eine moderne 3D-API im Zusammenspiel mit einer modernen 3D- Grafikkarte verwendet. Die folgende Abbildung 1 aus der DirectX 8 SDK Doku zeigt eine grafische bersicht dieser Pipeline.


Abbildung 1: Direct3D Pipeline

Am oberen Ende der Pipeline fttern wir Vertexdaten entweder direkt oder ber Modelle und grafische Primitive in die Pipeline ein. Von da ab bernimmt die Pipeline mit den von uns gestellten Parametern die Arbeit an den Vertexdaten, bis diese als schne Pixel auf dem Bildschirm (Frame-Buffer) erscheinen. Nun erkennen wir, dass diese Pipeline nicht gradlinig verluft, sondern an zwei Stellen eine Abzweigung aufweist. Hier knnen die Vertices jeweils einen von zwei optionalen Wegen gehen ... oder besser gesagt, wir geben ihnen vor, welchen der beiden mglichen Wege sie gehen mssen.

Zuerst einmal mssen die Vertices transformiert und beleuchtet werden. Bereits hier gibt es die erste Entscheidungs-Alternative fr uns. Traditionell gelangen die Vertices in die T'nL Sektion der Pipeline. Hier werden sie ber die gesetzten Welt-, Kamera- und Projektionsmatrizen transformiert und dann optional ber aktive Lichtquellen beleuchtet wenn diese Option in der API aktiviert ist. Je nach Grafikkarte werden diese beiden Schritte entweder in Software durch die API oder direkt in Hardware auf der Grafikkarte durchgefhrt. Seit Einfhrung der Shader steht jetzt aber eine Alternative zur Verfgung, nmlich der Vertex-Shader. 
Smtliche bisher genannten Berechnungen werden auf den Vertexdaten durchgefhrt. Nun knnen wir aber direkt die GPU, also den Prozessor der Grafikkarte, ber Vertex-Shader programmieren. So ein Vertex-Shader ist ein Assembler-hnliches Programm, welches auf jeden Vertex angewendet wird, der in seine Finger kommt. Anstatt also den traditionellen T'nL Weg zu gehen, knnen wir alternativ einen Vertex-Shader zur Grafikkarte schicken, der dann smtliche Operationen an den Vertexdaten vornimmt. Dabei bedeutet alternativ aber wirklich, dass es sich bei dem Vertex-Shader nicht um zustzliche Berechnungen handelt. Wenn wir einen Vertex-Shader fr die Grafikkarte einstellen (den man jederzeit ndern oder deaktivieren kann), dann mssen wir smtliche Transformationen und Beleuchtungen an den Vertexdaten entweder vor dem Rendern selber in unserem Programm implementieren oder ber den Vertex-Shader berechnen lassen. Die traditionelle T'nL Komponente steht uns nicht mehr zur Verfgung, sie wird durch den Vertex-Shader komplett ersetzt.

Nach diesem Schritt sind smtliche Berechnungen auf den Vertexdaten abgeschlossen. Nun gehen wir weiter in der Pipeline. Im nchsten Schritt fhrt die API/Grafikkarte das Backface Culling, Viewport Mapping usw. durch und rasterisiert zweidimensionale Dreiecke aus den ursprnglich dreidimensionalen Vertexdaten. Jetzt haben wir keine Vertexdaten mehr, sondern Pixeldaten. Im nchsten Schritt der Pipeline haben wir die zweite Entscheidungsmglichkeit. Traditionell folgt nun die Multitexturing Komponente der API/Grafikkarte. Hier werden verschiedene Texturen auf die Pixeldaten geblendet um verschiedene Effekte zu erzielen. Beispielsweise eine Basistextur, eine Lightmap und eine Detailmap. Seit Einfhrung der Shader gibt es aber nun die Mglichkeit, alternativ einen Pixel-Shader einzusetzen. Dieser Pixel-Shader ist, ebenso wie ein Vertex-Shader, ein Assembler-hnliches Programm, welches wir an die Grafikkarte senden. In diesem Programm knnen wir dann direkt auf der GPU die Pixeldaten frei nach unseren Vorstellungen manipulieren.

Nachdem dann in der Pipeline alle pixelbasierten Berechnungen ausgefhrt wurden, entweder traditionell ber multiple Texture-Stages oder ber einen Pixel-Shader, folgen die restlichen Berechnungen, die die API/Grafikkarte durchzufhren hat. Das sind Nebel-Effekte, Alpha-, Stencil- und Depth-Tests, das Alpha-Blending und schliesslich das Rendern auf das Render-Target (Textur oder Frame-Buffer). Und damit haben wir auch schon die gesamte 3D-Pipeline gedanklich im Griff.


Missverstndnisse und Wahrheiten ber Shader

In Bezug auf Shader gibt es immer wieder das eine oder andere Missverstndnis, was genau Shader eigentlich sind und welchem Zweck sie dienen. Grundstzlich ist hier festzustellen, dass der Name Shader an sich schon mal etwas irrefhrend ist. Zwar verwendet man Vertex- und Pixel-Shader durchaus dazu, Shading-Operationen (also Beleuchtung usw.) durchzufhren. Dies ist aber nur ein kleiner Teil der mglichen Aufgaben, die ein Shader bernehmen kann. Allerdings sind die Mglichkeiten der Shader bei weitem nicht unendlich. Zu den Vertex-Shader gibt es beispielsweise folgende Einschrnkungen zu beachten. Sobald man einen Vertex-Shader verwendet verliert man die Transformation und Beleuchtung durch die API. Man muss also wirklich smtliche Transformationen und Beleuchtungen dann selber durch den Vertex-Shader durchfhren lassen. Eine weitere Verwechslung besteht darin, dass einige Leute glauben durch Vertex-Shader knne man Vertexdaten erzeugen. Und das ist schlichtweg falsch. Abgesehen von Bezier-Patches, wo Vertices zwischen Kontrollpunkten von der Grafikkarte generiert werden, ist es unmglich, neue Vertices durch einen Shader zu erzeugen. Ein Vertex-Shader arbeitet nur mit den Daten, die man der Grafikkarte durch entsprechende Render-Aufrufe (z.B. ber einen Vertex-Buffer) geschickt hat. Es gilt auch zu beachten, dass immer nur ein Vertex-Shader auf der Grafikkarte aktiv sein kann. Diesen kann man beliebig oft wechseln, aber mehr als je ein Vertex- und Pixel-Shader kann von keiner heutigen Grafikkarte verarbeitet werden. Weitere Einschrnkung erfahren die Shader dadurch, dass ein Shader nicht ein unendlich langes Programm sein kann. Ein Vertex-Shader darf beispielsweise maximal 128 Instruktionen enthalten, ein Pixel-Shader gerade mal 12 bis 28 Instruktionen (je nach Pixel-Shader Version der Grafikkarte).
Eines der grten Miverstndnisse bei Pixel-Shadern ist die Meinung, dass Pixel-Shader auf dem Frame-Buffer arbeiten und nur die Pixel des Bildschirms bearbeiten. Das ist nicht so. Pixel-Shader arbeiten auf den rasterisierten Daten der Dreiecke der zu rendernden Geometrie. Jeder Pixel der durch die Pipeline kommt, wird durch den Pixel-Shader bearbeitet. Ob dieser nachher durch andere Pixel berschrieben wird, ausserhalb des Screen-Space liegt und durch Transparenz-Effekte untergeht, ist irrelevant fr den Pixel-Shader.

Bleibt noch die Frage, warum es diese Shader berhaupt gibt, wenn sie uns doch so viel zustzliche Arbeit bescheren beispielsweise die Transformation und Beleuchtung der Vertexdaten zu Fuss. Nun, ber die Shader ist es mglich die Daten wesentlich flexibler zu beeinflussen, als dies ber die bisherige, fixed-function Pipeline ohne Shader der Fall war. Hier will ich nur ein kleines Beispiel nennen. Untersttzt DirectX bisher beispielsweise lediglich Flat- und Gouraud-Shading fr die Beleuchtung, und verwendet dazu im letzteren Fall Interpolation zwischen den ber die Vertices-Normalenvektoren berechneten Vertexfarben. ber Vertex-Shader ist es nun mglich, selbst Farbwerte fr die Lichter zu berechnen, und zwar ber die GPU und nicht mehr ber die CPU. Das entlastet unsere CPU schon ziemlich. Doch es wir noch besser. ber Pixel-Shader kann man durchaus die Beleuchtung vollkommen frei nicht je Vertex und dann per Interpolation, sondern in Echtzeit je Pixel berechnen. ber die fixed-function Pipeline ist das nicht mglich, und beispielsweise einer der Tricks, mit denen die Doom III Engine auch mit Low-Poly Modellen unheimlich realistisch wirkende 3D-Modelle darstellen kann, da diese abhngig von der Position zur Lichtquelle per Pixel in Echtzeit beleuchtet und schattiert werden.


Shader und Versionen

Wie fast bei jeder extrem dynamischen Technologie, die sich stetig und mit hohem Tempo weiterentwickelt, gibt es Probleme was die Verbreitung angeht. Grundstzlich ist es zwar beispielsweise mglich, Vertex-Shader in Software zu emulieren (was DirectX im SOFTWARE_VERTEXPROCESSING Modus auch automatisch tut), bei Pixel-Shadern ist dies jedoch nicht mglich. Eine Emulation ist zudem auch nicht empfehlenswert, weil dies so langsam ist dass das Programm nicht mehr in Echtzeit ausgefhrt werden kann. Pixel-Shader knnen nicht emuliert werden, weil die eigentlichen Pixeldaten ja erst auf der Grafikkarte bei der Rasterisation erzeugt werden.

Die Moral von der Geschichte ist jedenfalls, dass es nur Sinn macht Vertex- und Pixel-Shader zu entwickeln, die auf der Zielhardware auch untersttzt werden. Bei den Vertex-Shadern sieht es so aus, dass es die Versionen 1.0, 1.1 und 2.0 gibt. Verwendet man die neuesten Treiber fr die jeweilige Grafikkarte, dann sollte die Version 1.1 berall zur Verfgung stehen. Mit Einfhrung von DirectX 9 wird die Version 2.0 langsam aber sicher zum aktuellen Standard bei den Vertex-Shadern.

Bei den Pixel-Shadern ist dies ein wenig schwieriger. Da diese nicht emuliert werden knnen, helfen auch neue Treiber fr Grafikkarten wenig, denn die Hardware muss fr die entsprechende Version ausgelegt sein. Hier gibt es die Version 1.0, 1.1, 1.2, 1.3 und 1.4. Man kann auch hier davon ausgehen, dass die Version 1.1 von allen aktuellen Grafikkarten, die berhaupt Pixel-Shader untersttzen, auch in Hardware implementiert ist. Die neueren Versionen erweitern die Pixel-Shader zwar um gewisse Funktionalitten und auch die Verfgbarkeit von mehr Instruktionen in einem Shader. Jedoch kann man nicht davon ausgehen, dass selbst Grafikkarten der Klasse GeForce3 Ti diese Versionen untersttzen. Die Version 1.4 ist sogar zur Zeit nur den allerneuesten Grafikkarten-Modellen vorbehalten.

Und die Moral von der Geschicht', verwende die neuesten Technologien nicht. Oder wenigstens nicht exklusiv ohne Alternative Implementierung falls die Hardware auf der das Programm luft eben nicht die neueste ist. Bei Pixel-Shadern sollte man heutzutage noch bei der Version 1.1 bleiben und sich bewut sein, dass bereits die Verwendung von Pixel-Shadern nur von Grafikkarten der Klasse GeForce 3 / 4 Ti berhaupt untersttzt wird und auf anderen Karten nicht emuliert werden kann.


Vertex Shader

Nachdem wir nun die theoretischen Grundlagen der Shader erarbeitet haben, knnen wir uns ein wenig nher mit den Vertex-Shadern befassen. Die Abbildung 2 zeigt eine Darstellung der Komponenten, die ein Vertex-Shader verwenden kann. Das Bild stammt aus der Doku des DirectX SDK. Kern der Berechnungen fr einen Vertex-Shader ist die sogenannte Vertex-ALU, die Vertex Arithmtic Logic Unit. In dieser ALU finden smtliche Berechnungen statt, die im Vertex-Shader durch Instruktionen festgelegt sind. Doch die Daten mssen auch irgendwie zu der ALU gelangen. Aus unserem Programm heraus setzen wir beispielsweise einen Vertex-Buffer als Input-Stream fr die Grafikkarte fest, und beschreiben dann ber Definitionen des Vertex-Shaders, welche Daten eines Vertex aus dem Buffer in welches Input-Register vor der ALU kommen. Zur Verfgung stehen die 16 Input-Register V0 bis V15. Verwendet man die fixed-function Pipeline, so legt man ber das FVF fest, welche Vertex-Komponente (Normalen-Vektor, Koordinaten, usw.) im Vertex-Format vorhanden ist, und Direct3D weist diese einzelnen Komponenten dann entsprechenden Input-Registern zu. Diese Arbeit erledigen wir bei der Implementierung eines Vertex-Shaders nun selbst, und knnen dabei alle 16 Register frei verwenden.


Abbildung 2: Vertex-Shader ALU

Als nchstes gibt es die sogenannten Konstanten-Register. Aus diesen kann die ALU, wie auch aus den Input-Registern, nur Daten lesen, aber nicht in sie schreiben. Die Konstanten-Register enthalten Daten, die fr alle Vertexdaten gleich sind. Hier gibt es eine Funktion des DIrect3D Device Interfaces, mit welcher man beliebige Daten in die Kontanten-Register schreiben kann. Beispielsweise sind die die Transformations-Matrix, Positionen von Lichtquellen und hnliches. Von solchen Konstanten-Registern bieten die Grafikkarten mindestens 96 an, einige neuere Grafikkarten noch mehr. Jedes dieser Register kann brigens 128 Bit Daten halten (analog zu SSE SIMD), also einen Vektor mit vier float Werten. Entsprechend belegt eine Matrix dann vier Konstanten-Register.

Durch die Instruktionen im Vertex-Shader kann man nun Werte aus den Konstanten-Registern auslesen und Berechnungen mit diesen Werten und den Daten aus den Input-Registern ausfhren. Als Zwischenspeicher fr Zwischenergebnisse (da man in Input- und Konstanten-Register nicht schreiben kann) stehen zwlf temporre Register zur Verfgung, die auch jeweils 128 Bit an Daten fassen. Die ALU kann sowohl Daten in die Register schreiben, als auch Daten aus ihnen lesen.

Am Ende der Berechnung schiebt die ALU dann die Werte in die Output-Register fr Vertex-Daten. Oder besser gesagt, es ist unsere Aufgabe als Programmierer eines Vertex-Shaders, die entsprechenden Daten nach den Berechnungen durch Vertex-Shader MOV Anweisungen in die zugehrigen Output-Register zu verschieben. Das ist selbst dann ntig, wenn keine Berechnungen auf den Daten gemacht wurden. Die Output-Register haben mehr oder weniger sprechende Namen. Beispielsweise mssen wir die Position eines Vertex aus dem entsprechenden Input-Register in das Output Register namens oPos verschieben, welches die Vertexdaten in transformierten Bildschirmkoordinaten erwartet.

Noch mal zur Wiederholung: Ein Vertex-Shader wird, wenn er aktiv ist, als Berechnung fr jeden Vertex angewendet der ber irgendeine der Draw-Funktionen zur Grafikkarte geschickt wird. Dieses Schicken positioniert die Vertexdaten entsprechend der Vorschrift des Programmierers in den Input.Registern aus denen der Vertex-Shader die Daten ausliest und bearbeitet. Die Grafikkarte erwartet dann, nach der Ausfhrung des Vertex-Shaders, entsprechende Daten in den Output-Registern. Diese werden dann in der Multitexturing-Einheit oder einem Pixel-Shader als Input verwendet.
Man sollte auch die Tatsache beachten, dass ein aktiver Vertex-Shader fr jeden Vertex einmal komplett durchgerechnet wird. wenn es also Daten zu berechnen gilt, die fr jeden Vertex eines Renderaufrufs gleich sind (beispielsweise die Transformations-Matrix), dann sollte man diese Berechnung durch das Programm ausfhren lassen, und dem Vertex-Shader diese vorberechneten Daten ber ein Konstanten-Register zugnglich machen. Wrde man diese Berechnungen im Vertex-Shader durchfhren, so wrde sie beispielsweise fr ein Modell mit 3333 Vertices auch 3333 durchgefhrt, obwohl ein einziges Mal gereicht htte. Der Overhead beim Befllen eines Konstanten-Registers der GPU vom RAM aus ber den BUS ist hierbei zu vernachlssigend gering.


Pixel Shader

Whrend Vertex-Shader also mit Vertices arbeiten, und Vertex-Daten manipulieren, arbeiten Pixel-Shader natrlich mit Pixeln, und zwar mit allen Pixeln einer grafischen Primitive die rasterisiert wurden. Pixel-Shader dienen dabei ausschlielich der Aufgabe, die Farbe des Pixels zu berechnen. Diese Farbe ergibt sich zum einen aus der Beleuchtung des Pixels, aber natrlich auch durch eine oder mehrere Texturen, Nebel und Alphablending. Ein Pixel-Shader ersetzt also die Multitexturing Einheit der 3D-Pipeline. Werfen wir mal einen Blick auf die Abbildung 3. Diese zeigt die Architektur der Pixel-Shader Einheit der Grafikkarte.


Abbildung 3: Pixel Shader ALU

Den ersten Input liefern die beiden Register v0 und v1. Diese beiden entsprechen den Output Registern oD0 und oD1 der Vertex-Shader Einheit, welche in der Abbildung 2 als Color0 und Color1 bezeichnet sind. In der Regel verwendet man diese, um dort ber den Vertex-Shader die berechneten Farben eines Vertex fr den Pixel-Shader (oder die Multitexturing Einheit) bereitzustellen (einmal Diffuse- und einmal Specular-Color). Natrlich kann man ber diese Register und einen Vertex-Shader auch beliebige anders zu verwendende Daten bergeben, wenn man ein dynamisches Duo aus eigenem Vertex- und Pixel-Shader implementiert.

Ebenso wie die Vertex-Shader Einheit hat auch die Pixel-Shader Einheit temporre Register fr 128 Bit Werte (Array von vier float's) namens r0 und r1. Dabei ist aber zu beachten, dass das Register r0 am Ende der Berechnungen als Output-Register verwendet wird. Der Inhalt dieses Registers wird dann auf den Wertebereich [0.0f, 1.0f] gerundet und als Ergebnis an die weiteren Stufen der 3D-Pipeline weitergereicht.

Die Konstanten-Register eines Pixel-Shaders sind mit denen eines Vertex-Shaders vergleichbar, lediglich der Wertebereich ist ein wenig anders definiert. Er sollt eim Bereich [-1.0f, +1.0f] liegen.

Die Textur-Register eines Pixel-Shaders liefern quasi die Texturkoordinaten eines Pixels, welche jedoch in der Regel sofort in den entsprechenden Farbwert aus der Textur umgewandelt werden. Hier gibt es einige Unterschiede zwischen den verschiedenen Pixel-Shader Versionen, daher gehe ich nicht nher darauf ein. Die Anzahl der verwendeten Register ist natrlich abhngig von der Anzahl der verwendeten Texture-Stages.


Toll, aber wozu brauche ich nun wirklich Shader?

Hm, wer sich diese Frage jetzt immer noch stellt, der hat anscheinend noch nicht aufmerksam genug mitgelesen. Vernnftig programmierte Shader bieten zwei Vorteile. Zum einen kann man mit ihrer Hilfe Berechnungen, die die fixed-function Pipeline auch durchfhren knnte, noch schneller durchfhren. Zum anderen kann man ber Vertex- und Pixel-Shader Effekte erzeugen, die mit der fixed-function Pipeline nicht mglich sind. Bei Direct3D ist man beispielsweise auf alle zur Verfgung gestellten Texture Stage Settings angewiesen. Also die Color- und Alpha- Argumente und Operationen. Das sind zugegebenermaen recht viele, doch mit Shadern kann man sich alle mglichen Berechnungen auf allen mglichen Argumenten selber programmieren. 

Mit einem Vertex-Shader kann man beispielsweise Character-Animation effizient programmieren, da man bei der Skin & Bones Technik alle Bones in Form von Matrizen ber konstante Register an einen Vertex-Shader senden kann. So kann man alle Vertices des Modells gleichzeitig zum Rendern schicken, und die Transformation der Skin anhand der Bones direkt auf der Grafikkarte durchfhren, ohne immer fr jeden Bone nur die jeweils abhngigen Vertices der Skin an die Grafikkarte schicken zu knnen. So spart man viel Zeit.

Durch Shader kann man beispielsweise auch die Limitierung der fixed-function Pipeline auf beispielsweise acht Hardware-Lights aufheben. Man kann so viele Lichtquellen in die Berechnung einbauen, wie man bentigt. Durch Pixel-Shader kann man aber nicht nur die Beleuchtung von 3D Grafik pixelgenau erzeugen, sondern man kann auch andere Effekte ganz einfach einbauen. Beispielsweise die Umrechnung aller gerenderten Pixel in ein Schwarz-Wei Bild fr Zwischensequenzen oder ein Edgar-Wallace Computerspiel :-)

Wie man sieht sind die Mglichkeiten der Shader sehr umfangreich. Durch Einfhrung von Hochsprachen wie bei DirectX 9 oder NVidia's CG wird dem Programmierer die Arbeit sogar noch einfacher gemacht. Man sollte aber bei all der Euphorie nie vergessen, immer eine Fallback-Option fr ltere Hardware zu bieten. Die Emulation umfangreicher Vertex-Shader ist natrlich wesentlich langsamer, als die fixed-function Pipeline und sollte daher in Release-Produkten nie verwendet werden. Die Emulation der Pixel-Shader ist ja wie bereits erwhnt gar nicht mglich. Da Shader aber wirklich nur in den teuren Grafikkarten untersttzt werden, muss man in seinen Programmen wenigstens fr die nchsten kommenden Jahre immer auch eine Alternative fr die fixed-function Pipeline zustzlich implementieren - so lange bis man davon ausgehen kann, dass auch der letzte potentielle Spieler eine entsprechende Grafikkarte hat.



Informationen: " ); ?> " ); ?> " ); ?> " ); ?>
WWW :$wwwtitel
Data:Projektdateien
Mail:$author ($email)
Nick:$nick