NeHe - Lektion 23 - Sphere Mapping Quadrics

Lektion 23



Sphere Environment Mapping ist eine schnelle Methode um Reflektionen auf einem matallischen oder reflektierenden Objekt in ihrer Szene hinzuzufügen. Obwohl es nicht so genau wie die Realität oder wie eine Cube Environment Map ist, ist es um einige schneller! Wir werden den Code aus Lektion achtzehn (Quadrics) als Basis für dieses Tutorial verwenden. Außerdem werden wir nicht die selben Textur-Maps verwenden, sondern eine für die Sphere Map und ein Hintergrundbild.

Bevor wir anfangen... Das "Red Book" definiert eine Sphere Map als ein Bild einer Szene auf einem metallischen Ball mit unendlicher Entfernung und unendlichem Brennpunkt. Nun, dass ist unmöglich im reellem Leben. Die beste Möglichkeit die ich gefunden habe, eine gutes Sphere Map Bild zu erzeugen, ohne eine Fisch-Augen-Lense zu benutzen, ist das Programm Adobe Photoshop.

Eine Sphere Map in Photoshop erzeugen:

Als erstes benötigen Sie ein Bild der Umgebung die Sie auf die Sphere mappen wollen. Öffnen Sie das Bild in Adobe Photoshop und markieren Sie das gesamte Bild. Kopieren Sie das Bild und erzeugen Sie ein neues PSD (Photoshop Format), das neue Bild sollte die selbe Größe als das kopierte Bild haben. Fügen Sie eine Kopie des Bildes in das neue Fenster ein, das wir erzeuge haben. Der Grund, warum wir eine Kopie erstellen, ist, dass Photoshop so seine Filter benutzen kann. Anstatt das Bild zu kopieren können Sie aus dem Drop-Down Menü Modus (Mode) auswählen und den RGB-Modus wählen. Dann sollten allen Filter verfügbar sein.

Als nächstes müssen wir die Größe des Bildes verändern, so dass die Bild-Dimensionen ein Vielfaches von 2 sind. Erinnern Sie sich daran, dass ein Bild, wenn es als Textur verwendet werden soll, eine Größe von 128x128, 256x256, etc. haben muss. Im Menü Bild (Image) wählen Sie Bild-Größe (Image Size), deaktivieren die Checkbox Proportionen erzwingen (Constraint Proportions) und ändern die Größe des Bildes auf eine gültige Textur-Größe. Wenn ihr Bild 100x90 groß ist, ist es besser, dass Bild auf 128x128 zu verändern, statt 64x64. Wenn das Bild kleiner gemacht wird, gehen viele Details verloren.

Als letztes wählen wir das Filter-Menü aus, wählen Verzerren (distort) und wenden einen spherischen Modifizieren an. Sie sollten sehen, dass der Mittelpunkt des Bildes ähnlich wie ein Ballon aufgeblasen wird, wobei es bei normalen Sphere Maps die äußere Fläche geschwärzt wird, was aber nichts ausmacht. Speichern Sie eine Kopie des Bildes als .BMP und Sie können anfangen zu programmieren!

Wir fügen diesmal keine neuen globalen Variablen hinzu, aber wir ändern das Textur-Array, damit es 6 Texturen aufnehmen kann.

GLuint    texture[6];                                // Speicher für 6 Texturen

Als nächstes habe ich die LoadGLTextures() Funktion modifiziert, so dass wir 2 Bitmaps laden können und 3 Filter erzeugen. (So, wie wir es im ursprünglichen Textur-Tutorial gemacht haben). Grundsätzlich durchlaufen wir zweimal eine Schleife und erzeugen jeweils 3 Texturen mit verschiedenen Filterungs-Modus. Fast der gesamte Code ist neu oder modifiziert.

int LoadGLTextures()                                // Lade Bitmaps und konvertiere sie in Texturen
{
    int Status=FALSE;                            // Status Indikator

    AUX_RGBImageRec *TextureImage[2];                    // erzeuge Speicherplatz für die Textur

    memset(TextureImage,0,sizeof(void *)*2);                       // Setze den Zeiger auf NULL

    // Lade Bitmap, überprüfe auf Fehler, wenn Bitmap nicht gefunden, beenden
    if ((TextureImage[0]=LoadBMP("Data/BG.bmp")) &&                // Hintergrund Textur
        (TextureImage[1]=LoadBMP("Data/Reflect.bmp")))            // Reflektions Textur (Spheremap)
    {
        Status=TRUE;                            // Setze den Status auf TRUE

        glGenTextures(6, &texture[0]);                    // erzeuge drei Texturen

        for (int loop=0; loop<=1; loop++)
        {
            // erzeuge die am nähsten gelegene gefilterte Textur
            glBindTexture(GL_TEXTURE_2D, texture[loop]);        // erzeuge Textur 0 und 1
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
            glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY,
                0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);

            // erzeuge linear gefilterte Textur
            glBindTexture(GL_TEXTURE_2D, texture[loop+2]);        // erzeuge Textur 2, 3 und 4
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY,
                0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);

            // erzeuge MipMapped Textur
            glBindTexture(GL_TEXTURE_2D, texture[loop+4]);        // erzeuge Textur 4 und 5
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
            gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY,
                GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);
        }
        for (loop=0; loop<=1; loop++)
        {
            if (TextureImage[loop])                        // Wenn Textur Existiert
            {
                    if (TextureImage[loop]->data)            // Wenn Textur Image Existiert
                    {
                            free(TextureImage[loop]->data);    // Gebe Textur Image Speicher frei
                    }
                    free(TextureImage[loop]);        // Gebe Image Struktur frei
            }
        }
    }

    return Status;                                // Gebe Status zurück
}

Wir werden den Würfel-zeichnen-Code etwas verändern. Statt 1.0 und -1.0 zu verwenden, werden wir 0.5 und -0.5 für die Normalen verwenden. In dem wir den Wert für die Normale ändern, können Sie die Reflektions-Map heran- und wegzoomen. Wenn der Normalen-Wert größer ist, wird das reflektierte Bild größer und kann klobig erscheinen. Indem der Normalen-Wert auf 0.5 und -0.5 reduziert wird, wird das reflektierte Bild etwas weggezoomt, so dass das reflektierte Bild auf dem Würfel überhaupt nicht klobig erscheint. Wird der Normalen-Wert zu niedrig angesetzt, wird das Ergebnis nicht sehenswert sein.

GLvoid glDrawCube()
{
        glBegin(GL_QUADS);
        // Frontseite
        glNormal3f( 0.0f, 0.0f, 0.5f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
        // Rückseite
        glNormal3f( 0.0f, 0.0f,-0.5f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
        // obere Seite
        glNormal3f( 0.0f, 0.5f, 0.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
        // untere Seite
        glNormal3f( 0.0f,-0.5f, 0.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
        // rechte Seite
        glNormal3f( 0.5f, 0.0f, 0.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
        // linke Seite
        glNormal3f(-0.5f, 0.0f, 0.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
    glEnd();
}

Nun fügen wir in InitGL zwei neue Funktions-Aufrufe ein, diese Aufrufe setzen den Textur-Erzeugungs-Modus für S und T auf Sphere Mapping. Die Textur-Koordinaten S, T, R & Q sind ähnlich den Objekt-Koordinaten X, Y, Z und W. Wenn Sie eine ein-dimensionale Textur (1D) verwenden, benutzen Sie die S Koordinate. Wenn ihre Textur zwei-dimensional ist, werden Sie die S & T Koordinaten verwenden.

Was der folgende Code also macht, ist, OpenGL mitzuteilen, wie die S und T Koordinaten automatisch für uns, basierend auf der Sphere-Mapping Formel, erzeugt werden. Die R und Q Koordinaten werden in der Regel ignoriert. Die Q Koordinate kann für fortgeschrittene Textur-Mapping-Extensionen verwendet werden und die R Koordinate mag vielleicht mal nützlich sein, wenn 3D Textur-Mapping in OpenGL eingeführt wurde, aber zum jetzigen Zeitpunkt ignorieren wir die R & Q Koordinaten. Die S Koordinate verläuft horizontal über die Seite unseres Polygons, die T Koordinate verläuft vertikal über die Seite unseres Polygons.

    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);            // Setze den Textur-Erzeugungs-Modus für S auf Sphere Mapping ( NEU )
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);            // Setze den Textur-Erzeugungs-Modus für T auf Sphere Mapping ( NEU )

Wir sind fast fertig! Wir müssen uns nur noch ums rendern kümmern. Ich habe einige Qudratic Objekte weggelassen, weil diese nicht sonderlich gut mit Environment Mapping gearbeitet haben. Als erstes müssen wir die Textur-Erzeugung aktivieren. Dann wählen wir die Reflektions-Textur (Sphere Map) aus und zeichnen unser Objekt. Nachdem alle Objekte die Sie sphere-mappen wollen, gezeichnet wurden, werden Sie die Textur-Erzeugung wieder ausstellen wollen, weil sonst alles andere auch ge-sphere-mapped wird. Wir deaktivieren Sphere-Mapping bevor wir den Hintergrund zeichen (wir wollen den Hintergrund nicht sphere mappen). Sie werden bemerken, dass der Befehl zum Textur binden ziemlich komplex aussieht. Alles was da gemacht wird, ist das Auswählen des Filters, der benutzt wird, wenn wir unsere Sphere Map oder das Hintergrund-Bild zeichnen.

int DrawGLScene(GLvoid)                                // Hier kommt der ganze Zeichnen-Kram hin
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // Lösche den Bildschirm und den Depth-Buffer
    glLoadIdentity();                            // Resette die Ansicht (View)

    glTranslatef(0.0f,0.0f,z);

    glEnable(GL_TEXTURE_GEN_S);                        // aktiviert Textur Koordinaten Erzeugung für S ( NEU )
    glEnable(GL_TEXTURE_GEN_T);                        // aktiviert Textur Koordinaten Erzeugung für T ( NEU )

    glBindTexture(GL_TEXTURE_2D, texture[filter+(filter+1)]);        // Wählt eine Sphere Map aus
    glPushMatrix();
    glRotatef(xrot,1.0f,0.0f,0.0f);
    glRotatef(yrot,0.0f,1.0f,0.0f);
    switch(object)
    {
    case 0:
        glDrawCube();
        break;
    case 1:
        glTranslatef(0.0f,0.0f,-1.5f);                    // zentriere den Zylinder
        gluCylinder(quadratic,1.0f,1.0f,3.0f,32,32);            // Ein Zylinder mit einem Radius von 0.5 und einer Höhe von 2
        break;
    case 2:
        gluSphere(quadratic,1.3f,32,32);                // Sphere mit einem Radius von 1 und 16 Segmente für Breite/Länge
        break;
    case 3:
        glTranslatef(0.0f,0.0f,-1.5f);                    // Zentriere den Kegel
        gluCylinder(quadratic,1.0f,0.0f,3.0f,32,32);            // Kegel mit einem Boden-Radius von 0.5 und einer Höhe von 2
        break;
    };

    glPopMatrix();
    glDisable(GL_TEXTURE_GEN_S);                        // deaktiviert Textur Koordinaten Erzeugung ( NEU )
    glDisable(GL_TEXTURE_GEN_T);                        // deaktiviert Textur Koordinaten Erzeugung ( NEU )

    glBindTexture(GL_TEXTURE_2D, texture[filter*2]);            // Wählt die Hintergrund Textur aus ( NEU )
    glPushMatrix();
        glTranslatef(0.0f, 0.0f, -24.0f);
        glBegin(GL_QUADS);
            glNormal3f( 0.0f, 0.0f, 1.0f);
            glTexCoord2f(0.0f, 0.0f); glVertex3f(-13.3f, -10.0f,  10.0f);
            glTexCoord2f(1.0f, 0.0f); glVertex3f( 13.3f, -10.0f,  10.0f);
            glTexCoord2f(1.0f, 1.0f); glVertex3f( 13.3f,  10.0f,  10.0f);
            glTexCoord2f(0.0f, 1.0f); glVertex3f(-13.3f,  10.0f,  10.0f);
        glEnd();

    glPopMatrix();

    xrot+=xspeed;
    yrot+=yspeed;
    return TRUE;                                // Weiter geht's
}

Das letzte, was wir noch erledigen müssen, ist die Aktualisierung des Code-Abschnittes, wo die Leertaste gedrückt wird, um die Änderungen zu reflektieren (Wortspiel ist hier nicht beabsichtigt), die wir an den zu rendernden Quadratic Objekten vorgenommen haben (Wir haben die Disks entfernt)

                if (keys[' '] && !sp)
                {
                    sp=TRUE;
                    object++;
                    if(object>3)
                        object=0;
                }

und ganz am Ende des Programms müssen wir den Quadric noch löschen, um Speicherlecks zu vermeiden.

    // Shutdown
    gluDeleteQuadric(quadratic);
    KillGLWindow();                                // Kill das Fenster
    return (msg.wParam);                            // Beende das Programm
}

Wir sind fertig! Nun können wir einige wirklich beeindruckende Ding mit Environment Mapping machen, wie die ziemlich genaue Reflektion eines Raumes! Ich plante für dieses Tutorial eigentlich wie man Cube Environment Mapping einsetze, aber meine aktuelle Grafikkarte unterstützt kein Cube Mapping. Vielleicht kaufe ich mir in einem Monat oder so eine GeForce 2 :) Außerdem habe ich mir Environment Mapping selbst beigebracht (hauptsächlich da ich fast keine Informationen darüber gefunden habe), so, wenn Sie etwas ungenaues in diesem Tutorial finden, schreiben Sie mir eine E-Mail oder lassen Sie es NeHe wissen.

Danke und viel Glück!

Besucht meine Seite: http://www.tiptup.com/

GB Schmick (TipTup)

Jeff Molofee (NeHe)

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

* DOWNLOAD Delphi Code für diese Lektion. ( Conversion by Alexandre Hirzel )
* DOWNLOAD Code Warrior 5.3 Code für diese Lektion. ( Conversion by Scott Lupton )
* 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 Anonymous )
* DOWNLOAD Java Code für diese Lektion. ( Conversion by Chris Veenboer )
* DOWNLOAD LCC Win32 Code für diese Lektion. ( Conversion by Robert Wishlaw )
* DOWNLOAD Linux/SDL Code für diese Lektion. ( Conversion by Arkadi Shishlov )
* DOWNLOAD LWJGL Code für diese Lektion. ( Conversion by Mark Bernard )
* DOWNLOAD Mac OS X/Cocoa Code für diese Lektion. ( Conversion by Bryan Blackburn )
* DOWNLOAD Mac OS X/Cocoa Code für diese Lektion. ( Conversion by Bryan Blackburn )
* 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.