So - wie versprochen gibts nun auch für dieses Forum mein Open - GL - Anfängertutorial.
es ist in derselben Fassung, wie damals das für das
DF:
Open Gl in Delphi (Kurztutorial)
In meinem mitlererweile 4. DCW-Tutorial geht es um Open Glide in (bzw. unter) Delphi. Ich habe zunächst nur ein Kurztutorial geschrieben, weil die Gesammtmaterie doch recht umfangreich ist und ein ausführlicheres Tut momentan für mich kaum realisierbar ist. In diesem Tut geht es also zunächst um die grundsätzlichen Dinge, die man für die Open Gl Programmierung unter Delphi braucht.
Inhalt
Einleitung
Inizialisierung
Das erste Objekt
Objekte Animieren - Drehen
Downloads
Einleitung
In diesem (Kurz-)Tutorial geht es also um
OpenGl unter Delphi. Da es sich nur um ein Kurztutorial handelt, werde ich nicht alles ausführlich erklären (um den gesammten Umfang der
OpenGL-Programmerung darzustellen, braucht man 1000 Seitige Bücher......), sondern ich werde lediglich versuchen, einen leichten Einstieg in das
OpenGL - Programmieren unter Delphi zu geben, welcher vorerst nur aus den Grundlegenden Basiks besteht. Es wäre sogar gut denkbar, dass ich irgendwann einmal (wenn ich mehr Zeit habe) noch ein zweites Open-Gl Tutorual schreibe, da in diesem noch einiges fehlt (verschiedene Projektionsmodi, Texturen, weitere Möglichkeiten der bewegung und Animation, verschiedene Lichter, displaylisten für flimmerfreie Scenen und Animationen, usw). Zuerst ist aber das "Fensterformen 2" - Tutorial dran.
Inizialisierung
Also: Der erste Schritt bei der
OpenGL - Programmierung ist die Inizialisierung des
OpenGL - Rendering Kontextes (bedeutet so viel wie das Formular zu einer für
OpenGl brauchbaren Zeichenfläche zu machen). Leider gibt es verschiedene Auffassungen darüber, wie Open Gl inizialisiert werden soll. Da ich hier nur ein Kurztutorial schreibe, und nicht jeden der nachfolgenden
OpenGl Befehler erklären will (würde die meisten Newbies ziemlich verwirren), habe ich eine Art Generalinizialisierung zusammengestellt, in welcher die wichtigsten Eigenschaften (wie z.b. der Tiefentest [mehr Geschwindigkeit] und der Backbuffer [Flimerfrei] aktiviert sind. Diese Inizialisierung können sowohl Newbies und auch Profies mit gutem Gewissen immer verwenden, da er die meisten wichtigkeiten
OpenGL beinhaltet (hat gleichzeitig die Konsequenz, dass ich nciht alles erklären brauche, weil es ja eh immer so gemacht werden aollte
). Genug der Worte - los gehts:
also:
das erste, was man immer machen sollte, ist Open Gl einzubinden ( uses Windows,.....,
OpenGl; - ganz oben im Queltext).
Nun brauchen wir ein paar Globale Variablen(! Privat - Variablen...):
Code:
private
mydc : HDC;
myrc : HGLRC;
myPalette : HPALETTE;
dazu kommt noch die Procedure "SetupPixelFormat"(auch im Private-Teil):
procedure SetupPixelFormat;
Damit hätten wir die wichtigesten Variablen schon mal Gesetzt. Nun muss noch die Prozedur "SetupPixelFormat" eingebaut werden (au backe: ziemlich lang):
Code:
procedure Tform1.SetupPixelFormat;
var
hheap : Thandle;
ncolors,i : integer;
lppalette : plogpalette;
byredmask, bygreenmask, bybluemask : byte;
npixelformat : integer;
pfd : Tpixelformatdescriptor;
begin
Fillchar(pfd,sizeof(pfd),0);
with pfd do
begin
nsize := sizeof(pfd);
nversion := 1;
dwflags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
ipixeltype := PFD_TYPE_RGBA;
cColorbits := 24;
cdepthbits := 32;
ilayertype := PFD_Main_Plane;
end;
nPixelFormat:= ChoosePixelFormat(myDC, @pfd);
SetPixelFormat(myDC, nPixelFormat, @pfd);
DescribePixelFormat(myDC, nPixelFormat,sizeof(TPixelFormatDescriptor),pfd);
if ((pfd.dwFlags and PFD_NEED_PALETTE) <> 0) then
begin
nColors := 1 shl pfd.cColorBits;
hHeap := GetProcessHeap;
lpPalette:= HeapAlloc (hHeap,0,sizeof(TLogPalette)+(nColors*sizeof(TPaletteEntry)));
lpPalette^.palVersion := $300;
lpPalette^.palNumEntries := nColors;
byRedMask := (1 shl pfd.cRedBits) - 1;
byGreenMask:= (1 shl pfd.cGreenBits) - 1;
byBlueMask := (1 shl pfd.cBlueBits) - 1;
for i := 0 to nColors - 1 do begin
lpPalette^.palPalEntry[i].peRed := (((ishr pfd.cRedShift) and byRedMask) *255)DIV byRedMask;
lpPalette^.palPalEntry[i].peGreen:= (((i shr pfd.cGreenShift)and byGreenMask)*255)DIV byGreenMask;
lpPalette^.palPalEntry[i].peBlue := (((i shr pfd.cBlueShift) and byBlueMask) *255)DIV byBlueMask;
lpPalette^.palPalEntry[i].peFlags:= 0;
end;
myPalette:= CreatePalette(lpPalette^);
HeapFree(hHeap, 0, lpPalette);
if (myPalette <> 0) then
begin
SelectPalette(myDC, myPalette, False);
RealizePalette(myDC);
end;
end;
Uf - ist ziemlich lang diese Prozedur.... aber wie gesagt, es ist eine Art Generalinizialisierund, die ihr einfach kopieren könnt
.
So - damit hätten wir schonmal die wichtigste (und längste) Prozedur geschaft. Nun brauchen wir noch eine Prozedur, die diese hier aufruft.
Dazu bietet sich natürlich die Form.create - Methode (oder besser: das Ereigniss der Methode) an. Also schreiben wir: (
oh nein, nicht schon wieder code)
Code:
procedure TForm1.FormCreate(Sender: TObject);
begin
form1.myDC:= GetDC(
Handle);
SetupPixelFormat;
myRC:= wglCreateContext(myDC);
wglMakeCurrent(myDC, myRC);
glEnable(GL_DEPTH_TEST);
glLoadIdentity;
end;
Zudem sollten wir uns auchnoch darum Kümmern, dass der Gerätekontext auch wieder Freigegeben wird:
Code:
procedure TForm1.FormDestroy(Sender: TObject);
begin
wglmakecurrent(0,0);
wgldeletecontext(mydc);
releasedc(
handle,mydc);
end;
Nun brauchen wir noch eine Prozedur, die unsere spätere Scene Zeichenet. Dazu richeten wir bei "Form1.onpain" eine neue Prozedur ein:
Code:
procedure TForm1.FormPaint(Sender: TObject);
begin
objekt;
end;
Die eben aufgerufene Prozedur "Objekt" ist die Prozedur, die dazu da ist, ein Objekt zu zeichnen. Allerdings existiert diese Prozedur noch nicht, also fügen wir sie zwichen "SetupPixelFormat" und "Formcreate" ein(ich habe ein paar komentare eingefügt, da diese Prozedur später noch wichig wird):
Code:
procedure Objekt;
begin
glClearColor(0, 0, 0.0, 1); // HintergundFarbe
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); //Farb und Tiefenpuffer löschen
glMatrixMode(GL_PROJECTION); // Darstellungsmodus = Projektionsmodus
glLoadIdentity;
glPolygonMode(GL_FRONT, GL_FILL); // Nur Vorderseiten darstellen (schneller) / Füllmodus : gefüllte Objekte
glMatrixMode(GL_MODELVIEW); // Koordinatensystem drehen
glLoadIdentity; // Setzt alles zurück auf Uhrsprungspunkt(sehr wichtig!)-ansonsten wird die Scene irgendwo dargestellt, aber nicht da, wo sie hin soll
glPushmatrix();
SwapBuffers(form1.myDC); //scene darstellen
end;
So damit hätten wir unsere Inizialisierung praktischfertig. Vielleicht ist es aufgefallen, dass ich zwischen "glpushmatix" und "swapbuffers(mydc)" eine Lehrzeile gelassen habe. Dort soll später unser Objekt hin. Darum kümmern wir uns im nächsten Kapitel. Achtung: Führt
Opengl-Programme nie im Debugger aus - es fürhrt manchmal zu Fehlern!.
Das erste Objekt
Nachdem wir diesen ganzen Inizialisierungsschrott endlich hinter uns haben, wollen wir beginnen, Objkete auf den Schirm zu bringen (bislang sieht uns meistens nur ein kleines Schwarzes Fenster an). So nun wollen wir unses ersten
OpenGl - Objekt zeichenen: Objekt werden in
Opengl immer mit "glbegin(objekttyp)" eingelitet und es hört immer mit "glend" auf. Das einfachste 3d - Objekt in
OpenGl (ich werde in diesem Tut nur 3d beachten, da 2d kaum wen mehr interessiert) ist ohne Zweifel der Würfel. Wir wollen nun also einen würfel zeichnen:
Dazu schreiben wir volgenden Quelltext in die "Objekt" - Prozedur undzwar zwischen "glPushmatrix();" und "SwapBuffers(form1.myDC);":
Code:
glBegin(GL_QUADS); //dieser Befehl zeichnet einen Würfel.
glVertex3d(0.5, -0.5, 0.5); //Linke Seite
glVertex3d(-0.5, -0.5, 0.5);
glVertex3d(-0.5, -0.5, -0.5);
glVertex3d(0.5, -0.5, -0.5);
glVertex3d(-0.5, -0.5, -0.5); //Rückseite
glVertex3d(-0.5, 0.5, -0.5);
glVertex3d(0.5, 0.5, -0.5);
glVertex3d(0.5, -0.5, -0.5);
glVertex3d(0.5, -0.5, -0.5); //Oberseite
glVertex3d(0.5, 0.5, -0.5);
glVertex3d(0.5, 0.5, 0.5);
glVertex3d(0.5, -0.5, 0.5);
glVertex3d(-0.5, -0.5, 0.5); //Vorderseite
glVertex3d(-0.5, 0.5, 0.5);
glVertex3d(0.5, 0.5, 0.5);
glVertex3d(0.5, -0.5, 0.5);
glVertex3d(-0.5, -0.5, 0.5); //Boden / Unterseite
glVertex3d(-0.5, 0.5, 0.5);
glVertex3d(-0.5, 0.5, -0.5);
glVertex3d(-0.5, -0.5, -0.5);
glVertex3d(-0.5, 0.5, 0.5); //Rechte Seite
glVertex3d(0.5, 0.5, 0.5);
glVertex3d(0.5, 0.5, -0.5);
glVertex3d(-0.5, 0.5, -0.5);
glEnd();
Zur Erklärung: Ein
Opengl-Würfel wird immer durch seine 6 Seiten gebildet. Diese Seiten werden wiederum durch ihre 4 Eckpunkte gebildet. Diese Echpunkte werden immer im Uhrzeigersinn angegeben (kann man am besten an der "Vorderseite sehen"). Dieses hat volgenden Grund: wenn
OpenGL eine Fläche betrachtet, deren Punkte im Uhrzeigersinn stehen, dann betrachtet es diese Seite als vorne (demnach von der anderen Seite als hinten). In unserer Definition haben wir angegeben, dass nur die Vordersiten gezeichnet werden sollen, um Rechenpower zu sparen.
Wenn wir nun die Szene starten, sehen wir einen weißen Würfel in unserem Fenster (besser: wir sehen seine Vorderseite). Insbesondere das Weiß sieht allerdings ziemlich pfade aus: unserer Würfel braucht eine Farbe. In
OpenGl gibt es zwei Möglichkeiten Farben zu setzen: den Befehl "glcolor3f" und "glcolor4f". Der erste verlangt 3 Parameter. Bei diesen Parametern handelt es sich um Float-Werte, die zwischen 0 und 1 liegen. Diese definieren die Farbe (
RGB). Bei "glcolor4f" ist es genau das selbe, blos das als 4. Parameter noch ein Transparenzwert benötigt wird - in unserem Beispiel brauchen wir aber keine Transparenz.
Wir schreiben nun also volgende Zeile vor "glBegin(GL_QUADS);":
Und schwups: Unserer Würfel wird Quietschgelb. Es ist insgesamt etwas ungewohnt, mit Werten zwischen 0 und 1 arbeiten zu müssen, aber ich glaube, das wichtigtste bekommt man hin ( glcolor3f(1,0,0) = rot , glcolor3f(1,1,0) = gelb , glcolor3f(0,1,0) = grün , glcolor3f(0,1,1) = blaugrün , glcolor3f(0,0,1) = blau , glcolor3f(1,0,1) = lila , glcolor3f(1,0.5,0) = orange) .
Allerdings ist das ganze noch sehr langweilig, weil wir unseren Würfel nur von vorne sehen...naja: da werden werden wir uns im nächsten Kapitel drum kümmern.
Objekte Animieren - Drehen
Es gibt viele Methoden, um
Opengl - Objekte zu bewegen, bzw zu animieren. in diesem Tutorial werden wir uns aber nur mit dem "Drehen" befassen.
Objekte in Oüpengl dreht man über den Befehl "glrotate()". Dieser Befehl verlangt 4 Parameter. Der erste gibt an, um wieviel Grad gedreht werde soll. Die anderen definieren eine Achse, um die gedreht werden soll. So dreht der Befehl "glrotate(45,1,0,0);" die nachfolgende Szene um 45 Grad um die X-Achse.
Ich will das ganze in einem Beispiel verdeutlichen:
Wir legen zwei globale Variablen "rotx" und "roty" an. Beide sind vom Typ "integer":
Code:
var
Form1: TForm1;
rotx : integer;
roty : integer;
Schreiben wir zwischen "glcolor3f" und "glbegin(...)" folgende Zeilen, die unseren Würfel drehen sollen:
Code:
glrotate(rotx,1,0,0); //drehung um die x-achse
glrotate(roty,0,1,0); //drehung um die y-achse
Nun brauchen wir noch eine Methode, mit denen wir "rotx" und "roty" ändern können. Wir bearbeiten also das "onkeydown"-ereigniss unbseres Fensters:
Code:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if key = vk_down then
begin
rotx := rotx+1;
end;
if key = vk_up then
begin
rotx := rotx-1;
end;
if key = vk_right then
begin
roty := roty+1;
end;
if key = vk_left then
begin
roty := roty-1;
end;
repaint;
end;
Und siehe da: Wenn wir nun unsere Scene starten, können wir unseren Würfel mit den Pfeiltasten drehen. Allerdings sieht die vordere ecke immer etwas merkwürdig aus (wird nicht richtig dargestellt). Dies hat damit zu tun, dass wir noch kein Licht haben, aber diesen Mangel zu beheben, würde den Umfang dieses Tutorials Sprengen. Wie zu anfang schon gesagt: es wäre gut denkbar, das ich irgendwann nochmal ein zweites
OpenGL - Tutorial schreibe - evtl muss für Lichter sogar ein extra Tut her. Ich hoffe es hat dennoch gefallen und war nicht zu schwer zu verstehen.
Downloads
Dieses Tutorial als HTML-Datei
Das Beispielprogramm
März/April 2002:
Mr_T
Geschrieben für
DCW-Group
********Doppelpost gelöscht********* FuckRacism
(Edited by Daniel B) Einrückungen, Fehlerbehebung