Glbitmap loader: Unterschied zwischen den Versionen
() |
(Codebeispiele und Erläuterungen sollten mit der aktuellen Version von glBitmap funktionieren) |
||
(16 dazwischenliegende Versionen von 9 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
− | {{ | + | {{Unvollständig}} |
= glBitmap - Texturenloader = | = glBitmap - Texturenloader = | ||
Zeile 7: | Zeile 7: | ||
Gefunden werden kann sie entweder im DGL-SDK oder auf der [http://www.dev-center.de/index.php?cat=header&file=glbitmap Webseite] von [[Benutzer:Lossy eX|Lossy eX]]. | Gefunden werden kann sie entweder im DGL-SDK oder auf der [http://www.dev-center.de/index.php?cat=header&file=glbitmap Webseite] von [[Benutzer:Lossy eX|Lossy eX]]. | ||
+ | |||
+ | {{Hinweis|Die glBitmap wird in Zukunft von der DelphiGL Community weiterentwickelt und gepflegt. Eine aktuelle Version kann entweder vom [[http://git.delphigl.com/?p=glBitmap.git;a=summary DGL git Repository]] oder unter folgenden Links bezogen werden: | ||
+ | *[[http://git.delphigl.com/?p=glBitmap.git;a=snapshot;h=refs/heads/master;sf=tgz aktuelle Release-Version]] | ||
+ | *[[http://git.delphigl.com/?p=glBitmap.git;a=snapshot;h=refs/heads/unstable;sf=tgz aktuelle Entwickler-Version]]}} | ||
== Allgemeines und Features == | == Allgemeines und Features == | ||
Zeile 12: | Zeile 16: | ||
Dieser Artikel geht nicht darauf ein was zu tun ist um in OpenGL eine Textur darstellen zu können. In ihm werde ich lediglich darauf eingehen was man tun kann und muss um mittels der glBitmap eine Textur laden zu können. Außerdem werden die verfügbaren Einstellungen und ihre Wirkungen erklärt. Für alles andere wäre es besser das [[Tutorial_lektion4|Tutorial über Texturen]] durchzulesen. | Dieser Artikel geht nicht darauf ein was zu tun ist um in OpenGL eine Textur darstellen zu können. In ihm werde ich lediglich darauf eingehen was man tun kann und muss um mittels der glBitmap eine Textur laden zu können. Außerdem werden die verfügbaren Einstellungen und ihre Wirkungen erklärt. Für alles andere wäre es besser das [[Tutorial_lektion4|Tutorial über Texturen]] durchzulesen. | ||
− | Der wichtigste Unterschied zu den bestehenden Loadern bringe ich gleich einmal als erstes. Die glBitmap legt die Texturen anders im Speicher ab. Um zwar im speziellen meine ich damit die 2D Texturen. Bei bestehenden Loadern werden diese Texturen mit der untersten Zeile zu erst im Speicher abgelegt. In der glBitmap werden sie aber mit | + | Der wichtigste Unterschied zu den bestehenden Loadern bringe ich gleich einmal als erstes. Die glBitmap legt die Texturen anders im Speicher ab. Um zwar im speziellen meine ich damit die 2D Texturen. Bei bestehenden Loadern werden diese Texturen mit der untersten Zeile zu erst im Speicher abgelegt. In der glBitmap werden sie aber mit '''der obersten Zeile zu erst abgelegt'''. Man könnte auch sagen, dass sie bei den bestehenden Loadern auf dem Kopf stehen. Beide Möglichkeiten funktionieren und sind korrekt. Das Einzige was sich in diesem Zusammenhang ändert ist die [[glTexCoord| Adressierung der Texturen]]. Sie müssen entsprechend geändert werden. Sollte das nicht möglich sein so besteht mit der Methode ''FlipVert'' die Möglichkeit die Textur zu spiegeln. Näheres in Abschnitt [[Glbitmap_loader#Texturen_konfigurieren|Texturen konfigurieren]]. Im Normalfall sollte das andere Verhalten nicht zu Problemen führen sondern die Arbeit damit vereinfachen und verständlicher gestalten. |
Die glBitmap holt sich ihre Daten ausschließlich aus Streams. Das zugrundeliegende Format wird automatisch anhand des Inhaltes der Daten ausgewählt. Es besteht also kein Notwendigkeit daran, dass der Entwickler wissen muss um welches Format es sich in Wirklichkeit handelt. | Die glBitmap holt sich ihre Daten ausschließlich aus Streams. Das zugrundeliegende Format wird automatisch anhand des Inhaltes der Daten ausgewählt. Es besteht also kein Notwendigkeit daran, dass der Entwickler wissen muss um welches Format es sich in Wirklichkeit handelt. | ||
Zeile 20: | Zeile 24: | ||
* Windows Bitmaps (*.bmp) Wird Automatisch auf 24 Bit gesetzt sollte es kleiner sein. | * Windows Bitmaps (*.bmp) Wird Automatisch auf 24 Bit gesetzt sollte es kleiner sein. | ||
* JPEG (*.jpg) | * JPEG (*.jpg) | ||
− | * TARGA (*.tga) unkomprimiert, 24 und 32 Bit. Ursprung unten links oder oben links | + | * TARGA (*.tga) komprimiert und unkomprimiert, 24 und 32 Bit. Ursprung unten links oder oben links |
* Portable Network Graphics (*.png) 24 Bit mit oder ohne Alphakanal. Es wird eine zusätzliche Bibliothek benötigt. | * Portable Network Graphics (*.png) 24 Bit mit oder ohne Alphakanal. Es wird eine zusätzliche Bibliothek benötigt. | ||
+ | * [[DDS|Direct Draw Surfaces]] (*.dds) 8,16,24,32 Bit DXT1,DXT3,DXT5, keine Cubemaps, keine Mipmaps | ||
Es besteht auch die Möglichkeit die Bilddaten dynamisch generieren zu lassen oder sie aus einer Instanz der Klasse TBitmap zu laden. Diese Instanz muss dann aber als Pixelformat 24 oder 32 Bit haben. | Es besteht auch die Möglichkeit die Bilddaten dynamisch generieren zu lassen oder sie aus einer Instanz der Klasse TBitmap zu laden. Diese Instanz muss dann aber als Pixelformat 24 oder 32 Bit haben. | ||
− | Um | + | |
+ | Um PNGs laden zu können muss eine externe Bibliothek namens [http://pngdelphi.sourceforge.net/ PNGImage] eingebunden und das Define ''pngimage'' hinzugefügt werden. Alternativ dazu kann das Define auch in der glBitmap auskommentiert werden. | ||
== Texturen laden == | == Texturen laden == | ||
− | Das Laden von Texturen kann auf 2 Weg geschehen. Bei Beiden ist aber zu beachten, dass | + | Das Laden von Texturen kann auf 2 Weg geschehen. Bei Beiden ist aber zu beachten, dass '''OpenGL richtig initialisiert worden sein muss''' bevor man eine Textur erzeugen kann. Was noch sehr wichtig ist. Es sollte niemals eine Textur bei jedem Rendervorgang angelegt und freigegeben werden! Es ist viel effektiver, wenn man die Textur zu beginn lädt und so lange im Speicher behält bis sie nicht mehr benötigt wird. Wenn man sie erst später benötigt, dann kann man sie auch später noch nachladen aber auch dann sollte sie so lange wie nötig existieren. |
=== Procedural === | === Procedural === | ||
Zeile 34: | Zeile 40: | ||
Hierbei genügt der Aufruf einer einzelnen Methode um eine Textur zu laden und diese an OpenGL zu übergeben. Allerdings auf Grund der Einfachheit steht nur das Laden aus Dateien oder Ressourcen zur Verfügung. Zusätzliche Funktionalität können dabei nicht benutzt werden. Generell empfehle ich immer die Objekt orientierte Variante. | Hierbei genügt der Aufruf einer einzelnen Methode um eine Textur zu laden und diese an OpenGL zu übergeben. Allerdings auf Grund der Einfachheit steht nur das Laden aus Dateien oder Ressourcen zur Verfügung. Zusätzliche Funktionalität können dabei nicht benutzt werden. Generell empfehle ich immer die Objekt orientierte Variante. | ||
− | + | '''var''' | |
Texture: TGLuint; | Texture: TGLuint; | ||
− | LoadTexture('<font color="blue">myTex\Wall.bmp</font>', Texture, False); | + | LoadTexture('<font color="blue">myTex\Wall.bmp</font>', Texture, False); ''<font color="green">// Datei laden</font>'' |
− | Damit wird die Datei | + | Damit wird die Datei ''Wall.bmp'' geladen und die TextureID wird in die Variable ''Texture'' geschrieben. Wenn die Textur dann benötigt wird, dann muss man diese entsprechend mit [[glBindTexture]] binden und das benötigte Texturtarget mit [[glEnable]] aktivieren. Das Target ist üblicherweise ''GL_TEXTURE_2D''. Wenn die Textur nicht mehr benötigt wird sollte sie mit [[glDeleteTextures]] auch wieder frei gegeben werden. |
=== Objekt orientiert === | === Objekt orientiert === | ||
Zeile 48: | Zeile 54: | ||
Als Erstes müssen wir eine Instanz der Texturenklasse definieren. Dies tun wir vorzugsweise als Member des Fensters auf dem wir zeichnen. | Als Erstes müssen wir eine Instanz der Texturenklasse definieren. Dies tun wir vorzugsweise als Member des Fensters auf dem wir zeichnen. | ||
− | TForm1 = | + | TForm1 = '''class'''(TForm) |
− | + | ''<font color="green">// Verschiene Elemente und Events des Fensters</font>'' | |
− | + | '''private''' | |
− | fTexture: TglBitmap2D; | + | fTexture: TglBitmap2D; ''<font color="green">// Instanz unserer Textur</font>'' |
− | + | '''end;''' | |
Nun müssen wir der Textur noch Leben einhauchen. | Nun müssen wir der Textur noch Leben einhauchen. | ||
− | + | '''procedure''' TForm1.FormShow(Sender: TObject); | |
− | + | '''begin''' | |
− | + | ''<font color="green">// OpenGL Initialisieren</font>'' | |
− | fTexture := TglBitmap2D.Create; | + | fTexture := TglBitmap2D.Create; ''<font color="green">// Instanz der Klasse erstellen</font>'' |
− | fTexture.LoadFromFile('<font color="blue">myTex\Wall.bmp</font>'); | + | fTexture.LoadFromFile('<font color="blue">myTex\Wall.bmp</font>'); ''<font color="green">// Datei laden</font>'' |
− | fTexture.GenTexture; | + | fTexture.GenTexture; ''<font color="green">// geladene Textur an OpenGL übergeben</font>'' |
− | + | '''end;''' | |
Für Schreibfaule besteht auch noch die Möglichkeit in einem überladenen Konstruktor einem Dateinamen anzugeben. | Für Schreibfaule besteht auch noch die Möglichkeit in einem überladenen Konstruktor einem Dateinamen anzugeben. | ||
− | fTexture := TglBitmap2D.Create('<font color="blue">myTex\Wall.bmp</font>'); | + | fTexture := TglBitmap2D.Create('<font color="blue">myTex\Wall.bmp</font>'); ''<font color="green">// Instanz erstellen und Datei laden</font>'' |
− | fTexture.GenTexture; | + | fTexture.GenTexture; ''<font color="green">// geladene Textur an OpenGL übergeben</font>'' |
In GenTexture wird im Normalfall immer überprüft ob die Textur eine gültige Größe hat. Sollte diese Überprüfung nicht gewünscht sein so kann man diese mit einem optionalen Parameter deaktivieren. Diese Überprüfung testet auch ob die Texturgröße eine Potenz von 2 ist (1, 2, 4, 8, 16 usw.) und wenn nicht ob solche Texturen von dem Rendercontex unterstützt werden. | In GenTexture wird im Normalfall immer überprüft ob die Textur eine gültige Größe hat. Sollte diese Überprüfung nicht gewünscht sein so kann man diese mit einem optionalen Parameter deaktivieren. Diese Überprüfung testet auch ob die Texturgröße eine Potenz von 2 ist (1, 2, 4, 8, 16 usw.) und wenn nicht ob solche Texturen von dem Rendercontex unterstützt werden. | ||
− | fTexture.GenTexture(False); | + | fTexture.GenTexture(False); ''<font color="green">// Es wird keine Prüfung der Texturgröße vorgenommen.</font>'' |
Wenn kein Fehler aufgetreten ist und alles so funktioniert hat wie es sein sollte, dann kann die Textur benutzt werden. | Wenn kein Fehler aufgetreten ist und alles so funktioniert hat wie es sein sollte, dann kann die Textur benutzt werden. | ||
− | fTexture.Bind; | + | fTexture.Bind; ''<font color="green">// Textur und entsprechendes Target wird aktiviert.</font>'' |
Sollte es nicht gewünscht werden, dass auch gleichzeitig das Target aktiviert wird so muss Bind mit dem Parameter False aufgerufen werden. Damit wird nur noch die Textur gebunden. | Sollte es nicht gewünscht werden, dass auch gleichzeitig das Target aktiviert wird so muss Bind mit dem Parameter False aufgerufen werden. Damit wird nur noch die Textur gebunden. | ||
Zeile 84: | Zeile 90: | ||
Wenn die Anwendung beendet wird müssen wir auch hier unsere Textur wieder frei geben. Dies geschieht so. | Wenn die Anwendung beendet wird müssen wir auch hier unsere Textur wieder frei geben. Dies geschieht so. | ||
− | fTexture.Free; | + | fTexture.Free; ''<font color="green">// Textur wieder frei geben.</font>'' |
− | Hierbei ist zu beachten, dass dies nach Möglichkeit | + | Hierbei ist zu beachten, dass dies nach Möglichkeit '''vor dem Löschen des Rendercontex''' geschehen sollte, da so die Textur zu einem sinnvollem Zeitpunkt gelöscht werden kann. Wenn der Rendercontex nicht mehr existiert, so sollte die Textur auch nicht mehr existieren und wenn man dann noch einmal versucht sie zu löschen so könnte es zu Problemen führen. Bisher ist mir kein Fall bekannt aber ausschließen kann man es auch nicht. Also zu erst immer die Texturen frei geben und anschließend des Rendercontex löschen. |
== Das leidige Thema Alphakanal == | == Das leidige Thema Alphakanal == | ||
− | Es gibt zwei Möglichkeiten einen Alphakanal in eine Textur zu integrieren. Der einfachste und schnellste Weg ist, wenn sich dieser Kanal von Beginn an in | + | Es gibt zwei Möglichkeiten einen Alphakanal in eine Textur zu integrieren. Der einfachste und schnellste Weg ist, wenn sich dieser Kanal von Beginn an in der Textur befindet. Dazu wird ein Grafikprogramm benötigt welches so etwas unterstützt und ein Dateiformat welches das Speichern eines Alphakanals ermöglicht. Ein solches Programm ist zum Beispiel [http://www.gimp.org/ Gimp]. Als mögliche Dateiformate kämen TGAs und PNGs in Frage. Alle anderen Formate unterstützen keinen Alphakanal. Sollte man sich für diesen Weg entscheiden so genügt es dann, ganz normal die Textur zu laden und zu benutzen und schon besitzen wir einen funktionierenden Alphakanal. Ich persönlich empfehle bis auf wenige Ausnahmen immer diesen Weg. |
− | Nichts desto trotz habe ich ja gesagt, dass es noch eine Möglichkeit gibt. Bei diesem Weg haben wir Alphakanal und RGB Daten in zwei getrennten Bildern vorliegen. Aber wie bekommen wir das jetzt in ein Bild? Dafür bietet die glBitmap die Möglichkeit einen Alphakanal aus einem separaten Bild zu laden. Zu Beginn benötigen wir jedoch wieder eine Instanz der Klasse und es müssen schon normale Bilddaten geladen worden sein. Und ganz Wichtig damit es überhaupt funktioniert. | + | Nichts desto trotz habe ich ja gesagt, dass es noch eine Möglichkeit gibt. Bei diesem Weg haben wir Alphakanal und RGB Daten in zwei getrennten Bildern vorliegen. Aber wie bekommen wir das jetzt in ein Bild? Dafür bietet die glBitmap die Möglichkeit einen Alphakanal aus einem separaten Bild zu laden. Zu Beginn benötigen wir jedoch wieder eine Instanz der Klasse und es müssen schon normale Bilddaten geladen worden sein. Und ganz Wichtig damit es überhaupt funktioniert. '''Der Aufruf muss vor ''GenTexture'' stattfinden, da sonst die Textur bereits an OpenGL übergeben wurde.''' |
− | fTexture.AddAlphaFromFile('<font color="blue">myTex\Wall_alpha.bmp</font>'); | + | fTexture.AddAlphaFromFile('<font color="blue">myTex\Wall_alpha.bmp</font>'); ''<font color="green">// Alphakanal aus Datei laden</font>'' |
− | Das war alles nun sind wir im Besitz einer Textur mit einem Alphakanal. Es besteht unter anderem auch die Möglichkeit den Alphakanal aus verschiedenen anderen Quellen zu laden (Resourcen, Streams, TBitmap). Zusätzlich dazu kann man den Kanal aber auch dynamisch erstellen. Dafür existiert die Methode | + | Das war alles nun sind wir im Besitz einer Textur mit einem Alphakanal. Es besteht unter anderem auch die Möglichkeit den Alphakanal aus verschiedenen anderen Quellen zu laden (Resourcen, Streams, TBitmap). Zusätzlich dazu kann man den Kanal aber auch dynamisch erstellen. Dafür existiert die Methode ''AddAlphaFromFunc''. Die Findigen unter euch habe aber bestimmt schon herausgefunden, dass man bei alle Methoden solch eine Function übergeben kann. Näheres dazu findet ihr im Abschnitt ''e = mc²'' |
Etwas was häufiger gebraucht wird habe ich von Hause aus schon implementiert. Und zwar das Erstellen eines Alphakanals anhand einer Farbe. | Etwas was häufiger gebraucht wird habe ich von Hause aus schon implementiert. Und zwar das Erstellen eines Alphakanals anhand einer Farbe. | ||
− | fTexture.AddAlphaFromColorKey(Red, Green, Blue, Deviation); | + | fTexture.AddAlphaFromColorKey(Red, Green, Blue, Deviation); ''<font color="green">// Alphakanal anhand einer Farbe hinzufügen</font>'' |
− | Dabei wird nur auf vollkommen durchlässig und nicht durchlässig unterschieden. Alle Pixel die die angegebene Farbe besitzen werden dabei als 0 (nicht Sichtbar) in den Alphakanal geschrieben alle anderen erhalten 255 (Sichtbar). Die | + | Dabei wird nur auf vollkommen durchlässig und nicht durchlässig unterschieden. Alle Pixel die die angegebene Farbe besitzen werden dabei als 0 (nicht Sichtbar) in den Alphakanal geschrieben alle anderen erhalten 255 (Sichtbar). Die ''Deviation'' bestimmt eine Abweichung um der die Farben abweichen dürfen um trotzdem noch als Transparent erkannt zu werden. Speziell für JPEGs ist das nicht unwichtig, da die Bilder beim Komprimieren leicht abgewandelt werden. |
Damit die OpenGL Primitiven jetzt transparent dargestellt werden muss der [[GlAlphaFunc|Alphatest]] oder das [[glBlendFunc|Blending]] aktiviert sein. Sonst nützt einem der Alphakanal nicht sonderlich viel. | Damit die OpenGL Primitiven jetzt transparent dargestellt werden muss der [[GlAlphaFunc|Alphatest]] oder das [[glBlendFunc|Blending]] aktiviert sein. Sonst nützt einem der Alphakanal nicht sonderlich viel. | ||
− | Entfernen kann man einen Alphakanal natürlich auch. Dazu genügt der Aufruf von | + | Entfernen kann man einen Alphakanal natürlich auch. Dazu genügt der Aufruf von ''RemoveAlpha''. Dieser muss natürlich nicht mit der glBitmap erstellt worden sein. Er kann auch aus einer Datei geladen worden sein. |
− | fTexture.RemoveAlpha; | + | fTexture.RemoveAlpha; ''<font color="green">// entfernt den Alphakanal</font>'' |
== Texturen konfigurieren == | == Texturen konfigurieren == | ||
Zeile 116: | Zeile 122: | ||
Folgende Einstellungsmöglichkeiten verändern ausschließlich nur das Verhalten der glBitmap. Sie haben keinen Einfluss auf die resultierende Textur. | Folgende Einstellungsmöglichkeiten verändern ausschließlich nur das Verhalten der glBitmap. Sie haben keinen Einfluss auf die resultierende Textur. | ||
− | + | '''''DeleteTextureOnFree''''' ist vom Typ Boolean und gibt an ob beim Freigeben der Klasse die Texture auch freigegeben wird oder ob weiter existieren darf. Standard wird sie mit freigegeben. | |
− | + | '''''FreeDataAfterGenTexture''''' ist vom Typ Boolean und gibt an ob die Bilddaten nach dem Erzeugen der Textur aus dem Hauptspeicher entfernt werden sollen oder nicht. Im Normalfall werden sie das auch. | |
− | + | '''''Target''''' ist vom Typ Cardinal und bestimmt das OpenGL Target. Wird von den Klassen auf den Standardwert gesetzt und muss nur dann auf einen anderen Wert gesetzt werden, wenn ein anderes Target verwendet werden soll. Zum Beispiel bei der Verwendung von Texture_rectangle. Es sollte aber immer direkt nach dem erstellen gesetzt werden, da die Textur sonst unbrauchbar gemacht werden könnte. | |
− | + | '''''Format''''' ist ein Aufzählungstyp und er erlaubt 4 Werte. | |
− | + | {| {{Prettytable}} | |
− | + | |tfDefault | |
− | + | |Dabei wird das Standardformat des Grafikkartentreibers benutzt. Je nach eingestellter Qualität kann dies 16 oder 32 Bit sein.</td> | |
− | + | |- | |
− | + | |tf16Bit | |
− | + | |Hier wird die Textur in 16Bit abgelegt. | |
− | + | |- | |
− | + | |tf32Bit | |
− | + | |Hier wird die Textur in 32Bit abgelegt. | |
− | + | |- | |
− | + | |tfCompressed | |
− | + | |Bei diesem Format wird zu erst geschaut ob eines der folgenden Kriterien erfüllt ist. Extension GL_ARB_texture_compression, OpenGL Version 1.3 oder die Extension GL_EXT_texture_compression_s3tc. Sollten alle 3 Möglichkeiten nicht funktionieren so wird das Standardformat vom Grafikkartentreiber benutzt. | |
− | + | |} | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | '''''MipMap''''' ist ebenfalls ein Aufzählungstyp und er erlaubt 3 Werte. | |
− | + | {|{{Prettytable}} | |
− | + | |mmNone | |
− | + | |Wie man vermuten könnte werden hierbei keine Mipmaps erstellt. | |
− | + | |- | |
− | + | |mmMipmap | |
− | + | |Ähnlich wie bei komprimierten Texturen werden zu erst verschiedene neue Möglichkeiten überprüft. Ab OpenGL Version 1.4 oder mit der Extension GL_SGIS_generate_mipmap unterstützt die Grafikkarte ein generieren der [[Mipmaps]]. Das ist natürlich wesentlich schneller als der Weg ausschließlich über die Software. Sollte es keinen anderen Weg geben so werden die [[Mipmaps]] mittels der GLU generiert. | |
− | + | |- | |
− | + | |mmMipmapGlu | |
− | + | |In diesem Falle werden die [[Mipmaps]] ausschließlich mit der GLU erstellt. | |
− | + | |} | |
− | |||
− | |||
− | |||
− | |||
− | Mithilfe der Methoden | + | Mithilfe der Methoden '''''SetFilter''''' und '''''SetWrap''''' können das Filterverhalten und das Beschränken der Texturkoordinaten angepasst werden. Eine genaue Auflistung möglicher Werte findet man beim Artikel zu [[glTexParameter]]. Das Aufrufen der Methoden ist überall dort möglich wo auch [[glTexParameter]] gültig ist. Mögliche Fehlerhafte Werte werden automatisch auf die vorhandene Textur angepasst. So wird bei SetFilter niemals ein [[MipMaps|MipMap]] bei die Filterung eingestellt wenn keine [[MipMaps]] erzeugt wurden. Das würde sonst dazu führen, dass die Textur nicht dargestellt werden könnte. |
− | + | '''''FlipVert''''' und '''''FlipHorz''''' sind Methoden die nichts anderes machen als das Bild entlang der entsprechenden Richtung (Vertikal und Horizontal) zu spiegeln. Dies wäre auch mit der Funktionsschnittstelle möglich aber besonders bei FlipVert wäre es unpraktisch da wir so nicht die komplette Zeile am Stück kopieren könnten, sondern nur Pixel für Pixel. | |
− | Zum Invertieren des Bildes genügt es die Methode | + | Zum Invertieren des Bildes genügt es die Methode '''''Invert''''' aufzurufen. Sie bekommt zwei boolsche Parameter mit denen man steuern kann was man invertieren möchte. Einen für RGB und einen für den Alphakanal. |
+ | |||
+ | Die Methode '''''ToNormalMap''''' wurde implementiert um aus einer 2D Textur eine NormalMap zu erzeugen. Er funktioniert ähnlich wie das NormalMapPlugin für Gimp. Die Methode bekommt 3 Parameter. Der Erste gibt an mit welcher Methode die Normalmap berechnet werden soll. Mögliche Werte hierfür sind | ||
+ | {|{{Prettytable}} | ||
+ | |nm4Samples</td> | ||
+ | |Hierbei werden die jeweils geradlinigen benachbarten Pixel zu gleichen Teilen in die Berechnung einbezogen. (Oben, Unten Recht und Links). Diese Methode ist die einfachste, schnellste und empfindlichste gegenüber Farbvariationen. | ||
+ | |- | ||
+ | |nmSobel | ||
+ | |Mit dieser Methode werden fließen benachbarten Pixel zu gleichen Teilen in die Berechnung ein. | ||
+ | |- | ||
+ | |nm3x3 | ||
+ | |Bei diese Methode fließen auch alle benachbarten Pixel in die Berechnung ein. Die diagonalen Pixel werden Doppelt gewertet. Die NormalMap wirkt so ruhiger und nicht so anfällig gegen kleine Farbveränderungen. | ||
+ | |- | ||
+ | |nm5x5 | ||
+ | |Diese Methode benutzt eine Matrix von 5*5 Pixeln um Ihre Berechnungen durch zu führen. Je weiter die Punkte vom Mittelpunkt entfernt sind so geringer wird deren Einfluss. | ||
+ | |} | ||
+ | |||
+ | Der zweite Parameter gibt eine Skalierung an. Mögliche Werte hierfür liegen im Bereich von -100 bis 100. Bei einer Skalierung von 0 würde sich die Normalmap Wohlgefallen auflösen, da sie dann praktisch eine gerade Fläche darstellen würde. Die Textur dürfte dann außergewöhnlich gleichfarbig blau werden.<br> | ||
+ | Mit dem dritten und letzten Parameter kann man die Berechnung der Normalmap von den RGB Werten auf den Alphakanal verschieben. Die Berechnung zieht sich dann nicht mehr die RGB Werte sondern den Alphakanal zu Rate. Der genaue Nutzen davon liegt zwar momentan selber vor mir verborgen aber wer weiß wozu man so etwas später noch gebrauchen kann. ;-) | ||
== e = mc² == | == e = mc² == | ||
− | Hierbei handelt es sich um eine Möglichkeit die Texturdaten zu manipulieren. Die Schnittstelle wurde so gestaltet, dass sie sehr flexibel und typisiert ist. Sprich, dass man immer genau worum es sich handelt und welche Felder unterstützt werden. Eine einfache Handhabung und eine gute Ausführungsgeschwindigkeit waren natürlich auch sehr wichtige Punkte dabei. Finden kann man die Funktionsschnittstelle | + | Hierbei handelt es sich um eine Möglichkeit die Texturdaten zu manipulieren. Die Schnittstelle wurde so gestaltet, dass sie sehr flexibel und typisiert ist. Sprich, dass man immer genau weiß worum es sich handelt und welche Felder unterstützt werden. Eine einfache Handhabung und eine gute Ausführungsgeschwindigkeit waren natürlich auch sehr wichtige Punkte dabei. Finden kann man die Funktionsschnittstelle beim Hinzufügen des Alphakanals oder immer wenn man möchte durch die Methode ''AddFunc''. Es gibt außerdem noch eine Reihe von Funktionen, die intern die Schnittstelle benutzen. Das wären zum Beispiel die Funktionen ''Invert'', ''AddAlphaFromColokey'', ''ToNormalMap'' und ''FillWithColor''. Die Klasse ''TglBitmapNormalMap'' generiert ihre Texturen zum Beispiel mit der Schnittstelle. |
Die Arbeitsweise der Funktionsschnittstelle ist recht einfach. Es wird nichts anderes gemacht als 1 Mal Zeile für Zeile den Pointer eines jeden Pixels an eine Funktion zu Übergeben und darauf zu vertrauen, dass sie das Richtige tut. Um die Ausführung zu Beschleunigen wird ausschließlich mit Pointern gearbeitet. Die Funktionen bekommen die Instanz des Objektes, die Größe, die Position, das Quellpixel, das Zielpixel und einen Pointer auf benutzerdefinierte Daten übergeben. Das ist natürlich eine ganze Menge Holz was da an Daten übergeben aber das ist alles notwendig um die gewünschte Flexibilität erreichen zu können. | Die Arbeitsweise der Funktionsschnittstelle ist recht einfach. Es wird nichts anderes gemacht als 1 Mal Zeile für Zeile den Pointer eines jeden Pixels an eine Funktion zu Übergeben und darauf zu vertrauen, dass sie das Richtige tut. Um die Ausführung zu Beschleunigen wird ausschließlich mit Pointern gearbeitet. Die Funktionen bekommen die Instanz des Objektes, die Größe, die Position, das Quellpixel, das Zielpixel und einen Pointer auf benutzerdefinierte Daten übergeben. Das ist natürlich eine ganze Menge Holz was da an Daten übergeben aber das ist alles notwendig um die gewünschte Flexibilität erreichen zu können. | ||
− | Betrachten wir das ganze mal an einem Beispiel. Und zwar dem Aufruf von | + | Betrachten wir das ganze mal an einem Beispiel. Und zwar dem Aufruf von ''Invert''. Zu erst einmal der Aufruf der Funktion. |
− | + | '''if''' ((UseRGB) '''or''' (UseAlpha)) | |
− | + | '''then''' AddFunc(glBitmapInvertFunc, False, Pointer(Integer(UseAlpha '''and''' HasAlpha) '''shl''' 1 '''or''' Integer(UseRGB))); | |
− | In Zeile 1 handelt es sich lediglich um eine kleine Sicherheit. Es bringt ja nichts, wenn man eine Funktion ausführen möchte die keinen Nutzen hat. Zeile 2 Ruft die Methode | + | In Zeile 1 handelt es sich lediglich um eine kleine Sicherheit. Es bringt ja nichts, wenn man eine Funktion ausführen möchte die keinen Nutzen hat. Zeile 2 Ruft die Methode ''AddFunc'' auf. Diese Methode bekommt als erstes unsere Funktion übergeben. Zu deren Aufbau komme ich gleich. Als Zweites bekommt ''AddFunc'' gesagt ob sie eine Kopie des Bildes erstellen soll. Für pixelübergreifende Funktionen ist dies unerlässlich, da man sich sonst seine noch benötigten Daten überschreiben würde. Der dritte Wert ist ein Pointer der zusätzliche Informationen hält. In diesem Falle handelt es sich lediglich um ein Bitset bei dem Bit 2 den Alphakanal und Bit 1 die RGB Werte repräsentiert. Es könnte sich dabei aber auch um einen Pointer auf ein Record handeln. |
− | + | '''procedure''' glBitmapInvertFunc(Sender : TglBitmap; '''const''' Position, Size: TglBitmapPixelPosition; | |
− | + | '''const''' Source, Dest: TglBitmapPixelData; '''const''' Data: Pointer); | |
− | + | '''begin''' | |
− | + | '''if''' (Integer(Data) '''and''' $1 > 0) '''then begin''' | |
− | Dest.ptRed^ := | + | Dest.ptRed^ := '''not''' Dest.ptRed^; |
− | Dest.ptGreen^ := | + | Dest.ptGreen^ := '''not''' Dest.ptGreen^; |
− | Dest.ptBlue^ := | + | Dest.ptBlue^ := '''not''' Dest.ptBlue^; |
− | + | '''end;''' | |
− | + | '''if''' (Integer(Data) '''and''' $2 > 0) '''then begin''' | |
− | Dest.ptAlpha^ := | + | Dest.ptAlpha^ := '''not''' Dest.ptAlpha^; |
− | + | '''end;''' | |
− | + | '''end;''' | |
− | + | '''''Position''''' und '''''Size''''' beinhalten die Aktuelle Position des Pixels und die Größe des Bildes. Das Record TglBitmapPixelPosition beinhält 4 Werte. Fields, X, Y, Z. Fields beschreibt welche Felder überhaupt zur Verfügung stehen. Mögliche Werte Dafür sind ffX, ffY und ffZ. Wofür X, Y und Z stehen erkläre ich jetzt mal nicht, dass sollte jedem klar sein. Bei einem 2D Bitmap sind üblicherweise die Felder X und Y mit leben gefüllt. Das Feld Z liegt brach. | |
− | + | '''''Source''''' und '''''Dest''''' beinhalten die Pixeldaten. Die Records sind ziemlich genau so aufgebaut wie das für die Position. Es gibt einen Wert ''Fields''. Mögliche Werte für ihn sind ffRed, ffGreen, ffBlue und ffAlpha. Diese Felder sind anders als bei bei Position keine richtigen Werte sondern Pointer die Direkt auf die Farbwerte in den Bildern zeigen. Es gibt deswegen ''Source'' und ''Dest'', da es ja die Möglichkeit gibt eine Kopie des Bildes zu erstellen. Sollte ohne Kopie gearbeitet werden so sind ''Source'' und ''Dest'' gleich. Bei hinzufügen eines Alphakanals bezieht sich ''Source'' auf die Pixeldaten im dem Quellbild und ''Dest'' wie gewohnt auf die Daten im Zielbild. | |
− | In dem obigen Beispiel werden die Bits des Parameters Data ausgewertet und die entsprechenden Kanäle negiert. Der Komplexibilität sind kaum Grenzen gesetzt. Alle Wissensdurstigen unter euch sollten sich dann auch mal die Methode | + | In dem obigen Beispiel werden die Bits des Parameters Data ausgewertet und die entsprechenden Kanäle negiert. Der Komplexibilität sind kaum Grenzen gesetzt. Alle Wissensdurstigen unter euch sollten sich dann auch mal die Methode ''ToNormalMap'' anschauen. Dort werden zwei Funktionen aufgerufen. In der Ersten werden Daten gesammelt mit denen in der Zweiten die Normalmap berechnet wird. Oder auch die Klasse ''TglBitmapNormalMap''. In dieser wird eine generelle Berechnungsroutine aufgerufen. Diese bekommt aber für jede Fläche der Map eine andere interne Methode übergeben. Mit dieser werden dann die Werte für jedes Pixel gefüllt. |
− | Noch ein Wort für alle Geschwindigkeitsfanatiker. Ich hatte ein mal spaßeshalber die Methode | + | Noch ein Wort für alle Geschwindigkeitsfanatiker. Ich hatte ein mal spaßeshalber die Methode ''FlipHorz'' nachgebaut und die Geschwindigkeit gemessen. Das Ergebnis hat mich selber sehr angenehm überrascht. Die Hardcodierte super optimierte Methode benötigte bei 300 Aufrufen, auf ein 512x512 Pixel großes Bild ca. 5 Sekunden. Die Methode mit dem Funktionsaufruf benötigte Lediglich 300 ms länger. Ich selber hätte eher mit dem Doppelten der Zeit gerechnet. |
== Cubemaps mit glBitmap == | == Cubemaps mit glBitmap == | ||
Zeile 207: | Zeile 222: | ||
Wie auch bei den 2D Texturen haben wir die Auswahl zwischen zwei möglichen Wegen. Der procedurale Weg unterscheidet sich lediglich in der Anzahl der angegebenen Texturen. Das sind diesmal natürlich ein paar mehr. | Wie auch bei den 2D Texturen haben wir die Auswahl zwischen zwei möglichen Wegen. Der procedurale Weg unterscheidet sich lediglich in der Anzahl der angegebenen Texturen. Das sind diesmal natürlich ein paar mehr. | ||
− | + | '''var''' | |
Texture: TGLuint; | Texture: TGLuint; | ||
− | + | ''<font color="green">// Cubemaps laden</font>'' | |
LoadCubeMap('<font color="blue">xpos.bmp</font>', '<font color="blue">xneg.bmp</font>', '<font color="blue">ypos.bmp</font>', '<font color="blue">yneg.bmp</font>', '<font color="blue">zpos.bmp</font>', '<font color="blue">zneg.bmp</font>', Texture, False); | LoadCubeMap('<font color="blue">xpos.bmp</font>', '<font color="blue">xneg.bmp</font>', '<font color="blue">ypos.bmp</font>', '<font color="blue">yneg.bmp</font>', '<font color="blue">zpos.bmp</font>', '<font color="blue">zneg.bmp</font>', Texture, False); | ||
Zeile 219: | Zeile 234: | ||
Der Objekt orientierte Weg gestaltet sich dieses mal ein wenig schreibintensiver. Es müssen ja auch schließlich 6 Texturen geladen werden. Aber das kann man auch praktischerweise in eine Schleife packen. Das praktische daran ist, dass wir alles mit nur einer Klasseninstanz lösen können (und auch müssen). | Der Objekt orientierte Weg gestaltet sich dieses mal ein wenig schreibintensiver. Es müssen ja auch schließlich 6 Texturen geladen werden. Aber das kann man auch praktischerweise in eine Schleife packen. Das praktische daran ist, dass wir alles mit nur einer Klasseninstanz lösen können (und auch müssen). | ||
− | fCubeMap := TglBitmapCubeMap.Create; | + | fCubeMap := TglBitmapCubeMap.Create; ''<font color="green">// Instanz der Klasse erstellen</font>'' |
− | fCubeMap.LoadFromFile('<font color="blue">xpos.jpg</font>'); | + | fCubeMap.LoadFromFile('<font color="blue">xpos.jpg</font>'); ''<font color="green">// Datei Laden</font>'' |
Die Textur wird normal angelegt und es wird wie gewöhnt ein Bild in den Speicher der Klasse geladen. Dieses Bild verhält sich wie jedes andere Bild. Es kann also wie gewöhnt manipuliert werden. Zum Beispiel durch die Funktionsschnittstelle. | Die Textur wird normal angelegt und es wird wie gewöhnt ein Bild in den Speicher der Klasse geladen. Dieses Bild verhält sich wie jedes andere Bild. Es kann also wie gewöhnt manipuliert werden. Zum Beispiel durch die Funktionsschnittstelle. | ||
− | fCubeMap.GenerateCubeMap(GL_TEXTURE_CUBE_MAP_POSITIVE_X); | + | fCubeMap.GenerateCubeMap(GL_TEXTURE_CUBE_MAP_POSITIVE_X); ''<font color="green">// Einzelnen Teil übergeben</font>'' |
− | Wenn die Textur also bereit zum Übergeben ist, dann müssen wir | + | Wenn die Textur also bereit zum Übergeben ist, dann müssen wir ''GenerateCubeMap'' mit dem entsprechenden Teil der Cubemap aufrufen. In diesem Falle ist es die positive X Achse. Das müssen wir dann natürlich noch mit allen anderen Maps machen. '''''Also Bild laden, evtl bearbeiten und dann an OpenGL übergeben.''''' Die Reihenfolge der Maps spielt hierbei keine Rolle. Wichtig ist nur, dass sie die selbe Auflösung und das selbe Format haben. Die Methode ''GenTexture'' steht bei dieser Klasse nicht zur Verfügung, da beim ersten Aufruf von GenerateCubeMap automatisch die OpenGL Textur erzeugt wird. '''''Die CubeMap ist nur dann einsetzbar, wenn alle Maps erfolgreich mit Daten versorgt wurden.''''' |
Beim Binden der CubeMap existiert ein zusätzlicher Parameter mit dem man die Aktivierung der automatische Generation der Texturkoordinaten deaktivieren kann. Standard ist dieses eingeschalten. Ähnlich wie beim Binden einer Textur. Dort kann man verhindern, dass das Target aktiviert wird. | Beim Binden der CubeMap existiert ein zusätzlicher Parameter mit dem man die Aktivierung der automatische Generation der Texturkoordinaten deaktivieren kann. Standard ist dieses eingeschalten. Ähnlich wie beim Binden einer Textur. Dort kann man verhindern, dass das Target aktiviert wird. | ||
+ | |||
+ | == Normalmaps mit glBitmap == | ||
+ | |||
+ | [[Normalmap|NormalMaps]] sind nichts anderes als Spezielle Cubemaps. Sie dienen dazu um Per Pixel Normale auch auf Systemen ohne Shaderhardware zu ermöglichen. Benutzt werden kann das zum Beispiel bei Bumpmapping mit [[GL_ARB_texture_env_dot3]]. | ||
+ | |||
+ | === Procedural === | ||
+ | |||
+ | Wie auch bei den 2D Texturen und Cubemaps haben wir die Auswahl zwischen zwei möglichen Wegen. Da diese Texturen dynamisch generiert werden fällt die Anzahl der Parameter ziemlich gering aus. Sie besteht lediglich aus einer Texturgröße. In diesem Falle haben die resultierenden Texturen eine Größe von 32x32 Pixeln. Der zweite Parameter ist ein altbekannter von uns. Er ermöglicht es uns eine TexturID zu empfangen. Auf die Handhabung mit dieser ID gehe ich nicht noch einmal ein, da das oberhalb schon oft genug besprochen wurde. | ||
+ | |||
+ | '''var''' | ||
+ | Texture: TGLuint; | ||
+ | |||
+ | ''<font color="green">// Normalmaps erzeugen</font>'' | ||
+ | LoadNormalMap(32, Texture); | ||
+ | |||
+ | === Objekt orientiert === | ||
+ | |||
+ | Dieser Weg ist etwa genau so schreibintensiv wie der Procedurale. Hierbei wird eine Normalmap erstellt bei der einzelnen Texturen eine Größe von 32x32 Pixeln haben. | ||
+ | |||
+ | fNormalMap := TglBitmapNormalMap.Create; ''<font color="green">// Instanz der Klasse erstellen</font>'' | ||
+ | fNormalMap.GenerateNormalMap(32); ''<font color="green">// Normalmap erzeugen</font>'' | ||
+ | |||
+ | Erstaunlich aber wahr. Das war bereits alles. Von nun an kann die Textur wie jede andere CubeMap auch verwendet werden. | ||
== Bekannte Probleme und Einschränkungen == | == Bekannte Probleme und Einschränkungen == | ||
− | Vor einer Weile wurde im Forum die Frage gestellt warum die glBitmap beim Laden eines | + | Vor einer Weile wurde im Forum die Frage gestellt warum die glBitmap beim Laden eines BMPs einen "Stream Read Error" produziert. Da das Bitmap nun mal ein sehr einfaches Format ist hat mich das auch sehr stark verwundert. Nach langem hin und her Rätseln kam ich dann dennoch darauf was das Problem war. Es lag daran, dass das Bitmap mit MS Paint abgespeichert wurde. Dieser war aber der Meinung er müsse im Bitmapheader eine Größe angeben die 2 Bytes größer war als die eigentlichen Daten. Diese 2 Bytes wurden auch aufgefüllt aber leider ist das TBitmap, welches ich zum Laden verwende, nicht so tolerant um darüber hinweg zu sehen. Und so bekommt man einen Fehler beim Laden. Den selben Fehler würde man auch bekommen wenn man das Bild in einem TImage anzeigen wollte. |
Abhelfen kann man dies in dem man ein [http://www.gimp.org/ alternatives Grafikprogramm (Gimp)] verwendet oder das Bild einmal mit dem Bildbetrachter [http://www.irfanview.de/ IrfanView] öffnet und abspeichert. | Abhelfen kann man dies in dem man ein [http://www.gimp.org/ alternatives Grafikprogramm (Gimp)] verwendet oder das Bild einmal mit dem Bildbetrachter [http://www.irfanview.de/ IrfanView] öffnet und abspeichert. | ||
Es muss nicht bei jedem mit MS Paint abgespeicherten Bild auftreten. | Es muss nicht bei jedem mit MS Paint abgespeicherten Bild auftreten. |
Aktuelle Version vom 8. Juni 2015, 21:29 Uhr
(Mehr Informationen/weitere Artikel) {{{1}}} |
Inhaltsverzeichnis
glBitmap - Texturenloader
Warum das Ganze und wo kann ich sie finden?
Früher benutzte ich die glBMP, da sie so "schön" Objekt orientiert war. Leider hatte mein Grafiktreiber zu der Zeit einen Fehler bei der Auswahl des Texturformates. So führte es dazu, dass sobald ich eine Textur mit Alphakanal und dem Standard Texturformat an OpenGL übergab, er als Farbtiefe 16 Bit verwendete. Wenn ich ein Foto genommen hätte wäre mir das wahrscheinlich gar nicht aufgefallen aber dummerweise (oder glücklicherweise) hatte ich aber eine Textur mit einem weichem Farbverlauf. Wie zu erwarten war sah das natürlich alles ziemlich schrecklich aus. Bis dahin kann der Loader ja nichts dafür wenn in einem Treiber ein Fehler existiert. Nur das kuriose an dem Ganzen war, dass meine Textur in Wirklichkeit gar keinen Alphakanal haben durfte. Also habe ich mich auf die Suche nach der Ursache gemacht und bin auch prompt fündig geworden. Der Loader war der Meinung er müsse alle Texturen mit einem Alphakanal versehen ungeachtet der Tatsache ob sie nun einen hatten oder eben nicht. Das ist natürlich eine Sache mit der ich mich nicht abfinden wollte. Kurz darauf begann ich also eine Ableitung der glBMP Texturenklasse zu erstellen und dort musste ich mit Schrecken feststellen, dass die Rechte in der Klasse so ungünstig vergeben wurden, dass ich keine andere Wahl gehabt hätte als direkt die Unit zu editieren. Da ich ungern externe Quellen editiere habe ich für mich die andere Alternative entschieden. Was dabei rausgekommen ist sieht man ja jetzt.
Gefunden werden kann sie entweder im DGL-SDK oder auf der Webseite von Lossy eX.
Die glBitmap wird in Zukunft von der DelphiGL Community weiterentwickelt und gepflegt. Eine aktuelle Version kann entweder vom [DGL git Repository] oder unter folgenden Links bezogen werden: |
Allgemeines und Features
Dieser Artikel geht nicht darauf ein was zu tun ist um in OpenGL eine Textur darstellen zu können. In ihm werde ich lediglich darauf eingehen was man tun kann und muss um mittels der glBitmap eine Textur laden zu können. Außerdem werden die verfügbaren Einstellungen und ihre Wirkungen erklärt. Für alles andere wäre es besser das Tutorial über Texturen durchzulesen.
Der wichtigste Unterschied zu den bestehenden Loadern bringe ich gleich einmal als erstes. Die glBitmap legt die Texturen anders im Speicher ab. Um zwar im speziellen meine ich damit die 2D Texturen. Bei bestehenden Loadern werden diese Texturen mit der untersten Zeile zu erst im Speicher abgelegt. In der glBitmap werden sie aber mit der obersten Zeile zu erst abgelegt. Man könnte auch sagen, dass sie bei den bestehenden Loadern auf dem Kopf stehen. Beide Möglichkeiten funktionieren und sind korrekt. Das Einzige was sich in diesem Zusammenhang ändert ist die Adressierung der Texturen. Sie müssen entsprechend geändert werden. Sollte das nicht möglich sein so besteht mit der Methode FlipVert die Möglichkeit die Textur zu spiegeln. Näheres in Abschnitt Texturen konfigurieren. Im Normalfall sollte das andere Verhalten nicht zu Problemen führen sondern die Arbeit damit vereinfachen und verständlicher gestalten.
Die glBitmap holt sich ihre Daten ausschließlich aus Streams. Das zugrundeliegende Format wird automatisch anhand des Inhaltes der Daten ausgewählt. Es besteht also kein Notwendigkeit daran, dass der Entwickler wissen muss um welches Format es sich in Wirklichkeit handelt.
unterstütze Formate
- Windows Bitmaps (*.bmp) Wird Automatisch auf 24 Bit gesetzt sollte es kleiner sein.
- JPEG (*.jpg)
- TARGA (*.tga) komprimiert und unkomprimiert, 24 und 32 Bit. Ursprung unten links oder oben links
- Portable Network Graphics (*.png) 24 Bit mit oder ohne Alphakanal. Es wird eine zusätzliche Bibliothek benötigt.
- Direct Draw Surfaces (*.dds) 8,16,24,32 Bit DXT1,DXT3,DXT5, keine Cubemaps, keine Mipmaps
Es besteht auch die Möglichkeit die Bilddaten dynamisch generieren zu lassen oder sie aus einer Instanz der Klasse TBitmap zu laden. Diese Instanz muss dann aber als Pixelformat 24 oder 32 Bit haben.
Um PNGs laden zu können muss eine externe Bibliothek namens PNGImage eingebunden und das Define pngimage hinzugefügt werden. Alternativ dazu kann das Define auch in der glBitmap auskommentiert werden.
Texturen laden
Das Laden von Texturen kann auf 2 Weg geschehen. Bei Beiden ist aber zu beachten, dass OpenGL richtig initialisiert worden sein muss bevor man eine Textur erzeugen kann. Was noch sehr wichtig ist. Es sollte niemals eine Textur bei jedem Rendervorgang angelegt und freigegeben werden! Es ist viel effektiver, wenn man die Textur zu beginn lädt und so lange im Speicher behält bis sie nicht mehr benötigt wird. Wenn man sie erst später benötigt, dann kann man sie auch später noch nachladen aber auch dann sollte sie so lange wie nötig existieren.
Procedural
Hierbei genügt der Aufruf einer einzelnen Methode um eine Textur zu laden und diese an OpenGL zu übergeben. Allerdings auf Grund der Einfachheit steht nur das Laden aus Dateien oder Ressourcen zur Verfügung. Zusätzliche Funktionalität können dabei nicht benutzt werden. Generell empfehle ich immer die Objekt orientierte Variante.
var Texture: TGLuint; LoadTexture('myTex\Wall.bmp', Texture, False); // Datei laden
Damit wird die Datei Wall.bmp geladen und die TextureID wird in die Variable Texture geschrieben. Wenn die Textur dann benötigt wird, dann muss man diese entsprechend mit glBindTexture binden und das benötigte Texturtarget mit glEnable aktivieren. Das Target ist üblicherweise GL_TEXTURE_2D. Wenn die Textur nicht mehr benötigt wird sollte sie mit glDeleteTextures auch wieder frei gegeben werden.
Objekt orientiert
Dieser Abschnitt beschäftigt sich ausschließlich mit den 2D Texturen. Aber grundsätzlich lässt sich alles hier erwähnte auch auf die 1D Texturen und die Cubemaps übertragen. Der Objekt orientierte Weg erfordert zwar eine wenig mehr Schreibarbeit aber nur so kann man den vollen Funktionsumfang der glBitmap genießen.
Als Erstes müssen wir eine Instanz der Texturenklasse definieren. Dies tun wir vorzugsweise als Member des Fensters auf dem wir zeichnen.
TForm1 = class(TForm) // Verschiene Elemente und Events des Fensters private fTexture: TglBitmap2D; // Instanz unserer Textur end;
Nun müssen wir der Textur noch Leben einhauchen.
procedure TForm1.FormShow(Sender: TObject); begin // OpenGL Initialisieren fTexture := TglBitmap2D.Create; // Instanz der Klasse erstellen fTexture.LoadFromFile('myTex\Wall.bmp'); // Datei laden fTexture.GenTexture; // geladene Textur an OpenGL übergeben end;
Für Schreibfaule besteht auch noch die Möglichkeit in einem überladenen Konstruktor einem Dateinamen anzugeben.
fTexture := TglBitmap2D.Create('myTex\Wall.bmp'); // Instanz erstellen und Datei laden fTexture.GenTexture; // geladene Textur an OpenGL übergeben
In GenTexture wird im Normalfall immer überprüft ob die Textur eine gültige Größe hat. Sollte diese Überprüfung nicht gewünscht sein so kann man diese mit einem optionalen Parameter deaktivieren. Diese Überprüfung testet auch ob die Texturgröße eine Potenz von 2 ist (1, 2, 4, 8, 16 usw.) und wenn nicht ob solche Texturen von dem Rendercontex unterstützt werden.
fTexture.GenTexture(False); // Es wird keine Prüfung der Texturgröße vorgenommen.
Wenn kein Fehler aufgetreten ist und alles so funktioniert hat wie es sein sollte, dann kann die Textur benutzt werden.
fTexture.Bind; // Textur und entsprechendes Target wird aktiviert.
Sollte es nicht gewünscht werden, dass auch gleichzeitig das Target aktiviert wird so muss Bind mit dem Parameter False aufgerufen werden. Damit wird nur noch die Textur gebunden.
Sollte man die Textur einmal nicht mehr benutzen wollen so genügt es entweder eine andere Textur zu binden oder Unbind von der Texturenklasse aufzurufen. Damit wird die Textur mit der TexturID 0 gebunden und das Target deaktiviert. Auch hier existiert wieder der Parameter der es verhindert, dass das Target deaktiviert wird.
Wenn die Anwendung beendet wird müssen wir auch hier unsere Textur wieder frei geben. Dies geschieht so.
fTexture.Free; // Textur wieder frei geben.
Hierbei ist zu beachten, dass dies nach Möglichkeit vor dem Löschen des Rendercontex geschehen sollte, da so die Textur zu einem sinnvollem Zeitpunkt gelöscht werden kann. Wenn der Rendercontex nicht mehr existiert, so sollte die Textur auch nicht mehr existieren und wenn man dann noch einmal versucht sie zu löschen so könnte es zu Problemen führen. Bisher ist mir kein Fall bekannt aber ausschließen kann man es auch nicht. Also zu erst immer die Texturen frei geben und anschließend des Rendercontex löschen.
Das leidige Thema Alphakanal
Es gibt zwei Möglichkeiten einen Alphakanal in eine Textur zu integrieren. Der einfachste und schnellste Weg ist, wenn sich dieser Kanal von Beginn an in der Textur befindet. Dazu wird ein Grafikprogramm benötigt welches so etwas unterstützt und ein Dateiformat welches das Speichern eines Alphakanals ermöglicht. Ein solches Programm ist zum Beispiel Gimp. Als mögliche Dateiformate kämen TGAs und PNGs in Frage. Alle anderen Formate unterstützen keinen Alphakanal. Sollte man sich für diesen Weg entscheiden so genügt es dann, ganz normal die Textur zu laden und zu benutzen und schon besitzen wir einen funktionierenden Alphakanal. Ich persönlich empfehle bis auf wenige Ausnahmen immer diesen Weg.
Nichts desto trotz habe ich ja gesagt, dass es noch eine Möglichkeit gibt. Bei diesem Weg haben wir Alphakanal und RGB Daten in zwei getrennten Bildern vorliegen. Aber wie bekommen wir das jetzt in ein Bild? Dafür bietet die glBitmap die Möglichkeit einen Alphakanal aus einem separaten Bild zu laden. Zu Beginn benötigen wir jedoch wieder eine Instanz der Klasse und es müssen schon normale Bilddaten geladen worden sein. Und ganz Wichtig damit es überhaupt funktioniert. Der Aufruf muss vor GenTexture stattfinden, da sonst die Textur bereits an OpenGL übergeben wurde.
fTexture.AddAlphaFromFile('myTex\Wall_alpha.bmp'); // Alphakanal aus Datei laden
Das war alles nun sind wir im Besitz einer Textur mit einem Alphakanal. Es besteht unter anderem auch die Möglichkeit den Alphakanal aus verschiedenen anderen Quellen zu laden (Resourcen, Streams, TBitmap). Zusätzlich dazu kann man den Kanal aber auch dynamisch erstellen. Dafür existiert die Methode AddAlphaFromFunc. Die Findigen unter euch habe aber bestimmt schon herausgefunden, dass man bei alle Methoden solch eine Function übergeben kann. Näheres dazu findet ihr im Abschnitt e = mc²
Etwas was häufiger gebraucht wird habe ich von Hause aus schon implementiert. Und zwar das Erstellen eines Alphakanals anhand einer Farbe.
fTexture.AddAlphaFromColorKey(Red, Green, Blue, Deviation); // Alphakanal anhand einer Farbe hinzufügen
Dabei wird nur auf vollkommen durchlässig und nicht durchlässig unterschieden. Alle Pixel die die angegebene Farbe besitzen werden dabei als 0 (nicht Sichtbar) in den Alphakanal geschrieben alle anderen erhalten 255 (Sichtbar). Die Deviation bestimmt eine Abweichung um der die Farben abweichen dürfen um trotzdem noch als Transparent erkannt zu werden. Speziell für JPEGs ist das nicht unwichtig, da die Bilder beim Komprimieren leicht abgewandelt werden.
Damit die OpenGL Primitiven jetzt transparent dargestellt werden muss der Alphatest oder das Blending aktiviert sein. Sonst nützt einem der Alphakanal nicht sonderlich viel.
Entfernen kann man einen Alphakanal natürlich auch. Dazu genügt der Aufruf von RemoveAlpha. Dieser muss natürlich nicht mit der glBitmap erstellt worden sein. Er kann auch aus einer Datei geladen worden sein.
fTexture.RemoveAlpha; // entfernt den Alphakanal
Texturen konfigurieren
Hier beginnt jetzt der Teil der für die Meisten recht wichtig sein dürfte. Bisher haben wir ja nur Daten geladen und diese an OpenGL übergeben. Nun wollen wir aber ein wenig an den Einstellungen rumschrauben und sehen was wir daraus machen können.
Folgende Einstellungsmöglichkeiten verändern ausschließlich nur das Verhalten der glBitmap. Sie haben keinen Einfluss auf die resultierende Textur.
DeleteTextureOnFree ist vom Typ Boolean und gibt an ob beim Freigeben der Klasse die Texture auch freigegeben wird oder ob weiter existieren darf. Standard wird sie mit freigegeben.
FreeDataAfterGenTexture ist vom Typ Boolean und gibt an ob die Bilddaten nach dem Erzeugen der Textur aus dem Hauptspeicher entfernt werden sollen oder nicht. Im Normalfall werden sie das auch.
Target ist vom Typ Cardinal und bestimmt das OpenGL Target. Wird von den Klassen auf den Standardwert gesetzt und muss nur dann auf einen anderen Wert gesetzt werden, wenn ein anderes Target verwendet werden soll. Zum Beispiel bei der Verwendung von Texture_rectangle. Es sollte aber immer direkt nach dem erstellen gesetzt werden, da die Textur sonst unbrauchbar gemacht werden könnte.
Format ist ein Aufzählungstyp und er erlaubt 4 Werte.
tfDefault | Dabei wird das Standardformat des Grafikkartentreibers benutzt. Je nach eingestellter Qualität kann dies 16 oder 32 Bit sein.</td> |
tf16Bit | Hier wird die Textur in 16Bit abgelegt. |
tf32Bit | Hier wird die Textur in 32Bit abgelegt. |
tfCompressed | Bei diesem Format wird zu erst geschaut ob eines der folgenden Kriterien erfüllt ist. Extension GL_ARB_texture_compression, OpenGL Version 1.3 oder die Extension GL_EXT_texture_compression_s3tc. Sollten alle 3 Möglichkeiten nicht funktionieren so wird das Standardformat vom Grafikkartentreiber benutzt. |
MipMap ist ebenfalls ein Aufzählungstyp und er erlaubt 3 Werte.
mmNone | Wie man vermuten könnte werden hierbei keine Mipmaps erstellt. |
mmMipmap | Ähnlich wie bei komprimierten Texturen werden zu erst verschiedene neue Möglichkeiten überprüft. Ab OpenGL Version 1.4 oder mit der Extension GL_SGIS_generate_mipmap unterstützt die Grafikkarte ein generieren der Mipmaps. Das ist natürlich wesentlich schneller als der Weg ausschließlich über die Software. Sollte es keinen anderen Weg geben so werden die Mipmaps mittels der GLU generiert. |
mmMipmapGlu | In diesem Falle werden die Mipmaps ausschließlich mit der GLU erstellt. |
Mithilfe der Methoden SetFilter und SetWrap können das Filterverhalten und das Beschränken der Texturkoordinaten angepasst werden. Eine genaue Auflistung möglicher Werte findet man beim Artikel zu glTexParameter. Das Aufrufen der Methoden ist überall dort möglich wo auch glTexParameter gültig ist. Mögliche Fehlerhafte Werte werden automatisch auf die vorhandene Textur angepasst. So wird bei SetFilter niemals ein MipMap bei die Filterung eingestellt wenn keine MipMaps erzeugt wurden. Das würde sonst dazu führen, dass die Textur nicht dargestellt werden könnte.
FlipVert und FlipHorz sind Methoden die nichts anderes machen als das Bild entlang der entsprechenden Richtung (Vertikal und Horizontal) zu spiegeln. Dies wäre auch mit der Funktionsschnittstelle möglich aber besonders bei FlipVert wäre es unpraktisch da wir so nicht die komplette Zeile am Stück kopieren könnten, sondern nur Pixel für Pixel.
Zum Invertieren des Bildes genügt es die Methode Invert aufzurufen. Sie bekommt zwei boolsche Parameter mit denen man steuern kann was man invertieren möchte. Einen für RGB und einen für den Alphakanal.
Die Methode ToNormalMap wurde implementiert um aus einer 2D Textur eine NormalMap zu erzeugen. Er funktioniert ähnlich wie das NormalMapPlugin für Gimp. Die Methode bekommt 3 Parameter. Der Erste gibt an mit welcher Methode die Normalmap berechnet werden soll. Mögliche Werte hierfür sind
nm4Samples</td> | Hierbei werden die jeweils geradlinigen benachbarten Pixel zu gleichen Teilen in die Berechnung einbezogen. (Oben, Unten Recht und Links). Diese Methode ist die einfachste, schnellste und empfindlichste gegenüber Farbvariationen. |
nmSobel | Mit dieser Methode werden fließen benachbarten Pixel zu gleichen Teilen in die Berechnung ein. |
nm3x3 | Bei diese Methode fließen auch alle benachbarten Pixel in die Berechnung ein. Die diagonalen Pixel werden Doppelt gewertet. Die NormalMap wirkt so ruhiger und nicht so anfällig gegen kleine Farbveränderungen. |
nm5x5 | Diese Methode benutzt eine Matrix von 5*5 Pixeln um Ihre Berechnungen durch zu führen. Je weiter die Punkte vom Mittelpunkt entfernt sind so geringer wird deren Einfluss. |
Der zweite Parameter gibt eine Skalierung an. Mögliche Werte hierfür liegen im Bereich von -100 bis 100. Bei einer Skalierung von 0 würde sich die Normalmap Wohlgefallen auflösen, da sie dann praktisch eine gerade Fläche darstellen würde. Die Textur dürfte dann außergewöhnlich gleichfarbig blau werden.
Mit dem dritten und letzten Parameter kann man die Berechnung der Normalmap von den RGB Werten auf den Alphakanal verschieben. Die Berechnung zieht sich dann nicht mehr die RGB Werte sondern den Alphakanal zu Rate. Der genaue Nutzen davon liegt zwar momentan selber vor mir verborgen aber wer weiß wozu man so etwas später noch gebrauchen kann. ;-)
e = mc²
Hierbei handelt es sich um eine Möglichkeit die Texturdaten zu manipulieren. Die Schnittstelle wurde so gestaltet, dass sie sehr flexibel und typisiert ist. Sprich, dass man immer genau weiß worum es sich handelt und welche Felder unterstützt werden. Eine einfache Handhabung und eine gute Ausführungsgeschwindigkeit waren natürlich auch sehr wichtige Punkte dabei. Finden kann man die Funktionsschnittstelle beim Hinzufügen des Alphakanals oder immer wenn man möchte durch die Methode AddFunc. Es gibt außerdem noch eine Reihe von Funktionen, die intern die Schnittstelle benutzen. Das wären zum Beispiel die Funktionen Invert, AddAlphaFromColokey, ToNormalMap und FillWithColor. Die Klasse TglBitmapNormalMap generiert ihre Texturen zum Beispiel mit der Schnittstelle.
Die Arbeitsweise der Funktionsschnittstelle ist recht einfach. Es wird nichts anderes gemacht als 1 Mal Zeile für Zeile den Pointer eines jeden Pixels an eine Funktion zu Übergeben und darauf zu vertrauen, dass sie das Richtige tut. Um die Ausführung zu Beschleunigen wird ausschließlich mit Pointern gearbeitet. Die Funktionen bekommen die Instanz des Objektes, die Größe, die Position, das Quellpixel, das Zielpixel und einen Pointer auf benutzerdefinierte Daten übergeben. Das ist natürlich eine ganze Menge Holz was da an Daten übergeben aber das ist alles notwendig um die gewünschte Flexibilität erreichen zu können.
Betrachten wir das ganze mal an einem Beispiel. Und zwar dem Aufruf von Invert. Zu erst einmal der Aufruf der Funktion.
if ((UseRGB) or (UseAlpha)) then AddFunc(glBitmapInvertFunc, False, Pointer(Integer(UseAlpha and HasAlpha) shl 1 or Integer(UseRGB)));
In Zeile 1 handelt es sich lediglich um eine kleine Sicherheit. Es bringt ja nichts, wenn man eine Funktion ausführen möchte die keinen Nutzen hat. Zeile 2 Ruft die Methode AddFunc auf. Diese Methode bekommt als erstes unsere Funktion übergeben. Zu deren Aufbau komme ich gleich. Als Zweites bekommt AddFunc gesagt ob sie eine Kopie des Bildes erstellen soll. Für pixelübergreifende Funktionen ist dies unerlässlich, da man sich sonst seine noch benötigten Daten überschreiben würde. Der dritte Wert ist ein Pointer der zusätzliche Informationen hält. In diesem Falle handelt es sich lediglich um ein Bitset bei dem Bit 2 den Alphakanal und Bit 1 die RGB Werte repräsentiert. Es könnte sich dabei aber auch um einen Pointer auf ein Record handeln.
procedure glBitmapInvertFunc(Sender : TglBitmap; const Position, Size: TglBitmapPixelPosition; const Source, Dest: TglBitmapPixelData; const Data: Pointer); begin if (Integer(Data) and $1 > 0) then begin Dest.ptRed^ := not Dest.ptRed^; Dest.ptGreen^ := not Dest.ptGreen^; Dest.ptBlue^ := not Dest.ptBlue^; end; if (Integer(Data) and $2 > 0) then begin Dest.ptAlpha^ := not Dest.ptAlpha^; end; end;
Position und Size beinhalten die Aktuelle Position des Pixels und die Größe des Bildes. Das Record TglBitmapPixelPosition beinhält 4 Werte. Fields, X, Y, Z. Fields beschreibt welche Felder überhaupt zur Verfügung stehen. Mögliche Werte Dafür sind ffX, ffY und ffZ. Wofür X, Y und Z stehen erkläre ich jetzt mal nicht, dass sollte jedem klar sein. Bei einem 2D Bitmap sind üblicherweise die Felder X und Y mit leben gefüllt. Das Feld Z liegt brach.
Source und Dest beinhalten die Pixeldaten. Die Records sind ziemlich genau so aufgebaut wie das für die Position. Es gibt einen Wert Fields. Mögliche Werte für ihn sind ffRed, ffGreen, ffBlue und ffAlpha. Diese Felder sind anders als bei bei Position keine richtigen Werte sondern Pointer die Direkt auf die Farbwerte in den Bildern zeigen. Es gibt deswegen Source und Dest, da es ja die Möglichkeit gibt eine Kopie des Bildes zu erstellen. Sollte ohne Kopie gearbeitet werden so sind Source und Dest gleich. Bei hinzufügen eines Alphakanals bezieht sich Source auf die Pixeldaten im dem Quellbild und Dest wie gewohnt auf die Daten im Zielbild.
In dem obigen Beispiel werden die Bits des Parameters Data ausgewertet und die entsprechenden Kanäle negiert. Der Komplexibilität sind kaum Grenzen gesetzt. Alle Wissensdurstigen unter euch sollten sich dann auch mal die Methode ToNormalMap anschauen. Dort werden zwei Funktionen aufgerufen. In der Ersten werden Daten gesammelt mit denen in der Zweiten die Normalmap berechnet wird. Oder auch die Klasse TglBitmapNormalMap. In dieser wird eine generelle Berechnungsroutine aufgerufen. Diese bekommt aber für jede Fläche der Map eine andere interne Methode übergeben. Mit dieser werden dann die Werte für jedes Pixel gefüllt.
Noch ein Wort für alle Geschwindigkeitsfanatiker. Ich hatte ein mal spaßeshalber die Methode FlipHorz nachgebaut und die Geschwindigkeit gemessen. Das Ergebnis hat mich selber sehr angenehm überrascht. Die Hardcodierte super optimierte Methode benötigte bei 300 Aufrufen, auf ein 512x512 Pixel großes Bild ca. 5 Sekunden. Die Methode mit dem Funktionsaufruf benötigte Lediglich 300 ms länger. Ich selber hätte eher mit dem Doppelten der Zeit gerechnet.
Cubemaps mit glBitmap
Das einzige worin sich CubeMaps von 2D Texturen unterscheidet ist das Laden. Aber das ist auch nicht viel komplizierter als bei 2D Texturen. Die Handhabung mit den Texturen (Binden, Frei geben) ist wie bei 2D Texturen weswegen ich das auch nicht noch einmal extra wiederholen werde.
Procedural
Wie auch bei den 2D Texturen haben wir die Auswahl zwischen zwei möglichen Wegen. Der procedurale Weg unterscheidet sich lediglich in der Anzahl der angegebenen Texturen. Das sind diesmal natürlich ein paar mehr.
var Texture: TGLuint; // Cubemaps laden LoadCubeMap('xpos.bmp', 'xneg.bmp', 'ypos.bmp', 'yneg.bmp', 'zpos.bmp', 'zneg.bmp', Texture, False);
Die ersten 6 Parameter geben die jeweils 6 Texturen an. Die Namen der Parameter geben auch gleichzeitig deren Ziel an. Also immer schön auf die Reihenfolge achten. In dem Siebten wird die Textur ID geschrieben und der Achte gibt an ob aus Resourcen geladen werden soll oder nicht.
Objekt orientiert
Der Objekt orientierte Weg gestaltet sich dieses mal ein wenig schreibintensiver. Es müssen ja auch schließlich 6 Texturen geladen werden. Aber das kann man auch praktischerweise in eine Schleife packen. Das praktische daran ist, dass wir alles mit nur einer Klasseninstanz lösen können (und auch müssen).
fCubeMap := TglBitmapCubeMap.Create; // Instanz der Klasse erstellen fCubeMap.LoadFromFile('xpos.jpg'); // Datei Laden
Die Textur wird normal angelegt und es wird wie gewöhnt ein Bild in den Speicher der Klasse geladen. Dieses Bild verhält sich wie jedes andere Bild. Es kann also wie gewöhnt manipuliert werden. Zum Beispiel durch die Funktionsschnittstelle.
fCubeMap.GenerateCubeMap(GL_TEXTURE_CUBE_MAP_POSITIVE_X); // Einzelnen Teil übergeben
Wenn die Textur also bereit zum Übergeben ist, dann müssen wir GenerateCubeMap mit dem entsprechenden Teil der Cubemap aufrufen. In diesem Falle ist es die positive X Achse. Das müssen wir dann natürlich noch mit allen anderen Maps machen. Also Bild laden, evtl bearbeiten und dann an OpenGL übergeben. Die Reihenfolge der Maps spielt hierbei keine Rolle. Wichtig ist nur, dass sie die selbe Auflösung und das selbe Format haben. Die Methode GenTexture steht bei dieser Klasse nicht zur Verfügung, da beim ersten Aufruf von GenerateCubeMap automatisch die OpenGL Textur erzeugt wird. Die CubeMap ist nur dann einsetzbar, wenn alle Maps erfolgreich mit Daten versorgt wurden.
Beim Binden der CubeMap existiert ein zusätzlicher Parameter mit dem man die Aktivierung der automatische Generation der Texturkoordinaten deaktivieren kann. Standard ist dieses eingeschalten. Ähnlich wie beim Binden einer Textur. Dort kann man verhindern, dass das Target aktiviert wird.
Normalmaps mit glBitmap
NormalMaps sind nichts anderes als Spezielle Cubemaps. Sie dienen dazu um Per Pixel Normale auch auf Systemen ohne Shaderhardware zu ermöglichen. Benutzt werden kann das zum Beispiel bei Bumpmapping mit GL_ARB_texture_env_dot3.
Procedural
Wie auch bei den 2D Texturen und Cubemaps haben wir die Auswahl zwischen zwei möglichen Wegen. Da diese Texturen dynamisch generiert werden fällt die Anzahl der Parameter ziemlich gering aus. Sie besteht lediglich aus einer Texturgröße. In diesem Falle haben die resultierenden Texturen eine Größe von 32x32 Pixeln. Der zweite Parameter ist ein altbekannter von uns. Er ermöglicht es uns eine TexturID zu empfangen. Auf die Handhabung mit dieser ID gehe ich nicht noch einmal ein, da das oberhalb schon oft genug besprochen wurde.
var Texture: TGLuint; // Normalmaps erzeugen LoadNormalMap(32, Texture);
Objekt orientiert
Dieser Weg ist etwa genau so schreibintensiv wie der Procedurale. Hierbei wird eine Normalmap erstellt bei der einzelnen Texturen eine Größe von 32x32 Pixeln haben.
fNormalMap := TglBitmapNormalMap.Create; // Instanz der Klasse erstellen fNormalMap.GenerateNormalMap(32); // Normalmap erzeugen
Erstaunlich aber wahr. Das war bereits alles. Von nun an kann die Textur wie jede andere CubeMap auch verwendet werden.
Bekannte Probleme und Einschränkungen
Vor einer Weile wurde im Forum die Frage gestellt warum die glBitmap beim Laden eines BMPs einen "Stream Read Error" produziert. Da das Bitmap nun mal ein sehr einfaches Format ist hat mich das auch sehr stark verwundert. Nach langem hin und her Rätseln kam ich dann dennoch darauf was das Problem war. Es lag daran, dass das Bitmap mit MS Paint abgespeichert wurde. Dieser war aber der Meinung er müsse im Bitmapheader eine Größe angeben die 2 Bytes größer war als die eigentlichen Daten. Diese 2 Bytes wurden auch aufgefüllt aber leider ist das TBitmap, welches ich zum Laden verwende, nicht so tolerant um darüber hinweg zu sehen. Und so bekommt man einen Fehler beim Laden. Den selben Fehler würde man auch bekommen wenn man das Bild in einem TImage anzeigen wollte.
Abhelfen kann man dies in dem man ein alternatives Grafikprogramm (Gimp) verwendet oder das Bild einmal mit dem Bildbetrachter IrfanView öffnet und abspeichert.
Es muss nicht bei jedem mit MS Paint abgespeicherten Bild auftreten.