Pixel Shader
Im folgenden Teil unseres Artikels befassen
wir uns mit den neuen Pixel Shader Modellen,
welche in DirectX 9.0 enthalten sind. Den
Modellen (1.1 - 1.4 gibt es seit DirectX
8.1) liegt folgender Gedanke zu Grunde, da
sie sich in ihrer Leistungsfähigkeit
unterscheiden:
- PS2.0 gilt als Basismodell, welches minimal
benötigt wird, um als DirectX 9.0 VPU
zu gelten.
- PS2.0 Extended ist eigentlich kein neues
Modell, es ist lediglich eine Erweiterung
des 2.0'er Modells, welches variabel durch CAP-Bits (Bitfelder, welche je nach
Fähigkeiten der Hardware gesetzt. werden)
zwischen dem PS2.0 und dem PS3.0 Modell liegt.
- PS3.0 ist als High-End Modell gedacht um
zukünftiger Hardware (und Herstellern)
ein Basismodell für ihre VPU's zu bieten,
es könnte in DirectX 10.0 als Basismodell
dienen.
- Daneben gibt es noch die Modelle PS 2_sw
und PS 3_sw, welche nur von der CPU ausgeführt
werden können und für Entwickler
gedacht sind .
Fähigkeiten
Vertex Shader Einheit |
DX8.1 PS1.1 |
DX8.1 PS1.4
|
DX9 PS2.0 |
DX9 PS2.0 Extended
/ PS 2_sw |
DX9 PS3.0
/ PS 3_sw |
R300 PS |
NV30 PS |
Version |
1_1 |
1_4
|
2_0 |
2_x (mit CAP Bits)
|
3_0
|
2_0 |
2_x (mit CAP Bits) |
max. Instruktionen (nicht Befehle!)
pro Durchlauf |
8 |
28
|
variabel,
je nach Hardware |
variabel,
je nach Hardware |
variabel,
je nach Hardware |
variabel,
je nach Hardware |
variabel,
je nach Hardware |
max. Instruktionen in einem Block |
8 |
14 pro Phase
2 Phasen möglich
|
96 |
96-512
|
512-32768
|
96 |
? |
Fließkomma-Genauigkeit |
- |
-
|
ja |
ja
|
ja
|
ja |
ja |
statische Flußsteuerung |
- |
-
|
- |
(Tiefe 1-4)
|
(Tiefe 1-4)
|
ja |
ja |
dynamische Flußsteuerung |
- |
-
|
- |
(Tiefe 0-24)
|
(Tiefe 0-24)
|
- |
ja |
Predication |
- |
-
|
- |
ja
|
ja
|
- |
ja |
Arbitrary Swizzle |
- |
-
|
- |
ja
|
ja
|
- |
ja ? |
Gradient Instructions
|
-
|
-
|
-
|
ja
|
ja
|
-
|
ja
|
No Dependent Read Limit
|
nicht frei möglich, nur in Verbindung
mit komplexen Operationen
|
1 pro r# Register,
nur in der 2. Phase |
jeweils max. 3 zusammengehörige
|
ja
|
ja
|
jeweils max. 3 zusammengehörige |
ja ?
|
No Texture Instruction Limit
|
-
|
-
|
32 |
ja
|
ja
|
32
|
ja ?
|
Loops
|
-
|
-
|
-
|
-
|
ja
|
-
|
-
|
Version
Gibt die Pixel Shader Version an. Aus der Version 2_0 wird 2_x, falls eines oder mehrere der folgenden CAP-Bits gesetzt bzw. die
Werte vom 2_0'er Standard abweichen:
D3DPS20CAPS_ARBITRARYSWIZZLE
D3DPS20CAPS_GRADIENTINSTRUCTIONS
D3DPS20CAPS_PREDICATION
D3DPS20CAPS_NODEPENDENTREADLIMIT
D3DPS20CAPS_NOTEXINSTRUCTIONLIMIT
DynamicFlowControlDepth
NumTemps
StaticFlowControlDepth
NumInstructionSlots
max. Instruktionen (nicht Befehle!) pro Durchlauf
Gibt an, wie lang ein Pixel Shader Programm
sein darf. Durch Schleifen und Sub-Routinen
kann ein PS-Programm deutlich länger
werden, als die max. 96 Instruktionen in
einem Block. Manche Befehle benötigen
mehrere Instruktionen, weshalb man Instruktion
und Befehl nicht gleichsetzen darf.
Fließkomma-Genauigkeit
Der Pixel Shader arbeiten ab der Version
2.0 mit Fließkomma-Genauigkeit. Diese
variiert zwischen min. 16 Bit (R300 - 24
Bit, NV30 - 32 Bit) für Farbwerte und
min. 24 Bit (R300 - 24 Bit, NV30 - 32 Bit) für alle anderen Daten.
statische Flußsteuerung (die Tiefe gibt an, wie tief ich die Flußsteuerung
verschalten kann!)
Statische Flußsteuerung ist rech einfach
erklärt:
PS_2_x
defb b3, TRUE
if b3
// wenn b3 TRUE ist, dann gehe hier hin
else
// sonst mache hier weiter
endif
Jedoch sind hier keine komplexen Vergleich
im IF-Block möglich, lediglich die "Constant Boolean " Register, welche pro Register nur
die Werte 0 oder 1 haben können, dürfen
nach dem IF stehen. Weitere Befehle sind
call und rep. Ich habe also bei der statischen
Flußsteuerung lediglich die "Constant Boolean" Register mit denen ich Vergleiche
anstellen kann. Dies ist für komplexe
Aufgaben sehr hinderlich, da man nur über
Umwege (wenn überhaupt) kompliziertere
Vergleichen hinbekommt.
dynamische Flußsteuerung (die Tiefe gibt an, wie tief ich die Flußsteuerung
verschalten kann!)
Dynamische Flußsteuerung ist viel leistungsfähiger
als ihr statische Vertreter.
PS_2_x
if_lt r3.x, r4.y
// wenn r3.x kleiner ist als r4.y, dann gehe
hier hin
else
// sonst mache hier weiter
endif
Hier habe ich wesentlich mehr Möglichkeiten
z.B. den IF-Block zu beginnen (_gt "größer", _lt "kleiner",
_ge "größer / gleich",
_le "kleiner / gleich", _eq "gleich",
_ne "ungleich") , ich bin nicht an boolsche Register gebunden.
Ebenso hilft die dyn. Flußsteuerung
bei Schleifen, welche ich durch feinere Vergleichsmöglichkeiten
z.B. "break_lt r3.x, r4.y" - bricht
aus momentaner Schleife aus, wenn r3.x <
r4.y - beenden kann.
Predication(als wahr behaupten)
Predication ist eine feine Sache. Im Grunde
etwas ähnliches wie eine Flußsteuerung,
jedoch ohne Sprünge (zumindest aus der
Sicht des Programmierers). Erstmal benötigen wir eine neue Art
von Register, das "Predicate Register".
Im PS 2_x Modell gibt es davon 1 Stück,
p0. Das Register ist ein 4D Vektor mit den
4 Komponenten xyzw, aber jede Komponente
(also x, y, z, w) ist ein boolsches Register,
kann also nur 0(false) oder 1(true) enthalten.
Es Spielt übrigens keine Rolle, ob man
bei den Registern x, y, z, w (vom Vertex
Shader) oder r, g, b, a (Pixel Shader) verwendet.
PS_2_x
setp_le p0, r0, r2.z
bedeuted folgendes:
IF (r0.x <= r2.z) THEN p0.x = true ELSE false
IF (r0.y <= r2.z) THEN p0.x = true ELSE false
IF (r0.z <= r2.z) THEN p0.x = true ELSE false
IF (r0.w <= r2.z) THEN p0.x:= true ELSE false
Andere Vergleiche sind auch möglich.
So jetzt haben wir unser p0 Register gesetzt.
Was machen wir nun damit?
Wir können nun p0 dazu verwenden, bestimmte
Befehle auszuführen, falls das Predicate
Register für die entsprechende Komponente
eine 1 enthält.
p0.x=true
p0.y=false
p0.z=false
p0.w=true
(p0) add r4, r5, r6
Ohne (p0) würde das PS-Programm folgendes
tun:
r4.x = r5.x + r6.x
r4.y = r5.y + r6.y
r4.z = r5.z + r6.z
r4.w = r5.w + r6.w
Mit dem (p0) Befehl jedoch, rechnet er:
IF (p0.x = true) THEN r4.x = r5.x + r6.x
IF (p0.y = true) THEN r4.y = r5.y + r6.y
IF (p0.z = true) THEN r4.z = r5.z + r6.z
IF (p0.w = true) THEN r4.w = r5.w + r6.w
Es würden sich also r4.y und r4.z nicht
verändern, da p0.y und p0.z nicht "true"
enthalten.
Arbitrary Swizzle
Dies erkläre ich weiter unten, in der
Modifikations- und Maskierbefehle Sektion.
Gradient Instructions
Mittels Gradient Instructions kann man im
Pixel Shader Informationen erhalten, in welchem
Winkel sich Daten im Rendertarget (Framebuffer
oder Texture) befinden. Dies wird für
anisotrophe Berechnungen benötigt. Bisher
hatte der Pixel Shader "keine Ahnung"
in welchem Winkel seine erstellten Pixeldaten
später aufs Polygon kommen.
No Dependent Read Limit
Dependent Read bezeichnet man die Fähigkeit,
durch den Registerinhalt Texturedaten zu
adressieren. Somit kann man z.B. Texturen
als Tabellen mit Werten verwenden und diese
dann gezielt verwenden. Die Pixel Shader
der Version 2.0 können hier nur 3 zusammengehörige
ausführen.
PS_2_0
...
def c0, 1.0f, 1.3f, 1.2f, 1.0f // definiere Konstante c0 (4D-Vector)
texld r1, t0, s1;
// lade Texturedaten(von Position 0,0) aus
der Texture 0 mittels Texturesampler 1 in
Input Register 1
add r1, r1, c0
// verändere Werte in r1
texld r2, r1, s3;
// Dependent Read/Abhängiges Lesen!!!
// lade Texturedaten(von Position r1.x, r1.y)
aus der Texture 1 mittels Texturesampler
3 in Input Register 2
texld r2, r2, s3;
// Dependent Read/Abhängiges Lesen mit veränderten Werte in r2 !!!
// lade Texturedaten(von Position r2.x, r2.y)
aus der Texture 2 mittels Texturesampler
3 in Input Register 2
texld r2, r2, s3;
// Dependent Read/Abhängiges Lesen mit veränderten Werte in r2 !!!
// lade Texturedaten(von Position r2.x, r2.y)
aus der Texture 2 mittels Texturesampler
3 in Input Register 2
texld r2, r2, s3;
// nicht mehr moglich mit den Pixel Shadern
der Version 2.0
No Texture Instruction Limit
Pixel Shader der Version 2.0 haben hier ein
Limit von 32. Ob mehr heute Sinn machen,
kann ich nicht beurteilen, aber auf jeden
Fall ein Weg zum völlig frei programmierbaren
Pixel Shader.
PS_2_0
texld r1, t0, s1;
// lade Texture 0 mittels Texturesampler
1 in Input Register 1
Loops
Loops / Schleifen wird es erst mit den Pixel
Shadern der Version 3.0 geben, somit haben
dann Vertex und Pixel Shader 3.0 den gleichen
Umfang an statischer und dynamischer Flußsteuerung.
Register
Register Typ |
Art der Zugriffe:
Use/Read/Write |
DX8.1 PS1.1 |
DX8.1 PS1.4
|
DX 9 PS2.0 |
DX9 PS2.0 Extended
/ PS 2_sw |
DX 9 PS3.0
/ PS 3_sw |
R300 PS
|
NV30 PS
|
Input Register - Führen Daten in den
Pixel Shader
|
Input / Color |
r |
2 (4D Vektor) |
2 (4D Vektor)
|
2 (4D Vektor) |
2 (4D Vektor) |
10 (4D Vektor) |
2 (4D Vektor)
|
2 (4D Vektor)
|
Temp |
r/w |
2 (4D Vektor) |
6 (4D Vektor)
|
12 (4D Vektor) |
12-32 (4D Vektor) |
12-32 (4D Vektor) |
12 (4D Vektor)
|
12 (4D Vektor)
|
Constant Float |
r |
- |
|
min. 12-32 (4D Vektor) |
min. 12-32 (4D Vektor) |
min. 12-224 (4D Vektor) |
12 (4D Vektor)
|
32 (4D Vektor) ?
|
Constant Integer |
r |
8 (4D Vektor) |
8 (4D Vektor)
|
- |
16 (4D Vektor) |
16 (4D Vektor) |
16 (4D Vektor)
|
16 (4D Vektor) ?
|
Constant Boolean |
r |
- |
-
|
16 Bits |
16 Bits |
16 Bits |
16 Bits
|
16 Bits
|
Face |
r |
- |
-
|
- |
- |
1 (Skalar) |
-
|
-
|
Loop Counter |
u |
- |
-
|
- |
- |
1 (Skalar) |
-
|
-
|
Predicate |
r |
- |
-
|
- |
1 (4D Vektor) |
1 (4D Vektor) |
-
|
?
|
Sampler
|
r
|
-
|
-
|
16 (4D Vektor)
|
16 (4D Vektor)
|
16 (4D Vektor)
|
16 (4D Vektor)
|
16 (4D Vektor)
|
Input Texture Coordinate
|
r
|
4 (4D Vektor)
|
6 (4D Vektor)
|
8 (4D Vektor)
|
8 (4D Vektor)
|
-
|
8 (4D Vektor)
|
8 (4D Vektor)
|
Position
|
r
|
-
|
-
|
-
|
-
|
1 (4D Vektor)
|
-
|
-
|
Anmerkungen
|
|
|
|
|
|
|
|
|
Output Register - Nehmen Resultate des Pixel Shaders auf
|
Color |
w |
1 (Skalar) |
1 (Skalar)
|
4 (Skalar) |
4 (Skalar) |
4 (Skalar) |
4 (Skalar)
|
4 (Skalar)
|
Depth |
w |
- |
1 (Skalar)
|
1 (Skalar) |
1 (Skalar) |
1 (Skalar) |
1 (Skalar)
|
1 (Skalar)
|
Anmerkungen
|
|
|
|
|
|
|
|
|
Bei den Pixel Shadern <2.0 habe alle Register
entweder ein Integer oder Festpunkt Format.
Ab 2.0 sind es Fließkomma-Register,
bis auf Input/Color Register <3.0. Die
Unterschiede zwischen der Version 2.0 und
3.0 sind doch noch recht groß.
Input / Color Register (v#)
"Input / Color Register" müssen
vor Begin des Pixel Shader Programms deklariert
werden. Bei Pixel Shadern <2.0 war dies
nicht nötig. Es wird ihre Verwendung
sowie der entsprechende Name festgelegt.
Im Vertex Shader Programm kann nur lesend
auf die "Input Register" zugegriffen
werden.
PS_2_0
dcl v0.xy // ich verwende
nur die x und y Komponenten des v0 Registers
Temp Register (r[n])
"Temp Register" sind Allzweckregister.
Auf sie kann man lesend und schreibend einwirken.
PS_1_1
mov r0.x, v0.x
Constant Register - Float (c[n]), Integer
(i#) und Boolean (b#)
"Constant Register" gibt es seit dem
Vertex Shader 2.0 drei Typen. Einmal Fleißkomma-Zahlen
und dann noch die Integer/Bool Versionen,
welche jedoch nur noch für Schleifen
und statische Flußsteuerung benötigt
werden.
PS_2_0
def c0, 1.0f, -1.0f, 0.5f, -0.5f // fülle die Konstante
c0 mit Werten
Face Register (vFace)
Wenn der Wert im Face Register <=0 ist,
dann sehen wir den Pixel von der Rückseite.
ps_3_0
dcl vFace
def c0, 0.0f, -1.0f, 0.5f, -0.5f // ein paar
nützliche Konstanten
def c1, 0.0f, 0.0f, 1.0f, 0.0f
// blau
def c2, 1.0f, 0.0f, 0.0f, 0.0f
// rot
if_gt c0.x, vFace
//
wenn 0>vFace, dann
mov oC0, c1
// sehen wir den Pixel von hinten,
färbe ihn blaue
else
mov oC0, c2
// sonst rot
endif
Loop Counter Register (aL)
Das "Loop Counter Register" wird
automatisch um 1 erhöht, wenn der Pixel
Shader in einen "loop" Block eintritt.
Predicate Register (p0)
Das "Predicate Register" wird,
wie im obigen Beispiel zu sehen war, zur
bedingungsabhängigen Ausführung
von Befehlen verwendet.
Sampler Register (s#)
Mit Hilfe des "Sampler Register"
können Texturedaten in Pixel Shader
geladen werden. Sie Beispiel weiter oben
( Texture Instruction Limit ).
Output Register (oC0, oC1, oC2, oC3, oDepth)
Ohne die "Output Register" würden
die fertigen Werte nie auf den Pixel gelangen.
Da es seit DirectX 9 vier Rendertargets gibt,
kann der Pixel Shader auch vier Werte rausschreiben.
Zusätzlich kann die Tiefen/Stencilinformation
des Pixels verändert werden.
Modifikatoren
Name |
Syntax |
anwendbar auf
|
DX8.1 PS1.1 |
DX8.1 PS1.4
|
DX 9 PS2.0 |
DX9 PS2.0 Extended
/ PS 2_sw |
DX 9 PS3.0
/ PS 3_sw |
R300 PS
|
NV30 PS
|
Multiply by 2
|
_x2
|
Befehl
|
ja
|
ja
|
-
|
-
|
-
|
2_0
|
2_x
|
Multiply by 4
|
_x4
|
Befehl
|
ja
|
ja
|
-
|
-
|
-
|
Multiply by 8
|
_x8
|
Befehl
|
-
|
ja
|
-
|
-
|
-
|
Divide by 2
|
_d2
|
Befehl
|
ja
|
ja
|
-
|
-
|
-
|
Divide by 4
|
_d4
|
Befehl
|
-
|
ja
|
-
|
-
|
-
|
Divide by 8
|
_d8
|
Befehl
|
-
|
ja
|
-
|
-
|
-
|
Bias
|
_bias
|
Quellregister
|
ja
|
ja
|
-
|
-
|
- |
Invert
|
1 -
|
Quellregister
|
ja
|
ja
|
-
|
-
|
-
|
Scale by 2
|
_x2
|
Quellregister
|
-
|
ja
|
-
|
-
|
-
|
Signed Scaling
|
_bx2
|
Quellregister
|
ja
|
ja
|
-
|
-
|
-
|
|
Absolute Value |
_abs |
Quellregister
|
- |
-
|
- |
- |
ja |
-
|
ja
|
Negate |
- |
Quellregister
|
ja |
ja
|
ja |
ja |
ja |
ja
|
ja
|
Centroid
|
_centroid
|
nur dcl Befehl
|
-
|
-
|
-
|
-
|
ja
|
-
|
-
|
Saturate |
_sat |
Befehl
|
- |
ja
|
ja |
ja |
ja
|
ja
|
ja
|
Partial Precision
|
_pp
|
Befehl
|
-
|
-
|
ja
|
ja
|
ja
|
ja
|
ja
|
Arbitrary Swizzle |
.xyzw or .rgba |
Quellregister
|
- |
- |
- |
ja |
ja |
-
|
ja
|
Replicate Swizzle
|
.xyzw or .rgba
|
Quellregister
|
ja
|
ja
|
ja
|
ja
|
ja
|
ja
|
ja
|
Write Mask |
.xyzw or .rgba
|
Zielregister
|
ja |
ja
|
ja |
ja |
ja |
ja
|
ja
|
Modifkatoren haben den großen Vorteil,
dass sie nicht als extra Instruktionen zählen.
Somit können sie unbeschwert angewendet
werden, wirken sich aber nicht auf die Anzahl
der Instruktionen aus. Die Anzahl der Modifikatoren
wurde ab Version 2.0 stark beschränkt,
was sicher mit dem größeren Wertebereich
bzw. der höheren Genauigkeit zu tun
hat. Somit sind die Skalierungsmodifikatoren
der älteren Pixel Shader nicht mehr
nötig. Ich werde hier auch nur die Modifikatoren
der Pixel Shader ab Version 2.0 erläutern.
Absolute Value
Hiermit wird der absolute Wert eines Registers
gebildet.
PS_3_0
add rDest, rSrc0, rSrc1_abs
Negate
Hiermit wird der Inhalt des Quellregisters
negiert.
PS_2_0
add rDest, - rSrc0, rSrc1
Centroid
Centroid wird bei aktiviertem MultiSampling
benötigt, da dort der Pixel Shader nur
pro Pixel arbeitet, nicht jedoch pro Sub-Pixel
wie bei SuperSampling. Da der Pixel Shader
nur im Zentrum (blaue Punkte) des Pixel sampled,
würde es bei folgendem Polygon zu Fehlern
führen, da die Farbwerte außerhalb
des Pixelzentrums liegen. Wo genau er nun
sampled ist den Hardwareherstellern überlassen.
PS_3_0
dcl_centroid_color v0
Saturate
Saturate begrenzt das Resultat einer Berechnung
auf den Wertebereich [0, 1]. Somit gibt es
keine Überläufe.
add_sat rDest, rSrc0, rSrc1
Replicate Swizzle
Mit Replicate Swizzle kann man bestimmen,
welche Quellregisterkomponenten in ein Zielregister
geschrieben werden.
add rDest, rSrc0 , rSrc1.xxyy // addiert zu
den xy Komponenten von rSrc0 jeweils nur
x von rSrc1
//
und zu den zw Komponenten von rSrc0 jeweils
nur y von rSrc1
Arbitrary Swizzle
Arbitrary Swizzle bezeichnet die Möglichkeit,
dass die Komponenten (x, y, z, w) beliebig
vertauscht werden können.
add rDest, rSrc0, rSrc1.yxzw // vertauscht x und y beim addieren
Write Mask
Eine "Write Mask" dient dazu, festzulegen,
welche Zielregisterkomponenten von einer
Operation verändert werden dürfen.
add rDest.xw, rSrc0, rSrc1
// nur die xw Komponenten werden durch die
Berechnung verändert
Befehle
Typ |
DX8.1 PS1.1 |
DX 9 PS1.4
|
|
DX 9 PS2.0 |
DX9 PS2.0 Extended
/ PS 2_sw
|
DX 9 PS3.0
/ PS 3_sw |
R300 PS
|
NV30 PS
|
Setup |
def, ps |
def, ps
|
|
def, ps, dcl,
dcl_textureType
|
-
|
defi, defb,
dcl_usage anstatt dcl
|
2_0 |
2_x |
Phase
|
-
|
phase
|
|
-
|
-
|
-
|
Texture
|
tex, texbem, texbeml, texcoord,
texkill, texm3x2pad, texm3x2tex, texm3x3pad, texm3x3spec, texm3x3tex,
texm3x3vspec, texreg2ar, texreg2gb |
texcrd, texdepth, texkill, texld
|
|
texkill, texld, texldb, texldp
|
texldd
|
texldl
|
Arithmetic |
add, cnd, dp3, lrp, mad,
mov, mul, nop, sub |
add, bem, cmp, cnd, dp3,
dp4, lrp, mad, mov,
mul, nop, sub
|
|
add, cmp, dp2add,
dp3, dp4, mad, mov,
mul, nop, rcp,
rsq, sub |
dsx, dsy
|
-
|
Macro-Ops |
- |
-
|
|
abs, crs, exp, frc, log, lrp,
m3x2, m3x3, m3x4, m4x3,
m4x4, max, min,
nrm, pow, sincos
|
-
|
-
|
Flow-Control |
- |
-
|
|
- |
break, break_comp, break_pred,
call, callnz, callnz_pred, else,
endif, endrep, if, if_comp, if_pred,
label, rep, ret, setp |
loop, endloop
|
Die Pixel Shader der Version 1.1 / 1.4 haben
eine stark unterschiedlichen Befehlssatz,
deshalb habe ich hier jeweis den kompletten
aufgeführt. Version 1.2 und 1.3 bieten
zusätzlich noch ein paar Befehle, jedoch
soll es hier um die Versionen 2.0 und höher
gehen. Ab der Version 2.0 gibt es einen einheitlichen
Befehlssatz. Als Basismodell dient der Pixel
Shader 2.0. Mit jeder weiteren Pixel Shader
Version kommen neue Befehle hinzu, die Befehle
der Vorgängerversionen (ab 2.0) bleiben
erhalten, deshalb habe ich nur die neuen
Befehle in der Tabelle aufgeführt.
Setup
Zum Setup gehören Befehle, welche die
Version des verwendeten Pixel Shaders festlegen,
sowie die Zuordnung zwischen den Input Registern
und deren Verwendung.
ps_2_0
// legt Version 2_0 des
PS fest
dcl v0
// zeigt dem PS,
dass man das v0 Register verwenden möchte
Phase
Phase wird nur im 1.4'er Pixel Shader verwendet,
da dort nur in Phase 2 bestimmte Operationen.
ps_1_4
// legt Version 1_4 des
PS fest
...
// irgendwelche Operatioen
phase
// schaltet auf
Phase 2 um
...
// irgendwelche Operatioen
Texture
Mittels der Texture Befehler werde Texturen
in den Pixel Shader geladen.
ps_2_0
// legt Version 2_0 fest
dcl t0
// zeigt dem PS, dass
man das t0 Register verwenden möchte
dcl s1
// zeigt dem PS, dass man das s1 Register
verwenden möchte
texld r0, t0, s1
// lädt die Texture 0 über den
Sampler 1 in r0
Arithmetic
Hier werden einfache mathematische Operationen
zur Verfügung gestellt.
ps_2_0
//
legt Version 2_0 fest
def c0, 1.30f, 1.50f, 1.60f, 1.70f / / definiert die Variable c0
mul r0, r0, c0.x
// multipliziert r0 mit c0.x (1.30f)
Macro-Ops
Hier werden komplexere mathematische Operationen
zur Verfügung gestellt. Man könnte
diese auch umgehen und selbst per arithmetischer
Operationen nachbilden.
ps_2_0
// legt Version 2_0 fest
abs r0, r0
// berechnet den
absoluten Wert von r0
Flow-Control
Hier werden alle Befehle der statischen und
dynamischen Flußsteuerung zusammengefasst.
Diese gibt es erst seit dem PS 2_x.
ps_2_x
if_lt r3.x, r4.y
// wenn r3.x kleiner ist als r4.y, dann gehe
hier hin
else
// sonst mache hier weiter
endif
Zusammenfassend kann man sagen, dass die
Pixel Shader ab Version 2.0 wesentlich einfacher
als die Versionen <=1.4 zu programmieren
sind. Ein Problem, genau wie bei den Vertex
Shadern, gibt es auch hier: Alles zwischen
Version 2.0 und 3.0 ist sehr "weich"
definiert. Hier muss der Programmierer noch
mehr aufpassen, als bei den Vertex Shadern,
weil es hier mehr Fähigkeiten gibt,
welche unterstützt werden könnten
oder ebne auch nicht. Deshalb wird man als
Spielerwahrscheinlich nie ein Spiel zu Gesicht
bekommen, welches 2_x Shader verwendet, da
einfach zu wenige Platformen dies unterstützen.
|