NeHe - Lektion 20 - Masken

Lektion 20



Willkommen zu Tutorial 20. Das Bitmap Bildformat ist auf fast jeden Computer verfügbar und auf fast jedem Betriebssystem. Es ist nicht nur einfach damit zu arbeiten, es auch sehr einfach zu laden und als Textur zu verwenden. Bisher haben wir Blending verwendet, um Text und andere Bilder auf den Screen zu bringen ohne den darunterliegenden Text oder das Bild zu löschen. Das ist effektiv, aber die Ergebnisse sind nicht immer gerade hübsch.

Meistens blendet eine geblendete Textur zu viel oder zu wenig. Wenn Sie ein Spiel machen, das Sprites verwendet, wollen Sie nicht, dass die Szene hinter ihrem Spieler durch dessen Körper durchscheint. Wenn Sie Text auf den Screen bringen, wollen Sie diesen solide und leicht lesbar haben.

Hier kommt 'maskieren' sehr nützlich. Maskierung ist ein zwei-Schritt-Prozess. Als erstes bringen wir ein Schwarz-Weiß-Bild unserer Textur auf den Screen. Das Weiß repräsentiert den transparenten Teil unserer Textur. Das Schwarz repräsentiert den soliden Teil unserer Textur. Auf Grund der Art des Blending, das wir benutzen, wird nur das Schwarz in unserer Szene erscheinen. Fast wie eine Stechform beim Kekse backen. Dann wechseln wir die Blending Modi und mappen unsere Textur auf den schwarzen Ausschnitt. Wieder wird auf Grund des Blending Modus, den wir verwenden, nur der Teil der Textur kopiert, der mit der schwarzen Maske übereinstimmt.

Ich werde das gesamte Programm in diesem Tutorial neu schreiben, mal von den Abschnitten abgesehen, die sich nicht geändert haben. Wenn Sie also bereit sind, etwas neues zu lernen, dann fangen wir an!

#include <windows.h>                                // Header Datei für Windows
#include <math.h>                                // Header Datei für Windows Math Library
#include <stdio.h>                                // Header Datei für Standard Input/Output
#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

Wir werden 7 globale Variablen in diesem Programm vewenden. masking ist eine boolean Variable (TRUE / FALSE), die indiziert, ob Maskierung ein- oder ausgeschaltet ist. mp wird dazu benutzt, um sicher zu gehen, dass die 'M' Taste nicht gedrückt bleibt. sp wird dazu benutzt, um sicher zu gehen, dass die 'Leertaste' nicht gedrückt bleibt und die Variable scene indiziert, ob oder nicht wir die erste oder zweite Szene zeichnen.

Wir schaffen Speicherplatz für 5 Texturen, indem wir die Variable texture[5] verwenden. loop ist unsere Zählvariable, die wir ein paar Mal in unserem Programm verwenden, zum setzen der Texturen, etc. Zu letzt haben wir noch die Variable roll. Wir werden roll zum bewegen der Texturen auf dem Screen verwenden. Das erzeugt einen netten Effekt! Wir werden außerdem die Objekte in Szene 2 rotieren lassen.

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    masking=TRUE;                                // Maskierung An/Aus
bool    mp;                                    // M gedrückt?
bool    sp;                                    // Leertaste gedrückt?
bool    scene;                                    // welche Szene soll gezeichnet werden

GLuint    texture[5];                                // Speicherplatz für unsere fünf Texturen
GLuint    loop;                                    // Schleifenvariable

GLfloat    roll;                                    // Rolling Textur

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

Der Bitmap-Lade Code hat sich nicht geändert. Es ist der selbe wie in Lektion 6, etc.

Im folgenden Code erzeugen für Speicherplatz für 5 Bilder. Wir leeren den Speicherplatz und laden alle 5 Bilder. Wir iterieren durch alle Bilder und konvertieren sie in Texturen, damit unser Programm sie nutzen kann. Die Texturen werden in texture[0-4] gespeichert.

int LoadGLTextures()                                // Lade Bitmaps und konvertiere in Texturen
{
    int Status=FALSE;                            // Status Indikator
    AUX_RGBImageRec *TextureImage[5];                    // erzeuge Speicherplatz für die Textur Daten
    memset(TextureImage,0,sizeof(void *)*5);                // Setze den Zeiger auf NULL

    if ((TextureImage[0]=LoadBMP("Data/logo.bmp")) &&            // Logo Textur
        (TextureImage[1]=LoadBMP("Data/mask1.bmp")) &&            // erste Maske
        (TextureImage[2]=LoadBMP("Data/image1.bmp")) &&            // erstes Bild
        (TextureImage[3]=LoadBMP("Data/mask2.bmp")) &&            // zweite Maske
        (TextureImage[4]=LoadBMP("Data/image2.bmp")))            // zweites Bild
    {
        Status=TRUE;                            // Setze Status auf TRUE
        glGenTextures(5, &texture[0]);                    // erzeuge fünf Texturen

        for (loop=0; loop// iteriere durch alle 5 Texturen
        {
            glBindTexture(GL_TEXTURE_2D, texture[loop]);
            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);
        }
    }
    for (loop=0; loop// iteriere durch alle 5 Texturen
    {
        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 die Image-Struktur frei
        }
    }
    return Status;                                // gebe Status zurück
}

Der ReSizeGLScene() Code hat sich nicht geändert, weshalb wir diesen überspringen.

Der Init Code ist ziemlich einfach. Wir laden unsere Texturen, setzen die Farbe, setzen und aktiviere Depth-Testing, schalten Smooth Shading ein und aktivieren Textur-Mapping. Einfaches Programm, weshalb keine komplexe Initialisierung nötig ist :)

int InitGL(GLvoid)                                // Der ganze Setup Kram für OpenGL kommt hier rein
{
    if (!LoadGLTextures())                            // Rufe Textur Lade Routine auf
    {
        return FALSE;                            // wenn Textur nicht geladen wurde, gebe FALSE zurück
    }

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // lösche den Hintergrund auf schwarz
    glClearDepth(1.0);                            // aktiviere das Löschen des Depth Buffer
    glEnable(GL_DEPTH_TEST);                        // aktiviere Depth Testing
    glShadeModel(GL_SMOOTH);                        // aktiviere Smooth Shading
    glEnable(GL_TEXTURE_2D);                        // aktiviere 2D Textur Mapping 
    return TRUE;                                // Initialisierung war OK
}

Nun zum spaßigen Teil. Unserem Zeichen-Code! Wir fangen so wie immer an. Wir löschen die Hintergrund-Farbe und den Depth Buffer. Dann resetten wir die Modelview Matrix und translatieren 2 Einheiten in den Screen hinen, so dass wir unsere Szene sehen können.

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 Modelview Matrix
    glTranslatef(0.0f,0.0f,-2.0f);                        // Bewege 5 Einheiten in den Screen hinein

Die erste Zeile wählt die 'logo' Textur aus. Wir mappen die Textur auf den Screen, indem wir einen Quad benutzen. Wir spezifizieren unsere vier Textur-Koordinaten entlängs unserer vier Vertices.

Überarbeitete Beschreibung von Jonathan Roy: Erinnern Sie sich daran, dass OpenGL ein Vertex-basiertes Graphic-System ist. Die meisten Parameter, die Sie setzen, werden als Attribute eines bestimmten Vertex aufgezeichnet. Textur-Koordinaten sind solche Attribute. Sie geben einfach die angewendeten Textur-Koordinaten für jeden Vertex eines Polygons an und OpenGL füllt automatisch die Oberfläche zwischen den Vertices mit der Textur, mittels eines Prozesses, der als Interpolation bekannt ist. Interpolation ist eine Standard Technik der Geometrie, die OpenGL bestimmen lässte, wie ein gegebener Parameter zwischen den Vertices variiert, nur indem der Wert, den der Parameter für die Vertices selber hat, bekannt ist.

Wie in den vorherigen Lektionen nehmen wir an, dass wir den Quad vor uns haben und setzen die Textur-Koordinaten wie folgt: (0.0, 0.0) für die untere linke Ecke, (0.0, 1.0) für die obere linke Ecke, (1.0, 0.0) für die untere rechte Ecke und (1.0, 1.0) für die obere rechte Ecke. Können Sie nun, mit diesen Gegebenheiten, sagen, welche Textur-Koordinate für den Mittelpunkt des Quads gilt? Richtig, (0.5, 0.5). Aber nirgends im Code haben Sie diese Koordinate angegeben, oder haben Sie? Wenn Sie den Quad zeichnen, berechnet OpenGL ihn für Sie. Und der Zauber ist, dass es das macht, egal welche Position, Größe oder Orientierung das Polygon hat!

In dieser Lektion fügen wir eine weitere interessante Verzerrung ein, indem wir die Textur-Koordinaten mit anderen Werten als 0.0 und 1.0 verbinden. Textur-Koordinaten sind von Natur aus normalisiert. Der Wert 0.0 mappt auf die eine Ecke der Textur, während der Wert 1.0 auf die gegeüberliegende Ecke mappt, was das Textur-Image über die gesamte Breite oder Höhe in nur einem Einheits-Schritt spannt, unabhängig von der Polygon-Größe oder der Bild-Größe in Pixeln (worüber wir uns keine Gedanken machen müssen, wenn wir texturieren und uns somit das Leben leichter gemacht wird). Über 1.0 findet beim Mapping ein Wrap-Around auf der anderen Ecke statt und die Textur wird wiederholt. In anderen Worten, die Textur-Koordinate (0.3, 0.5) zum Beispiel mappt auf genau den selben Pixel im Textur-Image wie die Koordinaten (1.3, 0.5) oder (12.3, -2.5). In dieser Lektion erreichen wir einen Kacheleffekt, indem wir den Wert 3.0 anstatt von 1.0 angeben, was unsere Textur also neun mal über die Oberfläche des Quad wiederholt (3x3 Kacheln).

Zusätzlich benutzen wir die roll Variable um die Textur über die Oberfläche des Quad zu translatieren (oder gleiten zu lassen). Ein Wert von 0.0 für roll, welcher zur vertikalen Textur-Koordinate addiert wird, bedeutet, dass Texturmapping auf der unteren Ecke des Quads an der unteren Ecke des Textur-Images beginnt, wie in der linken Abbildung zu sehen ist. Wenn roll gleich 0.5 ist, beginnt das mappen auf der unteren Ecke des Quads mit der oberen Hälfte des Image (siehe Abbildung auf der rechten Seite). Rolling Texturen können für großartige Effekte verwendet werden, wie zum Beispiel: vorüberziehende Wolken, Wörter, die sich um eine Objekt herum bewegen, etc.

    glBindTexture(GL_TEXTURE_2D, texture[0]);                // wähle unsere Logo-Textur aus
    glBegin(GL_QUADS);                            // fange an, einen texturierten Quad zu zeichnen
        glTexCoord2f(0.0f, -roll+0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // unten links
        glTexCoord2f(3.0f, -roll+0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // unten rechts
        glTexCoord2f(3.0f, -roll+3.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // oben rechts
        glTexCoord2f(0.0f, -roll+3.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // oben links
    glEnd();                                // fertig mit dem Zeichnen des Quad

Wie dem auch sei... zurück zur Realität. Nun aktivieren wir Blending. Damit dieser Effekt funktioniert, müssen wir auch Depth Testing deaktivieren. Es ist sehr wichtig, dass Sie das machen! Wenn Sie Depth Testing nicht deaktivieren, werden Sie wahrscheinlich nichts sehen. Ihr gesamtes Bild wird verschwinden!

    glEnable(GL_BLEND);                            // aktiviere Blending
    glDisable(GL_DEPTH_TEST);                        // deaktiviere Depth Testing

Das erste, was wir nach der Aktivierung von Blending und der Deaktivierung des Depth Testing machen, ist die Überprüfung, ob wir unser Image maskieren oder es auf die gute alte Art blenden. Die folgende Zeile Code überprüft, ob masking gleich TRUE ist. Wenn dem so ist, setzen wir unser Blending, so dass unsere Maske korrekt auf den Screen gezeichnet wird.

    if (masking)                                // Ist Masking aktiviert?
    {

Wenn masking gleich TRUE ist, wird die folgende Zeile das Blending für unsere Maske vorbereiten. Eine Maske ist nur eine Kopie der Textur, die wir auf den Screen zeichnen wollen, allerdings in schwarz-weiß. Alles was in der Maske weiß ist, wird transparent sein. Alles was in der Maske schwarz ist, wird UNDURCHSICHTIG sein.

Der folgende Blend-Befehl macht das folgende: Die Ziel-Farbe (Screen-Farbe) wird auf schwarz gesetzt, wenn der Teil unserer Maske, der auf den Screen kopiert wird, schwarz ist. Das bedeutet, dass der Teil unseres Screens, der mit den schwarzen Teilen unserer Maske übereinstimmt, schwarz wird. Alles was auf dem Screen unter der Maske ist, wird auf schwarz gelöscht. Der Teil des Screens, der mit dem weißen Teil der Maske übereinstimmt, wird nicht geändert.

        glBlendFunc(GL_DST_COLOR,GL_ZERO);                // Blende Screen Farbe mit Null (Schwarz)
    }

Nun überprüfen wir, welche Szene gezeichnet werden soll. Wenn scene gleich TRUE ist, werden wir die zweite Szene zeichnen. Wenn scene gleich FALSE ist, werden wir die erste Szene zeichnen.

    if (scene)                                // zeichnen wir die zweite Szene?
    {

Wir wollen die Dinge nicht zu groß erscheinen lassen, weshalb wir eine weitere Einheit in den Screen hinein translatieren. Das reduziert die Größe unserer Objekte.

Nachdem wir in den Screen translatiert haben, rotieren wir von 0-360 Grad, abhängig vom roll Wert. Wenn roll gleich 0.0 ist, werden wir um 0 Grad rotieren. Wenn roll gleich 1.0 ist, rotieren wir um 360 Grad. Ziemlich schnelle Rotation, aber ich wollte nicht eine neue Variable erzeugen, nur um das Bild im Zentrum des Screens rotieren zu lassen. :)

        glTranslatef(0.0f,0.0f,-1.0f);                    // Translatiere eine Einheit in den Screen hinein
        glRotatef(roll*360,0.0f,0.0f,1.0f);                // Rotiere auf der Z-Achse um 360 Grad

Wir haben bereits das rolling Logo auf dem Screen und wir haben die Szene auf der Z-Achse rotiert, was alle Objekte gegen den Uhrzeigersinn rotieren lässt, nun müssen wir nur noch überprüfen, um Masking angeschaltet ist. Wenn dem so ist, zeichnen wir unsere Maske und dann unser Objekt. Wenn Masking ausgeschaltet ist, zeichnen wir nur unser Objekt.

        if (masking)                            // Ist Masking an?
        {

Wenn masking gleich TRUE ist, wird der obige Code unsere Maske auf den Screen zeichnen. Unser Blend-Modus sollte richtig gesetzt sein, da wir bereits auf Masking überprüft haben, während wir Blending gesetzt haben. Nun müssen wir nur noch die Maske auf den Screen zeichnen. Wir wählen Maske 2 aus (da dies die zweite Szene ist). Nachdem wir die Masken-Textur ausgewählt haben, texturieren wir wir diese auf den Quad. Der Quad ist 1.1 Einheiten nach links und rechts, so dass er etwas mehr als den gesamten Screen einnimmt. Wir wollen nur eine Textur zeigen, so dass unsere Textur-Koordinaten nur von 0.0 bis 1.0 gehen.

Nachdem wir unsere Maske auf den Screen gezeichnet haben, wird eine solide schwarze Kopie unserer letztlichen Textur auf dem Screen erscheinen. Das letztendliche Ergebniss wird so aussehen, als wenn jemand mit einem Messer die Form unserer finalen Textur auf dem Screen ausgeschnitten hätte und einen leeren schwarzen Raum hinterlassen hätte.

            glBindTexture(GL_TEXTURE_2D, texture[3]);        // wähle die zweite Masken-Textur
            glBegin(GL_QUADS);                    // fange an einen texturierten Quad zu zeichnen
                glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // unten links
                glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // unten rechts
                glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // oben rechts
                glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // oben links
            glEnd();                        // fertig mit zeichnen des Quads
        }

Nun, da wir unsere Maske auf dem Screen gezeichnet haben, wird es Zeit, zum Blending Modus wieder zu wechseln. Dieses Mal teilen wir OpenGL mit, jeden Teil unserer farbigen Textur auf den Screen zu kopieren, der NICHT schwarz ist. Da die finale Textur eine exakte Kopie der Maske, allerdings farbig, ist, sind die einzigen Teile unserer Kopie, die auf den Screen gezeichnet werden, Teile sind, die direkt auf die schwarzen Teile der Maske gezeichnet werden. Da die Maske schwarz ist, wird nicht auf dem Screen durch unsere Textur durchschimmern. Das lässt uns mit einer sehr solide aussehenden Textur, die sich über den Screen bewegt, zurück.

Beachten Sie, dass wir das zweite Bild auswählen, nachdem wir den finalen Blending-Modus ausgewählt haben. Dies wähle unser farbiges Bild aus (das Bild, auf dem unsere zweite Maske basiert). Beachten Sie auch, dass wir dieses Bild direkt auf die Maske zeichnen. Selbe Textur-Koordinaten, selbe Vertices.

Wenn wir keine Maske haben, wird unser Bild immer noch auf den Screen kopiert, aber mit dem, was zuvor auf dem Screen war, geblendet wird.

        glBlendFunc(GL_ONE, GL_ONE);                    // kopiere Image 2 Farbe auf den Screen
        glBindTexture(GL_TEXTURE_2D, texture[4]);            // wähle die zweite Image Textur aus
        glBegin(GL_QUADS);                        // fange an einen texturierten Quad zu zeichnen
            glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // unten links
            glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // unten rechts
            glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // oben rechts
            glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // oben links
        glEnd();                            // fertig mit dem Zeichnen des Quad
    }

Wenn scene gleich FALSE war, zeichnen wir die erste Szene (meine favorisierte).

    else                                    // Ansonsten
    {

Wir fangen damit an zu überprüfen, ob masking gleich TRUE oder FALSE ist, so wie im obigen Code.

        if (masking)                            // Ist Masking an?
        {

Wenn masking gleich TRUE ist, zeichnen wir unsere Maske 1 auf den Screen (die Maske für die Szene 1). Beachten Sie, dass die Textur sich von rechts nach links bewegt (roll wird zur horizontalen Textur-Koordinate addiert). Wir wollen, dass diese Textur den gesamten Screen füllt, was auch der Grund ist, warum wir nicht weiter in den Screen translatiert haben.

            glBindTexture(GL_TEXTURE_2D, texture[1]);        // wähle die erste Masken-Textur aus
            glBegin(GL_QUADS);                    // fange an texturierten Quad zu zeichnen
                glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // unten links
                glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // unten rechts
                glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // oben rechts
                glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // oben links
            glEnd();                        // fertig mit zeichnen des Quads
        }

Erneut aktivieren wir Blending und wählen unsere Textur für die erste Szene. Wir mappen die Textur auf seine Maske. Beachten Sie, dass wir diese Textur ebenfalls bewegen, ansonsten würde die Maske und das finale Bild nicht zueinander passen.

        glBlendFunc(GL_ONE, GL_ONE);                    // kopiere Image 1 Farbe auf den Screen
        glBindTexture(GL_TEXTURE_2D, texture[2]);            // wähle die erste Image Textur aus
        glBegin(GL_QUADS);                        // fange an texturierten Quad zu zeichnen
            glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // unten links
            glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // unten rechts
            glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // oben rechts
            glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // oben links
        glEnd();                            // fertig mit Zeichnen des Quads
    }

Als nächstes aktivieren wir Depth Testing und deaktivieren Blending. Das verhindert, dass komische Dinge im Rest unseres Programms passieren :)

    glEnable(GL_DEPTH_TEST);                        // aktiviere Depth Testing
    glDisable(GL_BLEND);                            // deaktiviere Blending

Letztendlich müssen wir nur noch den Wert von roll inkrementieren. Wenn roll größer als 1.0 ist, subtrahieren wir 1.0. Das verhindert, dass der Wert roll zu hoch wird.

    roll+=0.002f;                                // inkrementiere unsere Textur Roll Variable
    if (roll>1.0f)                                // Ist Roll größer als eins
    {
        roll-=1.0f;                            // subtrahiere 1 von Roll
    }

    return TRUE;                                // alles war OK
}

Der KillGLWindow(), CreateGLWindow() und WndProc() Code hat sich nicht geändert, weshalb wir ihn überspringen.

Das erste was Sie an Änderung im WinMain() Code feststellen werden, ist der Fenster-Titel. Er ist nun mit "NeHe's Masking Tutorial" betitelt. Ändern Sie ihn zu dem was Sie auch wollen :)

int WINAPI WinMain(    HINSTANCE    hInstance,                // Instanz
            HINSTANCE    hPrevInstance,                // vorherige Instanz
            LPSTR        lpCmdLine,                // Kommandozeilen Parameter
            int        nCmdShow)                // Fenster Anzeige Status
{
    MSG    msg;                                // Windows Nachrichten Struktur
    BOOL    done=FALSE;                            // Bool Variable um die Schleife zu beenden

    // Frage den Benutzer, in welchen Modus er starten will
    if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
    {
        fullscreen=FALSE;                        // Fenster-Modus
    }

    // erzeuge unser OpenGL Fenster
    if (!CreateGLWindow("NeHe's Masking Tutorial",640,480,16,fullscreen))
    {
        return 0;                            // Beende, wenn Fenster nicht erzeugt wurde
    }

    while(!done)                                // Schleife die so lange läuft, bis done=TRUE
    {
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))            // Wartet eine Nachricht?

        {
            if (msg.message==WM_QUIT)                // Haben wir eine Nachricht zum beenden erhalten?
            {
                done=TRUE;                    // Wenn ja done=TRUE
            }
            else                            // Wenn nicht, bearbeite die Fenster-Nachrichten
            {
                TranslateMessage(&msg);                // Übersetze die Nachricht
                DispatchMessage(&msg);                // bearbeite die Nachricht
            }
        }
        else                                // Wenn da keine Nachrichten sind
        {
            // Zeichne die Szene. Schau nach der ESC-Taste und Beendigungs-Nachrichten von DrawGLScene()
            if ((active && !DrawGLScene()) || keys[VK_ESCAPE])    // Aktiv? Wurde eine Beendigung erhalten?
            {
                done=TRUE;                    // ESC oder DrawGLScene signalisiert zum Beenden
            }
            else                            // Es ist noch nicht Zeit zum beenden, zeichne Screen neu
            {
                SwapBuffers(hDC);                // Swap Buffers (Double Buffering)

Nun zu unserem einfachen Tasten-Händel Code. Wir überprüfen, ob die Leertaste gedrückt wurde. Wenn das der Fall ist, setzen wir die sp Variable auf TRUE. Wenn sp gleich TRUE ist, wird der folgende Code nicht ein zweites Mal ausgeführt, bis die Leertaste wieder losgelassen wird. Das verhindert, dass das Programm zwischen den Szenen hin und her springt. Nachdem wir sp auf TRUE gesetzt haben, wechseln wir die Szene. Wenn sie TRUE war, wird sie gleich FALSE, wenn sie FALSE war, wird sie TRUE. In unserem obigen Zeichnen-Code wird, wenn scene gleich FALSE ist, die erste Szene gezeichnet. Wenn scene gleich TRUE ist, wird die zweite Szene gezeichnet.

                if (keys[' '] && !sp)                // wurde die Leertaste gedrückt?
                {
                    sp=TRUE;                // Teile dem Programm mit, dass die Leertaste gedrückt wird
                    scene=!scene;                // wechsel von einer Szene zur anderen
                }

Der folgende Code überprüft, ob wir die Leertaste losgelassen haben (wen NICHT ' '). Wenn die Leertaste losgelassen wurde, setzen wir sp auf FALSE und lassen unser Programm wissen, dass die Leertaste NICHT gedrückt wird. Indem wir sp auf FALSE setzen, wird der obige Code erneut prüfen, ob die Leertaste erneut gedrückt wurde und falls ja, fängt das Ganz von vorne an.

                if (!keys[' '])                    // wurde die Leertaste losgelassen?
                {
                    sp=FALSE;                // Teile dem Programm mit, dass die Leertaste losgelassen wurde
                }

Der nächste Code-Abschnitt überprüft, ob die 'M'-Taste gedrückt wurde. Wenn das der Fall ist, setzen wir die mp Variable auf TRUE und teilen unserem Programm somit mit, dass es nicht erneut überprüfen soll, bis die Taste losgelassen wurde und wir wechseln masking von TRUE auf FALSE oder von FALSE auf TRUE. Wenn masking gleich TRUE ist, wird der Zeichnen-Code Masking einschalten. Wenn es FALSE ist, wird Masking ausgeschaltet. Wenn Masking ausgeschaltet ist, wird das Objekt auf den Screen geblendet, indem das alte gute Blending benutzt wird, was wir auch bisher verwendet haben.

                if (keys['M'] && !mp)                // wurde M gedrückt?
                {
                    mp=TRUE;                // Teile dem Programm mit, dass M gedrückt ist
                    masking=!masking;            // wechsel Masking Modus AUS/AN
                }

Das letzte Code-Stück überprüft, ob wir aufgehört haben 'M' zu drücken. Wenn dem so ist, wird mp FALSE, dass unser Porgamm wissen lässt, dass wir nicht länger die 'M' Taste halten. Wenn die 'M' Taste erst einmal losgelassen wurde, können wir sie erneut drücken, um Maskin an oder auszuschalten.

                if (!keys['M'])                    // wurde M losgelassen?
                {
                    mp=FALSE;                // Teile dem Programm mit, dass M losgelassen wurde
                }

Wie in den vorherigen Tutorials, stellen wir sicher, dass der Fenster-Titel korrekt ist.

                if (keys[VK_F1])                // Wurde F1 gedrückt?
                {
                    keys[VK_F1]=FALSE;            // Wenn ja, setze Taste auf FALSE
                    KillGLWindow();                // Kill unser aktuelles Fenster
                    fullscreen=!fullscreen;            // Wechsel zwischen Fullscreen und Fester-Modus
                    // Erzeuge unser OpenGL Fenster erneut
                    if (!CreateGLWindow("NeHe's Masking Tutorial",640,480,16,fullscreen))
                    {
                        return 0;            // Beende, wenn das Fenster nicht erzeugt wurde
                    }
                }
            }
        }
    }
    // Shutdown
    KillGLWindow();                                // Kill das Fenster
    return (msg.wParam);                            // Beende das Programm
}

Eine Maske zu erzeugen ist nicht schwierig. Etwas zeitaufwendig. Am besten erstellen Sie eine Maske, indem Sie bereits ein Bild haben, dass Sie in einem Grafikprogramm öffnen oder einem Programm wie Infranview öffnen und es auf ein Gray-Scale-Bild reduzieren. Nachdem Sie das gemacht haben, erhöhen Sie den Kontrast, so dass die Grauen Pixel schwarz werden. Sie können ebenfalls versuchen, die Helligkeit hochzustellen, etc. Es ist wichtig, dass das weiß ganz helles weiß ist und das schwarz reines schwarz ist. Wenn Sie irgendwelche grauen Pixel in Ihrer Maske haben, werden diese in Ihrem Bild transparent dargestellt. Der zuverlässigste Weg sicherzugehen, dass Ihre Maske eine perfekte Kopie Ihres Bildes ist, ist, dass Sie das Bild noch mal schwärzen. Es ist ebenfalls sehr wichtig, dass Ihr Bild einen SCHWARZEN Hintergrund hat und die Maske einen WEIßen Hintergrund hat! Wenn Sie eine Maske erzeugen und einen rechteckige Form um die Textur bemerken, ist entweder ihr weiß nicht hell genug (255 oder FFFFFF) oder ihr schwarz ist kein wirkliches schwarz (0 oder 000000). Folgend können Sie ein Beispiel einer Maske und dem Bild sehen, dass über die Maske passt. Das Image kann jegliche Farbe haben, solange der Hintergrund schwarz ist. Die Maske muss einen weißen Hintergrund haben und eine schwarze Kopie Ihres Bildes.

Dies ist die Maske ->   Dies ist das Bild ->


Eric Desrosiers hat darauf hingewiesen, dass Sie auch jeden Wert jedes Pixels in Ihrem Bild überprüfen können, während Sie es laden. Wenn Sie die Pixel transparent haben wollen, können Sie diesen einen Alpha-Wert von 0 geben. Für all die anderen Farben können Sie einen Alpha-Wert von 255 übergeben. Diese Methode wird ebenfalls funktionieren, benötigt aber etwas extra Coding. Das aktuelle Tutorial ist einfach und benötigt nur wenig Extra-Code. Ich bin nicht verschlossen gegenüber anderen Techniken, aber wenn ich ein Tutorial schreibe, versuche ich den Code so einfach verstehbar wie möglich und so einfach wie möglicht zu nutzen, zu machen. Ich wollte nur darauf hinweisen, dass es immer andere Wege gibt, um etwas zum laufen zu bringen. Danke für's Feedback Eric.

In diesem Tutorial habe ich Ihnen einen einfachen, aber effektiven Weg gezeigt, um Textur-Ausschnitte auf den Screen zu zeichnen, ohne den Alpha-Channel zu verwenden. Normales Blending sieht in der Regel schlecht aus (Texturen sind entweder transparent oder sie sind es nicht) und Texturierung mit Alpha-Channel setzt vorraus, dass Ihr Bild den Alpha-Channel unterstützt. Bitmaps sind recht einfach, wenn man mit ihnen arbeitet, aber sie unterstützen keinen Alpha-Channel. Dieses Programm zeigt uns, wie man die Beschränkungen von Bitmap-Bildern umgeht, während ein cooler Weg demonstriert wurde um überlappende Effekte zu erzeugen.

Dank an Rob Santa für die Idee und für den Beispiel-Code. Ich habe zuvor noch nie von diesem kleinen Trick gehört, bis er mich darauf hingewiesen hat. Er wollte auch, dass ich darauf hinweise, dass, obwohl dieser Trick funktioniert, er zwei Durchgänge benötigt, was eine Performance-Einbuße mit sich zieht. Er empfiehlt, dass Sie Texturen verwenden, die den Alpha-Channel unterstützen, wenn Sie komplexe Szenen haben.

Ich hoffe, Sie haben dieses Tutorial genossen. Wenn Sie Verständniss-Probleme hatten oder einen Fehler in diesem Tutorial gefunden haben, lassen Sie es mich bitte wissen. Ich möchte das beste verfügbare Tutorial machen. Ihr Feedback ist wichtig!

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 Alexandre Ribeiro de Sá )
* DOWNLOAD Irix / GLUT Code für diese Lektion. ( Conversion by Rob Fletcher )
* DOWNLOAD Java Code für diese Lektion. ( Conversion by Jeff Kirby )
* DOWNLOAD LCC Win32 Code für diese Lektion. ( Conversion by Robert Wishlaw )
* DOWNLOAD Linux Code für diese Lektion. ( Conversion by Daniel Davis )
* 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 Visual Basic Code für diese Lektion. ( Conversion by Edo )
* 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.