Buchempfehlung
Windows-Programmierung. Das Entwicklerhandbuch zur WIN32-API
Windows-Programmierung. Das Entwicklerhandbuch zur WIN32-API
"Der" Petzold, das über 1000 Seiten starke Standardwerk zum Win32-API - besonders nützlich u. a. bei der GUI-Programmierung in FreeBASIC! [Mehr Infos...]
FreeBASIC-Chat
Es sind Benutzer im FreeBASIC-Chat online.
(Stand:  )
FreeBASIC bei Twitter
Twitter FreeBASIC-Nachrichten jetzt auch über Twitter erhalten. Follow us!

Tutorial

OpenGL und FreeBASIC - eine kleine Einführung - Teil 5 - Texturen

von MitgliedEastler_dartSeite 2 von 6

Erst mal so einfach, wie es geht

Um fürs Erste nicht komplizierter als nötig zu werden, gehen
wir mal von festen Vorgaben aus:

Nehmen wir ein Beispielbild, in der Auflösung 128x128 Pixel,
welches auf unserer Festplatte im unkomprimierten Bitmapformat
steht.
Dafür hab ich hier das Bild mauer_128.bmp,
mauer_128.bmp
welches Sie per Rechtsklick und "Speichern unter"
auf Ihre Festplatte speichern können.
Das Bild sollten Sie in dasjenige Verzeichnis speichern,
in dem später die fertige Exe-Datei, die wir hier erarbeiten,
stehen wird.

So, also was haben wir als Grunddaten:
Eine Bilddatei, die Bildgröße ist 128x128 Pixel, die Farbtiefe
ist in dem Beispielbild 24-bit, die Datei ist nicht komprimiert.

die Datei in den Ram

Was wir brauchen, ist ein Ramspeicherbereich, in den das
Bild aus der Datei reinpaßt. Und da wir für unser erstes
Beispiel wissen, daß in der Datei ein bild mit 128x128 Pixel
liegt, können wir hier vorläufig mit festen Werten Arbeiten.

Da wir später diesen Rambereich an OpenGL so übergeben wollen,
daß bereits OpenGL V1.0 dies beherrscht, müssen wir uns auf
32-bit Farbtiefe im Ram festlegen. Der BLOAD-Befehl ist so clever,
daß er beim Einlesen einer Bitmap-Datei in einen RAM-BEREICH
die Daten in dasjenige Format umwandelt, mit dem ImageCreate den
Bereich initialisiert hat, bei uns also 32-bit.
Die Bildqualität liegt dabei bei 24-bit, die restlichen 8-bit
sind für den Alpha-Kanal reserviert, der ist in BMP-Dateien
aber normalerweise gar nicht vorhanden, sodaß dieser Wert
im Ramspeicher nur initialisiert wird.

Also egal, ob Sie nun eine Bmp-Datei in 1-bit, 4-bit, 8-bit
oder in 24-bit haben, wenn Sie diese per BLOAD in den mit
ImageCreate auf 32-bit reservierten Bereich laden, steht
sie dort als 24-bit-Bild.

Und somit sind die Fakten für ImageCreate jetzt vollständig:
Bild-Breite = 128pixel Bild-Hoehe= 128pixel Farb-Tiefe = 32bits

ImageCreate gibt uns dann einen Pointer/Zeiger auf den
Datenbereich im RAM zurück, den wir in einer Variablen
festhalten müssen. Also dimmen wir die Variable "DateiImRamPtr"
DIM AS BYTE PTR DateiImRamPtr
Dazu wäre zu sagen, laut FB-Hilfe sollte die Variable
als "ANY PTR" gedimmt werden. Für das weitere Bearbeiten
des RAM-Bildes bräuchten wir aber ein Byte-Pointer. So
wie ich das getestet habe, läuft das auch so, fehlerfrei.

Da wir nun eine Variable für den Zeiger haben, können wir
den Ram anfordern und mit schwarz(0) füllen:
DateiImRamPtr = IMAGECREATE(128, 128, 0, 32)

Bleibt nur noch, das Bild von der Festplatte in diesen
Rambereich reinkopieren:
BLOAD("mauer_128.bmp", DateiImRamPtr)
Bload will nur den Dateinamen und den Zeiger auf den RAM.
Am Dateinamen (.bmp)erkennt BLOAD, daß er eine Bitmap
einlesen soll, im Rambereich steht von ImageCreate schon
drin, daß das Bitmap-Bild mit 32-bit Farbtiefe in den
Ram geschrieben werden soll.

Daraus ergibt sich für das Kopieren der Festplattendatei
in den Ram folgendes Listing:

DIM AS BYTE PTR DateiImRamPtr
DateiImRamPtr = IMAGECREATE(128, 128, 0, 32)
BLOAD("mauer_128.bmp", DateiImRamPtr)

Bild im RAM für OpenGL anpassen

Jetzt wirds ein bisschen kniffelig.
Bis jetzt hatte ich immer behauptet, OpenGL würde
solche BMP-Bilder freudig annehmen. Ganz so einfach
ist es jedoch nicht. So eine Bitmap im Ram hat
nämlich die Werte für Rot und Blau vertauscht,
gegenüber dem, was OpenGL erwartet.

Das Problem liegt aber nicht bei FreeBasic,
sondern am Format der 24-bittigen Bitmaps.
Laut Dateibeschreibungen sind nur in dieser
Farbtiefe bereits in der Datei die Farben
vertauscht. In vielen anderen Fällen ist das
von Vorteil, bei uns hier mit FreeBasic und
OpenGL ist es leider eine Extrahürde.


Und auch noch zusätzlich steht das Problem an,
daß für FreeBasic es von Vorteil ist, wenn
das Bild beim Laden per BLOAD in den Ram
senkrecht gespiegelt wird. Tja, für FreeBasic
mag das ja ein Vorteil sein, bei uns stört das.
Jedoch nur, wenn die Ausrichtung eines Bildes
einen tieferen Sinn hat, z.B. Schriften drin
sind oder so.
Erst mal geh ich davon aus, daß wir nur einfache
Texturen verwenden, also Holzmaserungen, Mauer-
bilder oder so, und bei denen sehe ich keine
Notwendigkeit, diese grundsätzlich zurück
zu spiegeln.
Für das zurück Spiegeln müßten wir einen zweiten
Bildpuffer anlegen, was ich aber hier der zur
Vereinfachung mal noch nicht machen will.
Wir merken uns: mit dieser Routine wird an unserem
Bild Oben und Unten vertauscht!

Also (erst mal) nur das Farbproblem lösen.
Das bedeutet, wir müssen jetzt das Bild Pixel
für Pixel durchgehen, und die Werte von Rot
und Blau austauschen.

Kann man das nicht gleich beim Datei-Einlesen machen?
klar, das ginge irgendwie auch, wäre aber besonders
langsam, von Festplatte einzeln ein Pixel nach dem
anderen zu holen. Und, wir würden damit darauf
verzichten, daß FreeBasic die 4-Bit/8-Bit-Bilder
uns komfortabel automatisch auf 24=32-Bit umsetzt.
Per BLOAD ins RAM, Pixel für Pixel Farben tauschen,
das ist der schnellste Weg und auch 4 und 8-bit-Bilder
können damit ebenfalls als Textur verwendet werden.

Eigentlich sollte es jetzt ein Einfaches sein, per
POINT die Farbwerte eines Pixels in eine Variable
zu holen, die Farbwerte Rot und Blau in der Variable
austauschen und per PSET wieder zurück schreiben.
Jedoch mußte ich feststellen, daß es Konstellationen
gibt, bei denen dabei Fehler entstehen.
Z.B. wenn wir das OpenGL-Fenster 16-bittig öffnen,
den Ramspeicher für das Bild aber 32-bittig erstellen,
dann gehen POINT und PSET davon aus, das Bild würde
16-bittig wie das OpenGL-Fenster im Ram stehen,
obwohl im Rambereich des Bildes klar drin steht,
daß es 32-bittig ist.

Somit fallen POINT und PSET aus, der sicherste Weg,
den ich gefunden hab, ist, die einzelnen Bytes der
Farbwerte im RAM direkt anzusprechen.
Nun hoffe ich, daß Sie sich damit schon auskennen,
damit ich das nicht allzu ausschweifend erklären muß.
Notfalls finden Sie weitere Erklärungen dazu in der
FreeBasic-Hilfe oder im Forum.
Also nur kurz, RAM-Speicher direkt ansprechen:
Für unser Beispiel braucht man dazu eine Variable, die
nicht Werte sondern RAM-Adressen speichert, sogenannte
POINTER, z.B. DIM AS BYTE PTR ByteZeiger.
Würden wir z.B. nun PRINT ByteZeiger schreiben,
erhielten wir eine lange Zahl, welche die Adresse im
Ramspeicher darstellt. Würden wir den Inhalt dieser
RAM-Speicheradresse anzeigen wollen, müßten wir einfach
einen Stern vor den Variablennamen setzen:
   PRINT *ByteZeiger
Damit holt FreeBasic an der Speicheradresse den Inhalt
des dort stehenden Bytes (Byte-Pointer!).
In unserem Fall müssen wir jedoch noch dereferenzieren,
sowas wie bei der Post:
   "Drei Häuser weiter als Hauptstrasse 100"
und das geschieht mit den eckigen Klammern:
   PRINT ByteZeiger[3]
Man beachte hierbei: durch die eckigen Klammern geht
FreeBasic bereits davon aus, der Inhalt der Ram-Adresse
ist gemeint, nicht der Adressenwert - BEI VERWENDEN VON
ECKIGEN KLAMMERN KEIN STERN DAVOR SETZEN.
Als letzte Info noch das Verändern des Inhaltes der
Pointervariable ByteZeiger:
Da in einer Pointervariable Adresswerte(Ganzzahlen)
stehen, darf man auch direkt andere Zahlen reinsetzen.
Somit kann man z.B. mit ByteZeiger = ByteZeiger + 4
dafür sorgen, daß beim nächsten Verwenden dieser
Pointervariable im RAM auf das vier Bytes weiter hinten
liegende Byte zugegriffen wird. So würden:
   PRINT ByteZeiger[4]
und
   ByteZeiger=ByteZeiger+4 : PRINT *ByteZeiger
genau das Gleiche bewirken, im zweiten Beispiel steht
dann aber die um 4 erhöhte Adresse in der Pointervariablen
gegenüber dem ersten Beispiel


Also heißt es, mit welchem Konstrukt können wir
ein Pixel nach dem Anderen holen.
Klar, FOR-NEXT ist die Devise. Dabei verschachteln
wir zwei solcher Schleifen ineinander, sodaß wir
mit mit den Angaben von Breite in Pixel und Hoehe in Pixel
arbeiten können:
FOR ZeilenZaehler = 0 TO 127
FOR PunktZaehler = 0 TO 127
[hier die Farbumsetzungen]
NEXT PunktZaehler
NEXT ZeilenZaehler


Innerhalb der Schleife müssen wir dann von jedem Pixel
den Farbwert holen. Wie jedoch schon gesagt, steht an
der Adresse, die ImageCreate zurückgibt, vor den eigentlichen
Pixel-Daten noch andere Infos, wie Farbtiefe, Breite
Größe etc.
Eigentlich könnten wir nun ermitteln, wie lang dieser
"InfoVorspann" ist, und entsprechend soviele Bytes
weiter hinten anfangen zu lesen.
Ab FB Ver 0.20 gibt es den Befehl ImageInfo, mit dem
man direkt die Adresse der Speicherstelle des ersten
Pixels des Bildes kriegen könnte. Dazu müßten wir
aber viele Variablen etc anlegen, um dieses ImageInfo
durzuführen. Gleichzeitig würde unsere Routine "nur"
ab FB-Version 0.20 laufen.
Deshalb hab ich einen kürzeren Weg gesucht. Im Grunde
ist es einfach, im Ram stehen Vorspann und Pixeldaten
hintereinander weg.
ImageCreate-Adresse + Länge des Vorspanns wäre die
Adresse für das erste Pixel des Bildes.
Und dieser Vorspann hat FreeBasic als Datentyp
hinterlegt, der heißt FB.IMAGE.
Die Länge eines Datentypes erhält man mit dem Befehl
LEN(). Somit liefert LEN(FB.IMAGE) die
Länge des Vorspanns. Wenn man nun diesen Wert zu der
Adresse von ImageCreate dazuzählt, hat man die
RAM-Adresse des ersten Pixels.
Wir dürfen die AdressVariable von ImageCreate jedoch
nicht verändern, um später den RAM-Speicher
auch wieder freigeben zu können, brauchen wir diese
Adresse noch.
Also Dimmen wir eine Zweite Byte-Pointer-Variable,
in die wir die Adresse der Pixeldaten reinsetzen:
DIM AS BYTE PTR SpBmpPixelZeiger
und belegen diese Variable mit der Adresse des
ersten Bildpixels:
SpBmpPixelZeiger = DateiImRamPtr + Len(FB.IMAGE)
(natürlich dimmen und belegen wir SpBmpPixelZeiger
noch vor dem Beginn der Schleifen)

Innerhalb der Schleifen wollen wir ja Rot und Blau
vertauschen, dazu brauchen wir Variablen, um die
beiden Werte erst mal zwischenzuspeichern.
DIM AS UINTEGER RotWert, BlauWert
Da die einzelnen Farbwerte die Werte von 0 bis 255
haben können, passen sie super in UINTEGER-Variablen.

So, Adresse der Pixel ist da, Zwischenspeicher
der Farbwerte ist da, somit können wir jetzt die
notwendigen Funktionen in die Schleifen setzen.
Einfach das Byte an Adresse SpBmpPixelZeiger für
den Rot-Wert einlesen, und das 2 Bytes dahinter
liegende für den Blauwert:
BlauWert = SpBmpPixelZeiger[0]
RotWert = SpBmpPixelZeiger[2]

und nun vertauscht zurückschreiben:
SpBmpPixelZeiger[0] = RotWert
SpBmpPixelZeiger[2] = BlauWert

Nun noch für den nächsten FOR-NEXT-Schleifendurchlauf
die Adresse der Pixeldaten um 4(bytes) erhöhen,
damit danach auf das zweite.... Pixel zugegriffen wird
(Sie erinnern sich? 32-bit speicherfarbtiefe = 4 bytes)
SpBmpPixelZeiger = SpBmpPixelZeiger+4

Damit hätten wir für das Farbe-Umsetzen folgendes
(Teil-)listing:

DIM AS BYTE PTR SpBmpPixelZeiger
DIM AS UINTEGER RotWert, BlauWert
DIM AS UINTEGER ZeilenZaehler, PunktZaehler
SpBmpPixelZeiger = DateiImRamPtr + Len(FB.IMAGE)
FOR ZeilenZaehler = 0 TO 127
   FOR PunktZaehler = 0 TO 127
      BlauWert = SpBmpPixelZeiger[0]        :'aus Ram das Byte mit dem Blauwert des grad zu bearbeitenden Pixels holen
      RotWert  = SpBmpPixelZeiger[2]        :'aus Ram das Byte mit dem Rotwert des grad zu bearbeitenden Pixels holen
      SpBmpPixelZeiger[0] = RotWert         :'den Rotwert an die Stelle des Blauwertes im RAM schreiben
      SpBmpPixelZeiger[2] = BlauWert        :'den Blauwert an die Stelle des Rotwertes im RAM schreiben
      SpBmpPixelZeiger = SpBmpPixelZeiger+4 :'Ram-Adresse um 4 bytes erhöhen = Start Pixeldaten des nächsten Pixels
   NEXT PunktZaehler
NEXT ZeilenZaehler

aufbereitetes Bild im Ram an OpenGL uebergeben

Nun haben wir die Daten so, wie wir sie OpenGL übergeben können.
Also ran ans Werk.

Als Erstes müssen wir OpenGL um einen Namen für die im
Grafikartenspeicher abzulegende Textur bitten, mit
glGenTextures 1, @TexturNummer
erledigen wir dies, wobei die Variable TexturNummer zuvor
als UINTEGER gedimmt sein sollte. Man beachte, daß hier
(das @-Zeichen davor) nicht die Variable, sondern deren
Adresse im RAM zu übergeben ist!
OpenGL schreibt in diese Variable "TexturNummer" den
Namen der Textur, für die nun "Platz" auf der Grafikkarte
eingerichtet ist.
Dabei spreche ich von TexturNAMEN, weil OpenGL (vorerst?)
zwar nur Nummern zurück gibt, diese jedoch bei mehreren Texturen
nicht zwingend fortlaufend sind, sondern auch wahllos
irgendwelche Zahlen sein können.

Nachdem nun ein Platz gerichtet ist, muß dieser unbedingt
gleich angesprochen werden, dies geschieht mit:
glBindTexture GL_TEXTURE_2D, TexturNummer

Damit haben wir alles, was wir für die Übergabe des Bildes
an OpenGL brauchen.
Auf der Grafikarte ist eine Textur im Index angelegt, wir
brauchen nur noch die Bilddaten zu übergeben.
Das geschieht für unsere Zwecke per glTexImage2D
wir wollen ja eine zweidimensionale Textur (Bild mit Höhe und Breite).
Als Parameter verlangt glTexImage2D folgende:
1: TextureArt = für uns immer GL_TEXTURE_2D
2: DetailMenge = für uns erst mal 0, hier könnte man mehrere
Texturbilder anlegen, die je nach Entfernung Kamera/Objekt
verwendet werden (= für den Anfang zu kompliziert)
3: Art, wie die Daten auf der Grafikkarte zu speichern sind.
Für unser erestes Beispiel hier, nehmen wir GL_RGB, daß heißt
daß wir RotWerte, GrünWerte und BlauWerte ablegen wollen,
(noch) keine Alpha-Werte. Mit diese Einstellung läuft unser
Beispiel in OpenGL-Fenstern von 15/16/24 und auch 32-bit Farbtiefe.
4:Breite des Bildes in Pixel - in unserem Beispiel also 128
5:Hoehe des Bildes in Pixel - in unserem Beispiel 128
6: Rahmen um die Textur? (0=keiner, 1=ja) - wir wollen keinen Rahmen = 0
7: Format der Farbangaben im RamSpeicher, bei uns GL_RGBA = 1Byte für Rot, eins für Grün, eins für Blau und noch eins für Alpha (=32-bit)
8: Datentyp im Ram, wir haben GL_UNSIGNED_BYTES dort stehen (Bytes die 1-255 als Wert aufnehmen können)
9: Adresse im RAM der Daten des ersten Pixels - ImageCreateAdresse + Vorspann

In der Zusammenfassung des gerade Gesagten sieht die
Übergabe des Bildes an OpenGL so aus:
glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)

zum Schluß sollten wir noch an den Filtern Texture_MIN und
Texture_MAG die Parameter setzen. Hier entscheiden wir darüber,
wie OpenGL mit den Texturen umgeht, wenn wir mit der Kamera
gaaanz weit weg vom Objekt sind, die texturierte Fläche also
recht klein ausfällt, und auch, wenn wir gaaanz nah dran
sind, die texturrierte Fläche also auf unsere Nase klebt.
Hier gilt, GL_Linear gibt ein schöneres Bild,
GL_Nearest spart Rechenpower, eine Textur, welche durch
GL_Nearest gefiltert wird bei zu nah dran oder zu weit weg
ungenau, die (errechneten)Pixel werden manchmal zu Blöcken.
Sie können auch für GL_TEXTURE_MIN_FILTER (wenn die Kamera weit weg ist)
einen anderen Wert als für GL_TEXTURE_MAG_FILTER(Kamera nah dran)
angeben, Ausprobieren ist gefragt. Mit GL_LINEAR auf beiden
Filtern ist die Textur immer schön weich gezeichnet.
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR

So, nachdem wir das Bild auch auf der Grafikarte gespeichert
haben können wir den reservierten RAM-Speicher wieder frei geben.
mit ImageDestroy(DateiImRamPtr) wird das Bild im RAM wieder
gelöscht, der Arbeitsspeicher ist wieder zugänglich.

Für den Teil "Bild an OpenGL" ergibt sich somit folgendes Listing:

DIM AS UINTEGER TexturNummer
glGenTextures 1, @TexturNummer
glBindTexture GL_TEXTURE_2D, TexturNummer
glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR
ImageDestroy(DateiImRamPtr)

Bild bei OpenGL als Texture verwenden

So, OpenGL hat nun unser Bild, wir können es mit dem "Namen,
der in unserer Variable TexturNummer liegt, verwenden.

Dazu müssen wir erst mal Texturieren erlauben:
glEnable GL_TEXTURE_2D
Danach noch eine Einstellung machen, daß OpenGL unser Bild
ohne Rücksicht auf Objektfarbe etc einfach draufklebt,
soll heißen, wie ein Abziehbild draufkleben, nix schimmert
durch oder sonstige Extras:
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)

Nun aktiviert man ein(eines der vielen? an OpenGL übergebenen) Bild
bei OpenGL als Texturebild, alles, was nach diesem Befehl kommt und
Texturen verwendet, nimmt dann dieses Bild dazu.
Bei mehreren Texturen muß man halt eins Aktivieren, das dazugehörige
Objekt erstellen, ein anderes aktivieren und danach das andere Objekt
erstellen.
Also sagen wir OpenGL, das es "unser" Bild künftig für Texturen verwenden
soll (in TexturNummer liegt der Name unseres Bildes bei OpenGL):
glBindTexture GL_TEXTURE_2D, TexturNummer

So, jetzt nur noch das Texturieren selber.
Ein Objekt erstellen, und dem eine Textur draufkleben bedeutet,
für jeden Punkt des Objektes angeben, wo dieser Punkt auf dem
Texturbild liegen soll.
Damit kann man Texturbilder verzerren, verziehen oder zurecht
biegen.
Klar, wir wollen hier immer noch so einfach wie möglich vorgehen,
wir erstellen also ein Viereck.
Solch ein Viereck besteht ja aus vier Punkten, die wir deffinieren
müssen. Zu jedem Punkt sagen wir dann gleich mit dazu, von welcher
Stelle im Texturbild das Pixel an diesem Punkt genommen wird.
Solche Angaben müssen vor der eigentlichen Punkt-Deffinition
im 3D-Raum erfolgen.

Hier mal eine Übersicht:
Ogl_tut_5_texturKoordinaten
Hier hab ich demonstrativ das Viereck in einer Größe
von 2 Einheiten hoch und 2 Einheiten breit angelegt,
um zu zeigen, daß die Werte des Vierecks nichts mit
den Werten im Texturbild zu tun haben!

Im Texturbild wird mittels Kommazahlen ein Punkt im Bild
per X-Pos, Y-Pos adressiert.
Dabei gilt, der Wert 0(null) = auf X ganz links, auf y ganz unten.
der Wert 1 auf X ist ganz rechts, auf Y ganz oben.
Es spielt keine Rolle, in welcher Pixelgröße (bei uns 128x128)
das Texturbild im Speicher steht.
Ob 64x64 oder gar 256,256, die Pos 1,1 ist immer ganz rechts und ganz oben,
die Pos 0,0 ganz links und ganz unten.

Natürlich können Zwischenwerte angegeben werden,
so z.B. der Bildmittelpunkt ist 0.5, 0.5,
also 0.5 in X-Richtung und 0.5 in Y-Richtung.
Aber auch z.B. 0.256 oder 0.3785961 sind möglich.
Würden wir z.B. in unserem Bild in X-Richtung das
67.Pixel brauchen, da wäre die Pos in X-Richtung
1.0/128*67 = 0.5234375.

Der Vollständigkeit halber muß ich noch erwähnen,
daß auch Werte größer 1.0 "richtige" Werte sind, sie stehen dafür,
daß OpenGL das Quellbild mehrfach neben- / untereinander
auf das Objekt klebt, z.B. mit den TexKoordinaten 3.0, 3.0
würde das Quellbild 3x neben und 3x untereinander zusammen-
geklebt werden, also 9 mal für die Fläche verwendet.

Aber wir machen es uns ja einfach, einfach die Ecken des Bildes
entsprechend auf die Ecken des Vierecks setzen.
Und dazu verwenden wir den Befehl von OpenGL:
glTexCoord mit einer 2 für zwei Koordinatenangaben(x/y)
und einem d für doppelte Wertgenauigkeit.
Also glTexCoord2d. Dahinter einfach die X-Pos und
dann die Y-Pos im Bild als Kommazahl.
Und dieser Punkt im Bild wird dem nachfolgenden 3D-Punkt zugeordnet.
Also: zuerst ein Pixel im Bild festlegen, danach den Punkt des
Objekts deffinieren. Danach das nächste Pixel im Bild benennen und
den nächsten 3D-Punkt des Objektes angeben usw.
Damit wir die Zuordnung eines Texturpunktes auf einen Punkt unseres
3D-Objektes besser erkennen können, schreiben wir die beiden
Befehle in eine einzige Zeile.
Sie wissen ja, mehrere Befehle in einer Zeile werden durch
Doppelpunkt voneinander getrennt.

Dem entsprechend und anhand des Übersichtsbildes vorhin,
ergibt sich folgende Objektdeffinition für unser Viereck:

glBegin GL_QUADS :'Beginn Beschreibungsliste Objekt Viereck
glTexCoord2d 0.0, 1.0 : glVertex3f -1.0, 1.0, -4.0 :'die linke obere Ecke des Rechtecks
glTexCoord2d 0.0, 0.0 : glVertex3f -1.0, -1.0, -4.0 :'die linke untere Ecke des Rechtecks
glTexCoord2d 1.0, 0.0 : glVertex3f +1.0, -1.0, -4.0 :'die rechte untere Ecke des Rechtecks
glTexCoord2d 1.0, 1.0 : glVertex3f +1.0, 1.0, -4.0 :'die rechte obere Ecke des Rechtecks
glEnd :'Fertig mit dem Objekt


Und jetzt alles zusammen

Ja, wir haben alles. Eine Code-Schnipserl, um Bmp-Bilddateien
von der Festplatte in den Ram zu kopieren, einen umd dieses
Ram-Bild farblich richtig zu stellen, und den dritten, um
dieses korrigierte Bild im Ram an OpenGL zu geben.
Dann hätten wir auch die Routine, um die Textur auf einem
Viereck zu verwenden.

Das ist zwar alles (noch) sehr speziell darauf zugeschnitten,
daß ein Bild mauer_128.bmp unkomprimiert und in
der Größe 128x128 Pixel in genau dem gleichen Verzeichnis
liegt, wie unser kompiliertes Programm, aber es würde
funktionieren. :-)

Wir wollen das doch mal "live" sehen, oder?
Also nehmen wir unser Listing aus Tutorial 4, und setzen die
hier erarbeiteten Code-Abschnitte an die richtigen Stellen:

'REM Tutorial 4 - OpenGL-Tutorial von Eastler
'-------------------------
'DIMs
'-------------------------
DIM SHARED AS STRING Tastendruck
DIM SHARED AS STRING Tlinks, Trechts, Tvor, Tzurueck, TCtrlVor, TCtrlZurueck, TCtrlLinks, TCtrlRechts
Tlinks       = CHR(255) & CHR( 75)  :' beim Drücken der Taste CursorLinks gibt die Tastatur CHR(255) & CHR( 75) zurück
Trechts      = CHR(255) & CHR( 77)  :' CursorRechts
Tvor         = CHR(255) & CHR( 72)  :' CursorHoch
Tzurueck     = CHR(255) & CHR( 80)  :' CursorRunter
TCtrlVor     = CHR(255) & CHR(141)  :'Ctrl oder STRG zusammen mit CursorHoch
TCtrlZurueck = CHR(255) & CHR(145)  :'Ctrl oder STRG zusammen mit CursorRunter
TCtrlLinks   = CHR(255) & CHR(115)  :'<--------CURSOR NACH LINKS-Variable mit Wert belegen
TCtrlRechts  = CHR(255) & CHR(116)  :'<--------CURSOR NACH RECHTS-Variable mit Wert belegenDIM AS SINGLE XRichtg, YRichtg, ZRichtg
DIM AS SINGLE XRichtg, YRichtg, ZRichtg
DIM AS SINGLE PyraXDrehw, PyraYDrehw, PyraZDrehw :'<------------------------ für Pyramide per Tasten drehen
DIM AS SINGLE WeltDrehX, WeltDrehY, WeltDrehZ
DIM AS INTEGER ConNr                :' Dateinummerspeicher beim Consolefenster öffnen
DIM SHARED AS DOUBLE Pi
Pi = 3.14159265358979323846
'-------------------------
'Includes
'-------------------------
#include "fbgfx.bi"
#include once "GL/gl.bi"
#include once "GL/glu.bi"
#include once "vbcompat.bi" '<--------------um Zahlen per "format()" formatieren zu können

'-------------------------
'Declarationen
'-------------------------
DECLARE SUB Koordinatensystem(was AS STRING, TxtPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE SUB Schachbrettboden(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE SUB Pyramide (was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)

'-------------------------
' das Fenster öffnen
'-------------------------
screen 19, 32, , 2

'-------------------------
' Open-GL Init
'-------------------------
glViewport 0, 0, 800, 600                      ' den Current Viewport auf eine Ausgangsposition setzen
glMatrixMode GL_PROJECTION                     ' Den Matrix-Modus Projection wählen
glLoadIdentity                                 ' Diesen Modus auf Anfangswerte setzen
gluPerspective 45.0, 800.0/600.0, 0.1, 100.0   ' Grundeinstellungen des Anezeigefensters festlegen
glMatrixMode GL_MODELVIEW                      ' Auf den Matrix-Modus Modelview schalten
glLoadIdentity                                 ' und auch diesen auf Anfangswerte setzen
'-----------------------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX HIER DAS CODESTÜCK Von Festplatte in den RAM XXXXXXXXXXXXXXXX
'-----------------------------------------------------------------------------------
DIM AS BYTE PTR DateiImRamPtr
DateiImRamPtr = IMAGECREATE(128, 128, 0, 32)
BLOAD("mauer_128.bmp", DateiImRamPtr)
'---------------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX GLEICH WEITER MIT FARBEN AUSTAUSCHEN XXXXXXXXXXXXXXXX
'---------------------------------------------------------------------------
DIM AS BYTE PTR SpBmpPixelZeiger
DIM AS UINTEGER RotWert, BlauWert
DIM AS UINTEGER ZeilenZaehler, PunktZaehler
SpBmpPixelZeiger = DateiImRamPtr + Len(FB.IMAGE)
FOR ZeilenZaehler = 0 TO 127
   FOR PunktZaehler = 0 TO 127
      BlauWert = SpBmpPixelZeiger[0]        :'aus Ram das Byte mit dem Blauwert des grad zu bearbeitenden Pixels holen
      RotWert  = SpBmpPixelZeiger[2]        :'aus Ram das Byte mit dem Rotwert des grad zu bearbeitenden Pixels holen
      SpBmpPixelZeiger[0] = RotWert         :'den Rotwert an die Stelle des Blauwertes im RAM schreiben
      SpBmpPixelZeiger[2] = BlauWert        :'den Blauwert an die Stelle des Rotwertes im RAM schreiben
      SpBmpPixelZeiger = SpBmpPixelZeiger+4 :'Ram-Adresse um 4 bytes erhöhen = Start Pixeldaten des nächsten Pixels
   NEXT PunktZaehler
NEXT ZeilenZaehler
'---------------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX UND RAM-BILD AN OPENGL ÜBERGEBRN XXXXXXXXXXXXXXXX
'---------------------------------------------------------------------------
DIM AS UINTEGER TexturNummer
glGenTextures 1, @TexturNummer
glBindTexture GL_TEXTURE_2D, TexturNummer
glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR
ImageDestroy(DateiImRamPtr)
'---------------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'---------------------------------------------------------------------------
glClearColor 0.5, 0.5, 0.50, 0.0               ' Setze Farbe für löschen auf Mittelgrau
glClearDepth 1.0                               ' Depth-Buffer Löschen erlauben
glEnable GL_DEPTH_TEST                         ' den Tiefentest GL_DEPTH_TEST einschalten
glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT  'Tiefen- und Farbpufferbits löschen
'---------------------------
'HAUPTTEIL
'---------------------------
Schachbrettboden("SetzKantenLaenge", "", 0.5,  0,    0)
Schachbrettboden("SetzQuadsZahl",    "", 24,   24,   0)     :'6 Einheiten +und- = 12Einheiten Koordiantensystem, bei 0.5 Quadgröße 24 Stück
Schachbrettboden("SetzFarbe1",       "", 0,    0,    0.5)   :'erste Farbe dunkleres blau
Schachbrettboden("SetzFarbe2",       "", 0.25, 0.25, 0.25)  :'erste Farbe dunkles grau
Schachbrettboden("SetzStartPos",     "", -6,   -6,   -1)     :'ganz vorne ganz links beginnen, Boden auf Hoehe(3.Para)-1 verlegen(Y)

Pyramide("SetzLaengen" , "", 1, 1,  1)
Pyramide("SetzFarbe1"  , "", 1, 0, 0)
Pyramide("SetzFarbe2"  , "", 0, 1, 0)
Pyramide("SetzFarbe3"  , "", 0, 0, 1)
Pyramide("SetzFarbe4"  , "", 1, 1, 0)
Pyramide("SetzFarbe5"  , "", 1, 0, 1)

DO UNTIL Tastendruck = CHR(27)                         :'die Schleife solange immer wiederholen, bis in der Variablen Tastendruck die Esc-Taste (chr(27) steht
   Tastendruck = INKEY                                 :'Jeder Tastendruck wird sofort in die Variable Tastendruck gespeichert
   '---------------------------
   'ProgrammSchleife
   '---------------------------

   'JE NACH TASTENDRUCK DEN ENTSPRECHENDEN POSITIONSWERT VERÄNDERN
   SELECT CASE Tastendruck
      CASE "K", "K"
         Koordinatensystem("AnAus", "", 0, 0, 0) :' Schalter umstellen, falls An ist, auf Aus, sonst auf An
      CASE TCtrlrechts                :'Falls die Tasten "Cursor nach rechts" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         XRichtg = XRichtg - COS((WeltDrehY * Pi) / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg - SIN((WeltDrehY * Pi) / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TCtrllinks                 :'Falls die Tasten "Cursor nach rechts" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY * Pi) / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY * Pi) / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TCtrlzurueck               :'Falls die Tasten "Cursor unten" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         YRichtg = YRichtg + 0.04 :'HOCH und Runter bleibt unberührt von der Y-Drehung
      CASE TCtrlvor                   :'Falls die Tasten "Cursor hoch" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         YRichtg = YRichtg - 0.04 :'HOCH und Runter bleibt unberührt von der Y-Drehung
      CASE TZurueck                   :'Falls die Taste "Cursor runter" gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY-90) * Pi / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY-90) * Pi / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TVor                       :'Falls die Taste "Cursor hoch" gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY+90) * Pi / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY+90) * Pi / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TLinks                      :'Falls die Taste "Cursor links" gedrückt wurde
         WeltDrehY = WeltDrehY - 1
      CASE TRechts                     :'Falls die Taste "Cursor rechts" gedrückt wurde
         WeltDrehY = WeltDrehY + 1
      CASE "x"
         PyraXDrehw=PyraXDrehw+1
      CASE "x"
         PyraXDrehw=PyraXDrehw-1
      CASE "y"
         PyraYDrehw=PyraYDrehw+12
      CASE "y"
         PyraYDrehw=PyraYDrehw-12
      CASE "z"
         PyraZDrehw=PyraZDrehw+1
      CASE "z"
         PyraZDrehw=PyraZDrehw-1
      CASE "i", "i"                       :'<------------------------
         ConNr = FREEFILE                 :'nächste freie Dateinummer für öffnen von FreeBasic holen und in ConNr merken
         open "con" FOR OUTPUT AS #ConNr  :'und nun die Konsole als Datei unter Nr ConNr öffnen
            PRINT #ConNr, "PyraXDrehw:" & format(PyraXDrehw,"0.00") & "  PyraYDrehw:" & format(PyraYDrehw,"0.00") & "  PyraZDrehw:" & format(PyraZDrehw,"0.00")
            PRINT #ConNr, "XRichtg:" & format(XRichtg,"0.00") & "  YRichtg:" & format(YRichtg,"0.00") & "  ZRichtg:" & format(ZRichtg,"0.00")
         CLOSE #ConNr
   END SELECT   'PRÜFEN, DASS DIE POSITIONSWERTE IN ALLEN DREI RICHTUNGEN ZWISCHEN -3 und +3 BLEIBEN
   IF XRichtg >  6 THEN XRichtg =  6           :'falls zu weit rechts, bei  6 festnageln
   IF XRichtg < -6 THEN XRichtg = -6           :'falls zu weit links,  bei -6 festnageln
   IF YRichtg >  6 THEN YRichtg =  6           :'falls zu weit hoch,   bei  6festnageln
   IF YRichtg < -6 THEN YRichtg = -6           :'falls zu weit runter, bei -6 festnageln
   IF ZRichtg >  5 THEN ZRichtg =  5           :'falls zu weit zurück, bei 10 festnageln
   IF ZRichtg < -10 THEN ZRichtg = -10           :'falls zu weit vor,    bei -6 festnageln
   glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT  :'bisherig erstellte Objekte löschen, unsere 3D-Welt wieder von Neuem an erstellen
   glPushMatrix                                        :'aktuelle Position + Drehgrade sichern (2.Zettel mit gleicher Pos auf PositionsSTACK)
    glRotatef WeltDrehY, 0, 1, 0                       :'Rotationsbefehl der Kamera, um Y ist zu drehen
    glTranslatef XRichtg, YRichtg, ZRichtg             :'Verschiebung der Kamera, auf neue Werte einstellen

    '-------------------------
    'AB HIER OBJEKTE ERSTELLEN
    '-------------------------
    glPointSize(5)                             :' Punktgröße auf 5 = deutlich groß
    glColor3f 1.0,0.0,0.0                      :' Anzeigefarbe auf ROT setzen
    Koordinatensystem("Anzeigen", "", 0, 0, 0) :' den Stringwert "Anzeigen" übergeben
    Schachbrettboden("BeschreibungsListeBoden", "", 0, 0, 0):' SchachbrettBoden in der 3D-Welt erstellen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'die erste Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    glTranslatef -2, 0, 0                                                    :'hier der Verschiebebefehl, damit die 2.Pyramide woanderst steht
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'und eine zweite Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen

    '-----------------
    'OBJEKTE ERSTELLEN
    '-----------------

'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX RECHTECK MIT TEXTUR ERZEUGEN XXXXXXXXXXXXXXXX
'-------------------------------------------------------------------
   glEnable GL_TEXTURE_2D          :'Texturieren mit dem grad aktiven Bild einschalten/erlauben
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL) :'texturieren "voll drauf" kein durchsichtig etc
   glBindTexture GL_TEXTURE_2D, TexturNummer      :'Unser Bild auf der Grafikkarte als aktives setzen
   glColor3f 1.0,0.0,0.0                          :'Anzeigefarbe (für das Rechteck) auf ROT setzen
   glBegin GL_QUADS                               :'Beginn Beschreibungsliste Objekt Viereck
      glTexCoord2d 0.0, 1.0 : glVertex3f -1.0,  1.0, -4.0  :'die linke obere Ecke des Rechtecks
      glTexCoord2d 0.0, 0.0 : glVertex3f -1.0, -1.0, -4.0  :'die linke untere Ecke des Rechtecks
      glTexCoord2d 1.0, 0.0 : glVertex3f +1.0, -1.0, -4.0  :'die rechte untere Ecke des Rechtecks
      glTexCoord2d 1.0, 1.0 : glVertex3f +1.0,  1.0, -4.0  :'die rechte obere Ecke des Rechtecks
   glEnd                                          :'Fertig mit dem Objekt
   glDisable GL_TEXTURE_2D         :'Texturieren ausschalten (es soll ja nur das Viereck texturiert werden, nicht die Pyramiden)
'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'-------------------------------------------------------------------


    flip                                               :'liebes OpenGL, zeig alles, was in der Schleife für dich vornedran steht, auf Monitor an
   '---------------------------
   'Ende der Schleife
   '---------------------------
   glPopMatrix                                         :' Aufgabe erledigt, den zweiten Zettel mit der geänderten Pos wegschmeißen, dann ist Alte Pos wieder aktuelle Pos
LOOP

END

'--------------------------------
SUB Koordinatensystem(was AS STRING, TxtPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   DIM AS INTEGER Zaehler
   STATIC AS INTEGER AnzeigeSchalter
   SELECT CASE UCASE(was)
      CASE "AnAus"
         IF AnzeigeSchalter = 0 THEN AnzeigeSchalter = 1 ELSE AnzeigeSchalter = 0
      CASE "Anzeigen"
        IF AnzeigeSchalter = 1 THEN
         glBegin GL_LINES:' Ja, wir beginnen mit der Beschreibungsliste fuer Linien
         'und zwar zuerst die positiven Bereiche jeder Achse
         glColor3f 1.0, 1.0, 1.0        :' Positives Maß = weiße Teilstriche (Rot 1.0, blau 1.0, grün 1.0 = weiß)
         FOR Zaehler = 1 TO 6 :'fuer die Maßeinheitspositionen 1, 2 und 3 Striche Ziehen
            'X-ACHSE
               'senkrechter Strich (wie Y-Achse)
            glVertex3f  Zaehler+Para1,  0.2+Para2,  0.0+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1, -0.2+Para2,  0.0+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
               'waagerechter Strich (wie Z-Achse)
            glVertex3f  Zaehler+Para1,  0.0+Para2,  0.2+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1,  0.0+Para2, -0.2+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'Y-ACHSE
               'Strich wie X-Achse
            glVertex3f  0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach +X, Tiefe = 0
            glVertex3f -0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach -X, Tiefe = 0
               'Strich wie Z-Achse
            glVertex3f  0.0+Para1, Zaehler+Para2,  0.2+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach vorne/+Z, Seite (X) = 0
            glVertex3f  0.0+Para1, Zaehler+Para2, -0.2+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach hinten/-Z, Seite (X) = 0
            'Z-ACHSE
               'Strich waagerecht (X)
            glVertex3f  0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach rechts +X, Hoehe = 0
            glVertex3f -0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach links -X, Hoehe = 0
               'Strich senkreicht (Y)
            glVertex3f  0.0+Para1,  0.2+Para2,  Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach oben +Y, Seite = 0
            glVertex3f  0.0+Para1, -0.2+Para2,  Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach unten -Y, Seite = 0
         NEXT Zaehler
         glColor3f 0.0, 0.0, 0.0        :' Negatives Maß = schwrze Teilstriche (alles auf 0)
         FOR Zaehler = -6 TO -1 :'fuer die Maßeinheitspositionen -3, -2 und -1 Striche Ziehen
         'X-ACHSE
            'senkrechter Strich (wie Y-Achse)
            glVertex3f  Zaehler+Para1,  0.2+Para2,  0.0+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1, -0.2+Para2,  0.0+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'waagerechter Strich (wie Z-Achse)
            glVertex3f  Zaehler+Para1,  0.0+Para2,  0.2+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1,  0.0+Para2, -0.2+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'Y-ACHSE
               'Strich wie X-Achse
            glVertex3f  0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach +X, Tiefe = 0
            glVertex3f -0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach -X, Tiefe = 0
               'Strich wie Z-Achse
            glVertex3f  0.0+Para1, Zaehler+Para2,  0.2+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach vorne/+Z, Seite (X) = 0
            glVertex3f  0.0+Para1, Zaehler+Para2, -0.2+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach hinten/-Z, Seite (X) = 0
            'Z-ACHSE
               'Strich waagerecht (X)
            glVertex3f  0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach rechts +X, Hoehe = 0
            glVertex3f -0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach links -X, Hoehe = 0
               'Strich senkreicht (Y)
            glVertex3f  0.0+Para1,  0.2+Para2,  Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach oben +Y, Seite = 0
            glVertex3f  0.0+Para1, -0.2+Para2,  Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach unten -Y, Seite = 0
         NEXT Zaehler
         'UND NUN DIE DREI ACHSEN SELBST:
         'X-ACHSE
         glColor3f 1.0, 0.0, 0.0        :' Xachse = rot
         glVertex3f  -6.0+Para1, 0+Para2, 0+Para3
         glVertex3f  +6.0+Para1, 0+Para2, 0+Para3
         'Y-ACHSE
         glColor3f 0.0, 1.0, 0.0        :' Yachse = grün
         glVertex3f  0+Para1, -6.0+Para2, 0+Para3
         glVertex3f  0+Para1, +6.0+Para2, 0+Para3
         'Z-ACHSE
         glColor3f 0.0, 0.0, 1.0        :' Zachse = blau
         glVertex3f  0+Para1, 0+Para2, -6.0+Para3
         glVertex3f  0+Para1, 0+Para2, +6.0+Para3
         glEnd
       END IF
   END SELECT
END SUB
'-------------------------
SUB Schachbrettboden(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   STATIC AS SINGLE QuadKantenLaenge, QuadsQuer, QuadsTief
   STATIC AS SINGLE Farbe1Rot, Farbe1Gruen, Farbe1Blau
   STATIC AS SINGLE Farbe2Rot, Farbe2Gruen, Farbe2Blau
   STATIC AS SINGLE StartPosX, StartPosZ, BodenHoehe
   STATIC AS INTEGER ZaehlerX, ZaehlerZ                   :'ForNext-Zählvars, ggf. rekursiver aufruf dieser Sub, drum STATIC
   SELECT CASE UCASE(was)
      CASE "SetzKantenLaenge"
         'Aufruf hierfür in Para1 die Länge der Kanten der Quadrate
         QuadKantenLaenge = Para1
      CASE "SetzQuadsZahl"
         'Aufruf hierfür in Para1 die Anzahl von Quads quer,
         'in Para2 die Anzahl in der Tiefenrichtung
         QuadsQuer = Para1
         QuadsTief = Para2
      CASE "SetzFarbe1"
         'in Para1 Para2 Para2 die Rot Grün und Blauwerte der ersten Quadratfarbe
         Farbe1Rot   = Para1
         Farbe1Gruen = Para2
         Farbe1Blau  = Para3
      CASE "SetzFarbe2"
         'in Para1 Para2 Para2 die Rot Grün und Blauwerte der ersten Quadratfarbe
         Farbe2Rot   = Para1
         Farbe2Gruen = Para2
         Farbe2Blau  = Para3
      CASE "SetzStartPos"
         'in Para1 Para2 Para2 die X und Z Position, von wo begonnen wird,
         'die vielen Quadrate zu erstellen
         StartPosX   = Para1
         StartPosZ   = Para2
         BodenHoehe  = Para3
      CASE "BeschreibungsListeBoden"
         'Hier erstellen wir die FOR-NEXT-SCHLEIFEN,
         'welche die OpenGL-Beschreibungsliste
         'zum Anzeigen des Bodens erstellen
         glBegin GL_QUADS
            FOR ZaehlerX = 1 TO QuadsQuer     :'Laufnr des grad zu zeichnenden Quads auf der X-Achse
               FOR ZaehlerZ = 1 TO QuadsTief  :'Laufnr des grad zu zeichnenden Quads auf der Z-Achse
                  'Die Farbe festlegen
                  IF ((ZaehlerX+ZaehlerZ)\2)*2=(ZaehlerX+ZaehlerZ) THEN
                     'Wenn die Summe von ZahlerX+ZaehlerY gerade ist Farbe1 nehmen
                     glColor3f Farbe1Rot, Farbe1Gruen, Farbe1Blau
                  ELSE
                     'Wenn die Summe von ZahlerX+ZaehlerY UNgerade ist Farbe2 nehmen
                     glColor3f Farbe2Rot, Farbe2Gruen, Farbe2Blau
                  END IF
                  'Die Eckpunkte der Quadrate benennen wir in der Reihenfolge GEGEN DEN UHRZEIGERSINN, von oben draufschauend
                  glVertex3f StartPosX+(ZaehlerX-1)*QuadKantenLaenge,  BodenHoehe,  StartPosZ+(ZaehlerZ-1)*QuadKantenLaenge
                  glVertex3f StartPosX+ ZaehlerX   *QuadKantenLaenge,  BodenHoehe,  StartPosZ+(ZaehlerZ-1)*QuadKantenLaenge
                  glVertex3f StartPosX+ ZaehlerX   *QuadKantenLaenge,  BodenHoehe,  StartPosZ+ ZaehlerZ   *QuadKantenLaenge
                  glVertex3f StartPosX+(ZaehlerX-1)*QuadKantenLaenge,  BodenHoehe,  StartPosZ+ ZaehlerZ   *QuadKantenLaenge
               NEXT ZaehlerZ
            NEXT ZaehlerX
         glEnd
      CASE ELSE
         'Hier kommen alle SUB-Aufrufe an, welche als
         'was-Parameter einen Eintrag haben, der hier
         'nicht ausgewertet wurde.
         'Tippfehler vom Programmierer????
   END SELECT
END SUB
'-------------------------
SUB Pyramide(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   'Pyramide erstellen, Grundfläche/Quadrat = auf Höhe 0, seitlich mittig auf X- und Z-Achse Positioniert
   'Grundflächengroesse = XLaenge x ZLaenge, Höhe Pyramide = ZLaenge
   STATIC AS SINGLE XLaenge, YLaenge, ZLaenge
   STATIC AS SINGLE Farbe1Rot, Farbe1Gruen, Farbe1Blau
   STATIC AS SINGLE Farbe2Rot, Farbe2Gruen, Farbe2Blau
   STATIC AS SINGLE Farbe3Rot, Farbe3Gruen, Farbe3Blau
   STATIC AS SINGLE Farbe4Rot, Farbe4Gruen, Farbe4Blau
   STATIC AS SINGLE Farbe5Rot, Farbe5Gruen, Farbe5Blau
   STATIC AS INTEGER ZaehlerX, ZaehlerZ                   :'ForNext-Zählvars, ggf. rekursiver aufruf dieser Sub, drum STATIC
   SELECT CASE UCASE(was)
      CASE "SetzLaengen"  : XLaenge   = Para1 : YLaenge     = Para2 : ZLaenge     = Para3
      CASE "SetzFarbe1"   : Farbe1Rot = Para1 : Farbe1Gruen = Para2 : Farbe1Blau  = Para3 :'ein Dreieck
      CASE "SetzFarbe2"   : Farbe2Rot = Para1 : Farbe2Gruen = Para2 : Farbe2Blau  = Para3 :'ein Dreieck
      CASE "SetzFarbe3"   : Farbe3Rot = Para1 : Farbe3Gruen = Para2 : Farbe3Blau  = Para3 :'ein Dreieck
      CASE "SetzFarbe4"   : Farbe4Rot = Para1 : Farbe4Gruen = Para2 : Farbe4Blau  = Para3 :'ein Dreieck
      CASE "SetzFarbe5"   : Farbe5Rot = Para1 : Farbe5Gruen = Para2 : Farbe5Blau  = Para3 :'der Boden/Quadrat
      CASE "BeschreibungsListe"
         glrotatef Para1, 1, 0, 0    :'<----------- um X-Achse drehen
         glrotatef Para2, 0, 1, 0    :'<----------- um Y-Achse drehen
         glrotatef Para3, 0, 0, 1    :'<----------- um Z-Achse drehen
         'die OpenGL-Beschreibungsliste zum Anzeigen der Pyramide erstellen
         glBegin GL_QUADS
            'der Boden der Pyramide als Quadrat, auf Höhe 0(Y-Achse),
            'seitliche Ausrichtungen = Quadratmitte = X-Achsenmitte und Z-Achsenmitte
            'damit Zentriert sitzt, für 1.Punkt einfach halbe Kantenlänge von 0 Abziehen, für 2. dazuaddieren
            'Reihenfolge Eckpunktangabe gegen Uhrzeigersinn VON UNTEN her gesehen (unten=Außenseite später)
            glColor3f  Farbe5Rot        , Farbe5Gruen      , Farbe5Blau
            glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2)
            glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2)
         glEnd
         glBegin GL_TRIANGLES
            '   Dreieckseite 1 =an Kante 1.+2.Quadratpunkt
            glColor3f  Farbe1Rot        , Farbe1Gruen      , Farbe1Blau
            glVertex3f 0-(XLaenge/2)    , 0       , 0-(ZLaenge/2)
            glVertex3f 0-(XLaenge/2)    , 0       , 0+(ZLaenge/2)
            glVertex3f 0                , YLaenge , 0
            '   Dreieckseite 2 =an Kante 2.+3.Quadratpunkt
            glColor3f  Farbe2Rot        , Farbe2Gruen      , Farbe2Blau
            glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0                , YLaenge , 0
            '   Dreieckseite 3 =an Kante 3.+4.Quadratpunkt
            glColor3f  Farbe3Rot        , Farbe3Gruen      , Farbe3Blau
            glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2)
            glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0                , YLaenge , 0
            '   Dreieckseite 4 =an Kante 3.+1.Quadratpunkt
            glColor3f  Farbe4Rot        , Farbe4Gruen      , Farbe4Blau
            glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2)
            glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2)
            glVertex3f 0                , YLaenge , 0
         glEnd
      CASE ELSE
         'Hier kommen alle SUB-Aufrufe an, welche als
         'was-Parameter einen Eintrag haben, der hier
         'nicht ausgewertet wurde.
         'Tippfehler vom Programmierer????
   END SELECT
END SUB

Und so sieht das ganze aus, wenn man ein bißchen in unserer 3D-Welt
per Cursor- bzw. Strg+Cursor-Tasten herumspaziert ist:
Ogl_Tut_5_texturiertes_Quad
mit X/x/Y/y/Z/z-Tasten die Pyramiden drehen geht auch :-)

Schön, aber fehleranfällig. Wir prüfen ja nicht, ob die geplante
Datei Mauer_128.bmp wirklich im Verzeichnis steht. Genauso wenig
checken wir ab, ob das Bild darin auch wirklich 128x128 Pixel
groß ist, ob es überhaupt eine Bitmap-Datei ist...

 

Gehe zu Seite Gehe zu Seite  1  2  3  4  5  6  
Zusätzliche Informationen und Funktionen
  • Das Tutorial wurde am 28.09.2008 von MitgliedEastler_dart angelegt.
  • Die aktuellste Version wurde am 30.09.2008 von MitgliedEastler_dart gespeichert.
  Bearbeiten Bearbeiten  

  Versionen Versionen