NeHe - Lektion 18 - Quadrics

Lektion 18



Quadrics

Quadrics sind eine Möglichkeit komplexe Objekte zu zeichen, die normalerweise mehrere FOR-Schleifen und einiges Hintergrundwissen in Trigonometrie benötigen würden.

Wir werden den Code aus Lektion sieben verwenden. Wir werden sieben Variablen hinzufügen und die Texturen ein wenig verändern um etwas Abwechslung hinein zu bringen :)

#include <windows.h>                    // Header Datei für Windows
#include <stdio.h>                    // Header Datei für Standard Eingabe/Ausgabe
#include <gl\gl.h>                    // Header Datei für die OpenGL32 Library
#include <gl\glu.h>                    // Header Datei für die GLu32 Library
#include <gl\glaux.h>                    // Header Datei für die GLaux Library

HDC        hDC=NULL;                // Privater GDI Device Context
HGLRC        hRC=NULL;                // Permanenter Rendering Context
HWND        hWnd=NULL;                // Enthält unser Fenster-Handle
HINSTANCE    hInstance;                // Enthält die Instanz der Applikation

bool    keys[256];                    // Array das für die Tastatur Routine verwendet wird
bool    active=TRUE;                    // Fenster Aktiv Flag standardmäßig auf TRUE gesetzt
bool    fullscreen=TRUE;                // Fullscreen Flag ist standardmäßig auf TRUE gesetzt
bool    light;                        // Beleuchtung AN/AUS
bool    lp;                        // L gedrückt?
bool    fp;                        // F gedrückt?
bool    sp;                        // Leertaste gedrückt?    ( NEU )

int    part1;                        // Start der Disc    ( NEU )
int    part2;                        // Ende der Disc    ( NEU )
int    p1=0;                        // Increase 1        ( NEU )
int    p2=1;                        // Increase 2        ( NEU )

GLfloat    xrot;                        // X Rotation
GLfloat    yrot;                        // Y Rotation
GLfloat xspeed;                        // X Rotation Geschwindigkeit
GLfloat yspeed;                        // Y Rotation Geschwindigkeit

GLfloat    z=-5.0f;                    // Tiefe in den Bildschirm

GLUquadricObj *quadratic;                // Speicher für unsere Quadratic Objecte ( NEU )

GLfloat LightAmbient[]=  { 0.5f, 0.5f, 0.5f, 1.0f };    // Ambiente Licht Werte
GLfloat LightDiffuse[]=     { 1.0f, 1.0f, 1.0f, 1.0f };    // Diffuse Licht Werte
GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };    // Licht Position

GLuint    filter;                        // Welcher Filter benutzt werden soll
GLuint    texture[3];                    // Speicher für 3 Texturen
GLuint  object=0;                    // Welches Object gezeichnet werden soll ( NEU )

LRESULT    CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);    // Deklaration für WndProc

Okay, gehen Sie nun runter zu IniGL(). Wir fügen hier 3 Zeilen Code ein, um unser Qudaratic zu initialisieren. Fügen Sie die 3 Zeilen hinter dem Teil ein, wo Sie light1 aktiveren, aber noch bevor Sie True zurückgeben. Die erste Codezeile initialisiert den Quadratic und erzeugt einen Zeiger auf den Speicherbereich, wo er sich befinden wird. Wenn er nicht erzeugt werden kann, wird 0 zurückgegeben. Die zweite Codezeile erzeugt die Normalen für das Quadratic, so das die Beleuchtugn gut aussieht. Andere mögliche Werte währen GLU_NONE udn GLU_FLAT. Als letztes aktiveren Textur-Mapping für unser Quadratic. Textur-Mapping ist irgendwie ziemlich linkisch und funktioniert nie so, wie sie sich es vorstellen, so wie sie es schon von der Kisten-Textur kennen.

    quadratic=gluNewQuadric();            // erzeugt einen Zeiger auf das Quadric Obkect ( NEU )
    gluQuadricNormals(quadratic, GLU_SMOOTH);    // erzeugt Normalen ( NEU )
    gluQuadricTexture(quadratic, GL_TRUE);        // erzeugt Textur Koordinaten ( NEU )

Nun habe ich mich entschlossen, den Würfel in diesem Tutorial beizubehalten, so dass Sie sehen können, wie Texturen auf Qudratic-Objekte gemapped werden. Ich habe mich entschlossen den Würfel in eine eigen Funktion zu verschieben, so das die Zeichnen-Funktion beim neu schreiben sauberer aussieht. Jeder sollte diesen Code wiedererkennen. =P

GLvoid glDrawCube()                    // Zeichne einen Würfel
{
        glBegin(GL_QUADS);            // Beginne Qudrate zu zeichnen
        // Front Face
        glNormal3f( 0.0f, 0.0f, 1.0f);        // Normalen die forwärts zeigen
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);    // Unten Links der Textur und des Qudrats
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);    // Unten rechts der Textur und des Qudrats
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);    // Oben rechts der Textur und des Qudrats
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);    // Oben Links der Textur und des Qudrats
        // Back Face
        glNormal3f( 0.0f, 0.0f,-1.0f);        // Normal Facing Away
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);    // Unten rechts der Textur und des Qudrats
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);    // Oben rechts der Textur und des Qudrats
        glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);    // Oben Links der Textur und des Qudrats
        glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);    // Unten Links der Textur und des Qudrats
        // Top Face
        glNormal3f( 0.0f, 1.0f, 0.0f);        // Normal Facing Up
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);    // Oben Links der Textur und des Qudrats
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);    // Unten Links der Textur und des Qudrats
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);    // Unten rechts der Textur und des Qudrats
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);    // Oben rechts der Textur und des Qudrats
        // Bottom Face
        glNormal3f( 0.0f,-1.0f, 0.0f);        // Normal Facing Down
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);    // Oben rechts der Textur und des Qudrats
        glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);    // Oben Links der Textur und des Qudrats
        glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);    // Unten Links der Textur und des Qudrats
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);    // Unten rechts der Textur und des Qudrats
        // Right face
        glNormal3f( 1.0f, 0.0f, 0.0f);        // Normal Facing Right
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);    // Unten rechts der Textur und des Qudrats
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);    // Oben rechts der Textur und des Qudrats
        glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);    // Oben Links der Textur und des Qudrats
        glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);    // Unten Links der Textur und des Qudrats
        // Left Face
        glNormal3f(-1.0f, 0.0f, 0.0f);        // Normal Facing Left
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);    // Unten Links der Textur und des Qudrats
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);    // Unten rechts der Textur und des Qudrats
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);    // Oben rechts der Textur und des Qudrats
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);    // Oben Links der Textur und des Qudrats
    glEnd();                    // Done Drawing Quads
}

Als nächstes ist die DrawGLScene-Funktion dran, wo ich eine einfache IF-Abfrage benutzt habe, um die einzelnen Objekte zu zeichnen. Außerdem habe ich eine statische Variable benutzt (eine lokale Variable, welche ihren Wert jeweils behält wenn sie aufgerufen wird), um einen coolen Effekt zu erzielen, wenn Teile der Disk gezeichnet werden. Ich schreibe die komplette DrawGLScene-Funktion der Übersicht halber komplett neu.

Sie werden bemerken, wenn ich über die benutzten Parameter spreche, ignoriere ich den eigentlichen ersten Parameter (quadratic). Dieser Parameter wird für alle Objekte benutzt, die wir neben dem Würfel zeichnen, weshalb ich ihn ignoriere, wenn ich über die Parameter spreche.

int DrawGLScene(GLvoid)                        // Hier kommt der ganze Zeichnen-Kram hin
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Löscht den Bildschirm und den Depth-Buffer
    glLoadIdentity();                    // Resettet die Ansicht (View)
    glTranslatef(0.0f,0.0f,z);                // In den Bildschirm hineinbewegen

    glRotatef(xrot,1.0f,0.0f,0.0f);                // Rotiere auf der X-Achse 
    glRotatef(yrot,0.0f,1.0f,0.0f);                // Rotiere auf der X-Achse

    glBindTexture(GL_TEXTURE_2D, texture[filter]);        // Wähle eine Filter-Textur

    // Dieser Code-Ausschnitt ist neu ( NEU )
    switch(object)                        // Überprüfe object um herauszufinden, was gezeichnet werden soll
    {
    case 0:                            // Zeichne Objekt 1
        glDrawCube();                    // Zeichne unseren Würfel
        break;                        // Fertig

Das zweite Objekt, das wir erzeugen, wird ein Zylinder sein. Der erste Parameter (1.0f) ist der Radius des Zylinders an seiner Basis (Boden). Der zweite Parameter (1.0f) ist der Radius des Zylinders an der Spitze. der dritte Parameter (3.0f) ist die Höhe des Zylinders (wie lang er ist). Der vierte Parameter (32) gibt an, wieviele Unterteilungen um die Z-Achse "herum" sind und letztendlich der fünfte Parameter (32) enthält die Anzahl der Unterteilungen "entlängs" der Z-Achse. Je mehr Unterteilungen es gibt, desto detailierter ist das Objekt. Indem Sie die Anzahl der Unterteilungen erhöhen, fügen Sie mehr Polygone dem Objekt hinzu. Demnach kaufen Sie sich Qualität mittel Geschwindigkeitseinbußen. Meistens ist es recht einfach ein gesundes Mittelmaß zu finden.

    case 1:                            // Zeichne Objekt 2
        glTranslatef(0.0f,0.0f,-1.5f);            // Zentriere den Zylinder
        gluCylinder(quadratic,1.0f,1.0f,3.0f,32,32);    // Zeichne unseren Zylinder
        break;                        // Fertig

Das dritte Objekt das wir erzeugen hat die Form einer CD. Der erste Parameter (0.5f) ist der innere Radius der Disk. Dieser Wert kann gleich null sein, wenn kein Loch in der Mitte gewünscht ist. Je größer der innere Radius, desto größer ist das Loch in der Mitte der Disc. Der zweite Parameter (1.5f) ist der äußere Radius. Dieser Wert sollte größer sein, als der innere Radius. Wenn Sie ihn ein klein wenig größer als den inneren Radius machen, erhalten Sie einen dünnen Ring. Wenn Sie ihn wesentlich größer machen, werden Sie einen dicken Ring erhalten. Der dritte Parameter (32) ist die Anzahl der Scheiben aus der die Disk besteht. Denken Sie bei Scheiben an Pizza-Stücke. Die mehr Scheiben Sie haben, desto runter ist sind die äußeren Ecken der Disc. Letztendlich enthält der vierte Parameter (32) die Anzahl der Ringe, aus der die Diskette besteht. Die Ringe sind ähnlich den Spuren einer Schallplatte. Kreise innerhalb von Kreisen. Diese Ringe unterteilen die Disk vom inneren Radius bis zum äußeren Radius und bringen mehr Detail. Nochmal, je mehr Unterteilungen es gibt, desto langsamer wird es laufen.

    case 2:                            // Zeichne Objekt 3
        gluDisk(quadratic,0.5f,1.5f,32,32);        // Zeichne eine Disc (CD Form)
        break;                        // Fertig

Unser viertes Objekt ist ein Objekt, von dem ich weiß, dass viele Schwierigkeiten hatten mit ihr hatten. Die Sphere (Kugel)! Diese ist relativ simpel. Der erste Parameter ist der Radius der Sphere. Für den Fall, dass Sie mit Radius/Diameter, etc. nicht vertraut sind, der Radius ist der Abstand vom Mittelpunkt des Objekts zum äußeren Rand des Objektes. In diesem Fall ist unser Radius 1.3f. Als nächstes haben wir unsere Unterteilungen um die Z-Achse "herum" (32) und unsere Unterteilungen "entlängs" der Z-Achse (32) Je mehr Unterteilungen Sie haben, desto glatter wir die Spehre aussehen. Spehren benötigen in der Regel recht wenig Unterteilungen, damit sie gut aussehen.

    case 3:                            // Zeichne Objekt 4
        gluSphere(quadratic,1.3f,32,32);        // Zeichne eine Sphere
        break;                        // Fertig

Unser fünftes Objekt wird erzeugt, in dem der selbe Befehl benutzt wird, den wir schon zur Erzeugung des Zylinders benutzt haben. Wenn Sie sich erinnern, als wir den Zylinder erzeugten kontrollierten die ersten beiden Parameter den Radius des Zylinders am Boden und an der Spitze. Um einen Kegel zu erhalten, klingt es logisch, dass wir den Radius an einem Ende einfach 0 setzen. Das erzeugt einen Punkt am entsprechenden Ende. Dementsprechend setzen wir den Radius der Spitze im unteren Code auf null. Das erzeugt unseren Punkt, wodurch auch unser Kegel erzeugt wird.

    case 4:                            // Zeichne Objekt 5
        glTranslatef(0.0f,0.0f,-1.5f);            // Zentriere den Kegel
        gluCylinder(quadratic,1.0f,0.0f,3.0f,32,32);    // Ein Kegel mit unterem Raidus von 0.5f und einer Höhe von 2
        break;                        // Fertig

Unser sechstes Objekt wird mit gluPartialDisc erzeugt. Das Objekt, dass wir mit diesem Befehl erzeugen, sieht genauso aus, wie die Disk, die wir vorhin erzeugt haben, mit dem Unterschied, dass der Befehl gluPartialDisc zwei neue Parameter hat. Der fünfte Parameter (part1) ist der Anfangs-Winkel ab wo wir die Disc zeichnen wollen. Der sechste Parameter ist der Sweep-Winkel. Der Sweep-Winkel ist die Distanz die wir vom aktuellen Winkel aus zurücklegen. Wir erhöhen den Sweep-Winkel, was ein langsames zunehmen im Uhrzeigersinn der Disc während des zeichnens, zur Folge hat. Wenn der Sweep-Winkel 360 Grad erreicht hat, erhöhen wir den Anfangs-Winkel, wodurch die Disc wieder langsam abnimmt. Danach starten wir erneut von vorne.

    case 5:                            // Zeichne Objekt 6
        part1+=p1;                    // Inkrementiere Start-Winkel
        part2+=p2;                    // Inkrementiere Sweep-Winkel

        if(part1>359)                    // 360 Grad
        {
            p1=0;                    // Stoppe mit der Inkrementierung des Anfangs-Winkels
            part1=0;                // Setze Anfangs-Winkel auf 0
            p2=1;                    // Beginne mit der Inkrementierung des Sweep-Winkels
            part2=0;                // Starte Sweep-Winkel bei 0
        }
        if(part2>359)                    // 360 Grad
        {
            p1=1;                    // Starte Inkrementierung des Anfangs-Winkel
            p2=0;                    // Stoppe Inkrementierung des Sweep-Winkels
        }
        gluPartialDisk(quadratic,0.5f,1.5f,32,32,part1,part2-part1);    // Eine Disk  wie die davor
        break;                        // Fertig
    };

    xrot+=xspeed;                        // Inkrementiere Rotation auf der X-Achse
    yrot+=yspeed;                        // Inkrementiere Rotation auf der Y-Achse
    return TRUE;                        // Weiter geht's 
}

Im KillGLWindow() Codesegment müssen wir den Quadratic wieder löschen, um den Speicher freizugeben. Wir machen das mit dem Befehl gluDeleteQuadratic.

GLvoid KillGLWindow(GLvoid)                    // Entferne das Fenster korrekt
{
    gluDeleteQuadric(quadratic);                // Lösche Quadratic - Gebe Ressourcen wieder frei

Nun zum letzten Teil, die Tasten-Eingabe. Fügen Sie das einfach da ein, wo wir den Rest der Tastatur-Eingabe überprüfen.

                if (keys[' '] && !sp)        // Wurde die Leertaste gedrückt?
                {
                    sp=TRUE;        // Wenn ja, setze sp auf TRUE
                    object++;        // wechsle durch die Objekte
                    if(object>5)        // Ist object größer als 5?
                        object=0;    // Wenn ja, dann auf 0 setzen
                }
                if (!keys[' '])            // Wurde die Leertaste wieder losgelassen
                {
                    sp=FALSE;        // Wenn ja, dann setze sp auf FALSE
                }

Das war's! Nun können Sie Quadrics in OpenGL zeichnen. Einige wirklich beeindruckende Dinge können mit Morphing und Quadrics gemacht werden. Die animierte Disk ist ein Beispiel einfachen Morphens.

All die Zeit haben, sich meine Web-Seite anzuschauen: TipTup.Com 2000.

GB Schmick (TipTup)

Jeff Molofee (NeHe)

* DOWNLOAD Visual C++ Code für diese Lektion.

* DOWNLOAD Borland C++ Builder 6 Code für diese Lektion. ( Conversion by Christian Kindahl )
* DOWNLOAD C# Code für diese Lektion. ( Conversion by Brian Holley )
* DOWNLOAD Code Warrior 5.3 Code für diese Lektion. ( Conversion by Scott Lupton )
* DOWNLOAD Cygwin Code für diese Lektion. ( Conversion by Stephan Ferraro )
* DOWNLOAD Delphi Code für diese Lektion. ( Conversion by Michal Tucek )
* DOWNLOAD Dev C++ Code für diese Lektion. ( Conversion by Dan )
* DOWNLOAD Euphoria Code für diese Lektion. ( Conversion by Evan Marshall )
* DOWNLOAD Game GLUT Code für diese Lektion. ( Conversion by Milikas Anastasios )
* DOWNLOAD Irix / GLUT Code für diese Lektion. ( Conversion by Rob Fletcher )
* DOWNLOAD Java Code für diese Lektion. ( Conversion by Jeff Kirby )
* DOWNLOAD JoGL Code für diese Lektion. ( Conversion by Abdul Bezrati )
* DOWNLOAD LCC Win32 Code für diese Lektion. ( Conversion by Robert Wishlaw )
* DOWNLOAD Linux Code für diese Lektion. ( Conversion by Simon Werner )
* DOWNLOAD Linux/GLX Code für diese Lektion. ( Conversion by Mihael Vrbanec )
* DOWNLOAD Linux/SDL Code für diese Lektion. ( Conversion by Ti Leggett )
* DOWNLOAD LWJGL Code für diese Lektion. ( Conversion by Mark Bernard )
* DOWNLOAD Mac OS Code für diese Lektion. ( Conversion by Anthony Parker )
* DOWNLOAD Mac OS X/Cocoa Code für diese Lektion. ( Conversion by Bryan Blackburn )
* DOWNLOAD MASM Code für diese Lektion. ( Conversion by Christophe )
* DOWNLOAD Visual C++ / OpenIL Code für diese Lektion. ( Conversion by Denton Woods )
* DOWNLOAD Pelles C Code für diese Lektion. ( Conversion by Pelle Orinius )
* DOWNLOAD SDL Code für diese Lektion. ( Conversion by Ken Rockot )
* DOWNLOAD Visual Basic Code für diese Lektion. ( Conversion by The Gilb )
* DOWNLOAD Visual Studio .NET Code für diese Lektion. ( Conversion by Grant James )



Deutsche Übersetzung: Joachim Rohde
Der original Text ist hier zu finden.
Die original OpenGL Tutorials stammen von NeHe's Seite.