Mysterium Render Target

Gerne werden Render-Targets für bestimmte Aufgaben empfohlen. Trotz der Tatsache, daß die Verwendung von Render-Targets zu den absoluten Grundlagen der Grafikprogrammierung gehören und auch sehr einfach zu verstehen und anzuwenden sind, kommen immer wieder Rückmeldungen, daß sich der Einsteiger noch nie mit Render-Targets beschäftigt hat und auch keine Idee hat, was das ist und wofür diese zu verwenden sind.

Dieser Beitrag wird eine kurze und knackige Einführung in das Thema Render-Targets geben und dem Einsteiger so wichtige Grundlagen vermitteln. Der ein oder andere interessierte Leser findet vielleicht ebenfalls noch ein paar nützliche Informationen.

Keine Angst, Render-Targets sind einfach, aber trotzdem extrem mächtig.

Hardware Buffer

Vertex-Buffer, die zum speichern von Vertices im Grafikkartenspeicher (VRAM) verwendet werden, kennen die meisten von euch sicherlich. Index-Buffer sind eng mit dieser Thematik verbunden und dürften daher ebenfalls bekannt sein. Es gibt aber noch eine dritte Buffer-Art, die gerade im XNA-Umfeld etwas im verborgenen liegt: Die sogenannten Pixel-Buffer.

Was ist ein Pixel-Buffer? Wie der Name schon sagt, enthalten Pixel-Buffer Pixel. Das ist interessant und klingt auch vollkommen logisch, aber was ist dann der Unterschied zu einer Textur? Tja, der Unterschied existiert nicht. Eine Textur ist ein Pixel-Buffer. Und um in diese Geschichte noch etwas mehr Material zu bekommen, nehmen wir direkt noch die Render-Targets und auch noch den sogenannten Back-Buffer hinzu. Klingt kompliziert? Nein, ist es wirklich nicht. Render-Targets, Texturen und der Back-Buffer sind sogenannte Pixel-Buffer. Also ganz einfach Speicherbereiche (oft im VRAM) die Pixeldaten enthalten. Nicht mehr, aber auch nicht weniger.

Das Interessante an diesen Speicherbereichen im VRAM ist, daß die GPU (Graphics Processing Unit, also das Hirn der Grafikkarte) besonders schnell Daten in diese Speicherbereiche schreiben kann und auch aus diesen auslesen kann. Der Sinn ist klar: Wir lesen Daten aus einer Textur und schreiben diese Daten in den Back-Buffer. Der Back-Buffer ist übrigens – etwas vereinfacht ausgedrückt – der Bildschirminhalt. Es werden also einfach Pixel-Daten kopiert und evtl. durch Shader auf ihrem Weg von einem Speicherbereich zu einem anderen verändert. Dies ist aber ein anderes Thema und wird hier nicht behandelt, da es zum Verständnis von Render-Targets nichts beiträgt. Dadurch das der Quell-Pixel-Buffer und der Ziel-Pixel-Buffer beide im schnellen RAM der Grafikkarte liegen, müssen die Daten nicht über den Bus. Die Grafikkarte hat diese Daten lokal und kann mit Höchstgeschwindigkeit darauf zugreifen.

Das RenderTarget

Das Render-Target ist nun einer dieser Speicherbereiche. Damit könnten wir uns natürlich folgendes vorstellen: Wir rendern die Textur nicht in den Back-Buffer, sondern in ein Render-Target. Dieses verwenden wir dann wieder als Textur. Ein Anwendungsgebiet wäre zum Beispiel ein Spiegel. Wir positionieren einfach eine Kamera, die „aus dem Spiegel heraus“ schaut und rendern die Szene in ein Render-Target. Dann rendern wir die Szene ganz normal mit unserer Spielerkamera und legen das „Spiegel-Render-Target“ als Textur auf den Spiegel. Einfaches Konzept, trotzdem ein toller Effekt.

Ein anderer Anwendungsfall sind sogenannte Post-Processing-Effekte. Wir rendern die gesamte Szene in ein Render-Target und können dieses dann als einzelne Textur erneut durch einen Shader jagen und damit z.B. eine Heat- oder Night-Vision erzeugen, oder aber einfach ein Schwarz/Weiß-Bild erzeugen.

Aber auch im Bereich LOD (Level of Detail) finden Render-Targets Verwendung um ein Objekt als Billboard darzustellen und somit weniger Vertices von der Grafikkarte rendern zu lassen.

Es gibt noch viele weitere Beispiele, wofür man Render-Targets verwenden kann. Man sieht also, daß diese sehr mächtig sind. Im folgenden werde ich erklären, wie diese verwendet werden, damit die Grundlage für weitere, fortgeschrittene Techniken zukünftig vorhanden sind.

Verwendung

Die Verwendung von Render-Targets ist – wie bereits geschrieben – sehr einfach. Man muss einfach nur der Grafikkarte sagen, daß sie nicht in den BackBuffer (was der Standard ist) rendern soll, sondern gibt ihr einfach ein RenderTarget vor.

Zuerst müssen wir natürlich ein RenderTarget erzeugen. Dies ist nicht weiter schwer, wir benötigen grundsätzlich nur die Größe und das Format:

RenderTarget2D myRenderTarget = new RenderTarget(GraphicsDevice, ...);

Die genaue Beschreibung aller Parameter findet sich in der MSDN.

Als nächstes müssen wir festlegen, in welches RenderTaraget wird zeichnen wollen. Haben wir ein neues RenderTarget festgelegt, dann werden alle Draw-Aufrufe (und auch GraphicsDevice.Clear gehört dazu) auf diesem RenderTarget aktiv und nicht mehr im BackBuffer. Dies ist solange gültig, bis wir der Grafikkarte etwas anderes befehlen. Ein RenderTarget kann ganz einfach vorgegeben werden:

GraphicsDevice.SetRenderTarget(...)

Auch hier gibt es wieder eine genauere Beschreibung in der MSDN.

An dieser Stelle können wir nun alles machen, was wir sonst auch machen. Wir löschen den Bildschirm z.B. mit einer schwarzen Farbe und rendern ein paar Sprites mit dem SpriteBatch.

Nachdem wir das RenderTarget so bemalt haben, wie wir es benötigen, müssen wir es zurücksetzen und den BackBuffer wieder aktivieren. Das funktioniert exakt so, als würden wir ein RenderTarget festlegen, allerdings übergeben wir als Referenz ein null. Dies befiehlt XNA wieder auf den BackBuffer zu wechseln.

Nachdem wir also jetzt das RenderTarget wieder freigegeben haben, können wir nun den Inhalt abholen. Wichtig ist, daß dies erst funktioniert, nachdem das RenderTarget freigegeben wurde. Wir erhalten den Inhalt des RenderTarget ganz einfach als Textur. Bis einschliesslich XNA 3.1 mussten wir die Textur mittels der GetTexture() Methode abholen, seit XNA 4.0 ist ein RenderTarget von Texture2D abgeleitet und kann sofort als Parameter z.B. für SpriteBatch verwendet werden.

Die Farbe lila

Wenn man mit RenderTargets arbeitet, besonders bei etwas komplizierteren Setups, bekommt man manchmal eine Textur die vollständig oder auch nur teilweise eine lila Färbung aufweist. Dies ist ein Debugging-Mechanismus. Wenn ein RenderTarget gesetzt wird, so hat dieses Standardmäßig die Farbe lila. Dies bedeutet, daß alle Stellen, an den letztendlich diese Farbe lila noch vorhanden ist nicht bemalt wurden. Auf diese Pixel wurde ganz einfach nicht schreibend zugegriffen. Dies ist sehr praktisch um bei der Entwicklung Fehler zu erkennen.

Preserve Contents

Bei der Erstellung eines RenderTargets gibt es einen Parameter RenderTargetUsage mit dem man das Verhalten eines RenderTargets maßgeblich beeinflussen kann. Dieser Parameter legt fest, wie sich ein RenderTarget verhalten soll, wenn es neu gesetzt wird. Hier ein Beispielcode:

GraphicsDevice.SetRenderTarget(myRenderTarget01);
// hier wird etwas gerendert
GraphicsDevice.SetRenderTarget(null);

Texture2D renderTarget01Texture = (Texture2D)myRenderTarget01; // unter XNA 3.1: myRenderTarget01.GetTexture();

// an dieser Stelle setzt nun die RenderTargetUsage an:
GraphicsDevice.SetRenderTarget(myRenderTarget01);

// ... weiterer Code ...

RenderTargetUsage legt nun fest, was passiert, wenn ein neues RenderTarget gesetzt wird. Es gibt dabei drei Möglichkeiten:

DiscardContents führt dazu, daß das RenderTarget leer ist. Dies ist das Standardverhalten auf der XBox (was unter anderem an der sehr begrenzten Menge von schnellem EDRam liegt). Mit dieser Option ist sichergestellt, daß man praktisch immer ein „frisches“ RenderTarget ohne Inhalt (also mit der Farbe lila) erhält.

PreserveContents war bis zu XNA 3.1 das Standardverhalten auf dem PC. Das RenderTarget hatte immer noch den gleichen Inhalt wie vorher, es musste also explizit gelöscht werden, wenn ein „frisches“ RenderTarget gewünscht war. Dies ist auf dem PC möglich, da dort normalerweise eine größere Menge an dediziertem Video-Ram auf der Grafikkarte vorhanden ist und die Buffer so nicht hin- und herkopiert werden müssen, wie dies auf der XBox notwendig ist. Mit neueren XNA-Versionen wurde auf allen Plattformen DiscardContents als Standard eingeführt, was anfangs zu kleineren Problemen und Mißverständnissen führte.

PlatformContents wählt automatisch eine sinnvolle Einstellung für die jeweilige Plattform. Dies ist mit Vorsicht zu genießen, da sich das Verhalten erstens zwischen XNA 3.1 und XNA 4.0 geändert hat und zweitens das Verhalten zwischen den Plattformen unterschiedlich ist. In meinen Augen macht diese Option nur sehr, sehr wenig Sinn und sollte nicht verwendet werden.

Ein Wort noch zu PreserveContents. Dies führt auf allen Plattformen, außer dem PC, immer zu einer Kopieroperation. Auf dem PC erfolgt dies nur unter gewissen Umständen (z.B. zuwenig oder gar kein Video-RAM). Wenn man also den Inhalt des RenderTargets nicht unbedingt benötigt, es z.B. sowieso löscht, dann sollte man diese Option nicht wählen, da sonst ein kleiner aber durchaus erkennbarer Performance-Einbruch entstehen kann.

MRT – MultiRenderTargets

MultiRenderTargets sind eine Technik, die nur sinnvoll mit Shadern verwendet werden kann. Dies ermöglicht es dem Entwickler, mehrere RenderTargets gleichzeitig zu verwenden. Der Shader kann dann gleichzeitig unterschiedliche Inhalte in die einzelnen RenderTargets schreiben. Dies ist auf jeden Fall eine recht fortgeschrittene Technik, auf die ich in diesem Grundlagen-Artikel nicht näher eingehen möchte. Oft kann man durch MRT mehrere Render-Passes zu einem einzelnen zusammenfassen, was durchaus einen Geschwindigkeitsvorteil bedeuten kann. Man sollte einfach davon gehört haben, damit man weis, daß es sowas gibt und deren Verwendung im Falle eines Falles in Erwägung zieht.

Das Ende naht

Ursprünglich hatte ich geplant, an dieser Stelle ein kleines Beispielprogramm anzubieten. Mir fällt allerdings gerade nichts ein, was ich zeigen könnte und ich denke, dies ist auch nicht unbedingt notwendig, da das Thema ja nicht sonderlich schwer ist. Sollte trotzdem ein Beispiel gewünscht sein, so bitte ich um konstruktive Vorschläge in den Kommentaren dieses Beitrages.

Eine kleine Information hätte ich zum Schluß noch. Man sollte bei der Verwendung von RenderTargets darauf achten, daß man den Bereich zwischen dem setzen eines RenderTargets und dem Freigeben des RenderTargets als abgeschlossene Klammer sieht. Es ist eine Art Transaktion. Dies bedeutet, daß man z.B. einen SpriteBatch nicht innerhalb einer dieser Klammern starten kann und in einer anderen wieder beenden kann. Seht ganz einfach den Bereich zwischen dem Setzen und Freigeben so an, als wäre es ein eigener Aufruf der Draw-Methode. Ein Bereich kennt den anderen sowenig wie ein Aufruf der Draw-Methode den vorherigen kennt.

Ich hoffe, daß ich mit diesem Beitrag ein paar interessante Informationen über RenderTargets liefern konnte. Gerne möchte ich, bei anhaltendem Interesse, in Folgebeiträgen detailliertere Informationen zur Verwendung und Anwendung von RenderTargets liefern. Themenvorschläge dazu können – wie immer (die Platte hat irgendwie nen Sprung) – in den Kommentaren hinterlassen werden.

Advertisements

Veröffentlicht am 25.02.2011 in Grundlagen, XNA, XNA 4.0 und mit , , , , , getaggt. Setze ein Lesezeichen auf den Permalink. 6 Kommentare.

  1. Wau so einen klaren Artikel darüber hätte ich mir schön vor längerem mal gewünscht ^^
    Bisher wusste ich nur, dass man RenderTragets irgendwie dazu benutzten kann SplitScreens oder Rückspiegel zu machen. Jetzt weis ich auch wie.

    Übrigens das wären auch meine Beispiel Programm anfragen, wobei ich glaube selber probieren bringt mehr.

    • Danke schön 🙂

      SplitScreen ist ein schlechtes Beispiel, dazu habe ich aber bereits einen Artikel in den Entwürfen, kann aber noch nicht sagen, wann der kommt.

      Der Rückspiegel ist ein sehr gutes Beispiel, wie Spiegel im Allgemeinen. Ich schau mal, ob ich dafür ein Beispiel mache…

  1. Pingback: SplitScreen: Mehrere Spieler auf einer Glotze « "Mit ohne Haare"

  2. Pingback: Terrain 101: Vertex- und Index-Buffer « "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: