<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
		<id>https://wiki.delphigl.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Deathball</id>
		<title>DGL Wiki - Benutzerbeiträge [de]</title>
		<link rel="self" type="application/atom+xml" href="https://wiki.delphigl.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Deathball"/>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php/Spezial:Beitr%C3%A4ge/Deathball"/>
		<updated>2026-05-30T22:19:41Z</updated>
		<subtitle>Benutzerbeiträge</subtitle>
		<generator>MediaWiki 1.27.4</generator>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20949</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20949"/>
				<updated>2007-10-03T18:35:04Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Mehrere Texturen in einem FBO */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=GL_EXT_FRAMEBUFFER_OBJECT=&lt;br /&gt;
==Einleitung==&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
==Das FBO erstellen==&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
==Einen Depthbuffer hinzufügen==&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
  GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
==Eine Textur hinzufügen==&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8, GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
==Die Fehlerabfrage==&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion [[glCheckFramebufferStatusEXT]]. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
  error: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
  error := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
  case s of:&lt;br /&gt;
    GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
      Exit;&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete attachment');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
      raise Exception.Create('Missing attachment');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete formats');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
    GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
      raise Exception.Create('Framebufferobjects unsupported');&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Die Codeübersicht==&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: 		GluInt;&lt;br /&gt;
  depthbuffer: 	GluInt;&lt;br /&gt;
  tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
  glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
  glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
  CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die Textur rendern==&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit [[glBindTexture]](GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
==Mipmaps==&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion [[glGenerateMipmapEXT]]. Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
  //...&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
  //Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl [[glGenerateMipmapEXT]] bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: 	GluInt;&lt;br /&gt;
  depthbuffer: 	GluInt;&lt;br /&gt;
  tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_MIPMAP_LINEAR);&lt;br /&gt;
  glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Und wieder aufräumen==&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
  glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
==Mehrere Texturen in einem FBO==&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTACHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS_EXT, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur, kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die zweite Textur rendern==&lt;br /&gt;
Mit dem Befehl [[glDrawBuffer]](GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
==Multiple Render Targets==&lt;br /&gt;
Mithilfe des Befehls [[glDrawBuffers]] kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffers(2, @buffers);&lt;br /&gt;
  //normal rendern&lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==MRT und GLSL==&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffers(2, @buffers);&lt;br /&gt;
  //Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der aufmerksame Leser merkts - der Delphi Code hat sich eigentlich nicht verändert :-). Also auf zu den Shadern. Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
  gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
==Und fertig!==&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
==Weiterführende Links==&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Pixelbuffer]]|-}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Framebufferobject]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20948</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20948"/>
				<updated>2007-10-03T09:10:05Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Mehrere Texturen in einem FBO */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=GL_EXT_FRAMEBUFFER_OBJECT=&lt;br /&gt;
==Einleitung==&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
==Das FBO erstellen==&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
==Einen Depthbuffer hinzufügen==&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
  GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
==Eine Textur hinzufügen==&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8, GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
==Die Fehlerabfrage==&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion [[glCheckFramebufferStatusEXT]]. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
  error: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
  error := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
  case s of:&lt;br /&gt;
    GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
      Exit;&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete attachment');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
      raise Exception.Create('Missing attachment');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete formats');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
    GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
      raise Exception.Create('Framebufferobjects unsupported');&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Die Codeübersicht==&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: 		GluInt;&lt;br /&gt;
  depthbuffer: 	GluInt;&lt;br /&gt;
  tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
  glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
  glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
  CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die Textur rendern==&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit [[glBindTexture]](GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
==Mipmaps==&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion [[glGenerateMipmapEXT]]. Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
  //...&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
  //Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl [[glGenerateMipmapEXT]] bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: 	GluInt;&lt;br /&gt;
  depthbuffer: 	GluInt;&lt;br /&gt;
  tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_MIPMAP_LINEAR);&lt;br /&gt;
  glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Und wieder aufräumen==&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
  glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
==Mehrere Texturen in einem FBO==&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTACHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur, kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die zweite Textur rendern==&lt;br /&gt;
Mit dem Befehl [[glDrawBuffer]](GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
==Multiple Render Targets==&lt;br /&gt;
Mithilfe des Befehls [[glDrawBuffers]] kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffers(2, @buffers);&lt;br /&gt;
  //normal rendern&lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==MRT und GLSL==&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffers(2, @buffers);&lt;br /&gt;
  //Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der aufmerksame Leser merkts - der Delphi Code hat sich eigentlich nicht verändert :-). Also auf zu den Shadern. Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
  gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
==Und fertig!==&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
==Weiterführende Links==&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Pixelbuffer]]|-}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Framebufferobject]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20947</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20947"/>
				<updated>2007-10-03T09:09:06Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=GL_EXT_FRAMEBUFFER_OBJECT=&lt;br /&gt;
==Einleitung==&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
==Das FBO erstellen==&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
==Einen Depthbuffer hinzufügen==&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
  GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
==Eine Textur hinzufügen==&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8, GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
==Die Fehlerabfrage==&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion [[glCheckFramebufferStatusEXT]]. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
  error: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
  error := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
  case s of:&lt;br /&gt;
    GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
      Exit;&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete attachment');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
      raise Exception.Create('Missing attachment');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete formats');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
    GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
      raise Exception.Create('Framebufferobjects unsupported');&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Die Codeübersicht==&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: 		GluInt;&lt;br /&gt;
  depthbuffer: 	GluInt;&lt;br /&gt;
  tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
  glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
  glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
  CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die Textur rendern==&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit [[glBindTexture]](GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
==Mipmaps==&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion [[glGenerateMipmapEXT]]. Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
  //...&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
  //Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl [[glGenerateMipmapEXT]] bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: 	GluInt;&lt;br /&gt;
  depthbuffer: 	GluInt;&lt;br /&gt;
  tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_MIPMAP_LINEAR);&lt;br /&gt;
  glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Und wieder aufräumen==&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
  glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
==Mehrere Texturen in einem FBO==&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur, kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die zweite Textur rendern==&lt;br /&gt;
Mit dem Befehl [[glDrawBuffer]](GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
==Multiple Render Targets==&lt;br /&gt;
Mithilfe des Befehls [[glDrawBuffers]] kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffers(2, @buffers);&lt;br /&gt;
  //normal rendern&lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==MRT und GLSL==&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffers(2, @buffers);&lt;br /&gt;
  //Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der aufmerksame Leser merkts - der Delphi Code hat sich eigentlich nicht verändert :-). Also auf zu den Shadern. Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
  gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
==Und fertig!==&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
==Weiterführende Links==&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Pixelbuffer]]|-}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Framebufferobject]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20937</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20937"/>
				<updated>2007-09-30T09:12:14Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Eine Textur hinzufügen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=GL_EXT_FRAMEBUFFER_OBJECT=&lt;br /&gt;
==Einleitung==&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
==Das FBO erstellen==&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
==Einen Depthbuffer hinzufügen==&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
  GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
==Eine Textur hinzufügen==&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8, GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
==Die Fehlerabfrage==&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion [[glCheckFramebufferStatusEXT]]. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
  error: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
  error := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
  case s of:&lt;br /&gt;
    GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
      Exit;&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete attachment');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
      raise Exception.Create('Missing attachment');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
      raise Exception.Create('Duplicate attachment');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete formats');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
    GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
      raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
    GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
      raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Die Codeübersicht==&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: 		GluInt;&lt;br /&gt;
  depthbuffer: 	GluInt;&lt;br /&gt;
  tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
  glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
  glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
  CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die Textur rendern==&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit [[glBindTexture]](GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
==Mipmaps==&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion [[glGenerateMipmapEXT]]. Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
  //...&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
  //Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl [[glGenerateMipmapEXT]] bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  fbo: 	GluInt;&lt;br /&gt;
  depthbuffer: 	GluInt;&lt;br /&gt;
  tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGenTextures(1, @tex);&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_MIPMAP_LINEAR);&lt;br /&gt;
  glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Und wieder aufräumen==&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
  glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
  glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
==Mehrere Texturen in einem FBO==&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur, kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die zweite Textur rendern==&lt;br /&gt;
Mit dem Befehl [[glDrawBuffer]](GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  //in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
==Multiple Render Targets==&lt;br /&gt;
Mithilfe des Befehls [[glDrawBuffers]] kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffers(2, @buffers);&lt;br /&gt;
  //normal rendern&lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
  glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==MRT und GLSL==&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
  glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
  glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
  glDrawBuffers(2, @buffers);&lt;br /&gt;
  //Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
  glPopAttrib;&lt;br /&gt;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der aufmerksame Leser merkts - der Delphi Code hat sich eigentlich nicht verändert :-). Also auf zu den Shadern. Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
  gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
  buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
  buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
==Und fertig!==&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
==Weiterführende Links==&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Pixelbuffer]]|-}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Framebufferobject]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial&amp;diff=20936</id>
		<title>Tutorial</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial&amp;diff=20936"/>
				<updated>2007-09-29T15:51:12Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Extensions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Auf dieser Seite findet ihr alle Tutorials die DelphiGL.com zu bieten hat. Falls ihr zu einem Thema kein Tutorial findet könnt ihr noch einen Blick auf die [[Techniken und Algorithmen]] werfen. Diese enthalten weitere Artikel zu bestimmten Techniken.&lt;br /&gt;
&lt;br /&gt;
=Hinweise=&lt;br /&gt;
Bitte tragt selbst '''keine''' eigenen Tutorials ohne Absprache mit [[Benutzer:Flash (Kevin Fleischer)|Flash]] bzw. [[Benutzer:Phobeus|Phobeus]] hier ein. Tutorials werden prinzipiell vom DGL-Team gegengelesen, ''bevor'' sie veröffentlicht werden.&lt;br /&gt;
&lt;br /&gt;
Wer mit dem Gedanken spielt, ein Tutorial für DGL zu schreiben, sollte sich bereits bei der Themenwahl mit dem DGL-Team absprechen. DGL ist sehr an neuen Tutorials interessiert. Nur sollte man beachten, dass nichts doppelt geschrieben wird, bzw. dass nicht 2 Mann zur selben Zeit das selbe Thema beackern.&lt;br /&gt;
&lt;br /&gt;
==Hinweis für alle Programmieranfänger==&lt;br /&gt;
Falls ihr OpenGL nutzen wollt aber noch keinerlei Programmiererfahrung habt, so solltet ihr zuerst eine Programmiersprache lernen.&lt;br /&gt;
&lt;br /&gt;
DGL kann folgende Tutorials empfehlen:&lt;br /&gt;
&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!Link&lt;br /&gt;
!Sprache&lt;br /&gt;
!Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://dsdt.info/tutorials/ Delphitutorials bei DSDT.info]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|'''D'''elphi '''S'''ource der '''D'''elphi '''T'''reff entstand durch den Zusammenschluss von Delphi-Source und dem Delphi-Treff. Diese Community verfügt über eine ausgezeichnete Sammlung von Tutorials zum Thema &amp;quot;Delphiprogrammierung&amp;quot;. Es werden nicht nur die Grundlagen erklärt sondern nahezu alle Bereiche der Programmierung mit Delphi. Außerdem verfügt diese Community über ein Forum welches mit seiner ''Bastelecke'' auch für Anfänger geeignet ist.&lt;br /&gt;
|-&lt;br /&gt;
|[http://crashkurs.christian-stelzmann.de/ Crashkurs von Christian Stelzmann]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Der Crashkurs von Christian erklärt neben den Grundlagen zur und über die Sprache auch die IDE anhand von zahlreichen einfachen Beispielen, die den Einstieg in Delphi stark vereinfachen und so eine einfache Einführung in die Arbeit mit Delphi bieten.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Tutorials=&lt;br /&gt;
{{Hinweis|Alle Tutorials sind der [[:Kategorie:Tutorial]] zugeordnet.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Schwierigkeitsgrad&lt;br /&gt;
!Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|{{Level_1}} &lt;br /&gt;
|Jeder Schritt wird ausführlich erklärt. Absolut Einsteigertauglich.&lt;br /&gt;
|-&lt;br /&gt;
|{{Level_2}} &lt;br /&gt;
|Verständnis der Grundprinzipien wird vorraus gesetzt. Die Materie bleibt aber einfach.&lt;br /&gt;
|-&lt;br /&gt;
|{{Level_3}}&lt;br /&gt;
|Ihr wisst um was es geht. Es wird erwartet, dass ihr selbst das Thema weiterdenkt.&lt;br /&gt;
|-&lt;br /&gt;
|{{Level_4}}  &lt;br /&gt;
|Das Tutorial bietet euch einen Einstieg. Es wird erwartet, dass ihr euch selbst noch eingehender mit den beschriebenen Techniken befasst. Für Fragen steht unser Forum zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|{{Level_5}}  &lt;br /&gt;
|Um das Tutorial zu verstehen werden Kenntnisse auf dem entsprechenden Gebiet vorrausgesetzt, welche über Grundwissen hinausgehen. Es wird erwartet, dass ihr euch eingehender mit dem Thema auseinandersetzt und auch andere Quellen lest/gelesen habt. Für Fragen steht unser Forum zur Verfügung.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Einsteiger-Tutorials ==&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{{Hinweis|Die Einsteiger-Tutorials wurden auf [[SDL]] umgestellt. Dadurch wurde das Laden von Texturen erleichtert, vor allem aber ermöglicht SDL plattformunabhängig zu programmieren. Ihr benötigt deshalb die ''SDL.pas''. Diese Datei und alle anderen benötigten Dateien findet ihr im '''[[DGLSDK]]'''}}&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Quickstart]] (VCL)&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_Lektion1.gif|right]] &amp;quot;Quickstart: OpenGL &amp;amp; Delphi&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Für alle, die einen schnellen Einstieg in die OpenGL Programmierung mit Delphi suchen, hat Flash hier einen Einstieg geschaffen. Neben dem erstellen eines eigenen OpenGL-Templates hat er für alle, die Großes mit OpenGL vorhaben, am Ende noch einige Hinweise bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
''In diesem Tutorial wird eine Codevorlage für OpenGL-Projekte geschaffen. Diese basiert allerdings auf Borlands Visual Component Library (VCL) und nicht auf SDL. '''Unabhängig davon beherbergt dieses Tutorial im 2. Teil einige allgemeingültige Hinweise für OpenGL-Anfänger.''' Wer nicht mit SDL arbeitet weil z.B. Plattformunabhängigkeit nicht gewünscht oder durch die Sprache bereits gegeben ist, kann diese Codebasis leicht auf seine Sprache übertragen.''&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Lektion 1]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_Lektion1.gif|right]] &amp;quot;Nicht zu weit aus dem Fenster lehnen&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Dieses Tutorial von Magellan ist für alle Neueinsteiger gedacht. Hier werden Grundlagen von &amp;quot;Was ist OpenGL&amp;quot; bis zu &amp;quot;Wie initialisiere ich OpenGL?&amp;quot; besprochen.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Lektion 2]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_Lektion2.jpg|right]] &amp;quot;Entdeckung einer neuen Welt&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Unter dieser Überschrift empängt euch Phobeus zu eurem ersten OpenGL Tutorial welches sich mit der Anwendung der OpenGL-API befasst.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Lektion 3]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_Lektion3.gif|right]] &amp;quot;Eine Welt des Grauens?&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Hinter diesem Titel verbirgt sich ein Einsteigertutorial zum Thema Matrizen in OpenGL. Wiederum führt euch Phobeus durch den Stoff.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Lektion 4]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Lektion4.png|right]] &amp;quot;Texturen, Tapeten und Ihre Tücken&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Wie bekomme ich ein Bild auf meine Primitiven? Wer sich diese Frage bei den vorigen Tutorials gestellt hat, der bekommt hier nun von Phobeus die Antworten.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Lektion 5]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Lektion5.jpg|right]] &amp;quot;Artenvielfalten und Ihre Folgen&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
In diesem Tutorial geht Phobeus auf weitere Zeichentechniken und Methoden zur Verbesserung der Performance unter OpenGL ein.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Lektion 7]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Lektion7.png|right]] &amp;quot;Verblendet!&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Wenn man halbdurchsichtige Fensterscheiben, Lightmaps oder etwas ähnliches in sein Projekt einbauen will, dann kommt man um Blending nicht herum. Wie man das ganze Nutzen kann ist in diesem Tutorial von Phobeus erklärt.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Lektion 8]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Lektion8.gif|right]] &amp;quot;Das Wesen von hell und dunkel - Licht&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Ohne Beleuchtung sieht selbst jede 3D-Umgebung platt aus. In diesem Tutorial wird primär der richtige Gebrauch des OpenGL-Lichtes beschrieben. Darüberhinaus wird noch die Berechnung von Normalen erklärt.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial 2D]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_2D.jpg|right]] &amp;quot;2D mit OpenGL&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
OpenGL ist zwar primär eine 3D-API, eignet sich aber, nicht zuletzt dank seiner leistungsstarken Hardwarebeschleunigung und der damit verbundenen Features, auch sehr gut für reine 2D-Anwendungen. Dieses Tutorial von Sascha Willems geht sehr ausführlich auf die Nutzung von OpenGL für 2D-Anwendungen ein.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Matrix2]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_Matrix2.png|right]] &amp;quot;Matrix2 - Matrizen und Matrixmanipulationen&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Das Thema Matrizen stellt OpenGL-Einsteiger regelmäßig vor &amp;quot;unlösbare&amp;quot; Aufgaben:&amp;lt;br&amp;gt;&lt;br /&gt;
''Wieso dreht sich der Würfel so komisch, und nicht um sich selbst?''&amp;lt;br&amp;gt; &lt;br /&gt;
''Wieso verschiebt OpenGL die Kugel denn dahin, und nicht dorthin?''&amp;lt;br&amp;gt;&lt;br /&gt;
Dieses Tutorial von Flash befasst sich mit den 3 Matrixtypen, die es in OpenGL gibt und erklärt was die Befehle '''glScale, glRotate und glTranslate''' wirklich machen.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Grundlagen-Tutorials ==&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Abseits eckiger Welten]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Abseits_eckiger_Welten.gif|right]] &amp;quot;Abseits eckiger Welten&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
3D-Welten bestehen seit eh und je aus Dreiecken, was sie oft recht eckig erscheinen lässt. In diesem Tutorial lernt ihr allerdings wie man mit Hilfe von Evaluatoren in OpenGL auch Rundungen erzeugen kann.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Renderpass]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_Renderpass.jpg|right]] &amp;quot;Renderpass - Die Welt daneben&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
OpenGL bietet nicht nur die Möglichkeit seine Szene direkt auf den Bildschirm zu rendern, sondern auch diese in eine Textur zu rendern um diese dann auf ein Objekt zu kleben. Dadurch ergeben sich diverse neue Möglichkeiten, wie z.B. Spiegel, Portale oder nachträgliche Manipulation der Textur. Wie ihr eure Szene in eine Textur bekommt, erfahrt ihr hier.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Selection]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Selection.gif|right]] &amp;quot;Objectselektion A&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Die Selektion von Objekten braucht man spätestens dann, wenn man ein komfortable Interaktion mit der Spielwelt erstellen will. Mit diesem Tutorial von DCW_MrT ist die Thematik kein Problem mehr.&lt;br /&gt;
|-&lt;br /&gt;
! [[Tutorial Objektselektion]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
| [[Bild:Tutorial_Selection_tut03.jpg|right]] &amp;quot;Objektselektion B&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
Wie simpel der Objektselektionsmodus von OpenGL zu benutzen ist, wird in diesem Tutorial von Sascha Willems vermittelt. Dazu gibt es den Source-Code und eine fertig kompilierte Anwendung.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial TexFilter]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_TexFilter.jpg|right]] &amp;quot;Texturfilterung - Texturen-Feintuning&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Texturen sind seit langem ein wichtiger Bestandteil im Bereich der Echtzeitgrafik. OpenGL bietet auf diesem Gebiet auch diverse Möglichkeiten Texturen auch filtern zu lassen. Welche das sind und wie sie sich auswirken, könnt ihr in diesem Tutorial von Delphic nachlesen.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Kamera1]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Kamera1.gif|right]] &amp;quot;Dreht sich das Universum um uns? und andere philosophische Fragen&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
In diesem Tutorial von Delphic geht es um die Kamera. Wie eine Kameraklasse gebaut sein könnte und was sie macht wird hier erklärt.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Effekte ==&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Nebel]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Nebel.jpg|right]] &amp;quot;DGL Fogging Tutorial&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Erstaunlich einfach lässt sich mit OpenGL Nebel darstellen. Wie einfach, zeigt euch Lithander in diesem Tutorial. &lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Partikel1]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Partikel1.gif|right]] &amp;quot;Partikel-Tutorial I&amp;quot;.&amp;lt;br&amp;gt;&lt;br /&gt;
Eine Partikel-Engine sollte in (fast) jeder Engine enthalten sein. Anhand dieses Tutorials von Lithander könnt ihr den Grundstein dazu legen.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial BumpMap]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_BumpMap.png|right]] &amp;quot;Bumpmapping - Plastisches 2D&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Bumpmapping ist eine immer stärker genutzte Technik, um polygonarme Oberflächen ohne Tesselation mit vorgegaukelten Details - abhängig vom Lichteinfall - zu versehen. In diesem Tutorial zeigt euch HomerS, wie man Bumpmapping über die NVIDIA-spezifischen Combiner realisiert.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Bumpmaps mit Blender]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_BumbBlend_Preview.png|right]]&lt;br /&gt;
Mit dem kostenlosen 3D-Program Blender können einfache Bumpmaps erstellt werden. Wie das geht, erfahrt ihr in diesem Tutorial von Flo. &lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Das Programm &amp;quot;Blender&amp;quot; bekommt ihr kostenlos auf [http://blender3d.org blender3d.org]}}&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial MultiTexturing]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Multitex05.jpg|right]] &amp;quot;Multitexturing&amp;quot;.&amp;lt;br&amp;gt;&lt;br /&gt;
Wie man mehrere Texturen auf ein Polygon bringen kann, erklärt auch Sascha Willems in diesem Tutorial zum Thema Multitexturing. Multitexturing ist seit OpenGL1.3 Teil des GL-Kerns und kann für z.B. für Lightmapping bzw. Detailmapping verwendet werden.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial StencilSpiegel]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_StencilSpiegel.jpg|right]] &amp;quot;Spiegelungen mit dem Stencil-Buffer&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Eine Echtzeit-Spiegelung ist einer der schönsten Effekte einer 3D-Welt und trägt viel zum Realismus bei. Eine Möglichkeit Spiegelungen über den Stencilpuffer zu realisieren, wird in diesem Tutorial von Sascha Willems erklärt.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial StereoSehen]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_StereoSehen.gif|right]]Auch wenn moderne Grafikkarten durch eine immer besser werdende Darstellung dem Benutzer versuchen das Betreten einer virtuellen 3D-Welt vorzugaukeln, so schafft es doch auch das beste Doom3-Monster nur bis zur Glasröhre zu erschrecken, rutscht dann aber quietschend davon ab. Was also tun, wenn man einen Schritt weiter gehen und dem Anwender wirkliches 3D bieten möchte? Wer eine 3D-Brille hat, wird mit diesem Tutorial von Nico Mechaelis eine Möglichkeit finden, die dritte Dimension am Computer zur erschließen.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Alphamasking]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Alphamasking_tut04.jpg|right]]In diesem kleinen aber feinen Tutorial erklärt Sascha Willems, wie man Alphamasking dazu verwenden kann, eine Szene optisch aufzuwerten und gleichzeitig Performance zu sparen.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Extensions==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Cubemap]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_Cubemap_mini.jpg|right]] &amp;quot;GL_ARB_Texture_Cubemap&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
In diesem Tutorial erklärt Sascha Willems die Grundfunktionen der '''GL_ARB_Texture_Cubemap'''-Extension und zeigt auch weiterführende Techniken auf.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Vertexbufferobject]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial Vertexbufferobject preview.jpg|right]] &amp;quot;GL_ARB_Vertex_Buffer_Object&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Wie man mit dieser Extension Vertexdaten schnell im Grafikkarten- und Hauptspeicher ablegt und darauf zugreift, erklärt Sascha Willems in diesem Tutorial.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Vertexprogramme]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_VP_preview.jpg|right]] &amp;quot;GL_ARB_Vertex_Program&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
In diesem umfangreichen Tutorial werden alle wichtigen Bereiche der Programmierung mit der GL_ARB_Vertex_Program-Extension abgedeckt. Man kann es als umfassende deutsche Referenz zu diesem Thema sehen, da auch alle wichtigen Funktionen aufgelistet werden und es auch sonst an nichts fehlt.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial NVOcclusionQuery]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_NVO_preview.jpg|right]] &amp;quot;NV_Occlusion_Query&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Lard Middendorf geht in diesem Tutorial auf die Verwendung der NV_Occlusion_Query-Extension ein.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Pixelbuffer]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_Pixelbuffer_preview.jpg|right]] &amp;quot;WGL_ARB_Pixel_Buffer&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Sascha Willems erklärt hier leicht verständlich den Umgang mit der Pixelbuffer-Extension. Als Additum wird auch noch auf die Nutzung dieser Extension für Shadowmapping eingegangen.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial_Framebufferobject]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_Framebufferobject_Preview.jpg|right]] &amp;quot;GL_EXT_FRAMEBUFFER_OBJECT&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Deathball erklärt in diesem Tutorial wie man Framebufferobjekte erstellt und diese zum Offscreenrendern benutzt.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Shader ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial glsl|Tutorial glSlang]]&lt;br /&gt;
{{Level_4}} &lt;br /&gt;
|[[Bild:Tutorial_glsl1.png|right]] &amp;quot;Einführung in GLSL&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Nach langem Ringen und Handeln hat es das ARB letztendlich doch geschafft, eine einheitliche Shaderhochsprache für OpenGL zu veröffentlichen, nämlich ''glSlang''. Mit glSlang können nun auch unter OpenGL Vertex- und Fragment-Shader in einer lesbaren, C-ähnlichen Hochsprache geschrieben werden, was deren Entwicklung stark vereinfacht. Diese Einführung von Sascha Willems ist weniger ein Tutorial, als ein kompletter Überblick über glSlang. Hier erfährt der Leser nicht nur etwas über die Benutzung von Shadern im Programm, sondern auch alles über die Sprachelemente, inklusive diverser Beispiele.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial glsl2|Tutorial glSlang 2]]&lt;br /&gt;
{{Level_4}} &lt;br /&gt;
|[[Bild:Tutorial_na.jpg|right]] &amp;quot;GLSL-Ergänzungen und Beispiele&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Dieses Tutorial von La_Boda ist eine direkte Fortsetzung des GLSL-Tutorials. Es behandelt verstärkt die Praxis und liefert anhand von einigen Beispielen eine bessere Sicht auf die bereits gelernte Theorie.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== KI ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial pathfinding|Tutorial Pathfinding]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_pathfinding.png|right]] &amp;quot;Pathfinding 1&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
In diesem Tutorial zeigt euch Flo wie man recht simpel eine Wegfindungsroutine implementiert. Besonders wenn man sowas ohne großen Lernaufwand in sein Spiel einbinden möchte, sollte man einen Blick riskieren.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial pathfinding2|Tutorial Pathfinding 2]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_pathfinding2.png|right]] &amp;quot;Pathfinding 2&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Gerade bei aktuelleren Programmen reicht es meist nicht aus, wenn man seine Einheiten nur in 8 Himmelsrichtungen bewegen kann. Frase stellt in diesem Tutorial vor, wie man eine Wegfindungsroutine implementieren kann, wie diese in einem heutigen RTS vorkommen kann.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Terrain und Landschaften ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Terrain1]]&lt;br /&gt;
{{Level_4}} &lt;br /&gt;
|[[Bild:Tutorial_Terrain1.gif|right]] &amp;quot;Aussenlandschaften mit System - Heightmaps&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Anhand der Graustufenwerte einer Bitmap-Datei kann man relativ einfach eine imposante Landschaft erstellen. Wie das ganze funktioniert erklärt euch Nico Michaelis in diesem Tutorial. Außerdem wird noch die Implementation von Skyboxen erklärt.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Terrain2|Tutorial Terrain 2]]&lt;br /&gt;
{{Level_4}} &lt;br /&gt;
|[[Bild:Tutorial_Terrain2.gif|right]] &amp;quot;Heightmap-Texturen&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Da das Terrain aus dem vorigen Tutorial noch recht eintönig grau aussieht und das natürlich geändert werden soll, erklärt euch Nico, wie man schmucke Schatten und Texturen für die Heightmap generieren kann.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Terrain3|Tutorial Terrain 3]]&lt;br /&gt;
{{Level_5}} &lt;br /&gt;
|[[Bild:Tutorial_Terrain3.gif|right]] &amp;quot;Terrain im Detail&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Da größere Landschaften aufgrund der hohen Polygonanzahl sehr aufwändig zu rendern sind erklärt euch Nico hier, wie man entfernte Dreiecke &amp;quot;zusammenlegen&amp;quot; kann, um Rechenleistung zu sparen.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Skyboxen]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Skybox_Vorschau.jpg|right]] &amp;quot;Skyboxen&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
In diesem Tutorial erklärt Sascha Willems kurz und bündig den Umgang mit Skyboxen, mit denen man Landschaften schnell sehr stark optisch aufwerten kann. Es wird sowohl auf die Erstellung der Skybox-Texturen mit dem Programm Terragen als auch die Implementation der Skybox in OpenGL eingegangen.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Spieletutorials und Softwareentwicklung==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Bomberman1|Tutorial Bomberman 1]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_Bomberman1.jpg|right]] &amp;quot;Bomberman1 - Codebasis und Editor&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Im ersten Teil dieser kleinen Tutorialserie zum Thema Spieleprogrammierung kümmern wir uns neben der Erstellung unseres Basiscodes auch um einen fertigen Editor. Nach der Durcharbeitung dieses Tutorials von Sascha Willems sollte der Leser alle Grundprinzipien zur Programmierung eines Spiels verstanden haben.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Bomberman2|Tutorial Bomberman 2]]&lt;br /&gt;
{{Level_3}}&lt;br /&gt;
|[[Bild:Tutorial_Bomberman2.jpg|right]] &amp;quot;Bomberman2 - Der 3D-Bombermanklon (Grundversion)&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Im zweiten Teil gehts ans Eingemachte, nämlich einen fertigen Bombermanklon. In Sascha Willems Tutorial werdet ihr zusätzlich zu den bereits erworbenen Prinzipien diverse Techniken die in so ziemlich jedem Spiel Verwendung finden erlernen.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Softwareentwicklung1|Tutorial Softwareentwicklung 1]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_SWE1.jpg|right]] &amp;quot;Softwareentwicklung1 - Objektorientierte Softwareentwicklung mit UML&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Im ersten Teil dieser Tutorialserie zum Thema Softwareentwicklung erklärt euch Flash, wie man an ein Projekt herangeht und herausfindet was eigentlich zu tun ist. Es geht dabei um Analyse der Anforderungen und die Modellierung sogenannter Use Cases. Die hier vorgestellten Arbeiten sind Grundlage um später Klassen zu finden auf denen dann der Code aufgebaut wird.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Softwareentwicklung2|Tutorial Softwareentwicklung 2]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_SWE2.jpg|right]] &amp;quot;Softwareentwicklung2 - Objektorientierte Softwareentwicklung mit UML&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Im zweiten und umfangreichsten Teil der Serie steht das Thema &amp;quot;Klassen&amp;quot; im Zentrum. Flash erklärt euch wie man aus den Use Cases Klassen ableitet und welche unterschiedlichen Einsatzgebiete Klassen innerhalb der Software haben können.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Softwareentwicklung3|Tutorial Softwareentwicklung 3]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_SWE3_Interface.jpg|right|128px]] &amp;quot;Softwareentwicklung3 - Objektorientierte Softwareentwicklung mit UML&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Im letzten Teil der Serie steht das Thema &amp;quot;Verhalten&amp;quot; im Zentrum. Flash erklärt euch wie die Klassen miteinander in Beziehung gesetzt werden. Außerdem wird nocheinmal auf den RUP als '''iterative''' Vorgehensweise eingegangen.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Mathematisches ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Lineare Algebra]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_lineare_Algebra_Preview.jpg|right]] &amp;quot;Vektorrechnung&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
In vielen Tutorials tauchen eine Menge Begriffe aus der linearen Algebra und analytischen Geometrie auf. Wer bei Begriffen wie Vektoren, Skalar-Produkt, Vektorkreuzprodukt, etc. ein wenig Auffrischung und Implementationshinweise braucht, ist mit diesem Tutorial von EternalLearner an der richtigen Stelle.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Nachsitzen]]{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_Nachsitzen_preview.png|right]] &amp;quot;3d-Mathematik&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Wer die mathematischen Grundlagen wie Sinus, Cosinus und Matrizen aus der Schule schon wieder vergessen oder noch vor sich liegen hat, der kann mit Hilfe dieses Tutorials von Nico Michaelis dieses Wissen erlernen bzw. auffrischen.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Objekt gedreht und dennoch nach vorne bewegt]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Objekt_gedreht_und_dennoch_nach_vorne_bewegt_preview.gif|right]] &amp;quot;Objekt gedreht und dennoch nach vorne bewegt&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
SchodMC erklärt in diesem Tutorial wie man ein Objekt drehen und dabei trotzdem vorwärts bewegen kann. Quer fahrende Autos sind zwar praktisch zum Einparken Aber doch eher selten ;-)&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Objekt immer um eigene Achse drehen]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Objekt_immer_um_die_eigene_Achse_drehen_preview.gif|right]] &amp;quot;Objekt immer um eigene Achse drehen&amp;quot;.&amp;lt;br&amp;gt;&lt;br /&gt;
Wenn ihr euer Objekt nun noch um die eigene Achse drehen wollt, seid ihr mit diesem Tutorial von SchodMC an der richtigen Adresse&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Separating Axis Theorem]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Noexist.jpg|right]] &amp;quot;Separating Axis Theorem&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
Seth erklärt in diesem Tutorial wie man Polygonkollisionen mithilfe des ''Seperating Axis Theorem'' erkennen kann. &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Grafik und Modellierung ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Modellierung eines Low-Poly Menschen Teil 1]]&lt;br /&gt;
{{Level_4}} &lt;br /&gt;
|[[Bild:Tutorial_Low-Poly-Mensch1_preview.jpg|right]] &amp;quot;Modellierung eines Low-Poly-Menschen&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Die Modellierung von organischen &amp;quot;Objekten&amp;quot; gehört mit zu den kompliziertesten Aufgaben, die es beim Modelling gibt. Wie man einen Menschen mit aus möglichst wenigen Polygonen mit dem Programm 3D Studio Max erstellen kann wird in diesem Tutorial von Sascha Willems erklärt.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Modellierung eines Low-Poly Menschen Teil 2]]&lt;br /&gt;
{{Level_4}} &lt;br /&gt;
|[[Bild:Tutorial_Low-Poly-Mensch2_preview.jpg|right]] &amp;quot;Kleider machen Leute (UVW-Mapping)&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Nun spendiert Sascha Willems seinem Menschen aus dem 1. Tutorial eine schmucke Uniform, so dass er sich endlich in der Öffentlichkeit blicken lassen kann.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Skripte ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Scripting mit JvInterpreterProgram]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_JvInterpreter_preview.png|right]] &amp;quot;Skripting mithilfe der Jedi-Code-Library&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
SchodMC erklärt hier wie man mit relativ wenig Aufwand Skripte in eigene Programme integrieren kann.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Scriptsprachen Teil 1]]&lt;br /&gt;
{{Level_5}} &lt;br /&gt;
|[[Bild:Tutorial_Skriptsprachen1_preview.gif|right]] &amp;quot;Scriptsprachen Teil 1 - Einführung&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Für alle die schon immer einmal wissen wollten wie ein Compiler arbeitet ist diese Tutorial-Reihe von Nico Michaelis genau das richtige.&lt;br /&gt;
&lt;br /&gt;
Der erste Teil widmet sich der Lexikalischen Analyse und dem Parsen des Quellcodes&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Scriptsprachen Teil 2]]&lt;br /&gt;
{{Level_5}} &lt;br /&gt;
|[[Bild:Tutorial_Skriptsprachen2_preview.gif|right]] &amp;quot;Scriptsprachen Teil 2 - Virtuelle Computer und Code Erzeugung&amp;quot;.&amp;lt;br&amp;gt;&lt;br /&gt;
Für alle die schon immer einmal wissen wollten wie ein Compiler arbeitet ist diese Tutorial-Reihe von Nico Michaelis genau das richtige.&lt;br /&gt;
&lt;br /&gt;
Der zweite Teil beschreibt den Bau einer VM und der Codeerzeugung&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Delphi allgemein ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
! [[Tutorial Debugging]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
| [[Bild:Tutorial Debugging.gif|right]] &amp;quot;Debugger benutzen und andere praktische Tipps&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
In diesem Tutorial werdet von Nico mit der Nutzung des Delphi-Debuggers eingeführt. Auch werden Wege zur Vermeidung von Fehlern aufgezeigt. Genauso findet Ihr aber auch in diesem Tutorial Hinweise, zu häufig begangenen Fehlern und deren Ursachen.&lt;br /&gt;
|-&lt;br /&gt;
! [[Tutorial Komponentenentwicklung]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
| [[Bild:Tutorial Komponentenentwicklung.png|right]] &amp;quot;Komponentenentwicklung&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
In diesem Tutorial werdet ihr ein wenig tiefer in die Entwicklung von Komponenten unter Delphi eingeführt. Es wird Grundwissen auf diesem Gebiet vorausgesetzt.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Multithreading]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_Multithreading.gif|right]] &amp;quot;Das Prinzip der Dualität&amp;quot;.&amp;lt;br&amp;gt; &lt;br /&gt;
Threads sind ein erweitertes Prinzip des bekannten Multitasking und werden innerhalb eines Programmes oft dazu genutzt, verschiedene Aufträge gleichzeitig abzuarbeiten. LossyEx erklärt euch hier deren Nutzung.&lt;br /&gt;
|-&lt;br /&gt;
! [[Tutorial Software-Synthesizer]]&lt;br /&gt;
{{Level_4}} &lt;br /&gt;
| [[Bild:Noexist.jpg|right]] &amp;quot;Software-Synthesizer&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
Computerspiele bestehen nicht nur aus Grafik. Dieses Tutorial widmet sich einem ganz anderen Bereich der Spieleprogrammierung: Der Erzeugung von synthetischen Sounds und Musik.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== SDL ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial SDL Einstieg]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Lektion1.gif|right]] &amp;quot;SDL-Einsteiger-Tutorial&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Crossplattform-Programmierung ist das Zauberwort, welches in den letzten Jahren - dank verstärkter Präsenz von Linux - immer wieder in den Programmierforen des Internets auftaucht. Für Grafikprogrammier ist [[SDL]] hier das Mittel der Wahl. Bekannte Titel wie &amp;quot;Quake III&amp;quot; und &amp;quot;Civilisation - Call to Power&amp;quot; bauen bereits auf diese Bibiothek für ihre Linux-Ports. Phobeus zeigt euch in diesem ersten SDL-Tutorial was SDL ist und wie man es benutzt.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial SDL_RWops]]&lt;br /&gt;
{{Level_2}}&lt;br /&gt;
| [[Bild:Noexist.jpg|right]] &amp;quot;SDL_RWops-Tutorial&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
SDL benutzt für den Dateizugriff SDL_RWops. [[Benutzer:Lord Horazont|Lord Horazont]] zeigt euch in diesen Tutorial wie man diese benutzt und auch komfortabler gestaltet.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Rendertechniken ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
! [[Tutorial Octree]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
| [[Bild:Tutorial Octree preview.jpg|right]] &amp;quot;Octrees&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
Octrees sind eine einfach zu implementierende, aber sehr effiziente Möglichkeit um besonders Outdoor-Szenen abhängig vom Betrachterblickfeld stark zu beschleunigen. In diesem Tutorial zeigt Shadow euch eine komplette Implementation eines Octrees inklusive ausgiebigem Quellcode.&lt;br /&gt;
|-&lt;br /&gt;
! [[Tutorial Frustum Culling]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
| [[Bild:Tutorial Frustum Culling.jpg|right]] &amp;quot;Frustum Culling&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
Frustum Culling ist eine einfache Möglichkeit die Rendergeschwindigkeit zu erhöhen, da man damit schon vor dem Senden der Daten an die Grafikkarten feststellen kann, welche Objekte sich im Blickfeld des Spielers befinden und welche nicht.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Raytracing ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
! [[Tutorial Raytracing - Grundlagen I]]&lt;br /&gt;
{{Level_4}} &lt;br /&gt;
| [[Bild:Tutorial_RaytracingI_thumb.jpg|right]] &amp;quot;Raytracing - Grundlagen I&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
Die Grundzüge des Raytracings Teil 1 - Von den Strahlen bis zur ersten Darstellung von Kugeln und Ebenen&lt;br /&gt;
|-&lt;br /&gt;
! [[Tutorial Raytracing - Grundlagen II]]&lt;br /&gt;
{{Level_4}} &lt;br /&gt;
| [[Bild:Tutorial_RaytracingI_thumb.jpg|right]] &amp;quot;Raytracing - Grundlagen II&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
Die Grundzüge des Raytracings Teil 2 - Einfache Basisobjekte, Transformationen und Phong-Lightning&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Sonstiges ==&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
!width=&amp;quot;15%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;85%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
! [[Tutorial GLScene]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
| [[Bild:Tutorial GLScene preview.jpg|right]] &amp;quot;Einführung in GLScene&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
GLScene ist eine zu erstaunlicher Größe angewachsene Komponentensammlung, die quasi einen OpenGL-Wrapper darstellt und euch dank Delphis VCL dadurch recht einfach OpenGL-Programme erstellen lässt. LaBoda gibt euch zu dieser Komponentensammlung in diesem Tutorial deshalb eine kleine Einführung.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:Tutorial_Framebufferobject_Preview.jpg&amp;diff=20935</id>
		<title>Datei:Tutorial Framebufferobject Preview.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:Tutorial_Framebufferobject_Preview.jpg&amp;diff=20935"/>
				<updated>2007-09-29T15:50:32Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20929</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20929"/>
				<updated>2007-09-28T21:01:36Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=GL_EXT_FRAMEBUFFER_OBJECT=&lt;br /&gt;
==Einleitung==&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
==Das FBO erstellen==&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
==Einen Depthbuffer hinzufügen==&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
==Eine Textur hinzufügen==&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
==Die Fehlerabfrage==&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion [[glCheckFramebufferStatusEXT]]. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	error: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	error := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Die Codeübersicht==&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die Textur rendern==&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit [[glBindTexture]](GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
==Mipmaps==&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion [[glGenerateMipmapEXT]]. Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl [[glGenerateMipmapEXT]] bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Und wieder aufräumen==&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
==Mehrere Texturen in einem FBO==&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur, kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die zweite Textur rendern==&lt;br /&gt;
Mit dem Befehl [[glDrawBuffer]](GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
==Multiple Render Targets==&lt;br /&gt;
Mithilfe des Befehls [[glDrawBuffers]] kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==MRT und GLSL==&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der aufmerksame Leser merkts - der Delphi Code hat sich eigentlich nicht verändert :-). Also auf zu den Shadern. Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
==Und fertig!==&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
==Weiterführende Links==&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Framebufferobject]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20928</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20928"/>
				<updated>2007-09-28T21:01:00Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=GL_EXT_FRAMEBUFFER_OBJECT=&lt;br /&gt;
==Einleitung==&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
==Das FBO erstellen==&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
==Einen Depthbuffer hinzufügen==&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
==Eine Textur hinzufügen==&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
==Die Fehlerabfrage==&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion [[glCheckFramebufferStatusEXT]]. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	error: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	error := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Die Codeübersicht==&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die Textur rendern==&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit [[glBindTexture]](GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
==Mipmaps==&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion [[glGenerateMipmapEXT]]. Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl [[glGenerateMipmapEXT]] bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Und wieder aufräumen==&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur, kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==In die zweite Textur rendern==&lt;br /&gt;
Mit dem Befehl [[glDrawBuffer]](GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
==Multiple Render Targets==&lt;br /&gt;
Mithilfe des Befehls [[glDrawBuffers]] kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==MRT und GLSL==&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der aufmerksame Leser merkts - der Delphi Code hat sich eigentlich nicht verändert :-). Also auf zu den Shadern. Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
==Und fertig!==&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
==Weiterführende Links==&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Framebufferobject]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20927</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20927"/>
				<updated>2007-09-24T19:59:15Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* MRT und GLSL */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
==GL_EXT_FRAMEBUFFER_OBJECT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion [[glCheckFramebufferStatusEXT]]. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit [[glBindTexture]](GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion [[glGenerateMipmapEXT]]. Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl [[glGenerateMipmapEXT]] bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur, kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl [[glDrawBuffer]](GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls [[glDrawBuffers]] kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der aufmerksame Leser merkts - der Delphi Code hat sich eigentlich nicht verändert :-). Also auf zu den Shadern. Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20926</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20926"/>
				<updated>2007-09-24T19:54:37Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
==GL_EXT_FRAMEBUFFER_OBJECT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion [[glCheckFramebufferStatusEXT]]. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit [[glBindTexture]](GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion [[glGenerateMipmapEXT]]. Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl [[glGenerateMipmapEXT]] bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur, kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl [[glDrawBuffer]](GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls [[glDrawBuffers]] kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20923</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20923"/>
				<updated>2007-09-24T10:37:29Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
==GL_EXT_FRAMEBUFFER_OBJECT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion [[glCheckFramebufferStatusEXT]]. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit [[glBindTexture]](GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion [[glGenerateMipmapEXT]]. Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl [[glGenerateMipmapEXT]] bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &lt;br /&gt;
	GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur, kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl [[glDrawBuffer]](GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls [[glDrawBuffers]] kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20922</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20922"/>
				<updated>2007-09-24T10:24:07Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Eine Textur hinzufügen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
==GL_FRAMEBUFFER_EXT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von glBindFramebufferEXT ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt  müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit glRenderbufferStorageEXT hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level.&lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion glCheckFramebufferStatusEXT. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit glBindTexture(GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion glGenerateMipmapEXT(). Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl glGenerateMipmapEXT()  bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 	GL_CLAMP_TO_EDGE;&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &lt;br /&gt;
	GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 	GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl glDrawBuffer(GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls glDrawBuffers kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20921</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20921"/>
				<updated>2007-09-24T09:06:32Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
==GL_FRAMEBUFFER_EXT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von glBindFramebufferEXT ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt  müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit glRenderbufferStorageEXT hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level. &lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion glCheckFramebufferStatusEXT. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit glBindTexture(GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion glGenerateMipmapEXT(). Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl glGenerateMipmapEXT()  bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 	GL_CLAMP_TO_EDGE;&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &lt;br /&gt;
	GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 	GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl glDrawBuffer(GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls glDrawBuffers kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20920</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20920"/>
				<updated>2007-09-24T09:05:21Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable}}&lt;br /&gt;
|widht=&amp;quot;80%&amp;quot;|&amp;lt;div style=&amp;quot;color:#F09000;font-size:125%&amp;quot;&amp;gt;&amp;lt;center&amp;gt;'''Hinweis:''' Dieser Artikel wird gerade Offline bearbeitet!&amp;lt;br&amp;gt; &lt;br /&gt;
Bitte haben Sie etwas Geduld und nehmen Sie keine Änderungen vor, bis der Artikel hochgeladen wurde.&amp;lt;br&amp;gt;&lt;br /&gt;
[[:Kategorie:Offline in Bearbeitung|(weitere Artikel)]]&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|width=&amp;quot;30%&amp;quot;|[[Bild:WIP Offline.jpg|right|100px]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt; [[Kategorie:Offline in Bearbeitung]] &amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GL_FRAMEBUFFER_EXT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von glBindFramebufferEXT ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt  müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit glRenderbufferStorageEXT hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level. &lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion glCheckFramebufferStatusEXT. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit glBindTexture(GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion glGenerateMipmapEXT(). Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl glGenerateMipmapEXT()  bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 	GL_CLAMP_TO_EDGE;&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &lt;br /&gt;
	GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 	GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl glDrawBuffer(GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls glDrawBuffers kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20919</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20919"/>
				<updated>2007-09-24T09:00:35Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* In die zweite Textur rendern */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable}}&lt;br /&gt;
|widht=&amp;quot;80%&amp;quot;|&amp;lt;div style=&amp;quot;color:#F09000;font-size:125%&amp;quot;&amp;gt;&amp;lt;center&amp;gt;'''Hinweis:''' Dieser Artikel wird gerade Offline bearbeitet!&amp;lt;br&amp;gt; &lt;br /&gt;
Bitte haben Sie etwas Geduld und nehmen Sie keine Änderungen vor, bis der Artikel hochgeladen wurde.&amp;lt;br&amp;gt;&lt;br /&gt;
[[:Kategorie:Offline in Bearbeitung|(weitere Artikel)]]&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|width=&amp;quot;30%&amp;quot;|[[Bild:WIP Offline.jpg|right|100px]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt; [[Kategorie:Offline in Bearbeitung]] &amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GL_FRAMEBUFFER_EXT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von glBindFramebufferEXT ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt  müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit glRenderbufferStorageEXT hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level. &lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion glCheckFramebufferStatusEXT. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit glBindTexture(GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion glGenerateMipmapEXT(). Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl glGenerateMipmapEXT()  bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 	GL_CLAMP_TO_EDGE;&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &lt;br /&gt;
	GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 	GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl glDrawBuffer(GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;-) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls glDrawBuffers kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20918</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20918"/>
				<updated>2007-09-24T08:56:11Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Einen Depthbuffer hinzufügen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable}}&lt;br /&gt;
|widht=&amp;quot;80%&amp;quot;|&amp;lt;div style=&amp;quot;color:#F09000;font-size:125%&amp;quot;&amp;gt;&amp;lt;center&amp;gt;'''Hinweis:''' Dieser Artikel wird gerade Offline bearbeitet!&amp;lt;br&amp;gt; &lt;br /&gt;
Bitte haben Sie etwas Geduld und nehmen Sie keine Änderungen vor, bis der Artikel hochgeladen wurde.&amp;lt;br&amp;gt;&lt;br /&gt;
[[:Kategorie:Offline in Bearbeitung|(weitere Artikel)]]&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|width=&amp;quot;30%&amp;quot;|[[Bild:WIP Offline.jpg|right|100px]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt; [[Kategorie:Offline in Bearbeitung]] &amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GL_FRAMEBUFFER_EXT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von glBindFramebufferEXT ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt  müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit glRenderbufferStorageEXT hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level. &lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion glCheckFramebufferStatusEXT. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit glBindTexture(GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion glGenerateMipmapEXT(). Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl glGenerateMipmapEXT()  bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 	GL_CLAMP_TO_EDGE;&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &lt;br /&gt;
	GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 	GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl glDrawBuffer(GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls glDrawBuffers kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20917</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20917"/>
				<updated>2007-09-24T08:55:38Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Das FBO erstellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable}}&lt;br /&gt;
|widht=&amp;quot;80%&amp;quot;|&amp;lt;div style=&amp;quot;color:#F09000;font-size:125%&amp;quot;&amp;gt;&amp;lt;center&amp;gt;'''Hinweis:''' Dieser Artikel wird gerade Offline bearbeitet!&amp;lt;br&amp;gt; &lt;br /&gt;
Bitte haben Sie etwas Geduld und nehmen Sie keine Änderungen vor, bis der Artikel hochgeladen wurde.&amp;lt;br&amp;gt;&lt;br /&gt;
[[:Kategorie:Offline in Bearbeitung|(weitere Artikel)]]&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|width=&amp;quot;30%&amp;quot;|[[Bild:WIP Offline.jpg|right|100px]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt; [[Kategorie:Offline in Bearbeitung]] &amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GL_FRAMEBUFFER_EXT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von glBindFramebufferEXT ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt  müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level. &lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion glCheckFramebufferStatusEXT. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit glBindTexture(GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion glGenerateMipmapEXT(). Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl glGenerateMipmapEXT()  bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 	GL_CLAMP_TO_EDGE;&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &lt;br /&gt;
	GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 	GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl glDrawBuffer(GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls glDrawBuffers kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20916</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20916"/>
				<updated>2007-09-24T00:21:23Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* GL_FRAMEBUFFER_EXT */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable}}&lt;br /&gt;
|widht=&amp;quot;80%&amp;quot;|&amp;lt;div style=&amp;quot;color:#F09000;font-size:125%&amp;quot;&amp;gt;&amp;lt;center&amp;gt;'''Hinweis:''' Dieser Artikel wird gerade Offline bearbeitet!&amp;lt;br&amp;gt; &lt;br /&gt;
Bitte haben Sie etwas Geduld und nehmen Sie keine Änderungen vor, bis der Artikel hochgeladen wurde.&amp;lt;br&amp;gt;&lt;br /&gt;
[[:Kategorie:Offline in Bearbeitung|(weitere Artikel)]]&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|width=&amp;quot;30%&amp;quot;|[[Bild:WIP Offline.jpg|right|100px]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt; [[Kategorie:Offline in Bearbeitung]] &amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GL_FRAMEBUFFER_EXT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt  müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level. &lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion glCheckFramebufferStatusEXT. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit glBindTexture(GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion glGenerateMipmapEXT(). Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl glGenerateMipmapEXT()  bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 	GL_CLAMP_TO_EDGE;&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &lt;br /&gt;
	GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 	GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;br /&gt;
&lt;br /&gt;
===Mehrere Texturen in einem FBO===&lt;br /&gt;
Wie vorhin erwähnt gibt es auch die Möglichkeit mehrere Texturen an ein Framebufferobjekt zu binden. Dazu schauen wir uns nochmal den Befehl zum hinzufügen einer Textur an:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, &lt;br /&gt;
	tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der zweite Parameter sagt OpenGL an welches COLOR_ATTATCHMENT die Textur zugefügt wird. Die derzeitigen Spezifikationen erlauben bis zu 16 COLOR_ATTACHMENTS (GL_COLOR_ATTACHMENT0_EXT bis GL_COLOR_ATTACHMENT15_EXT). Allerdings ist die wirkliche Anzahl von möglichen COLOR_ATTACHMENTS durch die Grafikkarte und deren Treiber begrenzt. Die maximale Anzahl kann durch folgenden Code ermittelt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_COLOR_ATTACHMENTS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Eine zweite, wie oben beschrieben erstellte Textur kann also mit folgendem Code zum Framebufferobjekt hinzugefügt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, &lt;br /&gt;
	GL_TEXTURE_2D, tex2, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die zweite Textur rendern===&lt;br /&gt;
Mit dem Befehl glDrawBuffer(GL_COLOR_ATTACHMENT'''n'''_EXT) lässt sich einstellen, in welchen Buffer man rendern möchte (wobei '''n''' den Buffer repräsentiert, also 0, 1, usw.). Standartmäßig ist GL_COLOR_ATTACHMENT0_EXT aktiviert (logisch, sonst hätten wir vorhin nicht viel gesehen, oder ;) ). Soll also nur in diesen gerendert werden, ist keine weitere Anweisung nötig. Ansonsten sieht der Code folgendermaßen aus:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Diesmal werden Viewport und Colorbuffer gespeichert, da Veränderungen immer das Framebufferobjekt und den eigentlichen Renderingkontext betreffen. Veränderungen am RC sind aber nicht gewollt, deswegen wird beides gespeichert und später wieder geladen.&lt;br /&gt;
Wie beim normalen Renderpass ist der Colorbuffer bereits mit den Daten die in die erste Textur geschrieben werden gefüllt. Deswegen leert man diesen normalerweise vor dem Rendern in die zweite Textur mithilfe von [[glClear]]:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT0_EXT rendern&lt;br /&gt;
	&lt;br /&gt;
	glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);&lt;br /&gt;
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
	&lt;br /&gt;
	//in die Textur von GL_COLOR_ATTACHMENT1_EXT rendern &lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFrameBuffer(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es ist im übrigen schneller ein FBO für mehrere Texturen zu nutzen als mehrere FBOs zu erstellen und zwischen ihnen zu wechseln.&lt;br /&gt;
&lt;br /&gt;
===Multiple Render Targets===&lt;br /&gt;
Mithilfe des Befehls glDrawBuffers kann in einem FBO gleichzeitig in mehrere Texturen gerendert werden. Die Namen der GL_COLOR_ATTACHMENTS werden in einem Array abgelegt und mit dem Befehl übergeben:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//normal rendern&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die Hardware und der Treiber legen die Grenze für die Anzahl von Texturen in die gleichzeitig gerendert werden kann. Der Wert kann mit folgendem Code erhalten werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
 	maxbuffers: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGetIntegeri(GL_MAX_DRAW_BUFFERS, @maxbuffers);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MRT und GLSL===&lt;br /&gt;
Solange man mindestens für die OpenGL Version 2.0 (bzw. GLSL Version 1.10) schreibt, kann man mithilfe von GLSL  gleichzeitig unterschiedliche Dinge in zwei Texturen rendern. Dazu benutzt man in GLSL anstelle von gl_FragColor den Array gl_FragData[]. Als Beispiel rendern wir ein rotes und ein grünes Viereck:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
//...&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
//...&lt;br /&gt;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,  fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
&lt;br /&gt;
	glDrawBuffers(2, @buffers);&lt;br /&gt;
	//Viereck zeichnen&lt;br /&gt;
&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der Vertexshader enthält nur das Nötigste:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_Position = ftransform();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und der Fragmentshader sieht folgendermaßen aus:&lt;br /&gt;
&amp;lt;glsl&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
	gl_FragData[1] = vec4( 0.0, 1.0, 0.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/glsl&amp;gt;&lt;br /&gt;
Und siehe da – die Textur die zu GL_COLOR_ATTACHMENT0_EXT gehört bekommt die Daten für ein rotes Quad und die zweite Textur die Daten für ein grünes Quad.&lt;br /&gt;
Eine Sache bleibt aber noch zu erwähnen: Es ist die Reihenfolge in der die Werte der COLOR_ATTACHMENTS übergeben werden, die bestimmt was gl_FragData[0] und was gl_FragData[1] ist. Wenn wir also geschrieben hätten&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	buffers: Array[0..1] of GlEnum;&lt;br /&gt;
&lt;br /&gt;
	buffers[0] := GL_COLOR_ATTACHMENT1_EXT;&lt;br /&gt;
	buffers[1] := GL_COLOR_ATTACHMENT0_EXT;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
dann würde gl_FragData[0] GL_COLOR_ATTACHMENT1_EXT beeinflussen und gl_FragData[1] GL_COLOR_ATTACHMENT0_EXT.&lt;br /&gt;
&lt;br /&gt;
===Und fertig!===&lt;br /&gt;
So! Geschafft. Schlussendlich bleibt zu sagen das Framebufferobjekte sehr nützliche Helfer sind, die sich relativ leicht handhaben lassen. &lt;br /&gt;
Zum Abschluss möchte ich euch nochmal daran erinnern, dass dies mein erstes Tutorial war, haltet euch also ordentlich ran mit Feedback ;-)&lt;br /&gt;
&lt;br /&gt;
Cya,&lt;br /&gt;
Deathball&lt;br /&gt;
&lt;br /&gt;
===Quellen===&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2331.asp&lt;br /&gt;
* http://www.gamedev.net/reference/articles/article2333.asp&lt;br /&gt;
&lt;br /&gt;
===Weiterführende Links===&lt;br /&gt;
* http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt&lt;br /&gt;
* http://http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20915</id>
		<title>Tutorial Framebufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Framebufferobject&amp;diff=20915"/>
				<updated>2007-09-24T00:03:15Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: Die Seite wurde neu angelegt: &amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt; {|{{Prettytable}} |widht=&amp;quot;80%&amp;quot;|&amp;lt;div style=&amp;quot;color:#F09000;font-size:125%&amp;quot;&amp;gt;&amp;lt;center&amp;gt;'''Hinweis:''' Dieser Artikel wird gerade Offline bearbeitet!&amp;lt;br&amp;gt; ...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable}}&lt;br /&gt;
|widht=&amp;quot;80%&amp;quot;|&amp;lt;div style=&amp;quot;color:#F09000;font-size:125%&amp;quot;&amp;gt;&amp;lt;center&amp;gt;'''Hinweis:''' Dieser Artikel wird gerade Offline bearbeitet!&amp;lt;br&amp;gt; &lt;br /&gt;
Bitte haben Sie etwas Geduld und nehmen Sie keine Änderungen vor, bis der Artikel hochgeladen wurde.&amp;lt;br&amp;gt;&lt;br /&gt;
[[:Kategorie:Offline in Bearbeitung|(weitere Artikel)]]&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|width=&amp;quot;30%&amp;quot;|[[Bild:WIP Offline.jpg|right|100px]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt; [[Kategorie:Offline in Bearbeitung]] &amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GL_FRAMEBUFFER_EXT==&lt;br /&gt;
===Einleitung===&lt;br /&gt;
Willkommen zu meinem ersten Tutorial. Das Thema des Tutorials sind Framebufferobjekte. Diese stellen derzeit die wohl einfachste Möglichkeit für Offscreen Rendering dar. Im Gegensatz zu Pixelbuffern sind sie plattformunabhängig und bieten automatisches Mipmapping. Jedoch benötigen sie mindestens OpenGL 1.1. Außerdem unterstützen Framebufferobjekte kein AntiAliasing. In der Vergangenheit hatten auch ATI Treiber einige Probleme mit ihnen, dies dürfte aber der Vergangenheit angehören. &lt;br /&gt;
&lt;br /&gt;
===Das FBO erstellen===&lt;br /&gt;
Zuerst einmal muss das Framebufferobjekt natürlich generiert werden. Dies läuft ähnlich wie das Erstellen einer Textur ab:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenFramebuffersEXT(1, @fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie bei allem anderen auch müssen wir das Framebufferobjekt jetzt binden, damit alles, was folgt sich auf dieses Objekt bezieht:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Der erste Parameter von [[glBindFramebufferEXT]] ist das Ziel an das gebunden wird. Derzeit existiert aber nur GL_FRAMEBUFFER_EXT.&lt;br /&gt;
&lt;br /&gt;
===Einen Depthbuffer hinzufügen===&lt;br /&gt;
Ein einfaches Framebufferobjekt ist noch ziemlich nutzlos. Deswegen fügen wir zuerst einmal einen der neu eingeführten Renderbuffer als Depthbuffer hinzu. &lt;br /&gt;
Renderbuffer sind im Prinzip Objekte, die benutzt werden wenn ein Buffer kein zugehöriges Texturformat besitzt. Dazu zählen u.a. der Stencilbuffer, der Accumulationbuffer und der Depthbuffer. Das Binden an den Accumulationbuffer wurde allerdings auf eine spätere Extension verschoben. &lt;br /&gt;
Wie auch beim Framebufferobjekt  müssen wir einen Renderbuffer zuerst generieren und dann binden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	depthbuffer: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Derzeit hat der Renderbuffer allerdings noch keinen Speicher zugeteilt bekommen. Deswegen sagen wir OpenGL, wie wir den Renderbuffer benutzen wollen und welche Größe er haben soll:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Mit [[glRenderbufferStorageEXT]] hat OpenGL den Speicher für den Renderbuffer, den wir als Depthbuffer nutzen, reserviert. Jetzt können wir unseren Depthbuffer zum FBO hinzufügen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Die letzte Anweisung sollte ziemlich einfach zu verstehen sein. Der zweite Parameter sagt einfach, als was der Renderbuffer genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Eine Textur hinzufügen===&lt;br /&gt;
Um Farbinformationen in unser Framebufferobjekt schreiben zu können, fügen wir eine Textur hinzu. Bevor wir sie hinzufügen können müssen wir sie natürlich noch erstellen:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	tex: GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTextures(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA8,&lt;br /&gt;
	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Es dürfte eigentlich nichts völlig Unverständliches da stehen. Wir erstellen die Textur, binden sie, stellen die Filtermodi ein und erzeugen im Speicher den entsprechenden Platz. Anzumerken ist aber, dass die Textur die '''gleiche Größe wie der Renderbuffer haben muss'''. Alle Größen müssen einheitlich sein. Man kann also nicht einen Renderbuffer von 512*512 und eine Textur von 256*256 erstellen und sie zusammen in einem FBO nutzen. Hinzugefügt wird sie nach dem gleichen Schema wie der Depthbuffer:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
  GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, nicht viel neues hier. Der zweite Parameter, GL_COLOR_ATTACHMENT0_EXT sagt OpenGL wo die Textur hinzugefügt werden soll (man kann mehrere hinzufügen, doch dazu später mehr). Der letzte Parameter beschreibt das Mipmaping-Level. &lt;br /&gt;
&lt;br /&gt;
===Die Fehlerabfrage===&lt;br /&gt;
Derzeit wissen wir nicht, ob alles so geklappt hat, wie wir es wollten oder ob wir einen Fehler gemacht haben. Deswegen ist nun die Fehlerabfrage an der Reihe. Dazu gibt es die Funktion glCheckFramebufferStatusEXT. Die Fehlerabfrage lässt sich schön in so einer Prozedur verpacken:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.CheckFBO;&lt;br /&gt;
var&lt;br /&gt;
	s: GlEnum;&lt;br /&gt;
begin&lt;br /&gt;
	s := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);&lt;br /&gt;
	case s of:&lt;br /&gt;
		GL_FRAMEBUFFER_COMPLETE_EXT:&lt;br /&gt;
			Exit;&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMOPLETE_MISSING_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Missing attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:&lt;br /&gt;
			raise Exception.Create('Duplicate attachment');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete dimensions');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete formats');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete draw buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:&lt;br /&gt;
			raise Exception.Create('Incomplete read buffer');&lt;br /&gt;
		GL_FRAMEBUFFER_UNSUPPORTED_EXT:&lt;br /&gt;
			raise Exception.Create('Framebuffer unsupported');&lt;br /&gt;
	end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Die Codeübersicht===&lt;br /&gt;
Zur Übersicht noch einmal der komplette Code zum erstellen eines Framebufferobjekts:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 		GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 		GluInt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
procedure TForm1.InitFBO;&lt;br /&gt;
begin&lt;br /&gt;
	glGenFramebuffersEXT(1, @fmo);&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glGenRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);&lt;br /&gt;
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 	GL_DEPTH_COMPONENT, 512, 512);&lt;br /&gt;
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,&lt;br /&gt;
	GL_RENDERBUFFER_EXT, depthbuffer); &lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, 	GL_NEAREST);&lt;br /&gt;
  	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,&lt;br /&gt;
 	GL_TEXTURE_2D, tex, 0);&lt;br /&gt;
	CheckFBO;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===In die Textur rendern===&lt;br /&gt;
Der eigentliche Rendervorgang ist ziemlich simpel und unterscheidet sich nicht sonderlich vom normalen Rendern. Das Framebufferobjekt teilt sich alle Einstellungen mit dem eigentlichen  Renderingkontext. Deswegen muss eigentlich nur das FBO gebunden und der Viewport auf die entsprechende Breite und Höhe eingestellt werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);&lt;br /&gt;
	glPushAttrib(GL_VIEWPORT_BIT);&lt;br /&gt;
	glViewPort(0, 0, 512, 512);&lt;br /&gt;
	&lt;br /&gt;
	//ganz normal rendern – das Ergebnis landet in der Textur&lt;br /&gt;
	&lt;br /&gt;
	glPopAttrib;&lt;br /&gt;
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie gesagt, hier gibt’s nichts bemerkenswertes: Viewport speichern, umstellen, rendern, Viewport laden und mit der letzten Zeile das FBO abschalten.&lt;br /&gt;
Die Textur die an das Framebufferobjekt gebunden wurde, enthält das Gezeichnete und wird ganz normal mit glBindTexture(GL_TEXTURE_2D, tex) benutzt.&lt;br /&gt;
&lt;br /&gt;
===Mipmaps===&lt;br /&gt;
FBOs bieten die Möglichkeit zum automatischem Erstellen der Mipmapdaten. Dazu gibt es die Funktion glGenerateMipmapEXT(). Man bindet die Textur zunächst und ruft dann die Funktion auf:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TForm1.Render;&lt;br /&gt;
begin&lt;br /&gt;
	//...&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&lt;br /&gt;
	//Textur danach ganz normal benutzen&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wenn man hingegen einen der Mipmap Filter verwendet muss der Befehl glGenerateMipmapEXT()  bevor der Status des FBOs überprüft oder versucht wird in die Textur zu rendern aufgerufen werden. Ein Beispiel wäre folgender Code in der Initialisierung des Framebufferobjektes:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
	fbo: 	GluInt;&lt;br /&gt;
	depthbuffer: 	GluInt;&lt;br /&gt;
	tex: 	GluInt;&lt;br /&gt;
&lt;br /&gt;
	glGenTextures(1, @tex);&lt;br /&gt;
	glBindTexture(GL_TEXTURE_2D, tex);&lt;br /&gt;
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, 	GL_UNSIGNED_BYTE, nil);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 	GL_CLAMP_TO_EDGE;&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &lt;br /&gt;
	GL_CLAMP_TO_EDGE);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 	GL_MIPMAP_LINEAR);&lt;br /&gt;
	glGenerateMipmapEXT(GL_TEXTURE_2D);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Und wieder aufräumen===&lt;br /&gt;
Beim Beenden unseres Programms soll das alles natürlich auch wieder gelöscht werden:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
	glDeleteFramebuffersEXT(1, @fbo);&lt;br /&gt;
	glDeleteRenderbuffersEXT(1, @depthbuffer);&lt;br /&gt;
	glDeleteTextures(1, @tex);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ich denke das Ganze ist selbsterklärend.&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=glOrtho&amp;diff=19453</id>
		<title>glOrtho</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=glOrtho&amp;diff=19453"/>
				<updated>2006-08-28T16:49:03Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Beschreibung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Bildwunsch|Ein Objekt (z.B. Würfel) in Orthogonaler und Perspektivischer Projektion vergleichen.}}&lt;br /&gt;
= glOrtho =&lt;br /&gt;
&lt;br /&gt;
== Name == &lt;br /&gt;
'''glOrtho''' - aktiviert einen orthogonalen 2D-Rendermodus &amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Delphi-Spezifikation ==&lt;br /&gt;
 procedure '''glOrtho'''(''left'', ''right'', ''bottom'', ''top'', ''znear'', ''zfar'' : double); &amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameter ==&lt;br /&gt;
{| border=1 rules=all&lt;br /&gt;
! '''left, right, bottom, top'''&lt;br /&gt;
| Definieren die 4 [[Clipping Plane]]s.&lt;br /&gt;
|-&lt;br /&gt;
! '''znear, zfar'''&lt;br /&gt;
| Setzen die Nah- und Fern- Clippingplane. Wenn die Fläche hinter dem User sein soll müssen die Angaben negativ sein.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beschreibung == &lt;br /&gt;
'''glOrtho''' erstellt eine Matrix für Parallel-Projektion. Die aktuelle Matrix wird dabei mit der neu erstellten Matrix multipliziert. Das bedeutet, wenn '''M''' die aktuelle und '''O''' die orthogonale Matrix ist, ergibt sich die neue Matrix aus: '''M := M*O'''&lt;br /&gt;
&lt;br /&gt;
Benutzen Sie [[glPushMatrix]] und [[glPopMatrix]] um den aktuellen [[Matrixstack]] zu speichern und zu laden.&lt;br /&gt;
&lt;br /&gt;
Die beiden Punkte (''left, bottom, -near'') und (''right, top, -near'') befinden sich auf der Nah-[[Clipping Plane]] und werden auf die linke-untere und rechte-obere Ecke des Fensters gemappt/abgebildet. Dabei wird die Position des Auges an Position (0,0,0) angenommen. ''-far'' bestimmt die Position der Fernen-[[Clipping Plane]]. Beide Ebenen, die Nahe und die Ferne Schnittebene (Clipping Plane), können negative Werte annehmen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt; Die erstellte Matrix hat die Form: [[Bild:GlOrtho_Matrix.png]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit ''f'' = ''zFar'', ''n'' = ''zNear''&amp;lt;br&amp;gt;&lt;br /&gt;
''l'' = ''left'','' r'' = ''right''&amp;lt;br&amp;gt;&lt;br /&gt;
''t'' = ''top'', ''b'' = ''bottom''&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''glOrtho''' schaltet OpenGL praktisch in einen 2D-Modus, wo die Z-Koordiante keine Rolle mehr im Bezug auf die letztendliche Größe eines Objektes hat (weit entfernte Objekte (mit hoher Z-Koordinate) werden genau so groß gezeichnet, wie Nahe.) &amp;lt;br&amp;gt;&lt;br /&gt;
Damit dient die Z-koordiante nur noch zur &amp;quot;Anordnung&amp;quot;&amp;quot; von Vorder- und Hintergründen auf der 2D-Zeichenfläche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Orthomode_Quads.jpg|thumb|left|300px|Quads an verschiedenen Stellen auf der X-Achse im Orthomodus]]&lt;br /&gt;
[[Bild:GluPerspective_Quads_X.jpg|thumb|right|300px|''Zum Vergleich:'' Quads an verschiedenen Stellen der X-Achse im perspektivischen Modus]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
[[Bild:Orthomode_Quads.jpg|thumb|left|300px|die gleichen Quads mit veränderten Positionen auf der Z-Achse im orthogonalen Modus]]&lt;br /&gt;
[[Bild:GluPerspective_Quads_Z.jpg|thumb|right|300px|''Zum Verlgeich:'' Die gleiche Reihe wie ''oben'', nur mit veränderten Positionen auf der Z-Achse, wieder im perspektivischen Modus]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Objekte die einen kleineren Z-Koordinate als ''znear'' bzw. einen größeren als ''zfar'' haben, werden nicht gezeichnet.''&lt;br /&gt;
&lt;br /&gt;
== Fehlermeldungen ==&lt;br /&gt;
* '''GL_INVALID_VALUE''' wird generiert wenn zNear gleich zFar, left gleich right oder top gleich bottom ist.&lt;br /&gt;
&lt;br /&gt;
* '''GL_INVALID_OPERATION''' wird generiert wenn '''glOrtho''' innerhalb eines [[glBegin]]-[[glEnd]] Blocks aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Zugehörige Werterückgaben ==&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_MATRIX_MODE|GL_MATRIX_MODE]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_MODELVIEW_MATRIX|GL_MODELVIEW_MATRIX]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_PROJECTION_MATRIX|GL_PROJECTION_MATRIX]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_TEXTURE_MATRIX|GL_TEXTURE_MATRIX]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch == &lt;br /&gt;
[[glFrustum]], [[glMatrixMode]], [[glMultMatrix]], [[glPushMatrix]], [[glViewport]], [[gluPerspective]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:GL|Ortho]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=glOrtho&amp;diff=19452</id>
		<title>glOrtho</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=glOrtho&amp;diff=19452"/>
				<updated>2006-08-28T16:48:39Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Beschreibung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Bildwunsch|Ein Objekt (z.B. Würfel) in Orthogonaler und Perspektivischer Projektion vergleichen.}}&lt;br /&gt;
= glOrtho =&lt;br /&gt;
&lt;br /&gt;
== Name == &lt;br /&gt;
'''glOrtho''' - aktiviert einen orthogonalen 2D-Rendermodus &amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Delphi-Spezifikation ==&lt;br /&gt;
 procedure '''glOrtho'''(''left'', ''right'', ''bottom'', ''top'', ''znear'', ''zfar'' : double); &amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameter ==&lt;br /&gt;
{| border=1 rules=all&lt;br /&gt;
! '''left, right, bottom, top'''&lt;br /&gt;
| Definieren die 4 [[Clipping Plane]]s.&lt;br /&gt;
|-&lt;br /&gt;
! '''znear, zfar'''&lt;br /&gt;
| Setzen die Nah- und Fern- Clippingplane. Wenn die Fläche hinter dem User sein soll müssen die Angaben negativ sein.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beschreibung == &lt;br /&gt;
'''glOrtho''' erstellt eine Matrix für Parallel-Projektion. Die aktuelle Matrix wird dabei mit der neu erstellten Matrix multipliziert. Das bedeutet, wenn '''M''' die aktuelle und '''O''' die orthogonale Matrix ist, ergibt sich die neue Matrix aus: '''M := M*O'''&lt;br /&gt;
&lt;br /&gt;
Benutzen Sie [[glPushMatrix]] und [[glPopMatrix]] um den aktuellen [[Matrixstack]] zu speichern und zu laden.&lt;br /&gt;
&lt;br /&gt;
Die beiden Punkte (''left, bottom, -near'') und (''right, top, -near'') befinden sich auf der Nah-[[Clipping Plane]] und werden auf die linke-untere und rechte-obere Ecke des Fensters gemappt/abgebildet. Dabei wird die Position des Auges an Position (0,0,0) angenommen. ''-far'' bestimmt die Position der Fernen-[[Clipping Plane]]. Beide Ebenen, die Nahe und die Ferne Schnittebene (Clipping Plane), können negative Werte annehmen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt; Die erstellte Matrix hat die Form: [[Bild:GlOrtho_Matrix.png]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit ''f'' = ''zFar'', ''n'' = ''zNear''&amp;lt;br&amp;gt;&lt;br /&gt;
''l'' = ''left'','' r'' = ''right''&amp;lt;br&amp;gt;&lt;br /&gt;
''t'' = ''top'', ''b'' = ''bottom''&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''glOrtho''' schaltet OpenGL praktisch in einen 2D-Modus, wo die Z-Koordiante keine Rolle mehr im Bezug auf die letztendliche Größe eines Objektes hat (weit entfernte Objekte (mit hoher Z-Koordinate) werden genau so groß gezeichnet, wie Nahe.) &amp;lt;br&amp;gt;&lt;br /&gt;
Damit dient die Z-koordiante nur noch zur &amp;quot;Anordnung&amp;quot;&amp;quot; von Vorder- und Hintergründen auf der 2D-Zeichenfläche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Orthomode_Quads.jpg|thumb|left|300px|Quads an verschiedenen Stellen auf der X-Achse im Orthomodus]]&lt;br /&gt;
[[Bild:GluPerspective_Quads_X.jpg|thumb|right|300px|''Zum Vergleich:'' Quads an verschiedenen Stellen der X-Achse im perspektivischen Modus]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
[[Bild:Orthomode_Quads.jpg|thumb|left|300px|die gleichen Quads mit veränderten Positionen auf der Z-Achse im orthogonalen Modus]]&lt;br /&gt;
[[Bild:GluPerspective_Quads_Z.jpg|thumb|right|300px|''Zum Verlgeich:'' Die gleiche Reihe wie ''oben'', nur mit veränderten Positionen auf der Z-Achse, wieder im perspektivischen Modus]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Objekte die einen kleineren Z-Koordinate als ''znear'' bzw. einen größeren als ''zfar'' haben, werden nicht gezeichnet.''&lt;br /&gt;
&lt;br /&gt;
== Fehlermeldungen ==&lt;br /&gt;
* '''GL_INVALID_VALUE''' wird generiert wenn zNear gleich zFar, left gleich right oder top gleich bottom ist.&lt;br /&gt;
&lt;br /&gt;
* '''GL_INVALID_OPERATION''' wird generiert wenn '''glOrtho''' innerhalb eines [[glBegin]]-[[glEnd]] Blocks aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Zugehörige Werterückgaben ==&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_MATRIX_MODE|GL_MATRIX_MODE]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_MODELVIEW_MATRIX|GL_MODELVIEW_MATRIX]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_PROJECTION_MATRIX|GL_PROJECTION_MATRIX]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_TEXTURE_MATRIX|GL_TEXTURE_MATRIX]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch == &lt;br /&gt;
[[glFrustum]], [[glMatrixMode]], [[glMultMatrix]], [[glPushMatrix]], [[glViewport]], [[gluPerspective]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:GL|Ortho]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:GluPerspective_Quads_Z.jpg&amp;diff=19451</id>
		<title>Datei:GluPerspective Quads Z.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:GluPerspective_Quads_Z.jpg&amp;diff=19451"/>
				<updated>2006-08-28T16:42:12Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: Reihe von Quads mit unterschiedlicher Entfernung (Position auf der Z-Achse) im perspektivischen Modus.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Reihe von Quads mit unterschiedlicher Entfernung (Position auf der Z-Achse) im perspektivischen Modus.&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=glOrtho&amp;diff=19450</id>
		<title>glOrtho</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=glOrtho&amp;diff=19450"/>
				<updated>2006-08-28T16:08:38Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Beschreibung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Bildwunsch|Ein Objekt (z.B. Würfel) in Orthogonaler und Perspektivischer Projektion vergleichen.}}&lt;br /&gt;
= glOrtho =&lt;br /&gt;
&lt;br /&gt;
== Name == &lt;br /&gt;
'''glOrtho''' - aktiviert einen orthogonalen 2D-Rendermodus &amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Delphi-Spezifikation ==&lt;br /&gt;
 procedure '''glOrtho'''(''left'', ''right'', ''bottom'', ''top'', ''znear'', ''zfar'' : double); &amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameter ==&lt;br /&gt;
{| border=1 rules=all&lt;br /&gt;
! '''left, right, bottom, top'''&lt;br /&gt;
| Definieren die 4 [[Clipping Plane]]s.&lt;br /&gt;
|-&lt;br /&gt;
! '''znear, zfar'''&lt;br /&gt;
| Setzen die Nah- und Fern- Clippingplane. Wenn die Fläche hinter dem User sein soll müssen die Angaben negativ sein.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beschreibung == &lt;br /&gt;
'''glOrtho''' erstellt eine Matrix für Parallel-Projektion. Die aktuelle Matrix wird dabei mit der neu erstellten Matrix multipliziert. Das bedeutet, wenn '''M''' die aktuelle und '''O''' die orthogonale Matrix ist, ergibt sich die neue Matrix aus: '''M := M*O'''&lt;br /&gt;
&lt;br /&gt;
Benutzen Sie [[glPushMatrix]] und [[glPopMatrix]] um den aktuellen [[Matrixstack]] zu speichern und zu laden.&lt;br /&gt;
&lt;br /&gt;
Die beiden Punkte (''left, bottom, -near'') und (''right, top, -near'') befinden sich auf der Nah-[[Clipping Plane]] und werden auf die linke-untere und rechte-obere Ecke des Fensters gemappt/abgebildet. Dabei wird die Position des Auges an Position (0,0,0) angenommen. ''-far'' bestimmt die Position der Fernen-[[Clipping Plane]]. Beide Ebenen, die Nahe und die Ferne Schnittebene (Clipping Plane), können negative Werte annehmen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt; Die erstellte Matrix hat die Form: [[Bild:GlOrtho_Matrix.png]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit ''f'' = ''zFar'', ''n'' = ''zNear''&amp;lt;br&amp;gt;&lt;br /&gt;
''l'' = ''left'','' r'' = ''right''&amp;lt;br&amp;gt;&lt;br /&gt;
''t'' = ''top'', ''b'' = ''bottom''&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''glOrtho''' schaltet OpenGL praktisch in einen 2D-Modus, wo die Z-Koordiante keine Rolle mehr im Bezug auf die letztendliche Größe eines Objektes hat (weit entfernte Objekte (mit hoher Z-Koordinate) werden genau so groß gezeichnet, wie Nahe.) &amp;lt;br&amp;gt;&lt;br /&gt;
Damit dient die Z-koordiante nur noch zur &amp;quot;Anordnung&amp;quot;&amp;quot; von Vorder- und Hintergründen auf der 2D-Zeichenfläche.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Orthomode_Quads.jpg|thumb|left|300px|Quads an verschiedenen Stellen auf der X-Achse im Orthomodus]]&lt;br /&gt;
[[Bild:GluPerspective_Quads_X.jpg|thumb|right|300px|''Zum Vergleich:'' Quads an verschiedenen Stellen der X-Achse im perspektivischen Modus]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Objekte die einen kleineren Z-Koordinate als ''znear'' bzw. einen größeren als ''zfar'' haben, werden nicht gezeichnet.''&lt;br /&gt;
&lt;br /&gt;
== Fehlermeldungen ==&lt;br /&gt;
* '''GL_INVALID_VALUE''' wird generiert wenn zNear gleich zFar, left gleich right oder top gleich bottom ist.&lt;br /&gt;
&lt;br /&gt;
* '''GL_INVALID_OPERATION''' wird generiert wenn '''glOrtho''' innerhalb eines [[glBegin]]-[[glEnd]] Blocks aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Zugehörige Werterückgaben ==&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_MATRIX_MODE|GL_MATRIX_MODE]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_MODELVIEW_MATRIX|GL_MODELVIEW_MATRIX]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_PROJECTION_MATRIX|GL_PROJECTION_MATRIX]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_TEXTURE_MATRIX|GL_TEXTURE_MATRIX]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch == &lt;br /&gt;
[[glFrustum]], [[glMatrixMode]], [[glMultMatrix]], [[glPushMatrix]], [[glViewport]], [[gluPerspective]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:GL|Ortho]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:GluPerspective_Quads_X.jpg&amp;diff=19449</id>
		<title>Datei:GluPerspective Quads X.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:GluPerspective_Quads_X.jpg&amp;diff=19449"/>
				<updated>2006-08-28T16:05:59Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: Quads an verschiedenen Stellen der X-Achse im perspektivischen Modus.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Quads an verschiedenen Stellen der X-Achse im perspektivischen Modus.&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:Orthomode_Quads.jpg&amp;diff=19448</id>
		<title>Datei:Orthomode Quads.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:Orthomode_Quads.jpg&amp;diff=19448"/>
				<updated>2006-08-28T15:41:22Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: Quads an verschiedenen Stellen auf der X-Achse im Orthomodus&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Quads an verschiedenen Stellen auf der X-Achse im Orthomodus&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Timebased_Movement&amp;diff=19351</id>
		<title>Timebased Movement</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Timebased_Movement&amp;diff=19351"/>
				<updated>2006-08-21T18:02:56Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Absolute Genauigkeit per Hardware */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Kurz-Beschreibung ==&lt;br /&gt;
'''Timebased Movement''' sorgt dafür, dass die Bewegungsgeschwindigkeiten nicht vom CPU-/Renderspeed abhängen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Prinzip ==&lt;br /&gt;
Nehmen wir an wir möchten einen Ball darstellen der sich mit konstanter Geschwindigkeit vom linken Bildschirmrand zum rechten Bildschirmrand bewegt.&lt;br /&gt;
&lt;br /&gt;
Das erste Bild wird den Ball ganz links zeigen. Auf dem nächsten Bild wird der Ball ein Stück weiter sein, die Frage ist nur wie weit? Die Länge der Strecke die der Ball zurücklegen muss legen wir nun mal willkürlich auf 1 Längeneinheit (kurz: LE) fest. Die Strecke soll er in 5 Sekunden zurück legen.&lt;br /&gt;
&lt;br /&gt;
Ein Rechner A braucht 1 Sekunde um 1 Bild zu Berechnen. Um das gewünschte Ergebnis zu erreichen müssten wir den Ball nach jedem Bild 0.2 Felder weiterbewegen.&lt;br /&gt;
&lt;br /&gt;
In einer Sekunde hätten wir also folgende Bilder:&lt;br /&gt;
* Bild 0: Ball Position: 0.0&lt;br /&gt;
* Bild 1: Ball Position: 0.2 (weiterbewegt um 0.2 LE in 1.0 Sekunden)&lt;br /&gt;
* Bild 2: Ball Position: 0.4 (weiterbewegt um 0.2 LE in 1.0 Sekunden)&lt;br /&gt;
* Bild 3: Ball Position: 0.6 (weiterbewegt um 0.2 LE in 1.0 Sekunden)&lt;br /&gt;
* Bild 4: Ball Position: 0.8 (weiterbewegt um 0.2 LE in 1.0 Sekunden)&lt;br /&gt;
* Bild 5: Ball Position: 1.0 (weiterbewegt um 0.2 LE in 1.0 Sekunden)&lt;br /&gt;
&lt;br /&gt;
Ein Rechner B braucht nur 0.5 Sekunden um ein Bild zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Ebenfalls in nur einer Sekunde hätten wir folgende Bilder:&lt;br /&gt;
* Bild 0:  Ball Position: 0.0&lt;br /&gt;
* Bild 1:  Ball Position: 0.1 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
* Bild 2:  Ball Position: 0.2 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
* Bild 3:  Ball Position: 0.3 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
* Bild 4:  Ball Position: 0.4 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
* Bild 5:  Ball Position: 0.5 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
* Bild 6:  Ball Position: 0.6 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
* Bild 7:  Ball Position: 0.7 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
* Bild 8:  Ball Position: 0.8 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
* Bild 9:  Ball Position: 0.9 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
* Bild 10: Ball Position: 1.0 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)&lt;br /&gt;
&lt;br /&gt;
Bei dem einen Rechner muss der Ball bei jedem Bild um 0.2 LE bewegt werden, beim andern Rechner nur 0.1 LE pro Bild. Tut man dies nicht, bewegt sich der Ball zu schnell oder zu langsam. &lt;br /&gt;
&lt;br /&gt;
Doch woher weiß man nun wie weit man seinen Ball auf den jeweiligen Rechner pro Bild bewegen muss?&lt;br /&gt;
&lt;br /&gt;
In unserem Beispiel bewegt sich der Ball mit der konstanten Geschwindigkeit von 0.2 LE pro Sekunde. Mit folgender Formel können wir bei konstanter Geschwindigkeit die neue Position berechnen:&lt;br /&gt;
&lt;br /&gt;
 neue_Position = Geschwindigkeit * vergangene_Zeit + alte_Position&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also nur eine Geschwindigkeit festlegen und messen wieviel Zeit vergangen ist um die neue Position zu berechnen zu können.&lt;br /&gt;
&lt;br /&gt;
Wohlgemerkt gilt diese Formel nur wenn sich das Objekt mit konstanter Geschwindigkeit fortbewegt.&lt;br /&gt;
&lt;br /&gt;
== Berechnung der vergangen Zeit ==&lt;br /&gt;
&lt;br /&gt;
Allgemein:&lt;br /&gt;
 VergangeneZeit := ( Zeit1 - Zeit0 ) / Frequenz;&lt;br /&gt;
&lt;br /&gt;
=== Millisekunden genau unter Windows===&lt;br /&gt;
Die wohl einfachste aber auch ungenaueste Möglichkeit unter Windows so etwas zu realisieren besteht darin mit Hilfe von GetTickCount die Zeit (in Millisekunden) seit dem letzen Windows-Start zu ermitteln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
//Benötigte Variablen&lt;br /&gt;
var&lt;br /&gt;
  BerechnungsZeit : LongWord;&lt;br /&gt;
  Position        : Double;&lt;br /&gt;
&lt;br /&gt;
procedure BeimStart; // Was hier drinnen steht sollte zum Start ausgeführt werden&lt;br /&gt;
begin&lt;br /&gt;
  BerechnungsZeit := GetTickCount();&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
procedure BerechnungsCheck; // Alle nötigen Berechnungen durchführen&lt;br /&gt;
const&lt;br /&gt;
  Frequenz = 1000; // Durch Windows festgelegt&lt;br /&gt;
var&lt;br /&gt;
  AktuelleZeit : LongWord;&lt;br /&gt;
begin&lt;br /&gt;
  AktuelleZeit := GetTickCount();&lt;br /&gt;
  Berechne((AktuelleZeit - BerechnungsZeit) / Frequenz);&lt;br /&gt;
  BerechnungsZeit := AktuelleZeit;&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
procedure Berechne(vergangene_Zeit : Double);&lt;br /&gt;
const&lt;br /&gt;
  Geschwindigkeit = 0.5;&lt;br /&gt;
begin&lt;br /&gt;
  {Alle Berechnungen stehen hier }&lt;br /&gt;
  // z.B.&lt;br /&gt;
  Position := Geschwindigkeit * vergangene_Zeit + Position;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Absolute Genauigkeit per Hardware ===&lt;br /&gt;
Man kann auch die Hardware zur Zeitmessung nutzen um so noch genauere Ergebnisse zu erziehlen. Es kann jedoch theoretisch sein, dass diese nicht zur Verfügung steht.&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var &lt;br /&gt;
[...] &lt;br /&gt;
 Freq, StartCount, EndCount: Int64; &lt;br /&gt;
 Speedfactor: Extended;&lt;br /&gt;
&lt;br /&gt;
const &lt;br /&gt;
 Scalefactor = 100;       //Der Scalefactor ist ein beliebiger Wert um den Speedfactor&lt;br /&gt;
                          //zu beeinflussen&lt;br /&gt;
[...]&lt;br /&gt;
procedure TForm1.FormCreate(Sender: TObject);&lt;br /&gt;
begin &lt;br /&gt;
 if not QueryPerformanceFrequency(Freq) then&lt;br /&gt;
  raise Exception.create('Kein Hardware Timer vorhanden');&lt;br /&gt;
 QueryPerformanceFrequency(Freq); //Frequenz des Rechners ermitteln &lt;br /&gt;
 [...]&lt;br /&gt;
end;&lt;br /&gt;
     &lt;br /&gt;
&lt;br /&gt;
procedure TForm1.IdleHandler(Sender: TObject; var Done: Boolean);&lt;br /&gt;
begin &lt;br /&gt;
 QueryPerformanceCounter(StartCount); //Zeit0 &lt;br /&gt;
 Form1.Render; &lt;br /&gt;
 QueryPerformanceCounter(EndCount); //Zeit1  &lt;br /&gt;
 Speedfactor := ((EndCount - StartCount) / Freq) * Scalefactor //(Zeit1 - Zeit0) / Frequenz&lt;br /&gt;
 [...]&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man hat nun den Speedfactor, den man mit sämtlichen Bewegungen multipliziert. Wenn der PC viele Frames per Second rendert wird der Speedfactor klein. Die Bewegungen werden dadurch naturlich auch kleiner. Wenn der PC wenige Frames per Second rendert wird der Speedfactor groß, die Bewegungen also auch größer.&lt;br /&gt;
&lt;br /&gt;
== Konstant Beschleunigte Bewegung ==&lt;br /&gt;
&lt;br /&gt;
Nehmen wir an wir möchten darstellen wie ein Ball zu Boden fällt. Der Ball sei 1 LE über dem Boden und wird dort zum Zeitpunkt 0s losgelassen. Die Kraft welche die Erdanziehung simuliert soll pro Sekunde die Geschwindigkeit des Balles um 1 LE/s erhöhen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die neue Geschwindigkeit die sich durch die Beschleunigung ergeben hat, läßt sich noch sehr einfach Berechnen:&lt;br /&gt;
 neue_Geschwindigkeit := Beschleunigung * vergangene_Zeit + alte_Geschwindigkeit;&lt;br /&gt;
&lt;br /&gt;
Wie sieht es aber mit der Position aus?&lt;br /&gt;
&lt;br /&gt;
Falsch wäre diese Berechnung:&lt;br /&gt;
 neue_Position := alte_Geschwindigkeit * vergangene_Zeit + alte_Position&lt;br /&gt;
Denn so würde sich der Ball von Bild 0 nach Bild 1 gar nicht bewegen.&lt;br /&gt;
&lt;br /&gt;
Genauso falsch wäre diese Berechnung:&lt;br /&gt;
 neue_Position := neue_Geschwindigkeit * vergangene_Zeit + alte_Position&lt;br /&gt;
&lt;br /&gt;
Denn dann würde sich der Ball sofort mit der vollen Geschwindigkeit bewegen und wäre viel zu schnell ab Boden.&lt;br /&gt;
&lt;br /&gt;
Wer sich ein wenig mit Physik auskennt weiß das man bei konstant Beschleunigter Bewegung so die neue Position ausrechnet:&lt;br /&gt;
 neue_Position := 0.5 * Beschleunigung * vergangene_Zeit * vergangene_Zeit + Geschwindigkeit * vergangene_Zeit + alte_Position&lt;br /&gt;
&lt;br /&gt;
Beispielcode:&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
var&lt;br /&gt;
  Position        : Double;&lt;br /&gt;
  Geschwindigkeit : Double;&lt;br /&gt;
procedure Berechne(vergangene_Zeit : Double);&lt;br /&gt;
const&lt;br /&gt;
  Beschleunigung = 1;&lt;br /&gt;
begin&lt;br /&gt;
  Position := 0.5*Beschleunigung * vergangene_Zeit * vergangene_Zeit + Geschwindigkeit * vergangene_Zeit + Position&lt;br /&gt;
  // Wichtig: alte Geschwindigkeit erst ändern wenn sie nicht mehr gebraucht wird.&lt;br /&gt;
  Geschwindigkeit := Beschleunigung * vergangene_Zeit + Geschwindigkeit&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
*[http://www.delphigl.com/script/do_show.php?name=bombman2&amp;amp;action=2 Bomberman DGL-Tutorial]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Renderpass&amp;diff=16214</id>
		<title>Tutorial Renderpass</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Renderpass&amp;diff=16214"/>
				<updated>2006-02-01T16:33:21Z</updated>
		
		<summary type="html">&lt;p&gt;Deathball: /* Die Welt ist eine (Matt-)Schreibe */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Renderpass - Die Welt daneben =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Greetings!&lt;br /&gt;
&lt;br /&gt;
Nach einer längeren Pause gibt es dann auch mal wieder eine Kleinigkeit von mir. Ich setze an dieser Stelle nicht die bisherige Tutorial-Serie fort, sondern mache diesmal etwas zu einem Spezial-Thema. Mir war gerade privat danach, mit einer anderen Technik rumzuspielen und da dachte ich, ich könnte gleich ein Tutorial drauß machen. Vor allem, weil hier eine Grundlage vermittelt wird, die unter anderem auch dafür eingesetzt werden kann, Objekte zu spiegeln oder für sie einen Schatten zu erzeugen. So kompliziert werden wir das Ganze aber heute gar nicht machen, sondern ich werde nur ein Beispiel geben, wie man mit Hilfe von mehren Render-Durchgängen eine Szene in Echtzeit rendert und auf einen Bildschirm projizieren kann.&lt;br /&gt;
&lt;br /&gt;
Vielleicht kennen einige ja noch die etwas älteren Spiele. Man ist an eine Kamera gegangen und konnte plötzlich das Geschehen auf einem anderen Teil der Karte verfolgen. Der Trick dabei bestand darin, dass man damals die Spielersicht nicht mehr berechnen mußte, sondern nur die Kamera an einen anderen Ort setzte. Dank der modernen Technik können wir jedoch direkt im Spiel eine Szene sehen, die in Echtzeit berechnet wird und irgendwo anders geschieht. Man stelle sich vor, dass man bei einem Auto-Rennen durch eine Stadt fährt und am Himmel ist ein Zeppelin mit einer Holo-Wand. Darauf wird gerade die Ziellinie gezeigt, die die Konkurrenz gerade überfährt! Oder einen Weltraum-Shooter, in dem man gerade einen Angriff auf den Feind fliegt und in dem plötzlich die Warnlampen angehen, ein kleines HUD-Fenster aufspringt und man in Echtzeit verfolgen kann, wie die Wärme-Rakete einem den Antrieb zerfetzt ... ahem *hust* Irgendwie alles nur negative Beispiele, das liegt an mir, nicht an der Technik ;D Viel Spaß beim &amp;quot;Spielen&amp;quot; ;))&lt;br /&gt;
&lt;br /&gt;
== Multiple Render-Passes ==&lt;br /&gt;
&lt;br /&gt;
=== Die Welt, die neben unserer liegt ===&lt;br /&gt;
&lt;br /&gt;
Zunächst sollten wir erstmal klären, was eigentlich ein Render-Pass ist. Moderne Spiele wie Doom3 versprechen bis zu 50 Render-Passes. Damit sind keineswegs Frames gemeint, sondern wie oft eine Szene gerendert wird, bevor sie letztendlich auf den Bildschirm gebracht wird. Was? Wieso sollte man denn eine Szene mehrfach rendern? Nun, dafür kann es unterschiedliche Gründe geben. Nehmen wir doch einfach mal an, wir wollen Multi-Texturing auf einem Rechner mit nur einer Textur-Unit realisieren. Folglich müssen wir zweimal ein Quad mit unterschiedlichen Texturen und Blending zeichnen, um das Ergebnis zu erzielen, welches wir wollen. Genauso kann es sein, dass wir auch andere Dinge mehrfach rendern müssen, um den Effekt zu erzielen, den wir auch wollen. Bei jedem dieser Vorgänge redet man von einem Pass (Durchgang). Man sollte klar dazu sagen, dass jeder zusätzliche Durchgang tödlich für die Geschwindigkeit ist und auch vermieden werden sollte! Wenn es geht...&lt;br /&gt;
&lt;br /&gt;
Aber nun nehmen wir doch mal an, wir wollen viele Bäume rendern, die in der Ferne stehen. Es bietet sich dann an, ein 3D-Modell aus der gebrauchten Perspektive auf eine Textur zu rendern und dann nur noch Quads mit der Textur zu rendern. Ein Quad hat in jedem Fall weniger Polygone als mehrere Bäume und auf die Distanz fällt das nicht mehr auf! Man redet hier übrigens von &amp;quot;Impostern&amp;quot;, einem modernen Billboarding-Verfahren, auf das wir sicher aber auch nochmal irgendwann zurückkommen. Wir befassen uns erstmal mit etwas Simplerem ;)&lt;br /&gt;
&lt;br /&gt;
=== Die Welt ist eine (Matt-)Schreibe ===&lt;br /&gt;
&lt;br /&gt;
Stellen wir uns einfach mal eine Szene vor, bei der wir dies gebrauchen könnten! In der Mitte der Szene befindet sich ein Objekt, welches sich bewegt. In unserem Fall habe ich die beliebte Pyramiden-Szene genommen ;) Jeweils links und rechts sind leicht schräg zwei Monitore. Genau in der Mitte leicht oberhalb befindet sich ein weiterer. Auf den Bildschirmen soll nun jeweils die Szene angezeigt werden, und zwar so, wie der Betrachter es auch vor einem Bildschirm sieht. Ich denke, bei dieser Aufgabenstellung würden die meisten schon aussteigen, da zahlreiche Probleme auf einen warten. Die Lösung ist jedoch verblüffend einfach!&lt;br /&gt;
&lt;br /&gt;
Damit sich jeder was drunter vorstellen kann, einmal ein kleines Bild der Szene:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Renderpass_whitescreen.gif]]&lt;br /&gt;
&lt;br /&gt;
Die weißen Flächen sind jeweils unsere Screens. Sehen erschreckend billig aus, gell? ;)&lt;br /&gt;
&lt;br /&gt;
Die Frage, die sich nun stellt ist, wie wir die Szene auf diese weißen Flächen bekommen. Wie wir bereits festgestellt haben, bieten sich hierfür Texturen an, und mit simplestem UV-Mapping haben wir dann auch eine Textur drauf. Mit jeder geladenen Textur ist dies kein Problem, allerdings wollen wir ja eine Szene darauf zeichnen. Nun, dann erzeugen wir sie uns eben, und zwar direkt im Speicher. Gleich nach dem Initialisieren von OpenGL reservieren wir uns ausreichend Speicher für die neue Textur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure CreateScreenTexture;&lt;br /&gt;
var pTexData: Pointer;&lt;br /&gt;
begin&lt;br /&gt;
  GetMem(pTexData, 256*256*3);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  // Textur generieren und drauf zeigen&lt;br /&gt;
  glGenTextures(1, @Screen);&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, Screen);&lt;br /&gt;
&lt;br /&gt;
  // Daten in den Speicher, Lineares Filtering aktivieren&lt;br /&gt;
  glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, pTexData);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
  // Freigeben&lt;br /&gt;
  FreeMem(pTexData);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke, dass niemand hier wirklich schockiert sein sollte oder man Unmögliches erwartet. Wir erzeugen im Speicher den Platz, den wir benötigen, erzeugen eine OpenGL Texture, übergeben den reservierten Speicher als Bilddaten, aktivieren gleich noch lineares Filtern (weil’s einfach besser aussieht *g*) und lassen den Pointer dann wieder &amp;quot;frei&amp;quot;. Fertig. Schon ist bei uns im Video-Speicher ein Bereich für unsere Textur reserviert und wartet darauf, von uns mißbraucht zu werden :)&lt;br /&gt;
&lt;br /&gt;
=== The first try ===&lt;br /&gt;
&lt;br /&gt;
Nun lautet die Frage, wie wir die Szene auf die Texture bekommen. Man kann es bereits erahnen, dass man die Szene rendern müßte und dann nicht auf dem Bildschirm, sondern auf der Textur ausgeben muß. Der Plan steht also fest! Zunächst rendern wir die Szene einmal auf eine Textur, rendern sie dann nochmal und kleben die &amp;quot;gewonnene&amp;quot; Textur auf die Screens. Zwei Renderpasses. Und wie sonst auch fangen wir erstmal mit etwas Simplem an! Setzen der glClearColor und löschen des Color- und Z-Buffers :)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glClearColor(0.0,0.0,0.0,0);&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soweit nichts wirklich Neues. Wir behalten im Hintergrund, dass wir für die Textur nur Speicher für 256x256 reserviert haben. Der Grund dafür leuchtet glaube ich auch ein! Viele Grafikkarten unterstützen nicht sehr große Texturen und der Speicher ist rar. Warum also für weiter entfernte Texturen soviel Speicher &amp;quot;verschwenden&amp;quot;? Allerdings werden wir in den meisten Fällen in weitaus höheren Auflösungen auf den Bildschirm rendern. Damit der ganze Bildschirm auch auf die Texture paßt und wir nicht nur einen Teil haben, müssen wir also die Ausgabe auf die Größe der Textur reduzieren. Kinderleicht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glViewport(0, 0, 256, 256);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke, man benötigt zum Erraten der Parameter auch keinen Hellseher. Unser Viewport (Ausgabe-Bereich) fängt bei 0/0 an und hört bei 256/256 auf. Wir beachten, dass bei OpenGL der Ursprung nicht oben links ist, sondern unten links. Ich gehe nun nicht näher darauf ein, wie genau die Szene gezeichnet wird. Wir gehen einfach davon aus, dass wir brav die Bildschirme und das Objekt in der Mitte gerendert haben:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Renderpass_viewport.gif]]&lt;br /&gt;
&lt;br /&gt;
Und kein Aufschrei, weil noch die Texturen fehlen! Ich habe der Übersicht halber diese noch nicht eingefügt. Anstatt die Szene nun allerdings auf dem Bildschirm auszugeben, &amp;quot;leiten&amp;quot; wir den Inhalt nun auf die Textur um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D, Screen);&lt;br /&gt;
  glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit [[glBindTexture]] weisen wir Screen als &amp;quot;aktuelle Textur&amp;quot; zu. Die darauf folgende Zeile sorgt dann dafür, dass wir auf Screen den momentanen Inhalt des Color-Buffers (ergo unserer Szene) schreiben. Die Parameter sollte man auch nicht weiter verändern, man wird meist damit zu recht kommen. Die beiden 256 erklären sich diesmal wohl auch von selbst.&lt;br /&gt;
&lt;br /&gt;
=== Und ab in die &amp;quot;richtige&amp;quot; Welt ===&lt;br /&gt;
&lt;br /&gt;
Erschreckend einfach oder? Wir haben nun unseren ersten Render-Pass hinter uns und haben die Szene auf einer Textur! Nun beginnen wir unseren zweiten Render-Pass und haben natürlich im Hinterkopf, dass der Color-Buffer noch vom ersten Durchgang voll ist! Also weg damit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glClearColor(0,0.0,0.3,0);&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man sollte zur Kenntnis nehmen, dass ich als &amp;quot;Reinigungs-Farbe&amp;quot; einen leicht blauen Schwarzton nehme. Macht die Szene interessanter und man erkennt die Screens anschließend besser ;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glViewport(0, 0, 800, 600);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch diese Zeile sollte nicht vergessen werden, damit wir wieder auf voller Bildschirmbreite rendern. Es bietet sich hier natürlich evtl. an, nicht mit fixen Werten zu arbeiten, sondern jeweils die aktuelle Fensterbreite und Höhe anzugeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBindTexture(GL_TEXTURE_2D,Screen);&lt;br /&gt;
  RenderScreens;&lt;br /&gt;
  DrawPyramidScene;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Huch! Das ist alles? Yes... Wir weisen als aktuelle Textur „Screen“ an und zeichnen dann die Bildschirme. Anschließend kommt noch das Objekt in der Mitte dazu. Drawpyramids bindet übrigens seine eigenen Texturen ein, so dass nicht &amp;quot;Screen&amp;quot; verwendet wird. Und fertig ist der zweite Pass und damit auch unsere Szene. OpenGL bringt den Color-Buffer an die Wand, Verzeihung, den Screen ;)&lt;br /&gt;
&lt;br /&gt;
Es kommt nur sehr darauf an, wie wir den ersten Pass durchführen. Ob wir uns mit dem Objekt in der Mitte zufrieden geben…&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Renderpass_simple.jpg]]&lt;br /&gt;
&lt;br /&gt;
…oder ob wir uns wie beim zweiten Render-Pass die Screen-Textur krallen und noch zusätzlich die Bildschirme einzeichnen. Wir erhalten dann einen verdammt interessanten rekursiven Effekt:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Renderpass_multiple.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Und die Perfektion... ===&lt;br /&gt;
&lt;br /&gt;
Damit wäre unser Ziel doch eigentlich erreicht, oder? Dachte ich mir zu diesem Zeitpunkt auch, allerdings habe ich mir noch etwas einfallen lassen. Die Szene ist eigentlich noch einen kleinen Tick zu langweilig! Man kann doch sicherlich noch ein wenig spielen?! Ich dachte mir, dass es doch ganz nett wäre, wenn man die Bildschirme abschalten könnte. Eigentlich nichts Schweres... einfach eine Noise-Textur nehmen und statt dem Screen-Bild drauf zeichnen. Allerdings würde das an sich recht langweilig aussehen, da man nur eine feste &amp;quot;Bildstörung&amp;quot; hätte. Jemand, der mal vor einem Fernseher ohne Empfang gesessen hat, wird jedoch festgestellt haben, dass wir dort durchaus eine Bewegung feststellen können. Was also tun? Einmal meinte einer zu mir ganz cool: &amp;quot;Hey, da nehmen wir einfach viele solcher Noise-Texturen und...&amp;quot; *zack* Schöner Blödsinn ;)&lt;br /&gt;
&lt;br /&gt;
Wir verwenden einfach die Textur-Matrix, um die Texture so zu bewegen, dass der Eindruck entsteht, dass sie sich bewegen würde. Zunächst erhöhen wir eine Variable vor dem ersten Render-Pass:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
x:=x+0.01;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variable verwenden wir dazu, dass der Bildschirm sich immer leicht von oben nach unten bewegt. Es entsteht somit die Illusion des Bildtasters, der das Signal auf die Röhre projiziert. (*hust* Seit wann haben Flachbildschirme Kathoden-Strahler *ggg*... bin echt geil im Märchen erzählen ^__-) Nun haben wir mehr eine einfache Bewegung der Textur von oben nach unten... langweilig! Das Bild soll flackern! Also werden wir chaotisch und erzeugen einfach wilde Zufallswerte, so dass die Textur sehr schnell von links nach rechts springt. Schon ergibt sich folgende Abfrage, bevor ein Screen gerendert wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
if s1 then&lt;br /&gt;
  begin&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D,screen);&lt;br /&gt;
    glMatrixMode(GL_TEXTURE);&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glMatrixMode(GL_MODELVIEW);&lt;br /&gt;
  end&lt;br /&gt;
  else&lt;br /&gt;
  begin&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D,noise);&lt;br /&gt;
    glMatrixMode(GL_TEXTURE);&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    gltranslate(random(100)/10,x,0);&lt;br /&gt;
    glMatrixMode(GL_MODELVIEW);&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In S1 ist gespeichert, welchen Zustand der Screen haben soll. If TRUE (an), dann nehmen wir die Screen-Textur und setzen die Textur-Matrix auf den Standard zurück; falls der Bildschirm aus sein soll, nehmen wir die Noise-Texture und verändern die Textur-Matrix so, wie wir sie brauchen. Das Ganze ist an sich nichts Neues, da dies bereits beim Matrix-Tutorial gezeigt wurde. Allerdings sieht man hier sehr schön, wie man zahlreiche KB für einen Effekt sparen kann, den man so ganz leicht &amp;quot;simulieren&amp;quot; kann ;) (sicherlich wäre es noch besser, die Textur ebenfalls im RAM zu erzeugen!)&lt;br /&gt;
&lt;br /&gt;
Besonders geil hierbei: auch in der Rekursion sind die Bildstörungen enthalten ohne weitere Source! Ist ein Bildschirm aus, sieht man eh nur noch das Rauschen und keine Rekursion! Der Code ist perfekt ;D&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Renderpass_noise.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
So, das war es mal wieder. Das Ganze war sicherlich nicht so ein Monster-Tutorial, wie ihr es sonst von mir gewohnt seid, aber ich denke, dass es trotzdem spannend und unterhaltsam war ;) Mir hat es zumindest sehr viel Spaß gemacht, weil man sich endlich mal kreativ auslassen konnte und ich denke, dass wir inzwischen auch ein Niveau erreicht haben, bei dem man sich den Techniken an sich widmen kann und nicht jeden einzelnen Schritt erklären muss. Laßt mich wissen, wie es Euch gefallen hat und ob ihr alle gut mitgekommen seid. Für Ideen und Kritiken stehe ich wie immer Verfügung!&lt;br /&gt;
&lt;br /&gt;
Btw: Ich frage mich langsam ernsthaft, wie ich auf den Gedanken gekommen bin, dass ein futuristischer Monitor sowas wie eine Störung beim Bildaufbau haben kann. Ich meine... nicht nur technisch! Man wird doch sowas sicherlich in der Zukunft in den Griff bekommen. Interessant ist dabei nur, dass in fast jedem Sciene-Fiction-Film sowas vorkommt. Lauter digitale Screens mit Bildstörungen, die auf analoge Übertragung schließen lassen :-/ . Wer ne Antwort weiß... i-Mehl an mich ;D&lt;br /&gt;
&lt;br /&gt;
Btw2: Dank an Anita für die germanistische Beratung ;)&lt;br /&gt;
&lt;br /&gt;
Viel Spaß beim Experimentieren!&lt;br /&gt;
&lt;br /&gt;
Euer&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Abseits_eckiger_Welten]]|[[Tutorial_Selection]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|NURBS]]&lt;/div&gt;</summary>
		<author><name>Deathball</name></author>	</entry>

	</feed>