<?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=Waran</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=Waran"/>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php/Spezial:Beitr%C3%A4ge/Waran"/>
		<updated>2026-05-30T23:30:05Z</updated>
		<subtitle>Benutzerbeiträge</subtitle>
		<generator>MediaWiki 1.27.4</generator>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24109</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24109"/>
				<updated>2009-08-08T16:45:01Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: Korrigierte Versionen der Dateien&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bot bisher leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren. Für Einsteiger mit weniger großen Ambitionen empfiehlt sich aber vorerst eher die [[Tutorial ColorPicking|shaderlose Variante]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]), welche mit einfacheren Mitteln auskommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performanceaspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder Verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-Variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den [http://home.arcor.de/m3ep/tut_colorpicking/tut_colorpicking_src.zip restlichen Code] analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-Prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts. Damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(y) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations bedingt&lt;br /&gt;
der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber kann sie also in ein internes Format umwandeln, welches&lt;br /&gt;
das volle Spektrum an Optimierungen und Cachetricks unterstüzt. Der Treiber&lt;br /&gt;
erhält alle Daten &amp;quot;am Stück&amp;quot; und kann sich 100%ig auf das Rendern&lt;br /&gt;
&amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht die Möglichkeit erhält, irgendetwas&lt;br /&gt;
absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
Es bringt auch nichts, die Displaylisten &amp;quot;unverbindlicher&amp;quot; zu halten und die&lt;br /&gt;
Spezifika wie Farbe in der Renderschleife festzulegen - damit würdet ihr nämlich&lt;br /&gt;
wieder beim Immediate Mode landen und hättet &amp;lt;i&amp;gt;trotz&amp;lt;/i&amp;gt; Listen, die durch die&lt;br /&gt;
Mischung mit Immediate Mode nicht mehr optimierbar und damit nutzlos geworden sind,&lt;br /&gt;
nichts gewonnen.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
&lt;br /&gt;
# Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
# Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
# Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
* Vertices&lt;br /&gt;
* Farbdaten oder Texturkoordinaten&lt;br /&gt;
* Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bedenkt auch, dass ihr mehrmals verwendete Vertices nicht mehrfach speichern&lt;br /&gt;
müsst. Benutzt stattdessen indizierte Arrays um den Speicherverbrauch zu senken&lt;br /&gt;
und ganz nebenbei die caching performance des Treibers zu erhöhen. &lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte, macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (siehe unten) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch drei Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
&lt;br /&gt;
== Dateien ==&lt;br /&gt;
* [http://home.arcor.de/m3ep/tut_colorpicking/tut_colorpicking_src.zip Quellcode des Beispieles]&lt;br /&gt;
* [http://home.arcor.de/m3ep/tut_colorpicking/tut_colorpicking_exe.zip Vorkompilierte Version des Beispieles (Windows)]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Objektselektion]]|[[Tutorial TexFilter]]}}&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24101</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24101"/>
				<updated>2009-08-08T01:58:41Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Objektselektion mit Color Picking */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bot bisher leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren. Für Einsteiger mit weniger großen Ambitionen empfiehlt sich aber vorerst eher die [[Tutorial ColorPicking|shaderlose Variante]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]), welche mit einfacheren Mitteln auskommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performanceaspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder Verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-Variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den {{ArchivLink|file=tut_colorpicking_src|text=restlichen Code}} analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-Prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts. Damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(y) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations bedingt&lt;br /&gt;
der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber kann sie also in ein internes Format umwandeln, welches&lt;br /&gt;
das volle Spektrum an Optimierungen und Cachetricks unterstüzt. Der Treiber&lt;br /&gt;
erhält alle Daten &amp;quot;am Stück&amp;quot; und kann sich 100%ig auf das Rendern&lt;br /&gt;
&amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht die Möglichkeit erhält, irgendetwas&lt;br /&gt;
absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
Es bringt auch nichts, die Displaylisten &amp;quot;unverbindlicher&amp;quot; zu halten und die&lt;br /&gt;
Spezifika wie Farbe in der Renderschleife festzulegen - damit würdet ihr nämlich&lt;br /&gt;
wieder beim Immediate Mode landen und hättet &amp;lt;i&amp;gt;trotz&amp;lt;/i&amp;gt; Listen, die durch die&lt;br /&gt;
Mischung mit Immediate Mode nicht mehr optimierbar und damit nutzlos geworden sind,&lt;br /&gt;
nichts gewonnen.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
&lt;br /&gt;
# Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
# Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
# Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
* Vertices&lt;br /&gt;
* Farbdaten oder Texturkoordinaten&lt;br /&gt;
* Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bedenkt auch, dass ihr mehrmals verwendete Vertices nicht mehrfach speichern&lt;br /&gt;
müsst. Benutzt stattdessen indizierte Arrays um den Speicherverbrauch zu senken&lt;br /&gt;
und ganz nebenbei die caching performance des Treibers zu erhöhen. &lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte, macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (siehe unten) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch drei Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
&lt;br /&gt;
== Dateien ==&lt;br /&gt;
* {{ArchivLink|file=tut_colorpicking_src|text=Quellcode des Beispieles}}&lt;br /&gt;
* {{ArchivLink|file=tut_colorpicking_exe|text=Vorkompilierte Version des Beispieles (Windows)}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Objektselektion]]|[[Tutorial TexFilter]]}}&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24032</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24032"/>
				<updated>2009-08-05T04:24:47Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bot bisher leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren. Für Einsteiger mit weniger großen Ambitionen empfiehlt sich aber vorerst eher die [[Tutorial ColorPicking|shaderlose Variante]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]), welche mit einfacheren Mitteln auskommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performanceaspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder Verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-Variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den {{ArchivLink|file=tut_colorpicking_src|text=restlichen Code}} analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-Prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts, damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(x) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations bedingt&lt;br /&gt;
der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber kann sie also in ein internes Format umwandeln, welches&lt;br /&gt;
das volle Spektrum an Optimierungen und Cachetricks unterstüzt. Der Treiber&lt;br /&gt;
erhält alle Daten &amp;quot;am Stück&amp;quot; und kann sich 100%ig auf das Rendern&lt;br /&gt;
&amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht die Möglichkeit erhält, irgendetwas&lt;br /&gt;
absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
Es bringt auch nichts, die Displaylisten &amp;quot;unverbindlicher&amp;quot; zu halten und die&lt;br /&gt;
Spezifika wie Farbe in der Renderschleife festzulegen - damit würdet ihr nämlich&lt;br /&gt;
wieder beim Immediate Mode landen und hättet &amp;lt;i&amp;gt;trotz&amp;lt;/i&amp;gt; Listen, die durch die&lt;br /&gt;
Mischung mit Immediate Mode nicht mehr optimierbar und damit nutzlos geworden sind,&lt;br /&gt;
nichts gewonnen.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
&lt;br /&gt;
# Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
# Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
# Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
* Vertices&lt;br /&gt;
* Farbdaten oder Texturkoordinaten&lt;br /&gt;
* Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bedenkt auch, dass ihr mehrmals verwendete Vertices nicht mehrfach speichern&lt;br /&gt;
müsst. Benutzt stattdessen indizierte Arrays um den Speicherverbrauch zu senken&lt;br /&gt;
und ganz nebenbei die caching performance des Treibers zu erhöhen. &lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte, macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (siehe unten) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch drei Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
&lt;br /&gt;
== Dateien ==&lt;br /&gt;
* {{ArchivLink|file=tut_colorpicking_src|text=Quellcode des Beispieles}}&lt;br /&gt;
* {{ArchivLink|file=tut_colorpicking_exe|text=Vorkompilierte Version des Beispieles (Windows)}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Objektselektion]]|[[Tutorial TexFilter]]}}&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24009</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24009"/>
				<updated>2009-08-03T19:34:32Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Einleitung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bot bisher leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren.&lt;br /&gt;
&lt;br /&gt;
Für Einsteiger mit weniger großen Ambitionen empfiehlt es sich aber eher das [[Tutorial von Tilman]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]) welches mit einfacheren Mitteln zurecht kommt.&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performanceaspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder Verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-Variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den {{ArchivLink|file=tut_colorpicking_src|text=restlichen Code}} analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-Prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts, damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(x) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations bedingt&lt;br /&gt;
der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber kann sie also in ein internes Format umwandeln, welches&lt;br /&gt;
das volle Spektrum an Optimierungen und Cachetricks unterstüzt. Der Treiber&lt;br /&gt;
erhält alle Daten &amp;quot;am Stück&amp;quot; und kann sich 100%ig auf das Rendern&lt;br /&gt;
&amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht die Möglichkeit erhält, irgendetwas&lt;br /&gt;
absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
Es bringt auch nichts, die Displaylisten &amp;quot;unverbindlicher&amp;quot; zu halten und die&lt;br /&gt;
Spezifika wie Farbe in der Renderschleife festzulegen - damit würdet ihr nämlich&lt;br /&gt;
wieder beim Immediate Mode landen und hättet &amp;lt;i&amp;gt;trotz&amp;lt;/i&amp;gt; Listen, die durch die&lt;br /&gt;
Mischung mit Immediate Mode nicht mehr optimierbar und damit nutzlos geworden sind,&lt;br /&gt;
nichts gewonnen.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
&lt;br /&gt;
# Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
# Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
# Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
* Vertices&lt;br /&gt;
* Farbdaten oder Texturkoordinaten&lt;br /&gt;
* Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bedenkt auch, dass ihr mehrmals verwendete Vertices nicht mehrfach speichern&lt;br /&gt;
müsst. Benutzt stattdessen indizierte Arrays um den Speicherverbrauch zu senken&lt;br /&gt;
und ganz nebenbei die caching performance des Treibers zu erhöhen. &lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte, macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (gibt es [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z hier] zum Herunterladen) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch drei Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
&lt;br /&gt;
== Dateien ==&lt;br /&gt;
* {{ArchivLink|file=tut_colorpicking_src|text=Quellcode des Beispieles}}&lt;br /&gt;
* {{ArchivLink|file=tut_colorpicking_exe|text=Vorkompilierte Version des Beispieles}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Objektselektion]]|[[Tutorial TexFilter]]}}&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24008</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24008"/>
				<updated>2009-08-03T18:29:39Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Die Renderschleife */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bietet momentan leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren.&lt;br /&gt;
&lt;br /&gt;
Für Einsteiger mit weniger großen Ambitionen empfiehlt es sich aber eher das [[Tutorial von Tilman]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]) welches mit einfacheren Mitteln zurecht kommt.&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performanceaspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder Verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-Variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den {{ArchivLink|file=tut_colorpicking_src|text=restlichen Code}} analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-Prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts, damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(x) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations bedingt&lt;br /&gt;
der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber kann sie also in ein internes Format umwandeln, welches&lt;br /&gt;
das volle Spektrum an Optimierungen und Cachetricks unterstüzt. Der Treiber&lt;br /&gt;
erhält alle Daten &amp;quot;am Stück&amp;quot; und kann sich 100%ig auf das Rendern&lt;br /&gt;
&amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht die Möglichkeit erhält, irgendetwas&lt;br /&gt;
absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
Es bringt auch nichts, die Displaylisten &amp;quot;unverbindlicher&amp;quot; zu halten und die&lt;br /&gt;
Spezifika wie Farbe in der Renderschleife festzulegen - damit würdet ihr nämlich&lt;br /&gt;
wieder beim Immediate Mode landen und hättet &amp;lt;i&amp;gt;trotz&amp;lt;/i&amp;gt; Listen, die durch die&lt;br /&gt;
Mischung mit Immediate Mode nicht mehr optimierbar und damit nutzlos geworden sind,&lt;br /&gt;
nichts gewonnen.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
&lt;br /&gt;
# Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
# Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
# Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
* Vertices&lt;br /&gt;
* Farbdaten oder Texturkoordinaten&lt;br /&gt;
* Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bedenkt auch, dass ihr mehrmals verwendete Vertices nicht mehrfach speichern&lt;br /&gt;
müsst. Benutzt stattdessen indizierte Arrays um den Speicherverbrauch zu senken&lt;br /&gt;
und ganz nebenbei die caching performance des Treibers zu erhöhen. &lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte, macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (gibt es [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z hier] zum Herunterladen) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch drei Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
&lt;br /&gt;
== Dateien ==&lt;br /&gt;
* {{ArchivLink|file=tut_colorpicking_src|text=Quellcode des Beispieles}}&lt;br /&gt;
* {{ArchivLink|file=tut_colorpicking_exe|text=Vorkompilierte Version des Beispieles}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Objektselektion]]|[[Tutorial TexFilter]]}}&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24002</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=24002"/>
				<updated>2009-08-03T10:58:08Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bietet momentan leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren.&lt;br /&gt;
&lt;br /&gt;
Für Einsteiger mit weniger großen Amibationen empfiehlt es sich aber eher das [[Tutorial von Tilman]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]) welches mit einfacheren Mitteln zurecht kommt.&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performanceaspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder Verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-Variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z restlichen Code] analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-Prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts, damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(x) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations bedingt&lt;br /&gt;
der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber kann sie also in ein internes Format umwandeln, welches&lt;br /&gt;
das volle Spektrum an Optimierungen und Cachetricks unterstüzt. Der Treiber&lt;br /&gt;
erhält alle Daten &amp;quot;am Stück&amp;quot; und kann sich 100%ig auf das Rendern&lt;br /&gt;
&amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht die Möglichkeit erhält, irgendetwas&lt;br /&gt;
absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
Es bringt auch nichts, die Displaylisten &amp;quot;unverbindlicher&amp;quot; zu halten und die&lt;br /&gt;
Spezifika wie Farbe in der Renderschleife festzulegen - damit würdet ihr nämlich&lt;br /&gt;
wieder beim Immediate Mode landen und hättet &amp;lt;i&amp;gt;trotz&amp;lt;/i&amp;gt; Listen, die durch die&lt;br /&gt;
Mischung mit Immediate Mode nicht mehr optimierbar und damit nutzlos geworden sind,&lt;br /&gt;
nichts gewonnen.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
&lt;br /&gt;
# Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
# Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
# Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
* Vertices&lt;br /&gt;
* Farbdaten oder Texturkoordinaten&lt;br /&gt;
* Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bedenkt auch, dass ihr mehrmals verwendete Vertices nicht mehrfach speichern&lt;br /&gt;
müsst. Benutzt stattdessen indizierte Arrays um den Speicherverbrauch zu senken&lt;br /&gt;
und ganz nebenbei die caching performance des Treibers zu erhöhen. &lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte, macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (gibt es [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z hier] zum Herunterladen) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch drei Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Objektselektion]]|[[Tutorial TexFilter]]}}&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Performance&amp;diff=23977</id>
		<title>Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Performance&amp;diff=23977"/>
				<updated>2009-08-02T23:02:07Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Performance Killer */  &amp;quot;Fiat Tutorial&amp;quot; - und es entstand.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
&lt;br /&gt;
Dieser Artikel liefert einige Tipps dazu wie man eine OpenGL-Anwendung optimieren kann. Einige der hier genannten Tipps schließen sich gegenseitig aus. Welche Vorgehensweise die beste ist hängt immer davon ab wo gerade der Engpass liegt. Es bringt zum Beispiel wenig den Shader zu optimieren, wenn eigentlich die zu langsame CPU das Problem ist.&lt;br /&gt;
&lt;br /&gt;
=== Grundlagen ===&lt;br /&gt;
Die folgenden Tipps sind eigentlich Pflicht, insbesondere da sie meist leicht zu realisieren sind. Beherzigt man diese überhaupt nicht, muss man sich nicht über eine unglaublich langsame Anwendung wundern. Die Reihenfolge gibt so ungefähr die Wichtigkeit an.&lt;br /&gt;
# Schließe nicht sichtbare Polygone so schnell wie möglich vom Rendering aus.&lt;br /&gt;
#* Aktiviere [[Backface Culling]].&lt;br /&gt;
#* Benutze [[Frustum Culling]].&lt;br /&gt;
#* Vor allem bei statischer Geometrie (die sich nicht bewegt) bieten sich die etablierten [[Techniken_und_Algorithmen#Raumunterteilungstechniken|Raumunterteilungstechniken]] wie BSP-Tree, Quad-Tree, usw. an.&lt;br /&gt;
# Vermeide Kommunikation zwischen CPU und GPU.&lt;br /&gt;
#* Lade nicht deine Texturen/Vertexdaten in jedem Frame neu auf die Grafikkarte hoch. Vermeide den Immediate Mode, also [[glBegin]]() und [[glEnd]](). Sofern du aber nur ein, zwei einzelne Dreiecke rendern möchtest ist der Immediate Mode akzeptabel.&lt;br /&gt;
#* Verwende [[VBO|Vertexbuffer-Objects]] oder [[Displayliste]]n.&lt;br /&gt;
# Verwende glDraw***-Aufrufe sparsam. Hiermit sind ALLE Befehle gemeint die irgendetwas rendern, z.B. [[glDrawArrays]], [[glDrawElements]], ..., aber auch [[glMultiDrawArrays]], usw...&lt;br /&gt;
#* Lieber ein paar Polygone mehr rendern, wenn du dadurch glDraw***-Aufrufe einsparen kannst.&lt;br /&gt;
#* Es spielt so gut wie keine Rolle, ob du 500 oder 1 Polygon renderst, da ein glDraw***-Aufrufe alleine schon ziemlich viel Zeit braucht.&lt;br /&gt;
# Optimiere deine Modelle. Nutze den Mesh-Optimierer in deiner 3D-Modellierungssoftware. Dies bringt zwar meist nicht viel, ist aber ja auch nicht mehr als ein Knopfdruck.&lt;br /&gt;
# Die Grafikkarte hat einen Cache für bereits vom Vertexshader verarbeitete Vertices. Dieser wird genutzt, wenn du [[Indices]] verwendest. Der Vertexshader muss also im optimalen Fall für jeden Vertex nur einmal ausgeführt werden, auch wenn der Vertex in mehreren Dreiecken verwendet wird. In jedem [[Mesh|geschlossenen]] und [[Mesh|2-mannigfaltigen]] Dreiecksnetz wird jeder Vertex im Durchschnitt von 6 Dreiecken verwendet. Hier lässt sich also so einiges an Speicher und Rechenleistung sparen.&lt;br /&gt;
# Nutze den Z-Buffer aus.&lt;br /&gt;
#* Sortiere nicht deine Polygone einzeln nach der Entfernung zur Kamera (Painters-Algorithm), sondern verwende den Z-Buffer.&lt;br /&gt;
#* Sofern du aufwendige Shader oder viele Texturen verwendest, rendere deine Objekte (nicht Polygone) von vorne nach hinten. Also grob sortieren und den Rest den Z-Buffer machen lassen.&lt;br /&gt;
#* Wenn du transparente Objekte hast, rendere zunächst von die undurchsichtigen Objekte. Die transparenten Objekte renderst du dann sortiert von hinten nach vorne.&lt;br /&gt;
# Vermeide wenn möglich häufige Shader-, Textur-, Material- und Statewechsel.&lt;br /&gt;
&lt;br /&gt;
===Performance Killer===&lt;br /&gt;
# Die OpenGL eigene Selektion wird nur schlecht als recht unterstützt. Eine [[Tutorial_ColorPicking|Color-Picking-Selection]] kann abhilfe leisten.&lt;br /&gt;
# [[glPushAttrib]] und glPopAttrib gelten laut [http://ati.amd.com/developer/gdc/2006/GDC06-OpenGL_Tutorial_Day-Hart-OpenGL_03_Performance.pdf diesem ATI Dokument] als &amp;quot;State Evils&amp;quot;, welche sich negativ auf die Performance auswirken.&lt;br /&gt;
# Sofern die Extension [http://www.opengl.org/registry/specs/ARB/texture_non_power_of_two.txt GL_ARB_texture_non_power_of_two] nicht unterstützt wird, könnte eine Grafikkarte bei Texturen deren Kantenlänge keiner 2er-Potenz entspricht (also z.B. 256, 512, 1024, ...) in den Software-Modus umschalten. Im Software-Modus wird die Grafikkarte komplett von der CPU emuliert. Das dies nicht schnell sein kann dürfte klar sein.&lt;br /&gt;
&lt;br /&gt;
=== Fortgeschrittene Techniken ===&lt;br /&gt;
Die folgenden Tipps sind nicht so einfach zu realisieren oder erfordern Kenntnisse über fortgeschrittene Features wie Shader, Instancing. Die Reihenfolge der Tipps ist hier nicht von Bedeutung.&lt;br /&gt;
# Ein glDraw*** wird nicht sofort ausgeführt, d.h. die CPU erhält die Kontrolle zurück bevor die GPU fertig mit rendern ist. Erst beim vertauschen von Front- und Backbuffer (&amp;quot;[[SwapBuffers]]&amp;quot;) oder einem expliziten [[glFinish]] wird synchronisiert. Folglich gebe zuerst der Grafikkarte was zu arbeiten, rechne dann deine Spiellogik, Physik, etc. auf der CPU und rufe dann erst [[SwapBuffers]] auf. Sowas geht natürlich nicht immer, aber man kann beispielsweise Physik und Rendern in zwei Frames aufspilten. Also du berechnest immer die Physik für das nächste Frame, während die GraKa das aktuelle Frame rendert. Siehe auch [[glFlush]].&lt;br /&gt;
# Überlege ob du Grafikspeicher sparen kannst indem du z.B. Texturkoordinaten oder Normalen zur Laufzeit im [[Shader]] berechnest.&amp;lt;br&amp;gt;(Beispiel: [[Shader#Beispiel:_Heightmap-Terrain|Heightmap-Terrain]])&lt;br /&gt;
# Wenn du viele identische Objekte renderst, überlege ob du [[GL_ARB_draw_instanced|Instancing]] einsetzen kannst. Erfordert allerdings halbwegs aktuelle Grafikhardware.&lt;br /&gt;
# Überlege ob du aufwendige Berechnungen, z.B. ein [[GLSL_Partikel_2|Partikelsystem]], nicht besser vollständig auf der Grafikkarte realisierst. Stichworte: [[Tutorial_Framebufferobject|Framebuffer-Objects]] und [[Shader#Transform-Feedback_(auch_Stream-Out)|Transform-Feedback]].&lt;br /&gt;
# Verzichte auf einen [[Shader|Geometryshader]], wenn du ihn nicht unbedingt benötigst.&lt;br /&gt;
# Mit [[Triangulation|Triangle Strips]] lässt sich die Anzahl der notwendigen Vertices (bzw. Indices) bis auf ein Drittel reduzieren. Auch hier kommt der Vertexcache der Grafikkarte zum Einsatz.&lt;br /&gt;
# Überlege ob du deine Modelle immer in höchster Detailstufe ( mit Texturierung, Beleuchtung und anderen teuren Effekten ) zeichnen musst, wenn nicht, verwende [[LOD]] ( ''Level-Of-Detail'' ).&lt;br /&gt;
# Überlege ob du bei statischen Szenen komplexe Beleuchtungsberechnungen durch [[Lightmaps]] ersetzen kannst. Lightmaps benötigen zwar einiges an Grafikspeicher, jedoch müssen Berechnungen z.B. für Schatten nur einmal durchgeführt werden. Dies funktioniert sowohl bei [[GLSL_Licht_und_Schatten|Shadow Maps]] als auch bei [[Volumetrische_Stencilschatten|volumetrischen Stencilschatten]]. Auch [[Reflexion]]en oder [[Kaustik]]en stellen kein Problem dar.&lt;br /&gt;
&lt;br /&gt;
=== Shader ===&lt;br /&gt;
Die folgenden Tipps beschäftigen sich mit der Optimierung von [[Shader]]n. Auch hier ist die Reihenfolge nicht von Bedeutung.&lt;br /&gt;
# Vermeide den Universal-Shader. Shader-Wechsel sind zwar aufwendig, aber eine gigantische if-Verzweigung die für jeden Vertex und jeden Pixel (!) ausgeführt werden muss ist noch wesentlich aufwendiger. Optimiere deinen Shader für die Aufgabe die er erfüllen muss.&lt;br /&gt;
# Vermeide aufwendige Berechnungen im Shader. Möglicherweise ist es sinnvoll komplexe Berechnungen im voraus zu berechnen und im Shader eine Lookup-Textur zu verwenden. Sofern eine komplexe Berechnung nur von Uniform-Variablen abhängig ist, ist es oft sinnvoll einfach eine weitere Uniform-Variable zu spendieren und einmal auf der CPU zu berechnen.&lt;br /&gt;
# Verwende nach Möglichkeit die in GLSL [[Tutorial_glsl#Eingebaute_Funktionen|integrierten Funktionen]]. Diese Funktionen können zum Teil wesentlich schneller sein da sie zum Teil direkt in der Hardware implementiert sind.&lt;br /&gt;
# Optimiere deine Shader so weit wie möglich. Effizienz hat hier Vorrang vor der Lesbarkeit, da die Operationen möglicherweise millionenfach pro Frame ausgeführt werden. Im Fall der Fälle dann einfach mal etwas ausführlicher kommentieren. &lt;br /&gt;
# Versuche insbesondere aufwendige Operationen wie zum Beispiel eine Wurzel zu vermeiden. Bedenke das sich solche Operationen auch in eingebauten Funktionen wie beispielsweise {{INLINE_CODE|length}}, {{INLINE_CODE|distance}} und {{INLINE_CODE|normalize}} verstecken können.&lt;br /&gt;
# Vermeide Random-Access auf Texturen. Zwei nebeneinander liegende Texel einer Textur können üblicherweise schneller aus dem Speicher geladen werden als zwei Texel an völlig unterschiedlichen Positionen in der Textur. Nutze den GPU-Cache!&lt;br /&gt;
# Versuche nicht Speicher zu sparen in dem du Variablen im Shader zusammenfasst. Beispielsweise macht eine Variable {{INLINE_CODE|vec4 positionAndSize}} wenig Sinn, wenn Position und Größe nur wenig miteinander zu tun haben und du z.B. ständig mit {{INLINE_CODE|positionAndSize.xyz}} arbeitest. Verwende lieber separate Variablen, also {{INLINE_CODE|vec3 position}} und {{INLINE_CODE|float size}}. So kann der Compiler besser optimieren.&amp;lt;br&amp;gt;Dies gilt natürlich nur für lokale Variablen im Shader. Wenn du mit dieser Methode Werte in einer Textur zusammenfassen kannst ist dies natürlich sehr sinnvoll!&lt;br /&gt;
# Es gibt diverse Tools mit denen du feststellen kannst wo genau deine Anwendung die meiste Zeit benötigt bzw. wo du optimieren solltest.&lt;br /&gt;
#* [http://developer.nvidia.com/object/nvperfkit_home.html NVIDIA PerfKit]&lt;br /&gt;
#* [http://news.developer.nvidia.com/2006/12/nvidia_perfgrap.html NVIDIA PerfGraph]&lt;br /&gt;
#* [http://developer.amd.com/gpu/shader/Pages/default.aspx AMD GPU ShaderAnalyzer]&lt;br /&gt;
#* bitte ergänzen...&lt;br /&gt;
&lt;br /&gt;
=== Quellen / Links ===&lt;br /&gt;
* [http://developer.nvidia.com/object/gpu_programming_guide.html NVIDIA GPU Programming Guide]&lt;br /&gt;
* [http://ati.amd.com/developer/gdc/2006/GDC06-OpenGL_Tutorial_Day-Hart-OpenGL_03_Performance.pdf OpenGL Performance Tuning, GDC 2006]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=23976</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=23976"/>
				<updated>2009-08-02T20:36:12Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bietet momentan leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren.&lt;br /&gt;
&lt;br /&gt;
Wenn es darum geht, sich &amp;quot;nur&amp;quot; schnell mit der grundlegenden Theorie zu&lt;br /&gt;
befassen, um dann im Rahmen kleiner Übungsprojekte Objekte anklicken zu können,&lt;br /&gt;
rate ich zunächst zum [[Tutorial von Tilman]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]), welches ganz ohne Shader oder&lt;br /&gt;
Berücksichtigung des internen Aufbaus von Grafiktreiber und Speicher auskommt.&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performance-aspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-Variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z restlichen Code] analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-Prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts, damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(x) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations-&lt;br /&gt;
bedingt der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber stellt aus diesen Displaylisten beim GL_COMPILE-Vorgang&lt;br /&gt;
ein statisches Vertex Buffer Object her, welches extrem effizient von der&lt;br /&gt;
Grafikkarte bearbeitet werden kann. Der Treiber erhält alle Daten &amp;quot;am Stück&amp;quot; und&lt;br /&gt;
kann sich 100%ig auf das Rendern &amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht&lt;br /&gt;
die Möglichkeit erhält, irgendetwas absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
&lt;br /&gt;
# Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
# Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
# Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
* Vertices&lt;br /&gt;
* Farbdaten oder Texturkoordinaten&lt;br /&gt;
* Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte - macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (gibt es [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z hier] zum Herunterladen) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch 3 Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Objektselektion]]|[[Tutorial TexFilter]]}}&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Objektselektion&amp;diff=23975</id>
		<title>Tutorial Objektselektion</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Objektselektion&amp;diff=23975"/>
				<updated>2009-08-02T20:33:55Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Download */  Navigation geupdated&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
Manchmal ist es nötig, zu wissen, welches auf den Schirm gebrachte Objekt vom Benutzer angewählt wurde. Egal ob man es für einen Karteneditor benötigt, um zu erfahren, welche Wand angeklickt wurde, oder für ein Strategiespiel, um herauszufinden, welches Gebäude angewählt wurde, es ist sehr einfach zu implementieren. Da ich noch keine guten Tutorials über OpenGLs Selection-Modus (besonders in Delphi) gesehen habe und diesen für den Karteneditor der ZornGL Engine benötigte, habe ich mich dran gemacht, herauszufinden wie diese Technik zu nutzen ist und werde es Ihnen in diesem Tutorial zeigen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Schritt 1 - Die Objektnamen auf den Name Stack legen==&lt;br /&gt;
Das Wichtigste am Selection Modus ist der Name Stack. Dies ist der Platz, an dem OpenGL die &amp;quot;Namen&amp;quot; der Objekte (trotz der Bezeichnung &amp;quot;Name&amp;quot; handelt es sich um Integer Werte) ablegt, die später zur Erkennung des gewählten Objekts zurückgegeben werden. Wie mit allen anderen OpenGL Stacks kann man Namen auf den Stack legen. Obwohl man auch Namen von diesem Stack herunternehmen kann, wird man davon kaum Nutzen machen. Beachten Sie, dass alle Kommandos zur Manipulation des Name Stacks nur im Selection Modus funktionieren, dazu aber später mehr. Sehen Sie sich zuerst dieses kleine Beispiel und seine Beschreibung an:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;const&lt;br /&gt;
 Sun   = 1;&lt;br /&gt;
 Mars  = 2;&lt;br /&gt;
 Earth = 3;&lt;br /&gt;
 Moon  = 4;&lt;br /&gt;
&lt;br /&gt;
procedure TGLForm.DrawScene;&lt;br /&gt;
begin&lt;br /&gt;
glInitNames;&lt;br /&gt;
glPushName(0);&lt;br /&gt;
...&lt;br /&gt;
glLoadName(Sun);&lt;br /&gt;
glCallList(SunList);&lt;br /&gt;
...&lt;br /&gt;
glLoadName(Mars);&lt;br /&gt;
glCallList(MarsList);&lt;br /&gt;
...&lt;br /&gt;
end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Nutzung des Selection Modus ist - wie Sie sehen können - sehr einfach. Zuerst müssen Sie den Name Stack leeren and dann muss mindestens ein Name auf den Stack gelegt werden. Wenn Sie dies nicht tun, wird der nächste [[glLoadName]]-Befehl keinen Namen auf den Stack legen, sondern einen GL_INVALID_OPERATION-Fehler auslösen.&lt;br /&gt;
&lt;br /&gt;
Nachdem der Name Stack also ordnungsgemäß initialisiert wurde, laden wir für jedes Objekt in unserer Szene (oder besser alle Objekte, die der Nutzer auswählen kann) einen Namen. Nach dem Laden des Namens zeichnen Sie Ihr Objekt. Dabei macht es keinen Unterschied ob dies eine Displayliste ist oder durch [[glBegin]] und [[glEnd]] geschieht.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Schritt 2 - Die ausgewählten Objekte finden==&lt;br /&gt;
Jetzt kommen wir zum Kern der Sache: Herauszufinden, welches Objekt unter dem Cursor liegt. Dies ist ein wenig komplizierter, aber sehen Sie sich dazu einfach folgende Prozedur und ihre Beschreibung an:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;function TGLForm.GetSelectBufferHit : Integer;&lt;br /&gt;
var&lt;br /&gt;
 SelectBuffer : array[0..512] of TGLUInt;&lt;br /&gt;
 Viewport     : TGLArrayi4;&lt;br /&gt;
 Hits,i       : Integer;&lt;br /&gt;
 HitZValue    : TGLUInt;&lt;br /&gt;
 Hit          : TGLUInt;&lt;br /&gt;
begin&lt;br /&gt;
glGetIntegerv(GL_VIEWPORT, @viewport);&lt;br /&gt;
glSelectBuffer(512, @SelectBuffer);&lt;br /&gt;
glRenderMode(GL_SELECT);&lt;br /&gt;
glInitNames;&lt;br /&gt;
glPushName(0);&lt;br /&gt;
glMatrixMode(GL_PROJECTION);&lt;br /&gt;
glPushMatrix;&lt;br /&gt;
 glLoadIdentity;&lt;br /&gt;
 gluPickMatrix(mx, viewport[3]-my, 1.0, 1.0, viewport);&lt;br /&gt;
 gluPerspective(45.0, ClientWidth/ClientHeight, 0.1, 1000);&lt;br /&gt;
 DrawScene;&lt;br /&gt;
 glMatrixMode(GL_PROJECTION);&lt;br /&gt;
glPopMatrix;&lt;br /&gt;
Hits := glRenderMode(GL_RENDER);&lt;br /&gt;
Hit       := High(TGLUInt);&lt;br /&gt;
HitZValue := High(TGLUInt);&lt;br /&gt;
for i := 0 to Hits-1 do&lt;br /&gt;
 if SelectBuffer[(i*4)+1] &amp;lt; HitZValue then&lt;br /&gt;
  begin&lt;br /&gt;
  Hit       := SelectBuffer[(i*4)+3];&lt;br /&gt;
  HitZValue := SelectBuffer[(i*4)+1];&lt;br /&gt;
  end;&lt;br /&gt;
Result := Hit;&lt;br /&gt;
end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wenn Sie die Variablendeklaration dieser Funktion betrachten, werden Sie sehen, dass ein Array namens SelectBuffer benötigt wird. Dies ist der Platz an dem OpenGL alle Treffer mit ihren Z-Werten speichern wird. Ein weiterer Nachteil des Selection-Modus ist die Tatsache, dass die Anzahl der Treffer, die man bekommt nicht bekannt ist. Deshalb muss ein Array fester Größe genutzt werden, was jedoch aufgrund der Tatsache, dass dies lokal deklariert ist, keine Probleme macht.&lt;br /&gt;
&lt;br /&gt;
Nach dem Sichern unseres Blickfeldes nutzen wir den Befehl [[glSelectBuffer]]''(size : Integer; buffer : PGLUint)'' um OpenGL die Größe und die Adresse unseres SelectBuffers mitzuteilen.&lt;br /&gt;
&lt;br /&gt;
Nun wechseln wir mit Hilfe des [[glRenderMode]]''(mode : cardinal)''-Befehls vom Rendermodus in den Selection-Modus, initialisieren den Name Stack und legen einen Namen auf ihn.&lt;br /&gt;
&lt;br /&gt;
Danach ist es Zeit, eine neue Projekionsmatrix zu erstellen. Da wir wollen, dass der Nutzer ein Objekt durch einen Mausklick anwählen kann, erstellen wir mit [[gluPickMatrix]]''(x, y, width, height: TGLdouble; viewport: TVector4i)'' eine neue Matrix, die an der Mausposition zentiert wird und 1x1 Pixel groß ist.&lt;br /&gt;
&lt;br /&gt;
Nach einem erneuten Setzen des FOV, des Aspektratios- und des zFar-Wertes zeichnen wir unsere Szene erneut. Diesmal wird sie jedoch im Selection-Modus gezeichnet und alle den Name Stack betreffenden Befehle werden jetzt ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir unsere Projektionsmatrix wiederhergestellt haben, wechseln wir wieder in den Rendermodus. Aber diesmal gibt der [[glRender]]-Befehl die Anzahl der Objekttreffer zurück, die wir für die spätere Nutzung speichern.&lt;br /&gt;
&lt;br /&gt;
Da wir jetzt also die Zahl der Treffer kennen, ist es sehr einfach, das vom Nutzer gewählte Objekt zu finden. Wie Sie wissen sollten, ist dies das Objekt mit dem niedrigsten Z-Wert (also das mit der geringsten Entfernung zum Betrachter).&lt;br /&gt;
Um dieses zu finden muss man erst wissen, was sich im SelectionBuffer befindet. Der Puffer besteht aus Trefferrecords, der wiederum aus folgenden Teilen besteht:&lt;br /&gt;
&lt;br /&gt;
*Anzahl der Namen auf dem Stack zum Zeitpunkt des Treffers&lt;br /&gt;
*Kleinste z-Koordinate aller Eckpunkte des gewählten Objekts&lt;br /&gt;
*Größte z-Koordinate aller Eckpunkte des gewählten Objekts&lt;br /&gt;
*Name des getroffenen Objekts&lt;br /&gt;
&lt;br /&gt;
Die einzigen benötigten Einträge sind der Zweite und der Letzte. Auf diese kann mit SelectBuffer[(n*4)+1] und SelectBuffer[(n*4)+3] zugegriffen werden, wobei n der gerade bearbeitet Treffer ist. Genau dies tut obige Prozedur in der am Ende gestarteten Schleife. Wenn die z-Koordinate des Treffers niedriger ist als die gespeicherte, wird sie durch selbige ersetzt. Außerdem wird gleichzeitig der Name des Treffers gespeichert.&lt;br /&gt;
Wenn die Schleife durchlaufen ist, haben wir also den Namen des Objektes, das sich unter dem Mauszeiger befand!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Das Beispielprogramm==&lt;br /&gt;
Ich habe ein kleines Beispielprogramm geschrieben, das ein winziges Universum simuliert.Beim Klick auf einen der Planeten sehen Sie im unteren Panel dessen Namen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Selection_selection01.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Download==&lt;br /&gt;
[http://delphigl.de/files/selectiontut.zip Das Selektionsdemo (inklusive Quellcode) herunterladen]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autor: [[Benutzer:Sascha_Willems|Sascha Willems]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Selection]]|[[Tutorial ColorPicking]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Objektselektion]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=23973</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=23973"/>
				<updated>2009-08-02T18:17:55Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Vertex Array / VBO */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bietet momentan leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren.&lt;br /&gt;
&lt;br /&gt;
Wenn es darum geht, sich &amp;quot;nur&amp;quot; schnell mit der grundlegenden Theorie zu&lt;br /&gt;
befassen, um dann im Rahmen kleiner Übungsprojekte Objekte anklicken zu können,&lt;br /&gt;
rate ich zunächst zum [[Tutorial von Tilman]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]), welches ganz ohne Shader oder&lt;br /&gt;
Berücksichtigung des internen Aufbaus von Grafiktreiber und Speicher auskommt.&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performance-aspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z restlichen Code] analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-&lt;br /&gt;
prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts, damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(x) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations-&lt;br /&gt;
bedingt der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das Kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber stellt aus diesen Displaylisten beim GL_COMPILE-Vorgang&lt;br /&gt;
ein statisches Vertex Buffer Object her, welches extrem effizient von der&lt;br /&gt;
Grafikkarte bearbeitet werden kann. Der Treiber erhält alle Daten &amp;quot;am Stück&amp;quot; und&lt;br /&gt;
kann sich 100%ig auf das Rendern &amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht&lt;br /&gt;
die Möglichkeit erhält, irgendetwas absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
&lt;br /&gt;
# Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
# Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
# Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
* Vertices&lt;br /&gt;
* Farbdaten oder Texturkoordinaten&lt;br /&gt;
* Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte - macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (gibt es [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z hier] zum Herunterladen) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch 3 Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Objektselektion]]|[[Tutorial TexFilter]]}}&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=23972</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=23972"/>
				<updated>2009-08-02T16:55:42Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bietet momentan leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren.&lt;br /&gt;
&lt;br /&gt;
Wenn es darum geht, sich &amp;quot;nur&amp;quot; schnell mit der grundlegenden Theorie zu&lt;br /&gt;
befassen, um dann im Rahmen kleiner Übungsprojekte Objekte anklicken zu können,&lt;br /&gt;
rate ich zunächst zum [[Tutorial von Tilman]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]), welches ganz ohne Shader oder&lt;br /&gt;
Berücksichtigung des internen Aufbaus von Grafiktreiber und Speicher auskommt.&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performance-aspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z restlichen Code] analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-&lt;br /&gt;
prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts, damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(x) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations-&lt;br /&gt;
bedingt der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das Kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber stellt aus diesen Displaylisten beim GL_COMPILE-Vorgang&lt;br /&gt;
ein statisches Vertex Buffer Object her, welches extrem effizient von der&lt;br /&gt;
Grafikkarte bearbeitet werden kann. Der Treiber erhält alle Daten &amp;quot;am Stück&amp;quot; und&lt;br /&gt;
kann sich 100%ig auf das Rendern &amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht&lt;br /&gt;
die Möglichkeit erhält, irgendetwas absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
1) Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
2) Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
3) Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
- Vertices&lt;br /&gt;
- Farbdaten oder Texturkoordinaten&lt;br /&gt;
- Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte - macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (gibt es [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z hier] zum Herunterladen) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch 3 Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial Objektselektion]]|[[Tutorial TexFilter]]}}&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=23971</id>
		<title>Tutorial ColorPicking Shader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_ColorPicking_Shader&amp;diff=23971"/>
				<updated>2009-08-02T16:51:01Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: Die Seite wurde neu angelegt: „=Objektselektion mit Color Picking=  ==Einleitung==  Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn ich euch sage, dass es dafür au...“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Objektselektion mit Color Picking=&lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Jaja, schon das dritte Selektionstutorial auf DelphiGL. Aber glaubt mir wenn&lt;br /&gt;
ich euch sage, dass es dafür auch gute Gründe gibt. DGL bietet momentan leider&lt;br /&gt;
nur Tutorials, die das Picking auf Basis von OpenGLs Namestack behandeln. Diese&lt;br /&gt;
Methode ist aber gerade für Einsteiger ungeeignet, nicht etwa, weil sie&lt;br /&gt;
unnötig kompliziert wäre, aber weil sie hoffnungslos veraltet, tendenziell&lt;br /&gt;
inkompatibel zu nVidias Treibern (Softwaremodus!) und enorm unflexibel ist.&lt;br /&gt;
Jedenfalls war es kein Anflug aus purer Bosheit, der die Khronos-Group dazu&lt;br /&gt;
bewogen hat, diese Methode in OpenGL3 zu entfernen.&lt;br /&gt;
&lt;br /&gt;
Ein Wort zur angestrebten Zielgruppe:&lt;br /&gt;
Bedenkt bitte, dass das Tutorial eher darauf ausgelegt ist, euch eine&lt;br /&gt;
performante, für große Projekte taugliche und vor allem zukunftssichere Methode&lt;br /&gt;
zu präsentieren.&lt;br /&gt;
&lt;br /&gt;
Wenn es darum geht, sich &amp;quot;nur&amp;quot; schnell mit der grundlegenden Theorie zu&lt;br /&gt;
befassen, um dann im Rahmen kleiner Übungsprojekte Objekte anklicken zu können,&lt;br /&gt;
rate ich zunächst zum [[Tutorial von Tilman]] ([http://s200005540.online.de/verschiedenes/colorpicking.pdf Vorabversion als PDF]), welches ganz ohne Shader oder&lt;br /&gt;
Berücksichtigung des internen Aufbaus von Grafiktreiber und Speicher auskommt.&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial soll es um Color Picking, also Selektion mithilfe von Farben,&lt;br /&gt;
gehen. Damit ihr euch ein Bild davon machen könnt wo ihr steht, wenn ihr die&lt;br /&gt;
Technik beherrscht, hier ein kurzer Überblick über die Vor- und Nachteile:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Vorteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nachteile&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Geschwindigkeit&lt;br /&gt;
:Ein Selektionsvorgang mit dieser Methode dauert niemals länger als ein ganz normales Rendering der Szene - i.d.R. ist es sogar schneller, da das Verfahren darauf aufbaut, viel &amp;quot;unter den Tisch fallen zu lassen&amp;quot;.&lt;br /&gt;
      &lt;br /&gt;
* Speichereffizienz&lt;br /&gt;
:Im Gegensatz zu strahlbasierten Verfahren, die im Härtefall eine eigene Kollisionsgeometrie benötigen, funktioniert Color Picking  mit Shadern &amp;quot;in place&amp;quot; und benötigt keinen zusätzlichen Speicher (oder nur vernachlässigbar wenig). Wird es ohne Shader implementiert, fallen mit jedem selektierbarem Objekt 3-4 Byte pro Vertex an (d.h. etwa 12 Bytes für ein Quad).&lt;br /&gt;
      &lt;br /&gt;
* Flexibilität&lt;br /&gt;
:Color Picking funktioniert mit jeder Grafik-Schnittstelle und jeder Art von Geometrie - das schließt 2D, 3D, animiert, dynamisch und alphamaskiert mit ein. Objekte müssen nicht, wie in diesem Beispiel, durch einen festen Index identifiziert werden, sondern können auch problemlos mit eigenen Werten oder sogar Funktionen versehen werden. Und das Beste:&lt;br /&gt;
      &lt;br /&gt;
* Simplizität&lt;br /&gt;
:Das Alles gibt es zum Preis eines kleinen Fragmentshaders. Mehr Aufwand ist kaum nötig, um so gut wie alle Sonderfälle abzudecken. Man kann als Fausregel festhalten: Alles was der Benutzer sieht, kann selektiert werden. Ohne Kompromisse! Pixelgenau. Ohne aufwändige Mathematik dahinter. Und alles _voll_ Hardwarebeschleunigt. Bei der shaderlosen Variante müssen sich allerdings ein paar Gedanken über die zugrundeliegende Geometrie gemacht werden (auch vom Performance-aspekt her betrachtet).&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;&lt;br /&gt;
* Reproduzierbarkeit&lt;br /&gt;
:Leider ist man bei einem hardwarebasierten Verfahren (trifft auf Color Picking mit OpenGL sowie Direct3D zu) auf Gedeih und Verderb dem Treiber ausgeliefert. Und damit hängt der Erfolg der Selektion theoretisch auch davon ab, wie gut sich der Treiberhersteller an Standards halten kann. Insbesondere Dinge wie Antialiasing können die Selektion verfälschen - in diesem Tutorial wird aber auch ein sehr effektiver Workaround dagegen angeboten. In der Praxis ist dieser Nachteil auch eher unbedeutend, weil man ein gewisses Maß an Standards durchaus vorraussetzen kann.&lt;br /&gt;
      &lt;br /&gt;
* Redundanz zum Physikcode&lt;br /&gt;
:Wenn alle mathematischen Grundlagen für eine Selektion via Raycasting geschaffen sind, wäre eine zusätzliche Implementierung von Color Picking nur Ballast. &amp;quot;Grundlagen für Raycasting&amp;quot; hört sich vielleicht abgehoben an, diese sind aber z.B. schon in einem einfachen Ego Shooter geschaffen, wenn man herauskriegen will, wo das Einschussloch gezeichnet werden soll oder welche Hitbox des Gegners getroffen wurde.&lt;br /&gt;
      &lt;br /&gt;
* Hardwareanforderungen&lt;br /&gt;
:Je mehr Features man nutzt (wie z.B. Shader), desto neuer muss die Grafikhardware sein. Die Methode, die hier im Tut vorgestellt wird, basiert auf GLSL und ist damit ab OpenGL 2.0 Core zu haben.&lt;br /&gt;
&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Theorie==&lt;br /&gt;
&lt;br /&gt;
Die Theorie hinter dem Color Picking ist ganz schnell erklärt. Man zeichnet&lt;br /&gt;
jedes selektierbare Objekt in einer einmaligen Farbe und merkt sich, welches&lt;br /&gt;
Objekt in welcher Farbe gezeichnet wurde.&lt;br /&gt;
Dann wird ein Pixel unter dem Mauszeiger ausgelesen - anhand der ausgelesenen&lt;br /&gt;
Farbe lässt sich auf das Objekt unter dem Mauszeiger zurückschließen. Das ist&lt;br /&gt;
vielleicht momentan noch ein bisschen sehr abstrakt, sollte aber schon klarer&lt;br /&gt;
werden, wenn ihr den Beispielcode verfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Implementierung==&lt;br /&gt;
&lt;br /&gt;
===Organisation===&lt;br /&gt;
&lt;br /&gt;
Vorweg muss gesagt sein: Wirklich alles was ich in dem Beispielcode mache, ist&lt;br /&gt;
nur eine Lösung von vielen. Wie euch durch die kurze Theorie schon dämmern&lt;br /&gt;
sollte gibt es etliche Möglichkeiten Color Picking zu implementieren. In diesem&lt;br /&gt;
Beispiel sieht das Ganze so aus:&lt;br /&gt;
Die Daten der selektierbaren Objekte (Displayliste, Position und ein String)&lt;br /&gt;
sind in einem Record zusammengefasst. Alle diese Records sind in einem Array&lt;br /&gt;
angeordnet und werden auch per Schleife gezeichnet... exakt dieser Array-Index&lt;br /&gt;
ist es nun, was jedem individuellen Objekt seine Identifizierbarkeit spendiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Shader===&lt;br /&gt;
&lt;br /&gt;
Jeder der bei Shader an böhmische Dörfer denkt, sollte sich vielleicht vorher&lt;br /&gt;
kurz das [[Tutorial glsl|GLSL-Tutorial]] auf DGL zu Gemüte führen. Prinzipiell handelt es sich&lt;br /&gt;
um kleine Programme, die Teile der festen OpenGL-Pipeline (dazu gehört z.B.&lt;br /&gt;
die Positionierung von Vertices und das Zeichnen oder verwerfen von Pixeln)&lt;br /&gt;
ersetzen. Shader können entweder in Assembler oder einer Sprache namens GLSL&lt;br /&gt;
in einer C-artigen Syntax geschrieben werden - in diesem Beispiel wird letztere&lt;br /&gt;
Methode verwendet, da die Assemblervariante kaum noch weiterentwickelt wird und&lt;br /&gt;
schlechter zu lesen ist.&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns nun die Shader an, der vor einem Selektionsvorgang gebunden und&lt;br /&gt;
anschließend wieder ausgeschaltet werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Vertexshader&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  gl_Position = ftransform();&lt;br /&gt;
  gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vertexshader macht offensichtlich nichts (oder besser: nur) weltbewegendes.&lt;br /&gt;
Jedes Vertex an seine Stelle setzen und Texturkoordinaten durchschleifen... aber&lt;br /&gt;
da fehlt doch was? Der Vertexshader übergibt gar keine Farbdaten - d.h. wir&lt;br /&gt;
können schonmal festhalten, dass die aktuelle Farbe in der Statemachine komplett&lt;br /&gt;
&amp;lt;b&amp;gt;ignoriert&amp;lt;/b&amp;gt; wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
// Fragmentshader&lt;br /&gt;
&lt;br /&gt;
uniform vec4 color;&lt;br /&gt;
uniform float use_alpha_tex;&lt;br /&gt;
uniform sampler2D sampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
  if (use_alpha_tex == 1.0)&lt;br /&gt;
    if (texture2D(sampler, vec2(gl_TexCoord[0])).a &amp;lt; 0.2)&lt;br /&gt;
      discard;&lt;br /&gt;
      &lt;br /&gt;
  gl_FragColor = color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier sieht es schon gleich viel interessanter aus. Der Fragmentshader bekommt&lt;br /&gt;
zwei im Programm gesetzte uniform Variablen: color und use_alpha_tex. Wie man in&lt;br /&gt;
der letzten Zeile sieht, ist der Wert dieser ominösen color-variable genau das,&lt;br /&gt;
was auch im Framebuffer erscheinen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use_alpha_tex&amp;quot; wird im Programm auf 1.0 gesetzt, wenn Texturen mit Alphakanal &lt;br /&gt;
verwendet werden.&lt;br /&gt;
Sollen solche Texturen verwendet werden, liest der Shader den aktuellen&lt;br /&gt;
Alphawert und verwirft das Fragment, wenn Alpha unter dem Schwellenwert 0.2&lt;br /&gt;
liegt. Es handelt sich also nur um einen handgemachten Alphatest.&lt;br /&gt;
&lt;br /&gt;
Dabei sei angemerkt, dass dies kein sonderlich stilvolles Vorgehen ist: Man&lt;br /&gt;
versucht möglichst, einen Shader nur auf eine ganz eng umrissene Aufgabe hin&lt;br /&gt;
zu gestalten - da haben solche Abfragen nichts zu suchen. Es dient in diesem&lt;br /&gt;
Tutorial aber der Übersichtlichkeit, also werden wir mal beide Augen zudrücken.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend kann man also sagen, dass wenn der Shader aktiviert ist, jedes&lt;br /&gt;
einzelne gezeichnete Pixel eine von uns im Hauptprogramm festgelegte Farbe&lt;br /&gt;
bekommt. Zusätzlich hat der Shader die Fähigkeit, bei Texturen mit Alphakanal&lt;br /&gt;
&amp;quot;unsichtbare&amp;quot; Pixel zu ignorieren und damit den Hintergrund durchscheinen zu&lt;br /&gt;
lassen.&lt;br /&gt;
&lt;br /&gt;
Tipp: Objekte mit Textur ohne Alphakanal zählen bei der Selektion wie Objekte&lt;br /&gt;
ganz ohne Textur, da es sich letztendlich nur um ein Quad handelt, das in einer&lt;br /&gt;
Einheitsfarbe eingefärbt werden soll.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Die Renderschleife===&lt;br /&gt;
&lt;br /&gt;
Sehen wir uns jetzt einmal den Code an, bei dem wirklich etwas gezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure Render;&lt;br /&gt;
const&lt;br /&gt;
  fac: single = 1/255;&lt;br /&gt;
var&lt;br /&gt;
  i: longword;&lt;br /&gt;
  iv: array [0..3] of byte absolute i; // iv und i überschneiden sich&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Kopf der Prozedur sieht man schon erste Vorbereitungen auf das, was gleich&lt;br /&gt;
während der Schleife passieren wird. Durch das absolute-Schlüsselwort überlappen&lt;br /&gt;
i und iv im Speicher. Rein praktisch bedeutet das, dass man über iv auf die&lt;br /&gt;
einzelnen Bytes der for-Variable i zugreifen kann, als wäre sie ein Byte-Array.&lt;br /&gt;
&lt;br /&gt;
Durch Multiplikation mit der Konstante fac wird ein Byte-Wert (0..255) in einen&lt;br /&gt;
Float im Bereich (0..1) umgerechnet. Dies ist notwendig, da die Grafikkarte&lt;br /&gt;
intern mit Floats arbeitet und man daher nur Floats übergeben kann (auf SM 4.0&lt;br /&gt;
Karten funktioniert auch Integer, ist aber sehr unperformant).&lt;br /&gt;
Das nächste Häppchen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
  // Das Erste Model soll texturiert werden&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glUniform1f(SelShader.use_alpha_tex, 1.0);&lt;br /&gt;
&lt;br /&gt;
  for i := 1 to high(Objekte) do&lt;br /&gt;
  begin&lt;br /&gt;
    if i = 2 then begin // Ab dem zweiten nicht mehr&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      glUniform1f(SelShader.use_alpha_tex, 0.0);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Offensichtlich sind unsere Objekte im Array so angeordnet, dass zuerst&lt;br /&gt;
texturierte Objekte (wir setzen use_alpha_tex auf 1.0 und aktivieren TEXTURE_2D)&lt;br /&gt;
gezeichnet werden sollen. Diese Art von Sortierung macht Sinn, da Statechanges&lt;br /&gt;
auf jeden Fall zu vermeiden sind, wenn sie vermeidbar sind. Gemeint ist mit&lt;br /&gt;
Sortierung nicht, dass Alphatexturen zuerst kommen müssen, das ist manchmal auch&lt;br /&gt;
gar nicht möglich, sondern das nicht mitten in der Schleife zwischen texturiert&lt;br /&gt;
und nicht texturiert gewechselt werden muss. Bei dem zweiten Durchlauf wird die&lt;br /&gt;
Texturierung deaktiviert und es geht ohne weiter (ergo: Es gibt nur ein&lt;br /&gt;
texturiertes Objekt). Der interessante Teil der Renderfunktion folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    // Den Index in eine Farbe umrechnen und im Shader setzen&lt;br /&gt;
    {$ifdef ENDIAN_BIG}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[1]*fac, iv[2]*fac, iv[3]*fac, 1.0);&lt;br /&gt;
    {$else}&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, 1.0);&lt;br /&gt;
    {$endif}&lt;br /&gt;
&lt;br /&gt;
    // Zeichnen&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Objekte[i].posx, Objekte[i].posy, 0);&lt;br /&gt;
    glCallList(Objekte[i].displayliste);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie versprochen werden die 3 untersten (ich sage bewusst nicht &amp;quot;ersten&amp;quot;) Bytes&lt;br /&gt;
einzeln in Floats umgerechnet und landen in color.r, color.g und color.b des&lt;br /&gt;
Shaders. color.a wird nicht verwendet und auf 1.0 gesetzt (1.0 ist der übliche&lt;br /&gt;
Standardwert für Alpha). Mit dem so präparierten Shader würde nun gezeichnet&lt;br /&gt;
werden, wäre er aktiviert.&lt;br /&gt;
&lt;br /&gt;
Die berechtigte Frage ist jetzt: Warum gibt es bei der Index-Umrechnung zwei&lt;br /&gt;
verschiedene Varianten? Der Grund ist die sogenannte Endianess oder Byte Order.&lt;br /&gt;
Bei Little-Endian-Systemen (z.B. x86) werden die Bytes mit höherer Adresse&lt;br /&gt;
signifikanter, bei Big Endian (z.B. PowerPC, Playstation 3) ist es genau&lt;br /&gt;
umgekehrt.&lt;br /&gt;
&lt;br /&gt;
Da wir nur 24 Bit (= 3 Byte) des Farbpuffers benutzen, interessiert uns das&lt;br /&gt;
signifikanteste Byte des DWords nicht. Da dieses auf Big-Endian-Systemen&lt;br /&gt;
gerade iv[0] ist, müssen die Offsets verschoben werden. Die folgende Grafik&lt;br /&gt;
verdeutlicht dies:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Endian.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich wird bei so einer einfachen Methode die Reihenfolge der&lt;br /&gt;
Farbkomponenten verändert. Aus RGB auf Little Endian wird BGR auf Big Endian. Da&lt;br /&gt;
wir die gefärbten Bilder allerdings sowiso nicht zu Gesicht bekommen, ist es&lt;br /&gt;
egal.&lt;br /&gt;
Ja, wir werden tatsächlich nie sehen können, was während der Selektion passiert.&lt;br /&gt;
Oder hat jemand bis jetzt ein SwapBuffers ausmachen können? Nein? Gut, das muss&lt;br /&gt;
so :3&lt;br /&gt;
&lt;br /&gt;
Wenn ihr den [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z restlichen Code] analysiert werdet ihr feststellen, dass SwapBuffers&lt;br /&gt;
nur in der Eventschleife von SDL aufgerufen wird. Wenn Render in der Select-&lt;br /&gt;
prozedur aufgerufen wird, werden die Buffer nicht getauscht (und damit das&lt;br /&gt;
Bild nicht angezeigt).&lt;br /&gt;
&lt;br /&gt;
Erratum: Wie ihr seht wurde die Grafik von mir korrigiert. Ich habe mich&lt;br /&gt;
entschlossen den Fehler nur durchzustreichen, damit ihr davon lernen könnt&lt;br /&gt;
und ihn nicht wiederholt:&lt;br /&gt;
Es handelt sich bei dieser Vorgehensweise NICHT um eine BITweise Verschiebung&lt;br /&gt;
nach links (SHL 8), sondern um eine BYTEweise. Was macht das schon aus?&lt;br /&gt;
Sehr viel! Wie &amp;quot;The Winner&amp;quot; mir freundlicherweise mitteilte, interpretiert die&lt;br /&gt;
CPU bei bitweisen Operationen das LongWord &amp;quot;am Stück&amp;quot;, also vom höchstwertigen&lt;br /&gt;
zum untersten Bit von Links nach Rechts, damit bedeutet ein SHL unabhängig von&lt;br /&gt;
der Plattform immer eine Multiplikation mit 2 (= Verschiebung um ein Bit in&lt;br /&gt;
Richtung des am meisten signifikanten Bits).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Der Selektionscode===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ihr wurdet von dem Bit- und Bytegeschubse noch nicht gänzlich&lt;br /&gt;
frustiert und überlegt, den Rest des Tutorials erstmal beiseite zu legen. Im&lt;br /&gt;
Selektionscode (folgt) kommt noch ein bisschen mehr davon ^_^&lt;br /&gt;
Bevor ich euch allerdings den Code gebe, solltet ihr erstmal wissen, was bei der&lt;br /&gt;
Selektion genau gemacht wird.&lt;br /&gt;
Anders als im kurzen Theorieteil besprochen lesen wir nicht nur einen Pixel,&lt;br /&gt;
sondern eine 3x3-Box (9 Pixel) um den Mauszeiger herum aus. Das tun wir, um die&lt;br /&gt;
Umgebung des Pixels zu untersuchen und damit Fehler durch Antialiasing sehr&lt;br /&gt;
effektiv zu erkennen. Das Problem mit AA ist, dass es Mischfarben erzeugt: Das&lt;br /&gt;
bedeutet wann man auf den Randbereich eines Objektes klickt, kann es sein, dass&lt;br /&gt;
man nicht die Farbe des Objektes erwischt, sondern _irgend eine_ Mischfarbe.&lt;br /&gt;
&lt;br /&gt;
Wird diese kritiklos wieder als Index interpretiert landen wir irgendwo im&lt;br /&gt;
Array und im besten Fall stürzt das Programm ab (im schlimmsten Fall passiert&lt;br /&gt;
&amp;lt;i&amp;gt;nichts&amp;lt;/i&amp;gt; und der Fehler bleibt uns bis zur Veröffentlichung erhalten).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
function Select(x, y: integer): longword;&lt;br /&gt;
var&lt;br /&gt;
  PixelData: array [0..9] of longword;&lt;br /&gt;
  count, i: longword;&lt;br /&gt;
begin&lt;br /&gt;
  // Koordinaten umrechnen (SDL-Fenster in OpenGL / 3x3-Box zentrieren)&lt;br /&gt;
  y := WINDOW_Y-y;&lt;br /&gt;
  if x &amp;gt; 0 then if x &amp;lt; WINDOW_X then dec(x) else dec(x,2);&lt;br /&gt;
  if y &amp;gt; 0 then if y &amp;lt; WINDOW_Y then dec(x) else dec(y,2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zuerst wird der &amp;quot;Mauszeiger&amp;quot; (eigentlich nur die Koordinaten, die wir bekommen&lt;br /&gt;
haben) von Fensterpixeln (Ursprung oben) in OpenGL-Pixel (Ursprung unten)&lt;br /&gt;
umgerechnet und wenn möglich nach unten links verschoben. Das verschieben sorgt&lt;br /&gt;
dafür, dass die angeklickte Stelle genau in der Mitte der gleich ausgelesenen&lt;br /&gt;
3x3-Box ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glUseProgram(SelShader.prog);&lt;br /&gt;
  glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
  glEnable(GL_SCISSOR_TEST);&lt;br /&gt;
  glScissor(x, y, 3, 3); // Die wenigsten Pixel müssen in den Buffer&lt;br /&gt;
&lt;br /&gt;
  Render;&lt;br /&gt;
  // Kein SwapBuffers. Das codierte Bild sieht eh nicht hübsch aus.&lt;br /&gt;
  &lt;br /&gt;
  glUseProgram(0);&lt;br /&gt;
  glClearColor(0.1, 0.1, 0.1, 1.0);&lt;br /&gt;
  glDisable(GL_SCISSOR_TEST);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Zeichnen sollte eigentlich selbsterklärend sein. Der Shader wird aktiviert,&lt;br /&gt;
die Hintergrundfarbe auf (0 0 0 0) gesetzt (entspricht dem Index 0!) und der&lt;br /&gt;
Scissor Test aktiviert. Der Test macht bei Selektion sehr viel Sinn, da wir uns&lt;br /&gt;
ja eigentlich nur für 9 Pixel interessieren - warum dann alle im meinem Fall&lt;br /&gt;
786.432 Pixel in den Framebuffer schreiben?&lt;br /&gt;
In Anbetracht dessen, dass Scissor so gut wie kostenlos ist, wäre das eine&lt;br /&gt;
unverzeiliche Sünde. Was der Test allerdings nicht verhindert ist, dass für&lt;br /&gt;
jedes Pixel der Fragmentshader abgearbeitet werden muss - seht es also nicht als&lt;br /&gt;
Freibrief für den Uber-Shader aus if-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Aufgerufen wird die normale Render-Prozedur, die wir so angepasst haben, dass&lt;br /&gt;
sie dem Shader, wenn er gebunden ist, die relevanten Daten zukommen&lt;br /&gt;
lässt. Nehmt das als Anreiz euer Rendering bei großen Szenen gut zu optimieren&lt;br /&gt;
(z.B. mit Octrees/BSP und Frustrum Culling) - eure Selektion profitiert genauso&lt;br /&gt;
davon. Nach dem Rendering wird Alles zurückgesetzt und erhält damit wieder&lt;br /&gt;
seine vorherige Ordnung.&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt endlich der &amp;lt;b&amp;gt;wirklich&amp;lt;/b&amp;gt; interessante Teil.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glReadPixels(x, y, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, @PixelData[0]);&lt;br /&gt;
&lt;br /&gt;
  count := 0;&lt;br /&gt;
&lt;br /&gt;
  for i := 0 to 9 do&lt;br /&gt;
  begin&lt;br /&gt;
    if PixelData[4] = PixelData[i] then inc(count);&lt;br /&gt;
    &lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      {$ifdef ENDIAN_BIG}&lt;br /&gt;
      result := (PixelData[4] shr 8)&lt;br /&gt;
      {$else}&lt;br /&gt;
      result := PixelData[4] and $FFFFFF;&lt;br /&gt;
      {$endif}&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  // Wenn diese Stelle erreicht wird, hat die Schleife nicht mehr als 3&lt;br /&gt;
  // gleichfarbige Pixel zusammenbekommen. Selektion ungültig.&lt;br /&gt;
  result := 0;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant, aber es bleibt weiterhin einfach (ich hoffe es war einfach, bis&lt;br /&gt;
jetzt ^^). Aus Vogelperspektive: Es werden 9 RGBA-Werte aus dem Framebuffer&lt;br /&gt;
geholt - Alpha wird immer 255, da wird nur 24 Bit benutzen (Standardwert von&lt;br /&gt;
OpenGL) - und in 9 DWords geschrieben.&lt;br /&gt;
Schließlich wird gezählt, wie viele Pixel mit PixelData[4] (das ist die Mitte,&lt;br /&gt;
der Mauszeigerpixel) identisch sind. Wenn es mindestens 4 Pixel sind, wird&lt;br /&gt;
die Selektion für gültig erklärt - d.h. der Mauszeigerpixel wird in result&lt;br /&gt;
geschrieben und die Funktion kehrt zurück.&lt;br /&gt;
Wenn die Funktion in der Schleife noch nicht verlassen wurde, wird result auf 0&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel in dem gezeigt wird, wie uns diesen Vorgehen hilft, Fehler zu&lt;br /&gt;
Erkennen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:AAc-Beispiel.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der von mir festgesetzte Schwellenwert &amp;quot;4&amp;quot; (bedenkt dabei, dass implementations-&lt;br /&gt;
bedingt der Pixel unter dem Mauszeiger mitgezählt wird) ist genaugenommen&lt;br /&gt;
variabel. Theoretisch würde vermutlich auch 3 reichen, aber ich gehe da gerne&lt;br /&gt;
auf Nummer sicher. Der Nachteil der Sicherheit ist, das man z.B. keine Linien&lt;br /&gt;
mit 1px Breite selektieren kann - das Kleinste selektierbare Objekt hat die&lt;br /&gt;
gezeichnete Größe von 2x2 Pixeln.&lt;br /&gt;
&lt;br /&gt;
Wie ihr seht, muss das LongWord vor der Rückgabe noch bearbeitet werden: Bei LE&lt;br /&gt;
reicht es, den Alpha-Wert per Bitmaske abzuschneiden. Bei Big Endian landet der&lt;br /&gt;
Alpha-Wert (glaube ich!) im untersten Byte. Daher muss in Richtung &amp;quot;unten&amp;quot;&lt;br /&gt;
verschoben werden, damit der Alpha-Wert entfernt und unser Pixel-Index im&lt;br /&gt;
unteren Bereich landet - schaut euch ruhig nochmal die Endian-Grafik an, wenn&lt;br /&gt;
ihr jetzt nur Bahnhof versteht.&lt;br /&gt;
Ich kann den Big Endian-Code leider mangels Maschine nicht testen. Sollte er&lt;br /&gt;
also auf die Art nicht funktionieren, sofort in den IRC kommen oder mir eine PM&lt;br /&gt;
schreiben (so kommt man auch an sein wohlverdientes Feedback).&lt;br /&gt;
&lt;br /&gt;
Und damit sind wir mit dem Thema durch. Was jetzt noch kommt ist lediglich für&lt;br /&gt;
den Spezialfall interessant, wenn man mit 32-Bit selektieren will... kann ja&lt;br /&gt;
sein, dass man die Bitschubserei satt hat oder 16.777.215 codierbare Objekte&lt;br /&gt;
neben dem Hintergrund nicht genug sind :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Ergänzung: 32/16 Bit Farbtiefe===&lt;br /&gt;
&lt;br /&gt;
32 Bit sind eigentlich leichter handzuhaben, als 24 Bit. Das kommt daher, dass&lt;br /&gt;
ein echter 32-Bit-Wert aus dem Framebuffer genau unserem (und eurem, gemeint&lt;br /&gt;
sind die BEler) nativen LongWord entspricht und damit die ganze Bitschubserei&lt;br /&gt;
inklusive Unterscheidung zwischen BE und LE überflüssig macht:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    glUniform4f(SelShader.color, iv[0]*fac, iv[1]*fac, iv[2]*fac, iv[3]*fac);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
    if count &amp;gt; 3 then&lt;br /&gt;
    begin&lt;br /&gt;
      result := PixelData[4];&lt;br /&gt;
      exit;&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass wir auf einmal Raum für 4.294.967.295 Objekte haben, was&lt;br /&gt;
definitiv mehr ist, als man jemals ausschöpfen könnte - an unsere&lt;br /&gt;
mitzeitreisdenden Leser: Das Tutorial stammt von 2009, das war damals so ;-)&lt;br /&gt;
&lt;br /&gt;
Der Nachteil an 32 Bit zur Selektion (ja, jetzt wird es nochmal kurz ernst) ist,&lt;br /&gt;
dass wir uns erstmal nicht darauf verlassen können, auch wirklich einen 32 Bit&lt;br /&gt;
Buffer vom Treiber zu bekommen, wenn wir einen anfordern. Ohne Überprüfungen&lt;br /&gt;
und Ausweichmöglichkeit auf 24 Bit läuft es also nicht.&lt;br /&gt;
Daraus folgt? Genau - wir brauchen sowiso den 24 Bit-Code für solche Härtefälle.&lt;br /&gt;
In Folge dessen ist der &amp;quot;Einfachheitsvorteil&amp;quot; von 32 Bit ad absurdum geführt&lt;br /&gt;
und wir können getrost auf 24 Bit setzen.&lt;br /&gt;
&lt;br /&gt;
Ein noch gravierenderer Nachteil ist, dass wir nun auch auf OpenGL-Funktionen&lt;br /&gt;
aufpassen müssen, die den Alphawert verwenden. Wenn beispielsweise Alphatest&lt;br /&gt;
während der Selektion aktiviert ist, werden selbst mit einer kulanten&lt;br /&gt;
Einstellung wie (GL_GREATER, 0.0) alle Selektionen von Objekten mit Alpha=0&lt;br /&gt;
verworfen. Das sind auf Little Endian die ersten 16.777.215 und bei Big Endian&lt;br /&gt;
jedes 256ste.&lt;br /&gt;
&lt;br /&gt;
Erlaubt der Treiber die Verwendung von lediglich 16 Bit, so wären noch 65535&lt;br /&gt;
Objekte selektierbar. Das ist zwar in der Regel ebenfalls mehr als genug, aber&lt;br /&gt;
leider erfordert 16 Bit aufgrund unterschiedlicher Bitlängen der Farbkomponenten&lt;br /&gt;
(R: 5, G: 6, B: 5) einen enormen Aufwand bei der Zerlegung/Zusammensetzung des&lt;br /&gt;
Index in/aus Farben, der nicht ohne massive Verwendung von Bitmasken und -shifts&lt;br /&gt;
auskommt.&lt;br /&gt;
Aus diesem Grund habe ich mich dazu entschlossen, das Thema &amp;quot;16 Bit&amp;quot; im Tutorial&lt;br /&gt;
nicht weiter zu behandeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shaderlose Variante (Anregung)==&lt;br /&gt;
&lt;br /&gt;
===Einleitung===&lt;br /&gt;
&lt;br /&gt;
In den letzten Kapiteln wurde die Implementierung mit Shadern besprochen. Diese&lt;br /&gt;
bieten, wie schon am Anfang kurz in den Vorteilen angerissen, ein Maximum an&lt;br /&gt;
Flexilibität und Speichereffizienz (da komplett unabhängig von der Geometrie)&lt;br /&gt;
bei unbeeinträchtigter Performance. Im Prinzip eine Win-Win-Situation, könnte&lt;br /&gt;
man meinen... leider gibt es aber auch noch Hardware, auf der keine Shader&lt;br /&gt;
laufen.&lt;br /&gt;
In diesem Kapitel soll es also darum gehen, euch eine Übersicht darüber zu&lt;br /&gt;
geben, was in einer solchen Situation machbar ist - inklusive Bewertung im&lt;br /&gt;
Kontext der angestrebten Zielgruppe (siehe &amp;quot;Einleitung&amp;quot;). Implementieren müsst&lt;br /&gt;
ihr es dann aber selbst. Wenn euch die Shaderlose Variante nicht weiter&lt;br /&gt;
interessiert, könnt ihr auch problemlos zum Schluss übergehen - ihr verpasst&lt;br /&gt;
nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Wesentlich schwieriger sind ohne Shader allerdings Spezialeffekte wie&lt;br /&gt;
transparente Texturen. Eine Lösung sieht so aus, dass ihr zunächst das&lt;br /&gt;
texturierte Objekt mit aktiviertem Alphatest in eine Stencil-Maske zeichnet.&lt;br /&gt;
Danach wird in einem zweiten Pass ein Quad mit der Indexfarbe gezeichnet -&lt;br /&gt;
durch die Maske werden nur solche Pixel geschrieben, die vorher den Alphatest&lt;br /&gt;
bestanden und damit die Stencilmaske geformt haben.&lt;br /&gt;
Nun aber zum grundlegenden Thema:&lt;br /&gt;
&lt;br /&gt;
Prinzipiell stehen euch ohne Shader zwei Möglichkeiten offen: 1) Einstreuen von&lt;br /&gt;
Immediate Mode in eure Renderschleife oder 2) Anpassung der Geometrie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Immediate Mode===&lt;br /&gt;
&lt;br /&gt;
Möglichkeit 1 sieht in Pseudocode folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  procedure Render(mode)&lt;br /&gt;
  &lt;br /&gt;
    if mode = rendermode&lt;br /&gt;
      Texturen aktivieren&lt;br /&gt;
      Lighting aktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
    if mode = selection&lt;br /&gt;
      Texturen deaktiveren&lt;br /&gt;
      Lighting deaktivieren&lt;br /&gt;
      usw.&lt;br /&gt;
&lt;br /&gt;
    foreach i in models&lt;br /&gt;
      if mode = rendermode&lt;br /&gt;
        Normale Farbe[i] setzen oder Textur binden&lt;br /&gt;
      if mode = selection&lt;br /&gt;
        glColor3ubv(@i) // nur Little Endian...&lt;br /&gt;
      &lt;br /&gt;
      Farblose(!) Geometrie[i] zeichnen&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vorteil wird schnell ersichtlich: Die Methode bleibt flexibel und einfach&lt;br /&gt;
handzuhaben - bei gleichzeitig guter Speichereffizienz. Was auf diese Art aber&lt;br /&gt;
leider niemals erreicht wird ist maximale Performance.&lt;br /&gt;
Durch die Verwendung von Immediate Mode wird dem Treiber jede Möglichkeit&lt;br /&gt;
geraubt, den Rendervorgang zu optimieren. Gleichzeitig kann der Treiber auch&lt;br /&gt;
keine Daten cachen (und ggf. wiederverwenden) sondern muss im Gegenteil noch&lt;br /&gt;
Leistung dafür verbrauchen, jederzeit auf jeden Fehltritt von euch vorbereitet&lt;br /&gt;
zu sein. Die Methode stößt also schnell an ihre Grenzen, wenn große Mengen&lt;br /&gt;
von Daten verarbeitet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Vertex Array / VBO===&lt;br /&gt;
&lt;br /&gt;
Betrachtet man Möglichkeit 2 genauer, stellt man fest, dass es auch hier wieder&lt;br /&gt;
mehrere Optionen gibt.&lt;br /&gt;
Die Displaylisten, die im Shaderteil verwendet werden, sind&lt;br /&gt;
&amp;lt;i&amp;gt;state complete&amp;lt;/i&amp;gt;. Das bedeutet, sie enthalten alle Informationen, die der&lt;br /&gt;
Treiber zum zeichnen braucht und sind nicht auf externe Aufrufe wie glColor&lt;br /&gt;
angewiesen - der Treiber stellt aus diesen Displaylisten beim GL_COMPILE-Vorgang&lt;br /&gt;
ein statisches Vertex Buffer Object her, welches extrem effizient von der&lt;br /&gt;
Grafikkarte bearbeitet werden kann. Der Treiber erhält alle Daten &amp;quot;am Stück&amp;quot; und&lt;br /&gt;
kann sich 100%ig auf das Rendern &amp;quot;konzentrieren&amp;quot;, weil der Benutzer gar nicht&lt;br /&gt;
die Möglichkeit erhält, irgendetwas absurdes zu tun.&lt;br /&gt;
&lt;br /&gt;
Will man diese Art von Displaylisten ohne Shader für die Selektion fit machen,&lt;br /&gt;
benötigt man auf der einen Seite Listen mit den &amp;quot;normalen Farben&amp;quot; (welche auch&lt;br /&gt;
schlichtweg &amp;quot;keine Farben&amp;quot; sein können, wenn nur Texturen verwendet werden) und&lt;br /&gt;
Listen mit eingebauten Selektionsfarben.&lt;br /&gt;
Bei dieser Methode wird die benötigte Geometrie &amp;lt;i&amp;gt;verdoppelt&amp;lt;/i&amp;gt; - mit anderen&lt;br /&gt;
Worten: Sie kommt aus Speichersicht überhaupt nicht in Frage.&lt;br /&gt;
&lt;br /&gt;
Es muss also ein Weg gefunden werden, der folgende Kriterien erfüllt:&lt;br /&gt;
1) Der Treiber muss seine Daten &amp;quot;am Stück&amp;quot; erhalten können.&lt;br /&gt;
2) Selektion und normales Rendering sollen die gleiche Geometrie nutzen.&lt;br /&gt;
3) Trotzdem müssen die Farben austauschbar sein.&lt;br /&gt;
&lt;br /&gt;
Die Lösung sieht so aus, dass wir selbst ein Vertex Array (Hauptspeicherbasiert)&lt;br /&gt;
oder ein Vertex Buffer Object (VRAM-Basiert) erstellen. Der Einfachheit halber&lt;br /&gt;
stelle ich hier nur die Variante mit den VAs vor - es sollte sich aber ziemlich&lt;br /&gt;
gut auf VBOs übertragen lassen.&lt;br /&gt;
&lt;br /&gt;
Es werden drei Arrays erstellt:&lt;br /&gt;
- Vertices&lt;br /&gt;
- Farbdaten oder Texturkoordinaten&lt;br /&gt;
- Selektionsfarben&lt;br /&gt;
&lt;br /&gt;
In OpenGL ist es nun möglich, mittels der gl*Pointer-Funktionen die Datenquellen&lt;br /&gt;
für einen Aufruf von glDrawArrays zu bestimmten. glVertexPointer zeigt stets auf&lt;br /&gt;
unsere Vertices.&lt;br /&gt;
&lt;br /&gt;
glColorPointer wird je nach dem, ob es sich um eine Selektion oder ein normales&lt;br /&gt;
Rendering handelt, auf die Farbdaten oder die Selektionsfarben gesetzt. Werden&lt;br /&gt;
nur Texturen verwendet, bleibt glTexCoordPointer immer auf diese gesetzt,&lt;br /&gt;
der Farbpointer immer auf die Selektionsfarben - und je nach Fall werden dann&lt;br /&gt;
GL_COLOR_ARRAY oder GL_TEXTURE_2D deaktiviert oder aktiviert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Schlusswort==&lt;br /&gt;
&lt;br /&gt;
Das war jetzt Color Picking. Ich sage es aber gerne nochmal: Bevor ihr euch&lt;br /&gt;
meine Implementation auf's Auge drücken lasst denkt daran, dass es nur eine&lt;br /&gt;
Lösung von vielen ist.&lt;br /&gt;
Wann immer in eurer Situation etwas anderes angebracht sein sollte - macht es&lt;br /&gt;
anders!&lt;br /&gt;
Es kann auch nicht schaden, wenn ihr euch den kompletten Beispielcode zum&lt;br /&gt;
Tutorial (gibt es [http://home.arcor.de/m3ep/tutorial/Selektion-Colorpicking.7z hier] zum Herunterladen) mal anseht. Neben dglOpenGL und&lt;br /&gt;
Jedi-SDL verwende ich noch 3 Units, die von mir sind. Ich erhebe keinen&lt;br /&gt;
Urheberrechtsanspruch auf diese Units, d.h. ihr könnt sie nach belieben&lt;br /&gt;
ausschlachten, verändern und selbst verwenden (meinetwegen auch kommerziell).&lt;br /&gt;
&lt;br /&gt;
Wenn ihr eine Frage oder Feedback zum Tutorial oder zum Verfahren habt, findet&lt;br /&gt;
ihr mich öfters im IRC-Channel von DGL oder erreicht mich bei Fragen von&lt;br /&gt;
öffentlichem Interesse auch im [http://delphigl.com/forum/index.php Forum].&lt;br /&gt;
&lt;br /&gt;
Gruß Waran&lt;br /&gt;
[[Kategorie:Tutorial|ColorPicking]]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial&amp;diff=23970</id>
		<title>Tutorial</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial&amp;diff=23970"/>
				<updated>2009-08-02T15:56:04Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Grundlagen-Tutorials */&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://www.delphi-treff.de/tutorials/ Delphitutorials bei www.delphi-treff.de]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Delphi Source der Delphi Treff entstand durch den Zusammenschluss von Delphi-Source und dem Delphi-Treff und wurde mittlerweile wieder in Delphi-Treff umbenannt. 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;
''Wer sehen möchte was DGLer aus dem hier Gelernten und ihrer Kreativität so machen, findet Beispiele davon in der [[DGL_Projekte]]-Ecke.''&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 6 ''&lt;br /&gt;
|[[Bild:Tutorial_na.jpg|right]] &amp;quot;Dark Engine&amp;quot;&amp;lt;br&amp;gt; &lt;br /&gt;
Dieses Tutorial wurde geplant aber niemals umgesetzt. Heute hätte es auch keine Relevanz mehr, da die Dark Engine nicht gepflegt wurde. Falls ihr ein gutes Einsteigertutorial als Ersatz vorschlagen wollt, könnt ihr dies im Feedback-Forum von DelphiGL.com tun.&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 ColorPicking]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
| [[Bild:Tutorial_ColorPicking_Screenie.png|right]] &amp;quot;Objektselektion mit Color Picking&amp;quot; &amp;lt;br&amp;gt;&lt;br /&gt;
Eine zukunftssichere und vor allem nVidia-Kompatible Lösung für das Selektionsproblem. Verwendet Shader und richtet sich eher an erfahrenere Leser, die eine Methode suchen, die sie so auch in größeren Projekten einsetzen könnten.&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 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 Delphic 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;
![[Tutorial Wassereffekt]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Wassertutorial-scrshot-preview.jpg|right]]In diesem Tutorial erklärt Lord Horazont wie man eine Wasserebene mit Brechungen mit und ohne Shader erzeugen kann.&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;
Diese Extension führte '''VBO'''s ein. Wie man mit damit 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 Delphic 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 Delphic, 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 Delphic 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 Delphic 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:SAT_Kollision.jpg|right|128px]] &amp;quot;Separating Axis Theorem&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
Seth erklärt in diesem Tutorial wie man Polygonkollisionen mit Hilfe des ''Seperating Axis Theorem'' erkennen kann. &lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Kollision1]]&lt;br /&gt;
{{Level_1}} &lt;br /&gt;
|[[Bild:Tutorial_Kollision1_Coll2.png|128px|right]] &amp;quot;Kollision 1&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
Reinhard Niehoff fragte uns ob er ein Tutorial über Kollision schreiben könnte. Das konnten wir nicht ablehnen, und so entstand dieses Tutorial in dem grundlegende Kollisionsberechnungen erklärt werden. Weitere sollen folgen. &lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Kollision2]]&lt;br /&gt;
{{Level_2}} &lt;br /&gt;
|[[Bild:Tutorial_Kollision2_Thumb.png|128px|right]] &amp;quot;Kollision 2&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
Beim zweiten Tutorial gehts mehr in die Tiefe und einige Spezialfälle bei Kugel-Polygon-Kollisionen werden erläutert. Mit dabei: Die [[Werkzeugkiste]] mit nützlichen Funktionen.&lt;br /&gt;
|-&lt;br /&gt;
![[Tutorial Kollision3]]&lt;br /&gt;
{{Level_3}} &lt;br /&gt;
|[[Bild:Tutorial_Kollision3_Thumb.png|128px|right]] &amp;quot;Kollision 3&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
Beim letzten Tutorial dieser Serie gehts darum, wie man die bisherigen Ansätze in einem Programm unterbringt.&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 Delphic 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 Delphic 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 Delphic 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>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:Tutorial_ColorPicking_Screenie.png&amp;diff=23969</id>
		<title>Datei:Tutorial ColorPicking Screenie.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:Tutorial_ColorPicking_Screenie.png&amp;diff=23969"/>
				<updated>2009-08-02T15:45:08Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: hat eine neue Version von „Datei:Tutorial ColorPicking Screenie.png“ hochgeladen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:Tutorial_ColorPicking_Screenie.png&amp;diff=23968</id>
		<title>Datei:Tutorial ColorPicking Screenie.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:Tutorial_ColorPicking_Screenie.png&amp;diff=23968"/>
				<updated>2009-08-02T15:40:12Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:Endian.png&amp;diff=23967</id>
		<title>Datei:Endian.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:Endian.png&amp;diff=23967"/>
				<updated>2009-08-02T15:27:14Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:AAc-Beispiel.png&amp;diff=23966</id>
		<title>Datei:AAc-Beispiel.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:AAc-Beispiel.png&amp;diff=23966"/>
				<updated>2009-08-02T15:26:50Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=21032</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=21032"/>
				<updated>2007-12-12T19:50:15Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* TGA Dateien - Warum? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Artikel geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von &amp;quot;neueren&amp;quot; OpenGL-Implementierungen (konkret: 1.2) direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Image Data Field     - die eigentlichen Pixeldaten&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
type&lt;br /&gt;
  TTGAFile = record&lt;br /&gt;
    iType: byte;  // should be 2&lt;br /&gt;
     w, h: word;  // Width, Height&lt;br /&gt;
      bpp: byte;  // Byte per Pixel&lt;br /&gt;
     data: ^byte; // Pixels, dynamic length&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
var f: file; bytes: longword;&lt;br /&gt;
begin&lt;br /&gt;
  assign(f, filename);&lt;br /&gt;
  reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
  // type&lt;br /&gt;
  seek(f, 2);&lt;br /&gt;
  blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
  // w, h, bpp&lt;br /&gt;
  seek(f, 12);&lt;br /&gt;
  blockread(f, result.w, 5);&lt;br /&gt;
  result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
  // data&lt;br /&gt;
  bytes := result.w * result.h * result.bpp;&lt;br /&gt;
  getmem(result.data, bytes);&lt;br /&gt;
  seek(f, 18);&lt;br /&gt;
  blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
  close(f);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
var&lt;br /&gt;
  tex: TTGAFile;&lt;br /&gt;
  glFormat: longword;&lt;br /&gt;
begin&lt;br /&gt;
  tex := LoadTGA(filename);&lt;br /&gt;
  if tex.iType = 2 then&lt;br /&gt;
  begin&lt;br /&gt;
    glGenTextures(1, @TexID);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
    if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
      else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
    FreeMem(tex.data);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieser Artikel war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20691</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20691"/>
				<updated>2007-08-07T17:54:49Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Definition eines Datentyps */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Artikel geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Image Data Field     - die eigentlichen Pixeldaten&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
type&lt;br /&gt;
  TTGAFile = record&lt;br /&gt;
    iType: byte;  // should be 2&lt;br /&gt;
     w, h: word;  // Width, Height&lt;br /&gt;
      bpp: byte;  // Byte per Pixel&lt;br /&gt;
     data: ^byte; // Pixels, dynamic length&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
var f: file; bytes: longword;&lt;br /&gt;
begin&lt;br /&gt;
  assign(f, filename);&lt;br /&gt;
  reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
  // type&lt;br /&gt;
  seek(f, 2);&lt;br /&gt;
  blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
  // w, h, bpp&lt;br /&gt;
  seek(f, 12);&lt;br /&gt;
  blockread(f, result.w, 5);&lt;br /&gt;
  result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
  // data&lt;br /&gt;
  bytes := result.w * result.h * result.bpp;&lt;br /&gt;
  getmem(result.data, bytes);&lt;br /&gt;
  seek(f, 18);&lt;br /&gt;
  blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
  close(f);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
var&lt;br /&gt;
  tex: TTGAFile;&lt;br /&gt;
  glFormat: longword;&lt;br /&gt;
begin&lt;br /&gt;
  tex := LoadTGA(filename);&lt;br /&gt;
  if tex.iType = 2 then&lt;br /&gt;
  begin&lt;br /&gt;
    glGenTextures(1, @TexID);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
    if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
      else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
    FreeMem(tex.data);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieser Artikel war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20690</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20690"/>
				<updated>2007-08-06T18:26:05Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Artikel geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
type&lt;br /&gt;
  TTGAFile = record&lt;br /&gt;
    iType: byte;  // should be 2&lt;br /&gt;
     w, h: word;  // Width, Height&lt;br /&gt;
      bpp: byte;  // Byte per Pixel&lt;br /&gt;
     data: ^byte; // Pixels, dynamic length&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
var f: file; bytes: longword;&lt;br /&gt;
begin&lt;br /&gt;
  assign(f, filename);&lt;br /&gt;
  reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
  // type&lt;br /&gt;
  seek(f, 2);&lt;br /&gt;
  blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
  // w, h, bpp&lt;br /&gt;
  seek(f, 12);&lt;br /&gt;
  blockread(f, result.w, 5);&lt;br /&gt;
  result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
  // data&lt;br /&gt;
  bytes := result.w * result.h * result.bpp;&lt;br /&gt;
  getmem(result.data, bytes);&lt;br /&gt;
  seek(f, 18);&lt;br /&gt;
  blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
  close(f);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
var&lt;br /&gt;
  tex: TTGAFile;&lt;br /&gt;
  glFormat: longword;&lt;br /&gt;
begin&lt;br /&gt;
  tex := LoadTGA(filename);&lt;br /&gt;
  if tex.iType = 2 then&lt;br /&gt;
  begin&lt;br /&gt;
    glGenTextures(1, @TexID);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
    if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
      else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
    FreeMem(tex.data);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieser Artikel war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20687</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20687"/>
				<updated>2007-08-05T21:25:03Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Die Datei laden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
type&lt;br /&gt;
  TTGAFile = record&lt;br /&gt;
    iType: byte;  // should be 2&lt;br /&gt;
     w, h: word;  // Width, Height&lt;br /&gt;
      bpp: byte;  // Byte per Pixel&lt;br /&gt;
     data: ^byte; // Pixels, dynamic length&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
var f: file; bytes: longword;&lt;br /&gt;
begin&lt;br /&gt;
  assign(f, filename);&lt;br /&gt;
  reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
  // type&lt;br /&gt;
  seek(f, 2);&lt;br /&gt;
  blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
  // w, h, bpp&lt;br /&gt;
  seek(f, 12);&lt;br /&gt;
  blockread(f, result.w, 5);&lt;br /&gt;
  result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
  // data&lt;br /&gt;
  bytes := result.w * result.h * result.bpp;&lt;br /&gt;
  getmem(result.data, bytes);&lt;br /&gt;
  seek(f, 18);&lt;br /&gt;
  blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
  close(f);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
var&lt;br /&gt;
  tex: TTGAFile;&lt;br /&gt;
  glFormat: longword;&lt;br /&gt;
begin&lt;br /&gt;
  tex := LoadTGA(filename);&lt;br /&gt;
  if tex.iType = 2 then&lt;br /&gt;
  begin&lt;br /&gt;
    glGenTextures(1, @TexID);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
    if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
      else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
    FreeMem(tex.data);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20686</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20686"/>
				<updated>2007-08-05T21:24:23Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Definition eines Datentyps */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
type&lt;br /&gt;
  TTGAFile = record&lt;br /&gt;
    iType: byte;  // should be 2&lt;br /&gt;
     w, h: word;  // Width, Height&lt;br /&gt;
      bpp: byte;  // Byte per Pixel&lt;br /&gt;
     data: ^byte; // Pixels, dynamic length&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
var f: file; bytes: longword;&lt;br /&gt;
begin&lt;br /&gt;
  assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
  // type&lt;br /&gt;
  seek(f, 2);&lt;br /&gt;
  blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
  // w, h, bpp&lt;br /&gt;
  seek(f, 12);&lt;br /&gt;
  blockread(f, result.w, 5);&lt;br /&gt;
  result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
  // data&lt;br /&gt;
  bytes := result.w * result.h * result.bpp;&lt;br /&gt;
  getmem(result.data, bytes);&lt;br /&gt;
  seek(f, 18);&lt;br /&gt;
  blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
  close(f);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
var&lt;br /&gt;
  tex: TTGAFile;&lt;br /&gt;
  glFormat: longword;&lt;br /&gt;
begin&lt;br /&gt;
  tex := LoadTGA(filename);&lt;br /&gt;
  if tex.iType = 2 then&lt;br /&gt;
  begin&lt;br /&gt;
    glGenTextures(1, @TexID);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
    if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
      else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
    FreeMem(tex.data);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20685</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20685"/>
				<updated>2007-08-05T21:21:28Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Die Datei laden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
type&lt;br /&gt;
  TTGAFile = packed record&lt;br /&gt;
    iType: byte;  // should be 2&lt;br /&gt;
     w, h: word;  // Width, Height&lt;br /&gt;
      bpp: byte;  // Byte per Pixel&lt;br /&gt;
     data: ^byte; // Pixels, dynamic length&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Packed&amp;quot; ist an dieser Stelle wichtig, da wir w,h und bpp in einem einzigen Zug einlesen&lt;br /&gt;
werden und diese Variablen deshalb auch im Speicher nebeneinander liegen sollten (und es sonst,&lt;br /&gt;
je nach dem wie der konkrete Compiler Daten standardmäßig anordnet, zu Problemen kommen kann).&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
var f: file; bytes: longword;&lt;br /&gt;
begin&lt;br /&gt;
  assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
  // type&lt;br /&gt;
  seek(f, 2);&lt;br /&gt;
  blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
  // w, h, bpp&lt;br /&gt;
  seek(f, 12);&lt;br /&gt;
  blockread(f, result.w, 5);&lt;br /&gt;
  result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
  // data&lt;br /&gt;
  bytes := result.w * result.h * result.bpp;&lt;br /&gt;
  getmem(result.data, bytes);&lt;br /&gt;
  seek(f, 18);&lt;br /&gt;
  blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
  close(f);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
var&lt;br /&gt;
  tex: TTGAFile;&lt;br /&gt;
  glFormat: longword;&lt;br /&gt;
begin&lt;br /&gt;
  tex := LoadTGA(filename);&lt;br /&gt;
  if tex.iType = 2 then&lt;br /&gt;
  begin&lt;br /&gt;
    glGenTextures(1, @TexID);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
    if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
      else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
    FreeMem(tex.data);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20684</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20684"/>
				<updated>2007-08-05T21:20:57Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Definition eines Datentyps */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
type&lt;br /&gt;
  TTGAFile = packed record&lt;br /&gt;
    iType: byte;  // should be 2&lt;br /&gt;
     w, h: word;  // Width, Height&lt;br /&gt;
      bpp: byte;  // Byte per Pixel&lt;br /&gt;
     data: ^byte; // Pixels, dynamic length&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Packed&amp;quot; ist an dieser Stelle wichtig, da wir w,h und bpp in einem einzigen Zug einlesen&lt;br /&gt;
werden und diese Variablen deshalb auch im Speicher nebeneinander liegen sollten (und es sonst,&lt;br /&gt;
je nach dem wie der konkrete Compiler Daten standardmäßig anordnet, zu Problemen kommen kann).&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
var f: file; bytes: longword;&lt;br /&gt;
begin&lt;br /&gt;
  assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
  // type&lt;br /&gt;
  seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
  // w, h, bpp&lt;br /&gt;
  seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
  result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
  // data&lt;br /&gt;
  bytes := result.w * result.h * result.bpp;&lt;br /&gt;
  getmem(result.data, bytes);&lt;br /&gt;
  seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
  close(f);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
var&lt;br /&gt;
  tex: TTGAFile;&lt;br /&gt;
  glFormat: longword;&lt;br /&gt;
begin&lt;br /&gt;
  tex := LoadTGA(filename);&lt;br /&gt;
  if tex.iType = 2 then&lt;br /&gt;
  begin&lt;br /&gt;
    glGenTextures(1, @TexID);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
    if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
      else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
    FreeMem(tex.data);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20683</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20683"/>
				<updated>2007-08-05T21:20:09Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Definition eines Datentyps */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
type&lt;br /&gt;
  TTGAFile = packed record&lt;br /&gt;
    iType: byte;  // should be 2&lt;br /&gt;
     w, h: word;  // Width, Height&lt;br /&gt;
      bpp: byte;  // Byte per Pixel&lt;br /&gt;
     data: ^byte; // Pixels, dynamic length&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Packed&amp;quot; ist an dieser Stelle wichtig, da wir w,h und bpp in einem einzigen Zug einlesen&lt;br /&gt;
werden und dese Variablen deshalb auch im Speicher nebeneinander liegen sollten (und es sonst,&lt;br /&gt;
je nach dem wie der konkrete Compiler Daten standardmäßig anordnet, zu Problemen kommen kann).&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
var f: file; bytes: longword;&lt;br /&gt;
begin&lt;br /&gt;
  assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
  // type&lt;br /&gt;
  seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
  // w, h, bpp&lt;br /&gt;
  seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
  result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
  // data&lt;br /&gt;
  bytes := result.w * result.h * result.bpp;&lt;br /&gt;
  getmem(result.data, bytes);&lt;br /&gt;
  seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
  close(f);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
var&lt;br /&gt;
  tex: TTGAFile;&lt;br /&gt;
  glFormat: longword;&lt;br /&gt;
begin&lt;br /&gt;
  tex := LoadTGA(filename);&lt;br /&gt;
  if tex.iType = 2 then&lt;br /&gt;
  begin&lt;br /&gt;
    glGenTextures(1, @TexID);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
    if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
      else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
    FreeMem(tex.data);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20682</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20682"/>
				<updated>2007-08-05T21:01:17Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
type&lt;br /&gt;
  TTGAFile = packed record&lt;br /&gt;
    iType: byte;  // should be 2&lt;br /&gt;
     w, h: word;  // Width, Height&lt;br /&gt;
      bpp: byte;  // Byte per Pixel&lt;br /&gt;
     data: ^byte; // Pixels, dynamic length&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
var f: file; bytes: longword;&lt;br /&gt;
begin&lt;br /&gt;
  assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
  // type&lt;br /&gt;
  seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
  // w, h, bpp&lt;br /&gt;
  seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
  result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
  // data&lt;br /&gt;
  bytes := result.w * result.h * result.bpp;&lt;br /&gt;
  getmem(result.data, bytes);&lt;br /&gt;
  seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
  close(f);&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
var&lt;br /&gt;
  tex: TTGAFile;&lt;br /&gt;
  glFormat: longword;&lt;br /&gt;
begin&lt;br /&gt;
  tex := LoadTGA(filename);&lt;br /&gt;
  if tex.iType = 2 then&lt;br /&gt;
  begin&lt;br /&gt;
    glGenTextures(1, @TexID);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
    if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
      else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
    FreeMem(tex.data);&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20681</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20681"/>
				<updated>2007-08-05T20:59:45Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Landkarte und Kompass - oder besser GPS :D */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS? ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  type&lt;br /&gt;
    TTGAFile = packed record&lt;br /&gt;
      iType: byte; // should be 2&lt;br /&gt;
       w, h: word; // Width, Height&lt;br /&gt;
        bpp: byte; // Byte per Pixel&lt;br /&gt;
      data: ^byte; // Pixels, dynamic length&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
  var f: file; bytes: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
    // type&lt;br /&gt;
    seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
    // w, h, bpp&lt;br /&gt;
    seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
    result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
    // data&lt;br /&gt;
    bytes := result.w * result.h * result.bpp;&lt;br /&gt;
    getmem(result.data, bytes);&lt;br /&gt;
    seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
    close(f);&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
  var&lt;br /&gt;
    tex: TTGAFile;&lt;br /&gt;
    glFormat: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    tex := LoadTGA(filename);&lt;br /&gt;
    if tex.iType = 2 then&lt;br /&gt;
    begin&lt;br /&gt;
      glGenTextures(1, @TexID);&lt;br /&gt;
      glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
      if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
        else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
      FreeMem(tex.data);&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20680</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20680"/>
				<updated>2007-08-05T20:58:58Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS :D ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  type&lt;br /&gt;
    TTGAFile = packed record&lt;br /&gt;
      iType: byte; // should be 2&lt;br /&gt;
       w, h: word; // Width, Height&lt;br /&gt;
        bpp: byte; // Byte per Pixel&lt;br /&gt;
      data: ^byte; // Pixels, dynamic length&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
  var f: file; bytes: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
    // type&lt;br /&gt;
    seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
    // w, h, bpp&lt;br /&gt;
    seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
    result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
    // data&lt;br /&gt;
    bytes := result.w * result.h * result.bpp;&lt;br /&gt;
    getmem(result.data, bytes);&lt;br /&gt;
    seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
    close(f);&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
  var&lt;br /&gt;
    tex: TTGAFile;&lt;br /&gt;
    glFormat: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    tex := LoadTGA(filename);&lt;br /&gt;
    if tex.iType = 2 then&lt;br /&gt;
    begin&lt;br /&gt;
      glGenTextures(1, @TexID);&lt;br /&gt;
      glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
      if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
        else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
      FreeMem(tex.data);&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20679</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20679"/>
				<updated>2007-08-05T20:58:48Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS :D ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  type&lt;br /&gt;
    TTGAFile = packed record&lt;br /&gt;
      iType: byte; // should be 2&lt;br /&gt;
       w, h: word; // Width, Height&lt;br /&gt;
        bpp: byte; // Byte per Pixel&lt;br /&gt;
      data: ^byte; // Pixels, dynamic length&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
  var f: file; bytes: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
    // type&lt;br /&gt;
    seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
    // w, h, bpp&lt;br /&gt;
    seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
    result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
    // data&lt;br /&gt;
    bytes := result.w * result.h * result.bpp;&lt;br /&gt;
    getmem(result.data, bytes);&lt;br /&gt;
    seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
    close(f);&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
  var&lt;br /&gt;
    tex: TTGAFile;&lt;br /&gt;
    glFormat: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    tex := LoadTGA(filename);&lt;br /&gt;
    if tex.iType = 2 then&lt;br /&gt;
    begin&lt;br /&gt;
      glGenTextures(1, @TexID);&lt;br /&gt;
      glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
      if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
        else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
      FreeMem(tex.data);&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20678</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20678"/>
				<updated>2007-08-05T20:58:26Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Ein Wort zur Kompression */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS :D ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  type&lt;br /&gt;
    TTGAFile = packed record&lt;br /&gt;
      iType: byte; // should be 2&lt;br /&gt;
       w, h: word; // Width, Height&lt;br /&gt;
        bpp: byte; // Byte per Pixel&lt;br /&gt;
      data: ^byte; // Pixels, dynamic length&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
  var f: file; bytes: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
    // type&lt;br /&gt;
    seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
    // w, h, bpp&lt;br /&gt;
    seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
    result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
    // data&lt;br /&gt;
    bytes := result.w * result.h * result.bpp;&lt;br /&gt;
    getmem(result.data, bytes);&lt;br /&gt;
    seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
    close(f);&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
  var&lt;br /&gt;
    tex: TTGAFile;&lt;br /&gt;
    glFormat: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    tex := LoadTGA(filename);&lt;br /&gt;
    if tex.iType = 2 then&lt;br /&gt;
    begin&lt;br /&gt;
      glGenTextures(1, @TexID);&lt;br /&gt;
      glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
      if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
        else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
      FreeMem(tex.data);&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20677</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20677"/>
				<updated>2007-08-05T20:57:13Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Ein Wort zur Kompression */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS :D ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Ein Wort zur Kompression ==&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
Wer ein gutes Format mit Kompression sucht, ist mit Microsoft's DDS sehr gut&lt;br /&gt;
beraten, da es ein Format ist, welches speziell für Texturen gedacht ist. Es&lt;br /&gt;
bringt diverse Features wie MipMaps, 3D-Texturen, Cubemaps u.v.m. unterstützt.&lt;br /&gt;
Zusätzlich unterstützen DDS-Dateien auch DXT-Kompression welche von der Grafikhardware&lt;br /&gt;
dekomprimiert wird und somit Bandbreite spart - was die Render- und Ladegeschwindigkeit&lt;br /&gt;
erhöht.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  type&lt;br /&gt;
    TTGAFile = packed record&lt;br /&gt;
      iType: byte; // should be 2&lt;br /&gt;
       w, h: word; // Width, Height&lt;br /&gt;
        bpp: byte; // Byte per Pixel&lt;br /&gt;
      data: ^byte; // Pixels, dynamic length&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
  var f: file; bytes: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
    // type&lt;br /&gt;
    seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
    // w, h, bpp&lt;br /&gt;
    seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
    result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
    // data&lt;br /&gt;
    bytes := result.w * result.h * result.bpp;&lt;br /&gt;
    getmem(result.data, bytes);&lt;br /&gt;
    seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
    close(f);&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
  var&lt;br /&gt;
    tex: TTGAFile;&lt;br /&gt;
    glFormat: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    tex := LoadTGA(filename);&lt;br /&gt;
    if tex.iType = 2 then&lt;br /&gt;
    begin&lt;br /&gt;
      glGenTextures(1, @TexID);&lt;br /&gt;
      glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
      if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
        else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
      FreeMem(tex.data);&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20676</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20676"/>
				<updated>2007-08-05T20:56:09Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: /* Ein Wort zur Kompression */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS :D ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Ein Wort zur Kompression ==&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
Wer ein gutes Format mit kompression sucht, ist mit Microsoft's DDS sehr gut&lt;br /&gt;
beraten, da es ein Format ist, welches speziell für Texturen gedacht ist. Es&lt;br /&gt;
bringt diverse Features wie MipMaps, 3D-Texturen, Cubemaps u.v.m. unterstützt.&lt;br /&gt;
Zusätzlich unterstützen DDS-Dateien auch DXT-Kompression welche von der Grafikhardware&lt;br /&gt;
dekomprimiert wird und somit Bandbreite spart - was die Render- und Ladegeschwindigkeit&lt;br /&gt;
erhöht.&lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  type&lt;br /&gt;
    TTGAFile = packed record&lt;br /&gt;
      iType: byte; // should be 2&lt;br /&gt;
       w, h: word; // Width, Height&lt;br /&gt;
        bpp: byte; // Byte per Pixel&lt;br /&gt;
      data: ^byte; // Pixels, dynamic length&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
  var f: file; bytes: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
    // type&lt;br /&gt;
    seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
    // w, h, bpp&lt;br /&gt;
    seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
    result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
    // data&lt;br /&gt;
    bytes := result.w * result.h * result.bpp;&lt;br /&gt;
    getmem(result.data, bytes);&lt;br /&gt;
    seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
    close(f);&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
  var&lt;br /&gt;
    tex: TTGAFile;&lt;br /&gt;
    glFormat: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    tex := LoadTGA(filename);&lt;br /&gt;
    if tex.iType = 2 then&lt;br /&gt;
    begin&lt;br /&gt;
      glGenTextures(1, @TexID);&lt;br /&gt;
      glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
      if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
        else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
      FreeMem(tex.data);&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20675</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20675"/>
				<updated>2007-08-05T20:46:42Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS :D ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Ein Wort zur Kompression ==&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, diein den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
Als Alternative zu komprimierten TGA-Dateien bietet sich Microsoft's DDS-Format&lt;br /&gt;
an, da es eine Komprimierung unterstützt, die direkt von der Grafikkarte gelesen&lt;br /&gt;
werden kann - d.h. die Dateien werden kleiner, zusätzlich spart man Bandbreite&lt;br /&gt;
und erhöht so effektiv die Lade- und Rendergeschwindigkeit.&lt;br /&gt;
&lt;br /&gt;
Alternativ lassen sich &amp;quot;normale&amp;quot; TGA's auch manuell mit einer effektiveren&lt;br /&gt;
Methode als RLE packen (Stichwort: Virtuelles Dateisystem).&lt;br /&gt;
Da dieses Tutorial aber eher für Anfänger gedacht ist gehe ich nun nicht weiter&lt;br /&gt;
darauf ein  :-) &lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  type&lt;br /&gt;
    TTGAFile = packed record&lt;br /&gt;
      iType: byte; // should be 2&lt;br /&gt;
       w, h: word; // Width, Height&lt;br /&gt;
        bpp: byte; // Byte per Pixel&lt;br /&gt;
      data: ^byte; // Pixels, dynamic length&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
  var f: file; bytes: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
    // type&lt;br /&gt;
    seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
    // w, h, bpp&lt;br /&gt;
    seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
    result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
    // data&lt;br /&gt;
    bytes := result.w * result.h * result.bpp;&lt;br /&gt;
    getmem(result.data, bytes);&lt;br /&gt;
    seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
    close(f);&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
  var&lt;br /&gt;
    tex: TTGAFile;&lt;br /&gt;
    glFormat: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    tex := LoadTGA(filename);&lt;br /&gt;
    if tex.iType = 2 then&lt;br /&gt;
    begin&lt;br /&gt;
      glGenTextures(1, @TexID);&lt;br /&gt;
      glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
      if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
        else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
      FreeMem(tex.data);&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20674</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20674"/>
				<updated>2007-08-05T20:40:49Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= TGA Dateien Laden =&lt;br /&gt;
&lt;br /&gt;
== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS :D ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Ein Wort zur Kompression ==&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, diein den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
Als Alternative zu komprimierten TGA-Dateien bietet sich Microsoft's DDS-Format&lt;br /&gt;
an, da es eine Komprimierung unterstützt, die direkt von der Grafikkarte gelesen&lt;br /&gt;
werden kann - d.h. die Dateien werden kleiner, zusätzlich spart man Bandbreite&lt;br /&gt;
und erhöht so effektiv die Lade- und Rendergeschwindigkeit.&lt;br /&gt;
&lt;br /&gt;
Alternativ lassen sich &amp;quot;normale&amp;quot; TGA's auch manuell mit einer effektiveren&lt;br /&gt;
Methode als RLE packen (Stichwort: Virtuelles Dateisystem).&lt;br /&gt;
Da dieses Tutorial aber eher für Anfänger gedacht ist gehe ich nun nicht weiter&lt;br /&gt;
darauf ein  :-) &lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  type&lt;br /&gt;
    TTGAFile = packed record&lt;br /&gt;
      iType: byte; // should be 2&lt;br /&gt;
       w, h: word; // Width, Height&lt;br /&gt;
        bpp: byte; // Byte per Pixel&lt;br /&gt;
      data: ^byte; // Pixels, dynamic length&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
  var f: file; bytes: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
    // type&lt;br /&gt;
    seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
    // w, h, bpp&lt;br /&gt;
    seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
    result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
    // data&lt;br /&gt;
    bytes := result.w * result.h * result.bpp;&lt;br /&gt;
    getmem(result.data, bytes);&lt;br /&gt;
    seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
    close(f);&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
  var&lt;br /&gt;
    tex: TTGAFile;&lt;br /&gt;
    glFormat: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    tex := LoadTGA(filename);&lt;br /&gt;
    if tex.iType = 2 then&lt;br /&gt;
    begin&lt;br /&gt;
      glGenTextures(1, @TexID);&lt;br /&gt;
      glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
      if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
        else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
      FreeMem(tex.data);&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20673</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20673"/>
				<updated>2007-08-05T20:39:31Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= TGA Dateien Laden =&lt;br /&gt;
&lt;br /&gt;
== TGA Dateien - Warum? ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision&lt;br /&gt;
Bilddateien mit der allseits bekannten Endung &amp;quot;.tga&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller&lt;br /&gt;
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen&lt;br /&gt;
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren&lt;br /&gt;
Ordnung vor.&lt;br /&gt;
&lt;br /&gt;
== Landkarte und Kompass - oder besser GPS :D ==&lt;br /&gt;
&lt;br /&gt;
Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist.&lt;br /&gt;
Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht&lt;br /&gt;
erraten - wir sind also auf fremde Dokumentation angewiesen.&lt;br /&gt;
Eine ganze Sammlung solcher &amp;quot;Datei-Spickzettel&amp;quot; findet man auf der Internetseite&lt;br /&gt;
http://www.wotsit.org/.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch&lt;br /&gt;
überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von&lt;br /&gt;
TGA-Dateien zu geben scheint.&lt;br /&gt;
&lt;br /&gt;
Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es&lt;br /&gt;
das gebräuchlichste Format ist.&lt;br /&gt;
&lt;br /&gt;
== Ein Wort zur Kompression ==&lt;br /&gt;
&lt;br /&gt;
Komprimierte TGA-Dateien lassen wir außen vor,&lt;br /&gt;
da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei&lt;br /&gt;
einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die&lt;br /&gt;
RLE-Kompression, diein den TGA-Dateien zum Einsatz kommt, ist vergleichsweise&lt;br /&gt;
schlecht.&lt;br /&gt;
&lt;br /&gt;
Als Alternative zu komprimierten TGA-Dateien bietet sich Microsoft's DDS-Format&lt;br /&gt;
an, da es eine Komprimierung unterstützt, die direkt von der Grafikkarte gelesen&lt;br /&gt;
werden kann - d.h. die Dateien werden kleiner, zusätzlich spart man Bandbreite&lt;br /&gt;
und erhöht so effektiv die Lade- und Rendergeschwindigkeit.&lt;br /&gt;
&lt;br /&gt;
Alternativ lassen sich &amp;quot;normale&amp;quot; TGA's auch manuell mit einer effektiveren&lt;br /&gt;
Methode als RLE packen (Stichwort: Virtuelles Dateisystem).&lt;br /&gt;
Da dieses Tutorial aber eher für Anfänger gedacht ist gehe ich nun nicht weiter&lt;br /&gt;
darauf ein  :-) &lt;br /&gt;
&lt;br /&gt;
== Definition eines Datentyps ==&lt;br /&gt;
&lt;br /&gt;
Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.&lt;br /&gt;
   Width of Image&lt;br /&gt;
  Height of Image&lt;br /&gt;
 Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal&lt;br /&gt;
                        vorhanden ist.&lt;br /&gt;
 Identification Field - die eigentlichen Pixeldaten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Damit haben wir alles benötigte zusammen und können diese Informationen schon&lt;br /&gt;
mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der &amp;quot;Length&amp;quot;-Spalte&lt;br /&gt;
in unserem Dokument von wotsit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  type&lt;br /&gt;
    TTGAFile = packed record&lt;br /&gt;
      iType: byte; // should be 2&lt;br /&gt;
       w, h: word; // Width, Height&lt;br /&gt;
        bpp: byte; // Byte per Pixel&lt;br /&gt;
      data: ^byte; // Pixels, dynamic length&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Die Datei laden ==&lt;br /&gt;
&lt;br /&gt;
Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen&lt;br /&gt;
sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit&lt;br /&gt;
Seek positioniert (siehe &amp;quot;Offset&amp;quot;-Spalte in unserem Dokument) und mit Blockread&lt;br /&gt;
gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb&lt;br /&gt;
können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination&lt;br /&gt;
eingelesen werden. Da wir mit &amp;quot;Bit pro Pixel&amp;quot; wenig anfangen können teilen wir&lt;br /&gt;
bpp anschließend durch 8 und erhalten &amp;quot;Bytes pro Pixel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten&lt;br /&gt;
sich natürlich auch Streams an. Allerdings ist dies für diesen&lt;br /&gt;
konkreten Fall nicht von Belang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  function LoadTGA(const filename: string): TTGAFile;&lt;br /&gt;
  var f: file; bytes: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    assign(f, filename); reset(f, 1);&lt;br /&gt;
&lt;br /&gt;
    // type&lt;br /&gt;
    seek(f, 2); blockread(f, result.iType, 1);&lt;br /&gt;
&lt;br /&gt;
    // w, h, bpp&lt;br /&gt;
    seek(f, 12); blockread(f, result.w, 5);&lt;br /&gt;
    result.bpp := result.bpp div 8;&lt;br /&gt;
&lt;br /&gt;
    // data&lt;br /&gt;
    bytes := result.w * result.h * result.bpp;&lt;br /&gt;
    getmem(result.data, bytes);&lt;br /&gt;
    seek(f, 18); blockread(f, result.data^, bytes);&lt;br /&gt;
&lt;br /&gt;
    close(f);&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis&lt;br /&gt;
dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein.&lt;br /&gt;
Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die&lt;br /&gt;
Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob&lt;br /&gt;
&amp;quot;bpp&amp;quot; den Wert 3 oder 4 erhalten hat).&lt;br /&gt;
&lt;br /&gt;
== Datei rein - Textur raus ==&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Erzeugung des Texturobjektes gleicht dem &amp;quot;SDL-Weg&amp;quot;. Nur wird&lt;br /&gt;
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen&lt;br /&gt;
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  FreeMem(TGARecord.data);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
wieder freizugeben!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  procedure TGATexture(const filename: string; var TexID: longword);&lt;br /&gt;
  var&lt;br /&gt;
    tex: TTGAFile;&lt;br /&gt;
    glFormat: longword;&lt;br /&gt;
  begin&lt;br /&gt;
    tex := LoadTGA(filename);&lt;br /&gt;
    if tex.iType = 2 then&lt;br /&gt;
    begin&lt;br /&gt;
      glGenTextures(1, @TexID);&lt;br /&gt;
      glBindTexture(GL_TEXTURE_2D, TexID);&lt;br /&gt;
&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;br /&gt;
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;
&lt;br /&gt;
      if tex.bpp = 3 then glFormat := GL_BGR&lt;br /&gt;
        else glFormat := GL_BGRA;&lt;br /&gt;
&lt;br /&gt;
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,&lt;br /&gt;
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);&lt;br /&gt;
&lt;br /&gt;
      FreeMem(tex.data);&lt;br /&gt;
    end;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch&lt;br /&gt;
viel Spaß und Erfolg bei euren zukünftigen Projekten.&lt;br /&gt;
&lt;br /&gt;
  Gruß Waran.&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20670</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20670"/>
				<updated>2007-08-04T01:23:00Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20669</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20669"/>
				<updated>2007-08-04T00:59:33Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: Die Seite wurde geleert.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20668</id>
		<title>TGA Bilder laden</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=TGA_Bilder_laden&amp;diff=20668"/>
				<updated>2007-08-04T00:59:02Z</updated>
		
		<summary type="html">&lt;p&gt;Waran: Die Seite wurde neu angelegt: [Inhalt wird von Phobeus gesichtet]&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[Inhalt wird von Phobeus gesichtet]&lt;/div&gt;</summary>
		<author><name>Waran</name></author>	</entry>

	</feed>