SpriteBatch RenderStates

Der SpriteBatch des XNA Framework ist eine tolle Sache und grundsätzlich sehr einfach zu verwenden. Leider machen sich viele Leute keine Gedanken darüber, was der SpriteBatch eigentlich ist und wie dieser funktioniert. Früher oder später kommt es dann, wie es kommen muss: Die ersten Probleme tauchen auf und der SpriteBatch wird schlecht geredet. Dabei trägt nicht der SpriteBatch die Schuld am Dilemma, sondern derjenige, der nicht die notwendigen Grundlagen kennt, um den SpriteBatch korrekt zu verwenden.

In diesem kurzen Artikel möchte ich ein paar Worte darüber verlieren, wie dieses nette Stückchen Software funktioniert und was es zu beachten gilt. Weitere Artikel werden später sicherlich folgen und noch weitere Aspekte beleuchten.

Wie ich bereits in meinem Artikel zum Early Z Culling angerissen habe, arbeitet die Grafikkarte wie ein Fließband (dort nennt man dies allerdings Pipelines). Der Hauptprozessor liefert Befehle und Daten an die Grafikkarte, die diese dann in darstellbare Bilder umwandelt. Dabei passieren die Daten (in der Regel Dreiecke und Texturen) viele Werkzeuge. Diese Werkzeuge nennt man Render States. Man kann sich dies so vorstellen, daß man ein Werkzeug einrichtet und diesem so mitteilt, wie es arbeiten soll. Wichtig ist dabei folgende Erkenntnis: Solange man die Einstellungen des Werkzeuges ändert, wird es bis in alle Ewigkeiten so weiter arbeiten, wie es eingerichtet wurde. Dies ist bei den Render States selbstverständlich nicht anders, daher gilt die Faustregel:

Ein Render State ist solange gültig, bis etwas anderes eingestellt wird.

Dieser kleine Satz klingt einfach, was er auch grundsätzlich ist, enthält aber eine sehr wichtige Botschaft, die oft schon das Problem bei der Verwendung des Sprite-Batch darstellt. Aus diesem Grund, solltest du diesen Satz verinnerlichen und dir gut merken.

Da der SpriteBatch ebenfalls die Grafikkarte verwendet, benötigt dieser selbstverständlich auch RenderStates und ändert diese so, wie es notwendig ist, um das gewünschte Ergebnis auf den Bildschirm zu bekommen. Dies ist auch richtig und gar nicht anders möglich, da dies ja dafür die Render States da sind. Dies bedeutet also, daß wir die Render States wieder so anpassen müssen, wie wir sie erwarten, nachdem wir den SpriteBatch verwendet haben. Dazu gibt es zwei Möglichkeiten:

1. manuelles zurücksetzen

Wir setzen einfach alle Render States so, wie wir sie erwarten. Dazu später mehr

2. Verwendung von SaveStateMode in SpriteBatch.Begin

Diese kleine Hilfe vom XNA Framework speichert alle Render States bei SpriteBatch.Begin ab, die geändert werden könnten und setzt diese in SpriteBatch.End nach dem zeichnen aller Sprites wieder so, wie sie vor Verwendung des SpriteBatch waren. Dies ist sehr bequem, allerdings zeitintensiv. Dabei wird nämlich nicht beachtet, welche RenderStates welche Einstellungen hatten (da dies auch wieder Zeit kostet, sogar noch mehr, als einfach neue RenderStates zu setzen), sondern es werden einfach alle gespeichert und später gesetzt. Für sehr performanten Code ist dies also nicht optimal und zu vermeiden.

Wichtig ist einfach die Erkenntnis, daß zwischen SpriteBatch.Begin und SpriteBatch.End Render States (und auch der verwendete Effekt, Vertex- und Index-Buffer, sowie die Vertex-Declaration) geändert werden. Daher sollte man zwischen diesen beiden Aufrufen keinen eigenen Code zum zeichnen verwenden und sich nicht auf vor der Verwendung von SpriteBatch eingestellte Render States verlassen.

Abschließend hier noch eine Auflistung aller RenderState-Changes die vom SpriteBatch verändert werden:

raphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
GraphicsDevice.RenderState.DepthBufferEnable = false;

GraphicsDevice.RenderState.AlphaBlendEnable = true;
GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add;
GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = false;

GraphicsDevice.RenderState.AlphaTestEnable = true;
GraphicsDevice.RenderState.AlphaFunction = CompareFunction.Greater;
GraphicsDevice.RenderState.ReferenceAlpha = 0;

GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Clamp;
GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Clamp;

GraphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Linear;
GraphicsDevice.SamplerStates[0].MinFilter = TextureFilter.Linear;
GraphicsDevice.SamplerStates[0].MipFilter = TextureFilter.Linear;

GraphicsDevice.SamplerStates[0].MipMapLevelOfDetailBias = 0.0f;
GraphicsDevice.SamplerStates[0].MaxMipLevel = 0;
Advertisements

Veröffentlicht am 24.10.2009 in Grundlagen, XNA, XNA 3.1 und mit , , , , getaggt. Setze ein Lesezeichen auf den Permalink. 3 Kommentare.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: