OpenGL in Visual Basic - Einführung
von Hans Henning Klein
Einführung
Willkommen zum ersten Tutorial über OpenGL-Grafiken. Es gibt vermutlich nur einen guten Grund, warum Sie hier sind: Sie beherrschen einigermaßen VB und schauen voller Neid auf die 3D-Anwendungen, welche in C++ programmiert werden. Nun, dann sind Sie hier genau richtig, denn mittels OpenGL kann man auch in VB beeindruckende Effekte realisieren. Bevor Sie jetzt aber damit beginnen, eine 3D-Engine für Doom8 zu planen, möchte ich auch hier die Grenzen aufweisen... Es ist mittels OpenGL durchaus möglich, optisch anspruchsvolle 3D-Applikationen zu entwickeln, welche auch vor Effekten wie Explosionen oder Nebel nicht kapitulieren, aber die Anzahl der darstellbaren Objekte ist in VB durch die schlechte Laufzeit begrenzt. Dieses Tutorial richtet sich daher eher an Entwickler, deren Ambitionen in der Verbesserung alltäglich verwendeter Programme liegen (Einen guten 3D-Explorer gibt es bis heute meines Wissens nicht...), oder deren Spiele eine gewisse grafische Komplexität nicht übersteigen. Letztere müssen nicht zwangsweise primitiv sein, aber es sollten eher Rollenspiele (RPG) statt Ego-Shooter sein.
Warum nun OpenGL? Nun, zum einen ist es einmal nicht von unserem Lieblingsmonopolisten, zum anderen ist es eine mit DirectX vergleichbare, wenn nicht sogar leistungsstärkere Sammlung von Klassen, die relativ leicht zu beherrschen sind.
Was sollten Sie mitbringen? Um diesem Tutorial zu folgen sollten Sie in der Lage sein Standard-Exe Programme in VB zu entwickeln. Sie sollten bereits ein paar einfache Projekte erstellt haben und gut dokumentierten Quellcode lesen können. Ich möchte damit Anfänger nicht unbedingt ausschließen, aber es wird im Zuge des Tutorials nicht auf Dinge eingegangen, die in VB selbstverständlich sind.
Abbildung 1: Viel Aufwand für ein schwarzes Fenster. Doch es ist die Grundlage für alles Folgende.
Der Anfang
Der erste Teil, den wir hier lernen, ist ein Fenster für unsere OpenGL-Programme zu erzeugen. Obwohl Sie später vermutlich diesen Teil als Modul speichern und in vielen Projekten einfach einbinden werden, sollten Sie sich trotzdem die Zeit nehmen, diesen Bereich zu verstehen. Das Studium der Grundlagen mag langweilig sein, aber es erspart Ihnen später stundenlanges Rumprobieren oder Suchen in diesem Tutorial.
Ein OpenGL-Projekt erstellen
Ein OpenGL-Projekt erstellen: Starten Sie ein neues Projekt, vorzugsweise als Standard-Exe. Öffnen Sie das Projekt-Menü und klicken Sie auf Verweise. Klicken Sie auf Durchsuchen und fügen Sie die vbogl.tlb hinzu. Diese Datei stellt die VB OpenGL API 1.2 zur Verfügung und ist in den Beispielprojekten dieses Tutorials enthalten. Öffnen Sie nun über das Menü Projekt die Projekt-Eigenschaften und stellen Sie als Start-Objekt Sub Main ein. Fügen Sie zu Ihrem Projekt ein Modul hinzu und erstellen Sie in diesem Modul eine neue Prozedur Main.
Das wird vielen jetzt seltsam erscheinen, aber eine OpenGL-Anwendung hat mit dem klassischen ereignisorientierten Programmablauf in VB nichts zu tun. Vielmehr läuft eine OpenGL-Anwendung in einer Endlosschleife. Bei jedem Durchlauf wird die Ausgabe gelöscht und neu gezeichnet.
Nur ein schwarzes Fenster
Der Aufwand, um nur ein schwarzes Fenster zu erzeugen, wird Ihnen im Moment unglaublich groß erscheinen. Schließlich geht das in VB um einiges einfacher. Aber ein schwarzes Fenster ist in OpenGL eigentlich schon eine komplette 3D-Welt, auch wenn wir zu Anfang noch kein Objekt darstellen.
Zuerst benötigen wir noch zwei Variablen:
Public PrgRun As Boolean 'Flag = Programm läuft Private hrc As Long 'Handle für den OpenGL Gerätekontext
Listing 1: Globale Variablen
Beginnen wir nun mit einer Prozedur, die alle Arbeiten zur Erstellung eines OpenGL-Fensters erledigt. Fügen Sie die Prozedur CreateGLWindow zu Ihrem Modul hinzu:
Public Function CreateGLWindow(frm As Form, Width As Integer, Height As Integer, Bits As Integer) As Boolean Dim pfd As PIXELFORMATDESCRIPTOR ' pfd erklärt Windows, wie das Fenster beschaffen sein soll Dim PixelFormat As GLuint ' enthält das Ergebnis vom Versuch, ein Fenster mit den gegebenen Parametern zu erstellen pfd.cColorBits = Bits 'Farbtiefe pfd.cDepthBits = 16 '16 Bit Tiefenpuffer 'Der Tiefenpuffer enthält die Entfernung eines Pixels zur Kamera (Betrachter). 'Er verhindert, daß Objekte im Hintergrund beim Zeichnen Objekte im Vordergund überlagern. pfd.dwFlags = PFD_DRAW_TO_WINDOW Or PFD_SUPPORT_OPENGL Or PFD_DOUBLEBUFFER 'PFD_DRAW_TO_WINDOW = das Format muss als Fenster sichtbar sein können 'PFD_SUPPORT_OPENGL = das Format muss OpenGL unterstützen 'PFD_DOUBLEBUFFER = das Format muss Double Buffering unterstützen pfd.iLayerType = PFD_MAIN_PLANE 'Die Hauptebene auf der gezeichnt wird. pfd.iPixelType = PFD_TYPE_RGBA 'Pixel werden im RGBA Modus dargestellt. 'RGB ist mit VB identisch. Für A wird ein Alpha-Wert für die Transparenz übergeben pfd.nSize = Len(pfd) 'Größe der Struktur sollte natürlich stimmen pfd.nVersion = 1 'Versionsnummer PixelFormat = ChoosePixelFormat(frm.hDC, pfd) 'Prüfen, ob das oben beschriebene Pixelformat verfügbar ist If PixelFormat <> 0 Then 'Das Format ist verfügbar. If SetPixelFormat(frm.hDC, PixelFormat, pfd) <> 0 Then 'Einrichten des Pixelformates war erfolgreich hrc = wglCreateContext(frm.hDC) If hrc <> 0 Then 'ein Rendering Kontext wurde erstellt If wglMakeCurrent(frm.hDC, hrc) <> 0 Then 'Der Kontext wurde aktiviert frm.Show 'Fenster anzeigen glShadeModel smSmooth 'schaltet schöne Farbübergange ein glClearColor 0#, 0#, 0#, 0# 'schwarzer Hintergrund glClearDepth 1# 'Tiefenpuffer zurücksetzten (später mehr) glEnable glcDepthTest 'Aktivierung des Tiefentests (später mehr) glDepthFunc cfLEqual 'Typ des Tiefentests (später mehr) glHint htPerspectiveCorrectionHint, hmNicest 'Art der Perspektiven-Ansicht 'hmNicest = beste Ansicht / hmFastest = schnellste Darstellung CreateGLWindow = True End If End If End If End If End Function
Listing 2: CreateGLWindow
Als nächstes benötigen wir noch eine Prozedur, die OpenGL mitteilt, ob sich die Fenstergröße geändert hat. Fügen Sie die Prozedur ReSizeGLScene zu Ihrem Modul hinzu:
Public Sub ReSizeGLScene(ByVal Width As GLsizei, ByVal Height As GLsizei) 'Anpassen der Größe und Initialisierung des OpenGL-Fensters If Height = 0 Then 'Die Fensterhöhe muss größer 0 sein Height = 1 'sonst kommt es zu einem "Division by Zero"-Fehler End If glViewport 0, 0, Width, Height 'leeren des aktuellen Viewports glMatrixMode mmProjection 'Auswahl der Projektionsmatrix glLoadIdentity 'setzt die aktuelle Modell-Matrix zurück gluPerspective 45#, Width / Height, 0.1, 100# 'Berechnung des aktuellen Seitenverhältnisses des Fensters glMatrixMode mmModelView 'Auswahl der Modelview Projektionsmatrix glLoadIdentity 'setzt die aktuelle Modell-Matrix zurück End Sub
Listing 3: ReSizeGLScene
Damit diese auch entsprechend aufgerufen wird, setzen wir einen Call in das Resize-Ereignis der Form:
Private Sub Form_Resize() ReSizeGLScene ScaleWidth, ScaleHeight End Sub
Listing 4: Form_Resize
Was wir jetzt noch benötigen, ist die Sub Main. In dieser Prozedur läuft die oben angesprochene Endlosschleife:
Public Sub Main() Dim frm As Form PrgRun = True 'Flag = das Programm läuft Set frm = New Form1 'Fenster für OpenGL-Ausgabe frm.ScaleMode = vbPixels 'das Fenster muss zwingend in Pixel bemessen werden If CreateGLWindow(frm, 640, 480, 16) Then 'Fenster initialisieren Do 'Endlosschleife, in der das Fenster laufend gelöscht und neu aufgebaut wird. 'Die Laufzeit dieser Schleife ist ausschlaggebend, wieviele Objekt gezeichnet werden können glClear clrColorBufferBit Or clrDepthBufferBit ' löscht das Fenster und den Tiefenpuffer glLoadIdentity 'setzt die aktuelle Modell-Matrix zurück SwapBuffers (frm.hDC) 'Puffer tauschen (Double Buffering) DoEvents Loop While PrgRun 'Programm nur beenden, wenn PrgRun = False 'PrgRun ist Global definiert und wird im KeyDown-Ereignis von Form1 bei drücken von Escape gesetzt. 'alles freigeben und Programm beenden If hrc <> 0 Then 'hatten wir einen Gerätekontext für OpenGL? wglMakeCurrent 0, 0 'Freigeben des Gerätekontexts wglDeleteContext (hrc) 'Freigeben des Renderingkontexts End If Unload frm Set frm = Nothing End End If End Sub
Listing 5: Sub Main
Um das Programm zu beenden, setzen wir im KeyDown-Event der Form PrgRun auf False falls die ESC-Taste gedrückt wird:
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) If KeyCode = vbKeyEscape Then PrgRun = False End If End Sub
Listing 6: Form_KeyDown
Das Ende vom Anfang
Das hat alles geklappt und Sie haben ein schwarzes Fenster erzeugt? Herzlichen Glückwunsch und willkommen in der 3D-Welt. Im 1. Kapitel lernen wir, wie einfache Flächen dargestellt werden.
Ihre Meinung
Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.