Workshop AtariMesa - Teil 1

Im Zuge der Milan-Entwicklungen wurde auch mit der Portierung des 3D-Standards OpenGL auf den Atari begonnen. Schon bald werden Ergebnisse präsentiert. In der st-computer führt Entwicklungsleiter Olaf Piesche persönlich in die Theorie ein.

OpenGL ist so etwas wie ein magisches Wort unter Programmierern und Softwareentwicklern, die auch nur im entferntesten etwas mit 3D-Grafik zu tun haben. Aber was genau ist OpenGL eigentlich? Wie wird es benutzt und programmiert? Zwei große Fragen, die wir zu beantworten versuchen werden.

Der Vollständigkeit halber: OpenGL ist ein geschütztes Warenzeichen von SGI. Alle anderen Warenzeichen oder urheberechtlich geschützten Ausdrücke werden hier unter Respektierung des Copyrights der jeweiligen Urheber verwendet. OpenGL wird in der folgenden Artikelserie als Referenz zu OpenGL-kompatiblen Libraries wie Mesa3D oder AtariMesa verwendet.

In dieser Serie will ich versuchen, Sie nicht mit langwierigen Theorie-Erklärungen gleich am Anfang zu langweilen, nur um Sie hinterher in Beispielcode zu begraben. Stattdessen werden wir in diesem Teil unserer Artikelserie eine Art generelle Einführung in OpenGL vornehmen und uns später durch das Aufbauen einer OpenGL-Applikation arbeiten und die notwendige Theorie und Mathematik erklären, wenn wir uns dem jeweiligen Thema nähern. Ich hoffe, dass dies diesen Artikel interessant und informativ hält.

Was ist es?

OpenGL ist die Industriestandard-3D-Library. Das hört sich nett an, sagt aber nicht allzuviel, also fangen wir am besten am Anfang an. OpenGL wurde ursprünglich von Silicon Graphics Inc. mit dem Ziel eine flexible, vielseitige und einfach zu benutzende Library mit einer API (Application Programming Interface) aufzubauen entwickelt, die von jedem, der 3D-Applikationen entwickelt, verwendet werden kann. Es gibt mehrere solche APIs; die bekanntesten sind wohl OpenGL, Direct3D und Glide. Direct3D wurde von Microsoft als Konkurrenz zu OpenGL entwickelt (mit bescheidenem Erfolg was Funktionalität wie auch Qualität angeht). Glide ist eine Library, deren Funktionalität und API recht ähnlich zu OpenGL ist und vom Grafikkartenhersteller 3DFX entwickelt wurde. Hardwareseitig wird Glide nur von 3DFX-Karten unterstützt.

Heute verwenden grob geschätzt mehr als 90 Prozent aller auf dem Markt befindlichen 3D-Programme und Spiele OpenGL für die 3D-Grafik. Mehr noch: Nahezu alle 3D-Grafikkarten, die heutzutage erscheinen, unterstützen OpenGL in ihrer Rendering-Hardware. Das heißt, dass jedes Spiel oder Programm, das OpenGL benutzt, automatisch von der Hardwarebeschleunigung profitieren wird. Auf den meisten Plattformen ist OpenGL eine shared library, die von diesen Applikationen aufgerufen wird. Aber was genau tut OpenGL denn nun?

OpenGL stellt eine Vielzahl von Low-Level 3D-Routinen zur Verfügung. Das heißt, es ist keine komplette 3D-Engine oder ein "3D-Spiel-Construction Kit". Die Library enthält Routinen für geometrische Transformationen (Verschiebung, Rotation, Projektion) und Darstellungsroutinen zum Rendern von Punkten, Linien, Polygonen (mit und ohne Texturen, Beleuchtung, Antialiasing und vielen anderen Features) auf dem Bildschirm. Im Klartext: Der ganze Kleinkram, mit dem sich der Programmierer sonst herumschlagen müsste um eine 3D-Applikation zu entwickeln, wird von OpenGL übernommen. Wenn Sie also ein 3D-Spiel entwickeln wollen, können Sie sich auf andere Aspekte wie die eigentliche 3D-Engine, Kl der Computergegner, Spieldesign und ähnliches konzentrieren.

Einige Beispiele von Programmen und Spielen, die OpenGL benutzen:

Hinzu kommen hunderte kommerzielle Programme und Unmengen an Open-Source- und GPL-Projekten.

Gibt es auch Raytracing- und Rendering-Software, die OpenGL benutzt? Ja, aber natürlich nicht zum Berechnen der eigentlichen Bilder. In solchen Programmen wird OpenGL meist benutzt, um die 3D-Szenen im Modeller darzustellen, um also auf diesem Wege von moderner 3D-Hardware profitieren zu können.

OpenGL auf dem Atari

Beim Portieren der OpenGL-kompatiblen Library Mesa3D von Brian Paul auf den Atari fand ich recht schnell zwei Dinge heraus, auf die die Entwickler der Library viel Wert gelegt haben: Flexibilität und Qualität. Die OpenGL-API bietet hunderte von Funktionen zum Generieren, Manipulieren und Rendern von 3D-Primitiven, und darüber hinaus können sogenannte "OpenGL-Extensions" in die Library integriert werden um noch mehr Funktionalität zur Verfügung zu stellen. Viele solche Extensions sind verfügbar, einige von den eigentlichen OpenGL-Entwicklern selbst programmiert, andere von Hard-und Softwareherstellern wie NVIDIA und ID Software. Wenn eine Extension entwickelt wird, kann diese an das OpenGL ARB (Architecture Review Board) weitergeleitet werden. Das ARB, das sich aus Mitgliedern führender Hard- und Softwareentwickler wie ID Software, Nvidia oder 3DFX zusammensetzt, entscheidet dann, ob die Extension zu einem festen Bestandteil der Library gemacht werden sollte. Auf diese Art ist OpenGL konstant in Weiterentwicklung, und wird von vielen professionellen Entwicklern verbessert und erweitert.

Die Darstellungsqualität ist ein weiterer wichtiger Faktor im Konzept. Zum Implementieren von OpenGL-kompatiblen Libraries wie Mesa3D hat SGI einen Satz sogenannter "Conformance Tests" entwickelt. Diese Tests rendern ganz einfach spezielle Bilder und Szenen (wie z.B. die Anzahl der Punkte einer bestimmten Größe, Polygone mit einer bestimmten Textur usw.) und vergleichen die Ergebnisse mit denen, die die originale SGI-Library produziert. Und eine OpenGL-kompatible Library darf sich nur dann kompatibel nennen, wenn ihre Ergebnisse von den Originalen um nicht mehr als einen bestimmten Prozentsatz abweichen. So wird sichergestellt, dass auf jeder Plattform und Grafikkarte OpenGL-Applikationen immer gleich aussehen und nahezu dieselben Ergebnisse produzieren.

Die Qualität der OpenGL-Routinen ist recht hoch. In vielen Fällen werden Fließkomma-Routinen verwendet um Werte für Beleuchtung, Texturemapping und 3D-Manipulationen zu berechnen. Natürlich werden Fließkomma-Routinen immer langsamer sein als Integer-Berechnungen - auch auf Pentium-lll-und PowerPC-Prozessoren. Doch OpenGL wurde ursprünglich für die Benutzung auf hochleistungsfähigen Plattformen entwickelt (schon einmal eine SGI Onyx in Aktion gesehen?), doch mit Hardwarebeschleunigung durch heutige 3D-Karten ist das alles auch im Heimbereich kein Problem mehr.

Dennoch: Beim Portieren der Library auf Ataris habe ich versucht (und versuche noch) viele der Rechenintensiven Teile durch optimierte Routinen und Festkommaberechnungen zu beschleunigen - selbstverständlich innerhalb der Conformance Tests.

Gehen Sie sofort ins Gefängnis, gehen Sie nicht über Los!

OpenGL verwendet eine sogenannte "Immediate State Machine". Das heißt, dass es eine Anzahl von Status-Variablen (State Variables) gibt, die angeben, wie OpenGL Primitiven auf den Bildschirm rendert, Transformationen und Beleuchtung ausführt oder sich ganz generell verhält. Beispiele wären Schalter, die Texturemapping ein- oder ausschalten oder angeben, ob Polygone mit Fiat- oder Gouraudshading zu zeichnen sind. Das Verändern dieser State Variables hat einen Effekt auf alles, was nach dem Ändern der Variablen getan wird. Man kann zum Beispiel eine Seite einer Pyramide Flat-Shaded (mit einer einzigen Farbe gefüllt) zeichnen, dann die State Variable für das Shading-Model auf Smooth-Shaded setzen und die anderen sichtbaren Seiten der Pyramide mit Smooth-Shading zeichnen. Alle Änderungen auf jede State-Variable haben nur einen Effekt auf Operationen, die nach der Änderung durchgeführt werden. So ist das Einstellen einer Füllfarbe oder einer Textur für Polygone ungefähr wie das Wechseln eines Stiftes zu einer anderen Farbe.

Primitiv

Es war nun schon mehrfach von Primitiven die Rede, und vielleicht ist es an der Zeit, aufzuklären, was das denn eigentlich ist. Unter Primitiven versteht man einfache (primitive) geometrische Objekte, die zusammengesetzt komplexere Objekte bilden können. In OpenGL stehen die Primitiven Punkt, Linie, Dreieck, Viereck und Polygon (mit mehr als 4 Ecken) zur Verfügung, Außerdem gibt es Erweiterungen der Primitiven, z.B. Line Strip, um einen Zug aus mehreren Linien zusammenzusetzen, Triangle Strips und Triangle Fans, um mehrere Zusammengesetzte Dreiecke schneller rendern zu können, und Quad Strips für zusammengesetzte Vierecke. Jede Primitive besteht aus ihren Eckpunkten (Vertices) und Kanten. Abbildung 1 verdeutlicht die verschiedenen Primitiven.

Die Line-, Triangle- und Quad-Strips und Triangle-Fans haben gegenüber mehreren einzelnen Linien, Dreiecken oder Vierecken den Vorteil, dass sie einen oder mehrere dreidimensionale Punkte (Vertex) gemein haben (shared vertices). Diese gemeinsamen Punkte brauchen in einem Strip nicht für jedes Polygon einmal, sondern nur für den gesamten Strip einmal definiert und in die OpenGL-Pipeline oder die Grafikhardware übertragen, rotiert, verschoben und projiziert zu werden. Das Rendern eines Triangle Strips ist also generell schneller als das Rendern mehrerer einzelner Dreiecke.

Sehen Sie sich zum Beispiel den Triangle-Strip aus 4 Dreiecken an. Zusammen genommen hätten vier einzelne Dreiecke 4*3 = 12 Eckpunkte (Vertices). Der Triangle-Strip hingegen hat nur 6 Vertices, um 4 zusammengesetzte Dreiecke darzustellen. Bei 4 Polygonen mag das noch nicht viel Unterschied machen, aber stellen Sie sich einmal eine Szene vor, in der 1500 Dreiecke gerendert werden - einzeln wären das 4500 Vertices, die alle rotiert, verschoben, vielleicht skaliert und projiziert werden müssen. Wenn man hier durch die Verwendung von Triangle-Strips ein- oder zweitausend Vertices sparen kann, kann dies einen gewaltigen Performance-Unterschied machen.

Konvex

Ein weiterer wichtiger Punkt ist, dass alle Polygone in OpenGL konvex sein müssen. Die Definition von konvex ist in diesem Fall, dass kein innerer Winkel eines Polygons größer als 180 Grad sein darf. Wie an Abbildung 2 zu sehen ist, ist das rechte Polygon konkav - es wird zwar von OpenGL immer noch gerendert, aber nicht korrekt dargestellt. Die einfachste Lösung ist, konkave Polygone durch mehrere konvexe zu ersetzen. In unserem Beispiel kann man das konkave Viereck durch zwei Dreiecke in einem Triangle-Strip ersetzen.

Es ist ohnehin empfehlenswert, sich soweit wie möglich auf Dreiecke zu stützen, da diese Primitive in den meisten OpenGL-lmplementationen (auch Grafikhardware) die ist, die am schnellsten gerendert wird. Dreiecke sind außerdem weniger fehlerträchtig, da sie immer konvex sind und immer in einer dreidimensionalen Ebene liegen (dazu später mehr), was eine weitere Beschränkung von OpenGL ist. Polygone mit mehr als 3 Ecken werden von den meisten OpenGL-lmplementationen automatisch in Dreiecke zerlegt, was einen gewissen Rechenaufwand mit sich bringt.

Transformationen, oder: Was ist die Matrix?

Alle geometrischen Transformationen (Verschiebung, Rotation, Skalierung) werden in OpenGL über Matrizen erledigt. Eine Matrix ist ein mathematisches Gebilde (prinzipiell ein zweidimensionales Array), das in einer bestimmten Art und Weise angewandt, komplexere mathematische Operationen ausführen kann. OpenGL-Matrizen sind 4x4-Matrizen.

Matrixoperationen können auf einigen Prozessoren (z.B. auch dem DSP des Atari Falcon) durch Spezialfunktionen erheblich beschleunigt werden. Die OpenGL-eigenen Matrixoperationen zum Durchführen geometrischer Transformationen werden inzwischen auch von diverser Grafikhardware unterstützt (z.B. ATI Radeon, Matrox G200/G400 oder NVIDIA GeForce), sodass hier eine erhebliche Beschleunigung von 3D-Engines, die eine in der Hardware implementierte 3D-API verwenden, zu verzeichnen ist. Die Hardwareunterstützung der geometrischen Operationen ist allgemein als "Hardware T&L" (Transforming and Lighting) bekannt.

Neben der Transformations-Matrix, die geometrische Transformationen von 3D-Objekten beschreibt, gibt es noch zwei weitere: Die Projektions-Matrix nimmt die Projektion der dreidimensionalen Koordinaten und Objekte auf die zweidimensionale Oberfläche des Bildschirms vor. Diese Matrix beeinflusst also direkt, wie stark die Perspektive der Darstellung ist und ihr Seitenverhältnis. Die Textur-Matrix ist für Operationen in den Texturen von Polygonen zuständig. Damit können z.B. recht einfach Spezialeffekte wie zoomende oder rotierende Texturen verwirklicht werden.

Ich habe bereits erwähnt, dass OpenGL eine Immediate State Machine ist, und das trifft auch auf die Matrixoperationen zu. Es gibt diverse Kommandos für geometrische Transformationen, die die entsprechende Matrix mit den richtigen Werten füllen. Um die verschiedenen Matrizen anwenden zu können gibt es ein Kommando, das zwischen den Matrixmodi umschaltet. Wenn der aktuelle Matrixmodus z.B. für die Transformations-Matrix (in OpenGL "Modelview-Matrix" genannt) gedacht ist, werden alle Transformationskommandos auf die Darstellung und Transformation übermittelter 3D-Koordinaten angewandt. Ist der Matrixmodus für die Textur-Matrix gesetzt, werden Skalierungs-, Rotations- und Verschiebungskommandos auf die nächste Textur angewandt, die einem Polygon zugewiesen wird.

Wenn nun Vertices und Primitiven definiert werden, werden diese Matrixoperationen auf alle 3D-Koordinaten (Modelview-Matrix), Texturkoordinaten (Texture-Matrix) oder das sichtbare Raumvolumen (in OpenGL "View Frustum" genannt und mit der Projection Matrix geändert, dazu aber in der nächsten Ausgabe mehr) angewandt, die nach dem Transformationskommando steht. Wenn also z.B. eine Rotation ausgeführt und danach ein Dreieck definiert wird, muss zur Zeichnung eines weiteren unrotierten Dreiecks die Transformationsmatrix wieder zurückgesetzt werden. Das gleiche trifft auf Translation (Verschiebung) und Skalierung zu.

Um nun nicht ständig Rotationen durchführen und wieder zurücksetzen zu müssen, bietet OpenGL einen sogenannten "Matrix Stack". Dieser verhält sich wie jeder andere Stack (Stapel) - also wie ein Stapel Papier - und kann bis zu 8 Matrizen (vom ARB für OpenGL-lmplementationen vorgegebenes Minimum) aufnehmen. Dadurch kann man einfach eine Transformationsmatrix mit bestimmten Werten füllen (über die Transformationskommandos) und dann auf dem Stack für spätere Benutzung ablegen, andere Transformationen ausführen und die vorige Matrix wieder vom Stack holen.

AtariMesa

AtariMesa ist eine Portierung der OpenGL-konformen 3D-Library Mesa3D von Brian Paul. Im Vergleich zu anderen Portierungsversuchen habe ich mich bemüht, die Library nicht nur auf dem Stand von OpenGL 1.2 vollständig lauffähig zu machen, sondern auch diverse Optimierungen vorzunehmen, die auch mit den Software-Renderern zumindest halbwegs annehmbare Frameraten möglich machen. Die Portierung und vor allem Optimierung von AtariMesa ist noch nicht abgeschlossen, aber ständig in Arbeit.

Ich hoffe, ein wenig Basiswissen mit diesem ersten Teil der OpenGL-Serie vermittelt zu haben. Mit dem Erscheinen dieser Ausgabe der st-computer sollte auch die AtariMesa-Website online stehen. Der Port einer ersten Version der Library steht kurz vor der Fertigstellung und wird bald zum Download bereitstehen. In den folgenden Teilen unserer Serie werden wir uns dann ein wenig mit der Initialisierung der 3D-Library und dem Rendern einer ersten Primitive beschäftigen. Bis dahin können Sie natürlich unter opengl.org ein wenig stöbern und auch sourceforge.net, eine Heimat vieler Open-Source-Projekte, bietet sich an.


Olaf Piesche
Aus: ST-Computer 01 / 2001, Seite 40

Links

Copyright-Bestimmungen: siehe Über diese Seite