Erste Schritte mit Shadern unter XNA

Shader werfen bunte Schatten
Kommentare

„BasicEffects“ sind eine bewährte Methode, um Farbeffekte zu erzielen, doch es geht auch anders. Dieser Artikel zeigt die Grundlagen für den Einsatz von Vertex- und Pixel-Shadern und was bei deren Nutzung zu beachten ist.

Die letzten beiden Artikel zeigten, wie man einen dreidimensionalen „Quälkasten“ konstruiert, in dem man einen Affen geräuschvoll mit Bällen bewerfen kann. Er funktioniert auf dem Notebook, der XBox und dem Windows Phone 7. Was im Hintergrund geschieht, blieb bisher noch verborgen. Der grundlegende Prozess folgt aus folgender Logik: Die von den Modellen gelieferten Vertices werden auf den Bildschirm transformiert. Danach zerlegt man sie in einzelne Pixel. Diese muss man auf den Bildschirm „zeichnen“, damit der Benutzer sie wahrnehmen kann. Seit Jahren erledigen dedizierte Grafikkarten einige dieser Aufgaben: Der in Abbildung 1 gezeigte Arbeitsablauf ist als FFP oder Fixed Function Pipeline bekannt.

Abb. 1: Die FFP als stark vereinfachtes Diagramm
Abb. 1: Die FFP als stark vereinfachtes Diagramm

Ab der achten Version der Grafikschnittstelle DirectX gab es stattdessen eine programmierbare Pipeline, die in Abbildung 2 dargestellt ist. Der Unterschied zwischen der FFP und der programmierbarer Pipeline lässt sich, wenn man die beiden Abbildungen vergleicht, auf den ersten Blick erkennen: Die Prozesse Vertextransformation und Pixeleinfärbung sind in der FFP Einzelschritte, in der programmierbaren Variante hingegen jeweils eigene Prozesse. Diese bezeichnet man in der Fachsprache als „Shader“. Der Vertex-Shader bearbeitet (transformiert) die eingehenden Vertices, während der Pixel-Shader auf Pixelebene arbeitet und Farbinformationen etc. einpflegt. Logischerweise ergibt sich dadurch eine enorme Flexibilität. In den nächsten Teilen dieser Serie sehen Sie Effekte, deren Realisierung Sie mit den in Abbildung 2 gezeigten Kästchen sicher kaum für möglich halten würden. Noch eine Anmerkung für die C++-Programmierer unter den Lesern: Ab der Version 10 von Direct3D können so genannte Geometry Shaders in einem Tesselation genannten Prozess neue Vertices „aus dem Nichts heraus“ konstruieren. Dieser Prozess ist hier aber nicht Thema, da XNA auf DirectX 9.0 basiert.

Abb. 2: Pipeline mit programmierbaren Elementen
Abb. 2: Pipeline mit programmierbaren Elementen
Mein erster Shader

Das im vorangegangenen Teil der Serie realisierte Programm verwendet logischerweise auch Shader, um die Elemente auf dem Bildschirm anzuzeigen. Allerdings erledigt Microsoft die Shader-Erstellung für uns: Der Schlüssel liegt in der verwendeten BasicEffect-Klasse. Sie kann die meisten primitiven Shader-Effekte ohne Shader-Code abbilden. Unser Beispiel soll nun von BasicEffect befreit werden. Leider geht dabei die Kompatibilität zu Windows Phone 7 verloren. Die Plattform unterstützt nämlich (aufgrund der Implementation des XNA Reach Profiles) anstatt eigener Shader nur die folgenden fünf Effektklassen, die an anderer Stelle näher behandeln werden sollen:

  • AlphaTestEffect
  • BasicEffect
  • DualTextureEffect
  • EnvironmentMapEffect
  • SkinnedEffect

Die in den folgenden Artikeln verwendeten Shader basieren auf einer HLSL (High Level Shader Language) genannten Programmiersprache, die stark an C angelehnt ist. Nach dem Öffnen des in den vorigen Teilen verwendeten Beispiels XBox3603D1 (samt dem klassischen Swing Sound von Kevin MacLeod) klicken wir das Ressourcenprojekt rechts an und erstellen einen Ordner namens Shaders. Darin erstellen wir per Rechtsklick und ADD NEW FILE ein neues Effect File namens FirstEffect.fx. Der vom Wizard erstellte Skelettcode sieht wie in Listing 1 aus.

Listing 1
float4x4 World;
float4x4 View;
float4x4 Projection;

// TODO: add effect parameters here.  

Der erste Teil der Shader-Datei enthält die Parameter. Sie sind insofern von Bedeutung, als dass sie die Methode zur Kommunikation zwischen XNA-Programm und Shader-Programm darstellen. Will man dem Shader etwas mitteilen, muss man das durch einen Parameter tun. In unserem Fall gibt es derzeit nur drei Parameter: die schon aus dem vorigen Artikel bekannten Matrizen. Der Typ float4x4 ist dabei ein HLSL-spezifischer Variablentyp, der ein 4×4-Array aus Floats beschreibt. Wir werden im Laufe unserer Experimente mit XNA noch einige solche Typen kennenlernen.

Der nächste, in jeder Shader-Datei vorkommende Teil, ist die Definition der Eingabe- und Ausgabe-Structs (Listing 2). Dabei fällt alten C-Hasen garantiert etwas Seltsames auf: Die Definition der Members der Strukturen sieht stark nach einem Bit-Feld aus. Das ist syntaktisch gesehen sogar richtig. Allerdings wird durch diese, in HLSL Semantics genannte Tags festgelegt, welche Aufgabe die Variablen haben. Unsere Variablen erfreuen sich des Typs POSITION0. Das heißt, dass es sich dabei um Positionsdaten eines Vertex‘ handelt. Der Datentyp float4 steht für ein vier Elemente umfassendes, eindimensionales Array.

Listing 2
struct VertexShaderInput
{
  float4 Position : POSITION0;

  // TODO: add input channels such as texture
  // coordinates and vertex colors here.
};

struct VertexShaderOutput
{
  float4 Position : POSITION0;

  // TODO: add vertex shader outputs such as colors and texture
  // coordinates here. These values will automatically be interpolated
  // over the triangle, and provided as input to your pixel shader.
};  

Der nächste Teil des Codes besteht aus Funktionen. In unserem Fall gibt es davon zwei: Eine realisiert den Pixel-Shader, die andere den Vertex-Shader. Als Erstes wollen wir uns dem Vertex-Shader zuwenden. Er ist relativ primitiv und realisiert im Prinzip nur das Multiplizieren der Matrizen, das wir vorher von Hand erledigt haben (Listing 3).

Listing 3
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
  VertexShaderOutput output;

  float4 worldPosition = mul(input.Position, World);
  float4 viewPosition = mul(worldPosition, View);
  output.Position = mul(viewPosition, Projection);

  // TODO: add your vertex shader code here.

  return output;
}  
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -