Tutorial Nr.1 - Skyboxen
Teil 1 - Die Skyboxtexturen mit Terragen erstellen
Dieser Teil des Tutorials beschäftigt sich mit dem Erstellen der für eine Skybox benötigten Texturen mit Hilfe des Programms Terragen (http://www.planetside.co.uk/terragen).Sie sollten jedoch grundlegende Kenntnisse über dieses Programm besitzen, da ich nicht zu sehr ins Detail gehen werde.Ich werde nur die Schritte erklären, die nötig sind um ein grundlegendes Terrain zu erstellen und die Skybox zu rendern.Sie sollten also zuerst besser das Online-Tutorial zu Terragen lesen.

Wenn Terragen gestartet wird, erscheint die Oberfläche wie abgebildet :



Wir bechäftigen uns zuerst mit dem rechten Fenster um unsere Landschaft zu generieren.Wählen Sie es aus und klicken sie auf den "Generate Terrain" Button um den folgenden Dialog zu öffnen :



In der Auswahlbox "Method" kann die zur Landschaftsgenerierung genutzte Methode gewählt werden, die mit den Optionen auf der rechten Seite noch angepasst werden kann.Ich habe mich dazu entschieden, für dieses Tutorial einen Canyon zu generieren.Klicken Sie nach ihrer Wahl auf "Generate Terrain" und Sie werden Ihre neu generierte Landschaft im "Landscape" Dialog erblicken :



Wenn ihnen das generierte Terrain nicht gefällt, können Sie es mit den oberen drei Buttons nachträglich anpassen.
Der Punkt von dem drei Linien ausgehen ist die Position des Betrachters.Mit einem Linksklick kann man dessen Position und mit einem Rechtsklick dessen Blickpunkt ändern.
Jetzt ist es Zeit die Oberflächentextur zu wählen.Sie könnten zwar selbst eine generieren, da dies jedoch sehr zeitintensiv ist habe ich mich dazu entschieden, eine der mit Terragen gelieferten Vorlagen zu öffnen.Klicken Sie dazu auf den "Open..." Button in der "Surface Map" Sektion des Dialogs und wählen Sie eine Vorlage aus.Natürlich kann diese im Nachhinein noch abgeändert werden.

Und jetzt zum "Rendering" Dialog :



Oben links können Sie ein Preview Ihrer Landschaft sehen, welches mit dem "Render Preview" Button aktualisiert werden kann.Aber benutzen Sie nicht die höchste Detailstufe nur um Ihr Preview zu rendern, da dies sonst relativ lange dauern kann.Diese Detailstufe sollte nur zum endgültigen Rendern genutzt werden, während für das Preview die mittlere Stufe ausreicht.
Unter "Image Size" können Sie die Größe Ihrer Skyboxtexturen wählen.Für eine Skybox sollten Höhe und Breite gleich sein.Ich habe 512x512 Pixel gewählt, so das die Skybox später detailliert genug aussieht um einen realisitschen Eindruck zu vermitteln.Aber vergessen Sie nicht, das größere Texturen mehr Speicher benötigen und Performance kosten.
Bevor Sie die Skybox rendern können, müssen noch einige Einstellungen geändert werden um das benötigte 90° Sichtfeld zu erhalten.Klicken Sie auf "Camera Settings" um den folgenden Dialog zu öffnen :



Für das korrekte Sichtfeld müssen Sie den "Zoom" Wert auf 1 setzen.Danach können Sie diesen Dialog auch schon wieder schliessen und zum "Rendering Control" Dialog wechseln.Setzen Sie dort alle Werte für die Kameraausrichtung ("Camera orientation") auf 0.
Nachdem Sie den Detailschieber auf Maxmimum gesetzt haben sind Sie Bereit die Texturen zu rendern.Der erste Teil wird bei einer Kopfausrichtung ("head orientation") von 0° generiert und stellt ihr hinteres Bild dar.Das rechte wird bei 90°, das vordere bei 180° und das linke bei 270° gerendert.
Setzen Sie die Kopfausrichtung jetzt wieder auf 0° und den "Pitch" auf 90° um den Himmel zu rendern, also ihr oberes Bild.Die untere Textur für die Skybox wird bei einem "Pitch" von -90° gerendert.
Die hier von mir gewählten Namen sind natürlich unverbindlich und an das Beispielprogramm angepasst.

Hier die 6 Texturen meiner Skybox :
Top
Back Right Front Left
Bottom
Teil 2 - Nutzung der Skybox in OpenGL
Nachdem Sie nun ihre Skyboxtexturen generiert haben ist es Zeit sie in Ihrer OpenGL-Anwendung zu nutzen.Ich werde hier nicht die Grundschritte zur Erstellung eines Renderkontextes oder zur Darstellung von Dreiecken zeigen.Wenn Sie darüber mehr wissen wollen, suchen sie eine der diversen OpenGL Seiten mit Toturials zu diesen Themen auf.
Schritt 1 - Die Texturen laden
Texturen mit der TglBMP Klasse zu laden ist sehr leicht. Der Einfachheit halber bevorzuge ich, alle Texturen der Skybox in einem Array zu organisieren. Ausserdem nutze ich lokal in der Ladeprozedur ein Array in dem alle Dateinamen abgelegt sind, so dass die Texturen leicht in einer Schleife geladen werden können :

procedure TGLForm.LoadSkyBox;
const
 SkyBoxName : array[0..5] of String = ('BK', 'FR', 'DN', 'UP', 'LF', 'RT');
var
 i : Integer;
begin
for i := 0 to High(SkyBoxTexture) do
 begin
 SkyBoxTexture[i] := TGLBmp.Create;
 SkyBoxTexture[i].LoadImage(SkyBoxName[i]+'.jpg');
 SkyBoxTexture[i].SetTextureWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
 SkyBoxTexture[i].GenTexture(False, False);
 end;
GenerateSkyBox(256, 256, 256);
end;

Das global deklarierte SkyBoxTexture-Array beinhaltet unsere sechs Texturen und im lokal deklarierten SkyBoxName-Array befinden sich alle zugehörigen Dateinamen für jede Textur, so dass Sie die Ladeprozedur ganz einfach an Ihre Namenskonventionen anpassen können.
Zuerst laden wir die Texturen in einer Schleife. Dabeu muß jedesmal die TglBMP Klasse erstellt werden, dann wird die Bilddatei geladen und der Texturewrap auf GL_CLAMP_TO_EDGE für die S und die T Koordinate gesetzt. Dies ist nötig damit unsere Kanten zusammenpassend erscheinen.
Beachten Sie auch das ich die GenTexutre-Prozedur der TglBMP Klasse abgeändert habe. Ihr werden jetzt zwei Parameter vom Typ BOOLEAN übergeben, wobei der erste angibt, ob die Textur im komprimierten S3 Format geladen gibt und der zweite angibt, ob das anisotropische Filtering genutzt werden soll. Wenn Sie diese Eigenschaften nutzen wollen, müssen Sie sich jedoch zuerst davon versichern, dass die Grafikkarte diese unterstützt.
Sie sollten jedoch davon absehen, Ihre Skybox Texturen komprimiert zu laden weil dadurch die Qualität stark herbgesetzt werden kann.
Schritt 2 - Die Displayliste erstellen
Nachdem die Texturen geladen wurden, generieren wir die Displayliste für unsere Skybox.Wie Sie bereits wissen sollten sind Displaylisten um ein Vielfaches schneller als die Quads in jedem Frame neu zu zeichnen.

procedure TGLForm.GenerateSkyBox(pWidth, pHeight, pLength : TGLFloat);
var
 px,py,pz : TGLFloat;
begin
List := glGenLists(1);
glNewList(List, GL_COMPILE);
 px := - pWidth  / 2;
 py := - pHeight / 2;
 pz := - pLength / 2;
 SkyBoxTexture[0].Bind;
 glBegin(GL_QUADS);
  glTexCoord2f(0, 0); glVertex3f(px,          py,           pz);
  glTexCoord2f(0, 1); glVertex3f(px,          py + pHeight, pz);
  glTexCoord2f(1, 1); glVertex3f(px + pWidth, py + pHeight, pz);
  glTexCoord2f(1, 0); glVertex3f(px + pWidth, py,           pz);
 glEnd;
 SkyBoxTexture[1].Bind;
 glBegin(GL_QUADS);
  glTexCoord2f(1, 0); glVertex3f(px,          py,           pz + pLength);
  glTexCoord2f(1, 1); glVertex3f(px,          py + pHeight, pz + pLength);
  glTexCoord2f(0, 1); glVertex3f(px + pWidth, py + pHeight, pz + pLength);
  glTexCoord2f(0, 0); glVertex3f(px + pWidth, py,           pz + pLength);
 glEnd;
 SkyBoxTexture[2].Bind;
 glBegin(GL_QUADS);
  glTexCoord2f(1, 1); glVertex3f(px + pWidth, py, pz);
  glTexCoord2f(1, 0); glVertex3f(px + pWidth, py, pz + pLength);
  glTexCoord2f(0, 0); glVertex3f(px,          py, pz + pLength);
  glTexCoord2f(0, 1); glVertex3f(px,          py, pz);
 glEnd;
 SkyBoxTexture[3].Bind;
 glBegin(GL_QUADS);
  glTexCoord2f(0, 0); glVertex3f(px,          py + pHeight, pz);
  glTexCoord2f(0, 1); glVertex3f(px,          py + pHeight, pz + pLength);
  glTexCoord2f(1, 1); glVertex3f(px + pWidth, py + pHeight, pz + pLength);
  glTexCoord2f(1, 0); glVertex3f(px + pWidth, py + pHeight, pz);
 glEnd;
 SkyBoxTexture[4].Bind;
 glBegin(GL_QUADS);
  glTexCoord2f(1, 0); glVertex3f(px, py,           pz);
  glTexCoord2f(0, 0); glVertex3f(px, py,           pz + pLength);
  glTexCoord2f(0, 1); glVertex3f(px, py + pHeight, pz + pLength);
  glTexCoord2f(1, 1); glVertex3f(px, py + pHeight, pz);
 glEnd;
 SkyBoxTexture[5].Bind;
 glBegin(GL_QUADS);
  glTexCoord2f(0, 0); glVertex3f(px + pWidth, py,           pz);
  glTexCoord2f(1, 0); glVertex3f(px + pWidth, py,           pz + pLength);
  glTexCoord2f(1, 1); glVertex3f(px + pWidth, py + pHeight, pz + pLength);
  glTexCoord2f(0, 1); glVertex3f(px + pWidth, py + pHeight, pz);
 glEnd;
glEndList;
end;

Dieser Teil des Quelltextes ist sehr einfach zu verstehen. Die Variablen px, py und pz werden genutzt um die Skybox zu zentrieren. Dann wird ein Quader mit den Kantenlängen pWidth, pHeight und pLength und den zugehörigen Texturen in unsere Displayliste kompiliert. Der erste Quad ist die hintere Seite, der zweite die vordere Seite, der dritte ist der Boden, der vierte der Deckel, der fünfte die linke Seite und der letzte ist die rechte Seite.
Schritt 3 - Die Skybox zeichnen
Nachdem die Skybox in eine Displayliste kompiliert wurde ist es sehr einfach sie zu zeichen :

procedure TGLForm.DrawScene;
begin
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
glClear(GL_DEPTH_BUFFER_BIT);
glRotatef(xrot, 1, 0, 0);
glRotatef(yrot, 0, 1, 0);
glCallList(List);
SwapBuffers(FDC);
end;

Einleitend wechseln wird in die Modelansichtsmatrix und laden dort die Grundmatrix. Dann muß nur der Tiefenpuffer gelöscht (und eventuell andere verwendete Puffer) werden, deen Farbpuffer müssen Sie nicht mehr löschen da die Skybox den ganzen Schirm füllt. Jetzt muss unsere Matrix nur noch entsprechend des Sichtwinkels des Betrachters rotiert werden, bevor unsere Displayliste aufgerufen wird. Nach dem Tauschen der Puffer sehen wir endlich unsere Skybox auf dem Schirm. So lässt sich mit wenig Aufwand viel Realismus zur Szene hinzufügen.

Jetzt könnte noch eine animierte Wolkenschicht wie in Quake3 oder Unreal zu sehen hinzugefügt werden. Dies werde ich vielleicht in einem späteren Tutorial nachholen.
Das Beispielprogramm
Und zu guter Letzt noch ein Bild unserer Skybox aus dem Demoprogramm :



Hinweis :
Für diesen Screenshot habe ich 512x512 Pixel große Texturen genutzt, während die Texturen im Download nur 256x256 Pixel groß sind!
Download
Die Skyboxdemo (inklusive Quellcode) herunterladen

Letzte Aktualisierung : 18.06.2004