Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Firefly

Alter Hase

  • »Firefly« ist der Autor dieses Themas

Beiträge: 484

Wohnort: Irgendwoundnirgendwo

  • Private Nachricht senden

1

09.09.2010, 18:42

2D Blitting mit Direct3D

Hallo Community,
nachdem schon viele andere gute Geister hier die Tutorialsektion bereichert haben, dachte ich mir, dass die Zeit gekommen ist auch mal etwas zu dieser Sektion beizutragen.



Blitting mit Direct3D

Wie der Titel beschreibt, geht es in diesem Tutorial um das Blitten von 2D-Grafiken mittels Direct3D. Das Problem ist, dass mit DirectDraw die 2D-Komponente von DirectX verschwunden ist. Um 2D Grafiken mittels Direct3D darzustellen, werden texturierte Quads verwendet.
Der Aufbau sieht also typischerweise wie folgt aus:



(Link)



Die Grafik zeigt eine Testtextur mit den dazugehörigen Texturkoordinaten. In meiner Engine verwende ich ausschließlich Shader und damit einhergehend auch keine vortransformierten Vertices.
Um das Quad also über den ganzen Bildschirm auszugeben, veranschaulicht der folgende Codeausschnitt die Vorgehensweise:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Vertex V[4]; //Z-Koordinaten auf 1.0f setzen 
V[0].x = -1.0f; 
V[0].y = 1.0f; 
V[0].tu = 0.0f; 
V[0].tv = 0.0f; 


V[1].x = +1.0f; 
V[1].y = 1.0f; 
V[1].tu = 1.0f; 
V[1].tv = 0.0f; 

V[2].x = +1.0f; 
V[2].y = -1.0f; 
V[2].tu = 1.0f; 
V[2].tv = 1.0f; 

V[3].x = -1.0f; 
V[3].y = -1.0f; 
V[3].tu = 0.0f; 
V[3].tv = 1.0f; 
//Zeichnen 
//Textur setzen 
//…. 

//Matrizen auf Identitätsmatrix stellen… 

pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, V, dwStride);



Als nächstes soll das Quad natürlich eine Größe und eine Position bekommen.
Die Umrechnung von Pixel zu DirectX-Koordinaten lässt sich dabei einfach per Dreisatz lösen.


C-/C++-Quelltext

1
2
float fStartX = 2.0f / ScreenWidth * x - 1.0f; 
float fEndX = 2.0f / ScreenWidth * x2 - 1.0f; 


Für die Y-Start- und Endkoordinaten ist ein wenig mehr Arbeit nötig. So kommt man nach Auflösen des Dreisatzes auf die Form

C-/C++-Quelltext

1
Y = 2.0f / h * y -1.0f 

Das Quad würde mit dieser Formel jedoch falsch wiedergegeben werden, da die Pixel-Y-Achse von oben nach unten verläuft und nicht von unten nach oben. Die y- Koordinaten müssen also gespiegelt werden:

C-/C++-Quelltext

1
y‘ = h – y 

Einsetzen liefert

C-/C++-Quelltext

1
Y = 2.0f(1.0f – y /h) 

Mit dieser Formel wird jedoch die Textur auch gespiegelt wiedergegeben. Um dieses Problem zu lösen, müssen die y Pixelkoordinaten der Y-Start und –End Werte vertauscht werden.
Die Blitfunktion last sich also wie folgt erweitern:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Vertex V[4]; //Z-Koordinaten auf 1.0f setzen 
Int x2 = x + w; 
Int y2 = y + h; 
float fStartX = 2.0f / ScreenWidth * x - 1.0f; 
float fEndX = 2.0f / ScreenWidth * x2 - 1.0f; 
float fStartY = 1.0f - 2.0f/ScreenHeight*y2; 
float fEndY = 1.0f - 2.0f/ScreenHeight*y; 




V[0].x = fStartX; 
V[0].y = fEndY; 
V[0].tu = 0.0f; 
V[0].tv = 0.0f; 


V[1].x = fEndX; 
V[1].y = fEndY; 
V[1].tu = 1.0f; 
V[1].tv = 0.0f; 

V[2].x = fEndX; 
V[2].y = fStartY; 
V[2].tu = 1.0f; 
V[2].tv = 1.0f; 

V[3].x = fStartX; 
V[3].y = fStartY; 
V[3].tu = 0.0f; 
V[3].tv = 1.0f; //Zeichnen 
//Textur setzen 
//…. 

//Matrizen auf Identitätsmatrix stellen… 

pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, V, dwStride);



Beim Zeichnen tritt jedoch ein unschönes Problem auf:



(Link)



Am Rand der Textur treten hässliche Artefakte auf. Dies liegt daran, dass ein Texel nicht einem Pixel gleicht.



(Link)



Die folgende Grafik von Microsoft illustriert das Problem anschaulich: Man sieht eine Bitmap die auf ein Pixelraster gelegt wird. Die Farbe des Pixels entspricht dabei der Mitte eines Kästchens. Die Farbe eines Bitmappixels hingegen ist die linke obere Ecke des Rasters. Als Folge interpoliert Direct3D bei unserem Quad die Farbe zwischen zwei Texeln und bildet nicht unser tatsächliches Pixel ab. Dadurch resultieren die unschönen Ränder.
Die Koordinaaten müssen also angepasst werden. Hierzu muss das Quad um die Länge eines halben Kästchens bzw. Pixels nach oben links verschoben werden.
Die Blitfunktion lässt sich also folgendermaßen korrigieren:


C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Vertex V[4]; //Z-Koordinaten auf 1.0f setzen 
Int x2 = x + w; 
Int y2 = y + h; 
float fDeltaX = 1.0f / ScreenWidth; 
float fDeltaY = 1.0f / ScreenHeight; 
float fStartX = 2.0f / ScreenWidth * x - 1.0f - fDeltaX; 
float fEndX = 2.0f / ScreenWidth * x2 - 1.0f - fDeltaX; 
float fStartY = 1.0f - 2.0f/ScreenHeight*y2 - fDeltaY; 
float fEndY = 1.0f - 2.0f/ScreenHeight*y - fDeltaY; 




V[0].x = fStartX; 
V[0].y = fEndY; 
V[0].tu = 0.0f; 
V[0].tv = 0.0f; 


V[1].x = fEndX; 
V[1].y = fEndY; 
V[1].tu = 1.0f; 
V[1].tv = 0.0f; 

V[2].x = fEndX; 
V[2].y = fStartY; 
V[2].tu = 1.0f; 
V[2].tv = 1.0f; 

V[3].x = fStartX; 
V[3].y = fStartY; 
V[3].tu = 0.0f; 
V[3].tv = 1.0f; //Zeichnen 
//Textur setzen 
//…. 

//Matrizen auf Identitätsmatrix stellen… 

pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, V, dwStride);



Mit der obigen Blitfunktion verschwinden die Ränder tatsächlich, aber wenn man für die Breite und Höhe andere Werte eingibt, als die originalen Texturmaße oder Vielfache davon, tauchen die störenden Ränder wieder auf.

Dieses Problem zu lösen hat lange Zeit gedauert. Am Anfang dachte ich, auch dieses über Koordinatenanpassung lösen zu können, aber bis jetzt habe ich noch keine Lösung gefunden. Das Problem wird verursacht durch „falsches“ lineares Textursampling. Die Lösung die ich schließlich gefunden habe, ist den Texturaddressmodus auf Wrap oder besser um keine zu starke Farbverzerrung zu erreichen auf Mirror zu stellen. Dadurch wird die Textur wiederholt an den Rändern, was dazu führt, dass die Kante verschwindet. Das Problem der Ränder lässt sich damit aber auch nur teilweise beheben. Die effektivste Lösung um saubere Ränder zu bekommen ist nur 2D-Grafiken zu verwenden, die die äußersten Pixel nicht benötigen und diese dann per Alphakey auszublenden.

Hoffe, das Tutorial wird so genehmigt und bin offen für Vorschläge, Kritik, Fehlerkorrekturen!

Firefly

Weiterführende Links:

http://msdn.microsoft.com/en-us/library/bb219690(VS.85).aspx
[url]http://nexe.gamedev.net/directKnowledge/default.asp?p=Mapping Texels To Pixels [/URL]
http://www.gamedev.net/reference/articles/article1972.asp
»Firefly« hat folgende Bilder angehängt:
  • pic02.jpg
  • pic04.jpg

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Firefly« (09.09.2010, 19:16)