SpriteBatch Magic: Grundlagen

XNA’s SpriteBatch ist eine der Geheimwaffen des XNA-Frameworks. Auf der einen Seite sieht er sehr einfach aus, was er auch sicherlich ist, auf der anderen Seite ist er sehr, sehr vielseitig und mächtig. Seit XNA 4.0 haben wir sogar noch einige Möglichkeiten mehr erhalten um dieses hervorragende Werkzeug zu einer deutlichen Arbeitserleichterung zu machen.

Die Zielgruppe dieses ersten Artikels der Reihe sind Einsteiger und interessierte Fortgeschrittene, die gerne genauer wissen möchten, was der SpriteBatch macht und wie dieser funktioniert. Dies sind die Grundlagen die als Basis für alle weiteren Artikel der Reihe dienen werden. In diesen weiteren Artikeln werde ich in kleinen Häppchen interessante Anwendungsmöglichkeiten für den SpriteBatch vorstellen und zeigen, wie man tolle Spezialeffekte erzeugen kann.

Der Sinn dieses Artikels ist also ganz einfach, dass der geneigte Entwickler eines der mächtigsten Werkzeuge, die er bereits in der Hand hält noch effektiver einsetzen kann und sich so Arbeit spart. Auf der anderen Seite werden aber auch Möglichkeiten aufgezeigt, wie man das gewisse Etwas mit XNA ins Spiel bekommt.

In längst vergangenen Zeiten gab es in Computern oft dedizierte Sprite-Hardware. Dies war bei den Klassikern von Commodore, Atari, Schneider, Texas Instruments und anderen so. Dies ist zwar interessant, aber für diesen Artikel nicht so wichtig. Ein wenig Recherche bringt über dieses Thema viele Informationen zutage. Wichtig ist eigentlich nur die Information, dass diese dedizierte Hardware seit einigen (vielen) Jahren nicht mehr im Computer oder der Grafikkarte existiert. Die Sprites sind tot, aber eine Alternative kann problemlos mit modernen Grafikkarten erzeugt werden. Im Verlaufe dieses Artikels und der gesamten Reihe ist also mit Sprite diese Alternative gemeint. Ein Sprite, dass mittels des SpriteBatch in XNA erzeugt wird.

Was ist nun dieses Sprite?

Ein modernes Sprite sind zwei Dreiecke, die ein Rechteck bilden und ständig parallel zur Monitorfläche ausgerichtet sind und eine orthographische Projektion haben.

Puh, langer Satz, der sich kompliziert anhört. Ich „brösele“ den daher etwas auf. Zwei Dreiecke sind ein Sprite, weil man aus zwei Dreiecken ein Rechteck bilden kann. Dies ist eine sehr gute Basis für ein Sprite und Grafikkarten sind hervorragend darin, Dreiecke darzustellen. Zwei sind auch nicht sonderlich viele, so dass die Grafikkarte problemlos eine große Anzahl von Sprites darstellen kann. Die orthographische Projektion bedeutet schlicht und einfach, dass es keine Tiefenverzerrung bzw. perspektivische Verzerrung gibt. Objekte werden mit zunehmender Tiefe nicht kleiner, sondern erscheinen immer gleich groß.

Sehr simpel und sehr effektiv. Das Interessante dabei ist nun, dass wir also die Vertex-Koordinaten aller Vertices zur Verfügung haben (sechs an der Zahl), die jeweiligen Texturkoordinaten, sowie eine Vertex-Color. Diese Daten werden geschickt eingesetzt, um eine Textur darzustellen. Die komplizierte Arbeit übernimmt dabei der SpriteBatch und wir müssen einfach nur die entsprechenden Methoden aufrufen.

Dabei gilt grundsätzlich folgende Vorgehensweise:

  • SpriteBatch.Begin stellt alle wichtigen RenderStates etc. ein, die für das Rendern von Sprites benötigt werden
  • SpriteBatch.Draw (oder DrawString) legt die Daten die zum Rendern eines Sprites notwendig sind in einen Puffer (der eigentliche „Batch“ also) ab
  • SpriteBatch.End rendert alle Daten im Puffer hinterinander um so StateChanges und DrawCalls möglichst gering zu halten

Näheres dazu findet ihr in meinem Grundlagenartikel SpriteBatch SpriteSortMode. Es gibt da nämlich ein paar Feinheiten, auf die wir gleich eingehen werden. In zwei weiteren Artikeln beschreibe ich, welche RenderStates genau gesetzt werden. Dies werde ich hier nicht nochmal wiederholen, sondern lediglich verlinken, dennoch sind dies wichtige Informationen. Der Artikel für XNA 3.1 befindet sich hier und der Artikel für XNA 4.0 hier.

Wir werden für unsere Verwendung des SpriteBatches mit eigenen Shadern ab jetzt ausschliesslich den SpriteSortMode.Immediate verwenden. Dies ist der einzige passende SpriteSortMode für unser Vorhaben, da nur dort der tatsächliche DrawCall auch in Draw geschieht und die Sprites nicht nach Textur oder Tiefe sortiert werden, so das unsere Änderungen auch verwendet werden können.

Wichtig ist auch die Informatione, dass XNA 3.1 für den SpriteBatch ShaderModell 2.0 einsetzt und in XNA 4.0 wird ShaderModell 3.0 eingesetzt. Dies ist wichtig, da wir mit unseren PixelShadern, die wir verändern werden, auch auf dieses Modell beschränkt sind. Es gibt einen kleinen Trick für XNA 3.1. Im AppHub befindet sich der Source des Shaders für den SpriteBatch. Wenn ihr diesen mit ShaderModell 3.0 kompiliert, und zusammen mit dem PixelShader setzt, dann könnt ihr auch das höhere Shader-Modell verwenden. Wichtig ist nur, dass das Modell von Pixel- und Vertex-Shader immer gleich sein müssen.

Das grundsätzliche Vorgehen ist nun ziemlich einfach. Wir starten wie gehabt den SpriteBatch mit .Begin und setzen dabei den SpriteSortMode.Immediate. Danach setzen wir alle RenderStates so, wie wir sie benötigen und starten unseren eigenen Effekt, der nur einen PixelShader enthält. Danach machen wir den zugehörigen Aufruf von Draw und rendern damit das Sprite. Sobald wir fertig sind, lassen wir SpriteBatch.End wieder alles aufräumen und den SpriteBatch abschliessen.

Dies klingt einfach und das ist es auch. Im Grunde genommen setzen wird schlicht und einfach einen Pixel-Shader, der dann exakt das macht, was wir wollen. Ein sehr einfacher Pixel-Shader (für Graustufen) sieht dabei wie folgt aus:

sampler textureSampler : register(s0);

struct VertexShaderOutput
{
    float4 Position : POSITION0;
	float3 texCoord : TEXCOORD0;
};

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    float4 color = tex2D(textureSampler, input.texCoord);
    float3 colrgb = color.rgb;
    colrgb = dot(colrgb, float3(0.3, 0.59, 0.11));

    return float4(colrgb.rgb, color.a);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

Das ist die gesamte Magie 🙂

Die C#-Seite ist ebenfalls nicht sonderlich kompliziert:

spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);

effect.Begin();
effect.CurrentTechnique.Passes[0].Begin();
spriteBatch.Draw(spriteTexture, Vector2.Zero, Color.White);
effect.CurrentTechnique.Passes[0].End();
effect.End();

spriteBatch.End();

Genau so, wie oben beschrieben. Sehr simpel, aber das Ergebnis überzeugt. Das Sprite wird nun, wie durch den Pixel-Shader vorgegeben, in Graustufen gerendert.

In XNA 4.0 funktioniert dies genau so, mit Ausnahme der etwas anderen Verwendung des Effects (Apply statt Begin-Klammern). Zusätzlich sind wir in XNA 4.0 noch ein wenig mächtiger geworden und haben nun auch die Möglichkeit, den Vertex-Shader auszutauschen. Dazu werde ich aber in einem (bzw. mehreren) Folge-Artikeln näheres erläutern.

An dieser Stelle sind wir auch bereits am Ende dieses kurzen Grundlagen-Artikels. Ich möchte an dieser Stelle noch schnell einen Querverweis auf das Shader 101 verlinken, denn dort werden viele interessante Dinge zu Shadern beschrieben und wie wir ja nun gelernt haben, können wir diese auch mit dem SpriteBatch verwenden.

Ich werde zukünftig – jedesmal, wenn ich einen neuen Artikel zum Thema schreibe – die nachfolgende Liste um einen Link auf diesen Artikel erweitern, so dass dieser Artikel nicht nur die Grundlagen beschreibt, sondern auch eine vollständige Liste aller Folgeartikel beeinhaltet und somit als Inhaltsverzeichnis dient. Die Folgeartikel werden kurz und knackig werden und jeweils einen Effekt (oder manchmal auch eine Effekt-Familie) beschreiben.

Folgeartikel:

SpriteBatch Magic: Grayscale

Advertisements

Veröffentlicht am 29.04.2011 in Grundlagen, SpriteBatch, XNA, XNA 3.1, XNA 4.0 und mit , , , , , getaggt. Setze ein Lesezeichen auf den Permalink. 4 Kommentare.

  1. WoW ich habe einiges gelernt 🙂
    Die Qualität deiner Artikel ist echt gestiegen!
    Danke dafür 🙂

  2. Super Post. Würde gern mehr Beitraege zu der Thematik lesen.

  3. Franziska Weißbach

    Viel Spaß dabei und mach weiter so wie bisher

  1. Pingback: SpriteBatch Magic: Grundlagen « "Mit ohne Haare"

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: