Thema: Delphi OpenGL für Anfänger

Einzelnen Beitrag anzeigen

Benutzerbild von Mr_T
Mr_T

Registriert seit: 7. Jun 2002
Ort: Eilsum
136 Beiträge
 
Delphi 2005 Personal
 

OpenGL für Anfänger

  Alt 12. Jun 2002, 18:48
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);":
Code:
glcolor3f(1,1,0);
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
  Mit Zitat antworten Zitat