28
Shaderprogrammierung Shaderprogrammierung T. Goeschel + F. Theilacker T. Goeschel + F. Theilacker 30.1.2008 30.1.2008

Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

Embed Size (px)

Citation preview

Page 1: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

ShaderprogrammierungShaderprogrammierung

T. Goeschel + F. TheilackerT. Goeschel + F. Theilacker

30.1.200830.1.2008

Page 2: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

ÜberblickÜberblick

1. Grundlagen

2. Die Sprache „HLSL“

3. Ein Shader-Compiler: „fxc“

4. Vertexshader

5. Pixelshader

6. Effect-Files

Page 3: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

Programmierer

Compiler

CPU GPUdelegiert einzelne Aufgaben an

C++

HLSL (+Compile

r)

Page 4: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Grafikkarten ursprünglich nur „Pixelarrays“, in denen für jeden Pixel Farbwerte abgespeichert wurden

• Hardware verwandelte die Inhalte dieser Arrays in analoge Signale für den Bildschirm

• schrittweise Zunahme der Farbtiefe und Auflösung (CGA, EGA,VGA, SVGA)

• seit 1990 Entwicklung von 3D-Beschleunigern: fähig, Vektordaten entgegenzunehmen und eigenständig zu rendern

• GPU übernahm rechenintensive Aufgaben von der CPU

• Entwicklung einer Render-Pipeline: Vektordaten + Transformationsmatrizen + „Renderstates“ werden komplett von GPU verarbeitet

Page 5: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Verknüpfung der Entwicklung von Grafikkarten mit DirectX

• DirectX7: Transformationsmatrizen + Renderstates

• DirectX8: Pixelshader + Vertexshader

• DirectX9: Einführung der Hochsprache „High Level Shader Language“

Page 6: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008
Page 7: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

Aufbau Vertexshader (vereinfacht)

• Input: VerticesVertices

16 Parameter aus je 4 Gleitkommazahlen (Position, Normale, diffuse/spekulare Farbe, Texturkoordinaten)

• Konstanten: 192 Werte aus je 4 Gleitkommazahlen (skalare Werte, Vektoren, Matrizen), vom Anwendungsprogramm gesetzt, Schnittstelle

zwischen Programm und Shader

• Register: Ablegen von Zwischenergebnissen, automatisch verwaltet von HLSL

• Output: 13 Werte aus je 4 Gleitkommazahlen, Ergebnisse wie z. B. geänderte Geometriedaten oder Texturkoordinaten

Page 8: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

Ablauf

• Laden der Inputdaten und Konstanten in den Shader

• Aktivierung des Shaders

• Verarbeitung jedes einzelnen Vertices, der für das Modell gerendert werden soll

• Verarbeitung „blind and stateless“

• Konstanten während des Laufes nicht veränderbar

• Fertigstellung wenn alle Vertices im Vertexbuffer abgearbeitet wurden

• falls weitere Modelle für die Szene zu rendern, gegebenenfalls nächsten Lauf starten (Konstanten können nun geändert oder sogar ein anderes Shaderprogramm gewählt

werden)

Page 9: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

Aufbau Pixelshader (vereinfacht)

• Input: einzelne PixelPixel

(in einem vorherigen Schritt erzeugt, z. B. durch Vertexshader und anschließende Interpolation)

• Konstanten: andere Daten als beim Vertexshader, z. B. Farbwerte

• Texturen: Informationen über die geladenen Texturen

• Register: Ablegen von Zwischenergebnissen, automatisch verwaltet von HLSL

• Output: Ergebnisse des Shaders: diffuser und spekularer Farbwert eines Pixels

Page 10: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

DIE SPRACHE „HLSL“DIE SPRACHE „HLSL“

• skalare Datentypen:

bool, half, int, float, double

ALLE werden als float emuliert (GPU ist Gleitkomma-Rechner!)

Deklaration wie in C++

• Vektoren:

int i;

float f;

float f = 47.11;

vector< type, size>;

vector< float, 4> meinvektorname;

int2 meinintegervektor;

float4 meinfloatvektor; float4 position; float4 farbwert;

position[0] = ...;position.g = ...;position.z = ...;

Page 11: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• „float4“ ~ „vector“

• Ansprechen der Felder über meinvektor[0], meinvektor[1], meinvektor[2], meinvektor[3] oder

meinvektor.r, meinvektor.g, meinvektor.b, meinvektor.a

oder

meinvektor.x, meinvektor.y, meinvektor.z, meinvektor.w

• Ansprechen von Feldern eines Arrays in Gruppen („swizzles“);

float3 richtung = { 1, 2.3, 4.56};

float4 position;

position = float4( 1.1, 2.2, 3.3., 4.4); // per Konstuktor

Float4 vier;

Float3 drei;

vier.xyz = drei;vier.xy = drei.yx;

vier.xy = drei.xz;

vier.xyz = drei.x;

Page 12: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Matrizen:

• Texturen und Sampler:

• Arrays:

matrix < type, rows, columns>

matrix < float, 3, 2> meinematrix

float3x2 matrix1;

int3x3 matrix2;

float4x4 matrix3; // der wichtigste Datentyp wegen Transformationsmatrizen

texture2D meinetextur;

sampler2D meinsampler; // Sampler fasst eine Textur mit Informationen zu ihrer Verarbeitung zusammen

float array[10];

array[3] = 1.234; // analog zu C++

Page 13: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Annotationen:

• Annotationen können für das Anwendungsprogramm Informationen bereithalten

• auf der Ebene von HLSL sind die Annotationen irrelevant, werden nicht auf Korrektheit überprüft

• Standard für den Austausch mit einer übergeordneten Applikation: Stichwort „SAS“ in der DirectX-Dokumentation

Texture2d meinetextur

<

string texturname = “meinbild.jpg“;

int hoehe = 256;

int breite = 128;

float3 colorkey = { 1, 1, 0 };

>;

Page 14: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Datenstrukturen:

• Speicherklassen: Variablen sind innerhalb einer Funktion lokal, außerhalb von Funktionen global. Erlaubt sind aber auch „const“, „extern“, „static“, „shared“, „uniform“...

• Operatoren: siehe DirectX-Hilfe

(sehr ähnlich den Operatoren in C, solange Skalarwerte berechnet werden, aber viele Operatoren auch mit Vektoren verwendbar)

• Kontrollflusssteuerung:

- GPU nicht für Fallentscheidungen + Schleifen konzipiert

- dennoch möglich (hoher Aufwand!): „if... else“-Bedingungen, „for“-Schleifen analog zu C++

struct meine_struktur // analog zu C++

{

int zahl;

float4 vektor;

};

Page 15: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Vordefinierte Funktionen

umfangreiches Angebot, z. B.

-> DirectX-Dokumentation

• Eigendefinierte Funktionen:

GPU auch nicht für Unterprogramme konzipiert, eigendefinierte Funktionen werden schlicht durch „Inlining“ integriert

HLSL kennt keine Zeiger, deshalb explizites Festlegen der Parameter, die nur mitgegeben werden und der Parameter, die auch zurückgeliefert werden sollen: „in“, „out“, „inout“

x = abs( x); // Absolutbetrag der Zahl x

v = cross( v1, v2); // Kreuzprodukt der Vektoren v1 und v2

l = length( v); // Länge des Vektors v

void meine_funktion( in float3 pos, out float x, inout int i);

Page 16: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Ein- und Ausgabesemantik:

- Shader haben Eingabe- und Ausgabeschnittstelle

- Eingabeschnittstelle: Shader greift sich die benötigten Daten aus der Renderpipeline

- Ausgabeschnittstelle: Shader übergibt fertige Daten zurück in die Pipeline

- Herstellung einer Beziehung zwischen Ein- / Ausgabestruktur des Shaders und dem Datenformat der Pipeline per Schlüsselwörtern („semantics“)

• Beispiel: Vertex-Shader will Vertexposition, Vertexnormale und 2-dimensionale Texturkoordinate aus Pipeline entgegennehmen

struct VS_INPUT

{

float4 pos;

float3 norm;

float2 tex;

};

struct VS_INPUT{

float4 pos : POSITION;

float3 norm : NORMAL;

float2 tex : TEXCOORD0;};

Semantics

Page 17: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Shader-Hauptprogramm:

es gibt immer einen „Entrypoint“

(vergleichbar mit „main“ bei C++, nur dass Name nicht festgelegt -> beim Kompilieren muss Name des Hauptprogramms extra angegeben werden)

struct VS_OUTPUT

{

float4 pos : POSITION;

float2 tex : TEXCOORD0;

};

VS_OUTPUT vs_main( VS_INPUT inp)

{

VS_OUTPUT outp;

// Output aus dem Input berechnen...

return outp;}

float4 vs_main( float4 inp : POSITION) : POSITION{

... // auch ohne eigene Struktur für Einstiegspunkt möglich!

}

Page 18: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

EIN SHADER-COMPILER: „FXC“EIN SHADER-COMPILER: „FXC“

• seperates Kompilieren sinnvoll zum Überprüfen auf Syntaxfehler

• Compiler „fxc“ zur Verfügung im DirectX-SDK Verzeichnis

• wichtig das „Target Profil“ (/T) und der „Entrypoint Name“ (/E)

(Dateiname „shader.vsh“, Vertexshader-Version 2.0, Entrypoint „vs_main“)

fxc /Tvs_2_0 /Evs_main shader.vsh

Page 19: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

VERTEXSHADERVERTEXSHADER

• rasante technische Entwicklung

Shaderversion 2.0 -> maximal 256 Instruktionen

Shaderversion 3.0 -> maximal 65.535 Instruktionen

• Beispiel: Entwicklung eines Vertexshaders, der dynamisch eine „Beule“ an einer Kugel erzeugt und diese um die Kugel herumlaufen lässt

• Position + Größe der „Beule“ müssen dem Shader dafür vor jedem Renderlauf mitgeteilt werden

• ohne Vertexshader eine extrem komplexe Aufgabe, für jeden Renderlauf müsste das komplette Modell geändert werden (im Vertexbuffer)

Page 20: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Festlegen der Schnittstellen

• Festlegen der Konstanten:

struct VS_INPUT

{

float4 pos : POSITION;

float2 tex : TEXCOORD0; // wird hier einfach nur weitergereicht

};

struct VS_OUTPUT

{

float4 transpos : POSITION;

float2 tex : TEXCOORD0; // für nachfolgende Texturierung

};

float4x4 vs_transformation; // World-, View- und Projektionstransformation

float4 vs_mittelpunkt; // Mittelpunkt der Beule

float vs_radius; // Radius der Beule

Page 21: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Rahmen für das Shaderprogramm:

VS_OUTPUT vs_main (VS_INPUT inp)

{

VS OUTPUT outp;

// hier kommt gleich der Shadercode hin

return outp;

}

Page 22: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Shaderprogramm: „Beulentransformation“ der Vertices

VS_OUTPUT vs_main (VS_INPUT inp)

{

VS OUTPUT outp;

float4 dir;

float len;

outp.transpos = inp.pos;

dir = inp.pos - vs_mittelpunkt;

len = length(dir);if ((len > 0) && (len < vs_radius))

{

dir = normalize( dir);

outp.transpos = vs_mittelpunkt + vs_radius * dir;

}

outp.transpos = mul( outp.transpos, vs_transformation);

outp.tex = inp.tex;

return outp;

}

Page 23: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• Der passende Anwendungscode:

(so könnte ein Test aussehen, ob im Vertexformat des Meshs alle Informationen vorhanden sind, die unser Shaderprogramm erwartet)

DWORD fvf;

fvf = (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1|D3DFVF_TEXCOORDSIZE2(0));

if(mesh->GetFVF() & fvf != fvf)

{

... Fehler, der Mesh hat nicht das erwartete FVF}

Page 24: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

Class shader

{

private:

...

LPDIRECT3DVERTEXSHADER9 vertexshader;LPD3DXCONSTANTTABLE vs_konstanten;

D3DXHANDLEvs_transformation;

D3DXHANDLE vs_mittelpunkt;

D3DXHANDLE vs_radius;

void create_shader();

public:shader();

~shader();void create( LPDIRECT3DDEVICE9 dev);

void render( D3DXMATRIX *trans, D3DXVECTOR4 *m, float r);

};

• create_shader: verbindet unter anderem die drei Handles mit den globalen Variablen im Vertexshader

• render: erhält bei jedem Aufruf Position und Radius der Beule, damit Beule um die Kugel

wandern kann

Page 25: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• create_shader()

void shader::create:shader()

{

LPD3DXBUFFER code = 0;

D3DXCompileShaderFromFile(“vs.vsh“, 0, 0, “vs_main“, “vs_1_1“, 0, &code,

0, &vs_konstanten);

device->CreateVertexShader((DWORD*)code->GetBufferPointer(),&vertexshader);

code->Release();

vs_transformation = vs_konstanten->GetConstantByName(0, “vs_transformation“);

vs_mittelpunkt = vs_konstanten->GetConstantByName(0, “vs_mittelpunkt“);

vs_radius = vs_konstanten->GetConstantByName(0, “vs_radius“);

}

Page 26: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• create_shader()

void shader::create:shader()

{

LPD3DXBUFFER code = 0;

D3DXCompileShaderFromFile(“vs.vsh“, 0, 0, “vs_main“, “vs_1_1“, 0, &code,

0, &vs_konstanten);

device->CreateVertexShader((DWORD*)code->GetBufferPointer(),&vertexshader);

code->Release();

vs_transformation = vs_konstanten->GetConstantByName(0, “vs_transformation“);

vs_mittelpunkt = vs_konstanten->GetConstantByName(0, “vs_mittelpunkt“);

vs_radius = vs_konstanten->GetConstantByName(0, “vs_radius“);

}

Page 27: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008

• mit diesen Mitteln nun die Renderfunktion:

void shader::render(D3DXMATRIX *trans, D3DXVECTOR4 *m, float r)

{

DWORD i;

vs_konstanten->SetMatrix( device, vs_transformation, trans);vs_konstanten->SetVector( device, vs_mittelpunkt, m);vs_konstanten->SetFloat( device, vs_radius, r);

device->SetVertexShader( vertexshader);

for( i = 0; i < anz_mat; i++)

{

device->SetMaterial( materialien+i); // Rendern des Meshs

device->SetTexture( 0, texturen[i]);

mesh->DrawSubset( i);

}

device->SetVertexShader( 0); // Zurückschalten auf die klassische Renderpipeline

}

Page 28: Shaderprogrammierung T. Goeschel + F. Theilacker 30.1.2008