NeHe - Lektion 46 - Fullscreen AntiAliasing

Lektion 46



Hallo Ihr alle, der freundliche Roach kommt mit einem interessanten Tutorial, dass euch helfen wird, eure Applikationen noch besser zu machen. Um Ihr OpenGL Programm besser aussehen zu lassen, kommen wir zu dem Problem Aliasing. Das sind die klobigen "Treppen" die bei diagonalen Linien neben den eigentlichen Pixeln auf dem Screen erscheinen. Z.B. Bad Mojo. Die Antwort lautet Anti-Aliasing und wird benutzt um diese "Treppen" zu glätten, um weichere Ecken für Objekte zu erzeugen. Ein Prozess, der eingesetzt wird, um Anti-Aliasing zu erreichen, wird "Multisampling" genannt. Die Idee ist, dass wir für jeden Pixel die benachbarten Pixel samplen und bestimmen, ob diese Ecke Anti-Aliased werden muss, also grundsätzlich "verschmieren" wir die Pixel selber und retuschieren somit die "Treppen".

Fullscreen Anti-Aliasing ist etwas, wo nicht-Echtzeit Renderprogramme immer im Vorteil waren. Wie dem auch sei, mit aktueller Hardware sind mit in der Lage, den selben Effekt in Echtzeit zu erreichen. Die ARB_MULTISAMPLE Extension erlaubt uns das. Grundsätzlich wird jeder Pixel mit seinem Nachbarn gesamplet, um das optimale Antialias herauszufinden und anzuwenden. Das kostet allerdings etwas und kann die Performance herunterziehen.

    Vid_mem = sizeof(Front_buffer) + sizeof(Back_buffer) + num_samples
        * (sizeof(Front_buffer) +sizeof(ZS_buffer))

Für detailliertere Informationen über Anti-Aliasing als auch die Informationen, die ich hier vorstelle, schauen Sie sich bitte folgende Links an:

GDC2002 -- OpenGL Multisample
OpenGL Pixel Formats und Multisample Antialiasing

Nachdem das gesagt wurde, hier ein kurzer Überblick, wie unser Prozess ablaufen wird. Anders als andere Extensionen, die was mit dem OpenGL Rendering zu tun haben, muss die ARB_MULTISAMPLE Extension während der Erstellung Ihres Rendering Fensters angewendet werden. Unser Prozess sollte wie folgt ablaufen:
  1. Erzeugen Sie ihr Fenster ganze normal
  2. Fragen Sie die möglichen Multisample Pixel Werte ab (InitMultisample)
  3. Wenn Multisampling verfügbar ist, zerstören Sie das Fenster und erzeugen Sie es mit unserem NEUEN pixelFormat erneut
  4. Für Teile, auf die wir Antialias anwenden wollen, rufen Sie einfach glEnable(GL_ARB_MULTISAMPLE); auf
Fangen wir am Anfang an und sprechen über unsere Source-Datei arbMultiCample.cpp. Wir beginnen mit den Standard Includes für gl.h & glu.h, sowie windows.h, über arb_multisample.h sprechen wir später.

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include "arb_multisample.h"

Die folgenden zwei Zeilen definieren unsere Einstiegspunkte in die WGL String Listing. Wir benutzen diese indem wir auf die Pixel Format Attribute zugreifen, um unser Sample-Format zu testen. Die anderen beiden Variablen werden an anderer Stelle im Programm benutzt, um auf unsere Daten zuzugreifen.

// Deklarationen die wir benutzen werden
#define WGL_SAMPLE_BUFFERS_ARB    0x2041
#define WGL_SAMPLES_ARB        0x2042

bool    arbMultisampleSupported    = false;
int    arbMultisampleFormat    = 0;

Als nächstes sprechen wir über WGLisExtensionSupported, welche benutzt wird, um die WGL Extensions Listing abzufragen, um zu überprüfen, ob ein vorhandenes Format auf dem System unterstützt wird. Die Beschreibungen des Codes folgen, wenn wir diesen durchgehen, da es einfacher ist, als das ständige Hin und Her gespringe in HTML...

ANMERKUNG: Der folgende Code wurde von Henry Goffin neu geschrieben. Seine Änderungen fügen hinzu: besseres parsen der unterstützten GL Extensionen und das beheben eines Problems, das auftrat, wenn der erste Check fehlschlug.

bool WGLisExtensionSupported(const char *extension)
{
    const size_t extlen = strlen(extension);
    const char *supported = NULL;

    // Versuche wglGetExtensionStringARB auf unseren aktuellen DC anzuwenden, wenn möglich
    PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");

    if (wglGetExtString)
        supported = ((char*(__stdcall*)(HDC))wglGetExtString)(wglGetCurrentDC());

    // wenn das fehlschlug, versuchen den Standard OpenGL Extension String
    if (supported == NULL)
        supported = (char*)glGetString(GL_EXTENSIONS);

    // wenn das auch fehlschlug, dürfen keine Extensions unterstützt werden
    if (supported == NULL)
        return false;

    // beginne Untersuchung am Anfang des Strings, inkrementiere um 1, bei einer falschen Übereinstimmung
    for (const char* p = supported; ; p++)
    {
        // p ist unsere nächste mögliche Übereinstimmung
        p = strstr(p, extension);

        if (p == NULL)
            return false;                        // Keine Übereinstimmung

        // Stelle sicher, dass die Übereinstimmung am Anfang des Strings ist oder dass das
        // vorhergehende Zeichen ein Leerzeichen ist, oder wir könnten versehentlich
        // "wglFunkywglExtension" mit "wglExtension" als Übereinstimmung finden

        // Stelle auch sicher, dass das nachfolgende Zeichen ein Leerzeichen oder NULL ist,
        // da sonst "wglExtensionTwo" mit "wglExtension" übereinstimmen könnte
        if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
            return true;                        // Übereinstimmung
    }
}

Die nächste Funktion ist der eigentliche Kern des Programms. Wir fragen hauptsächlich ab, ob unsere ARB Extension vom System unterstützt wird. Von da aus, machen wir weiter und fragen unseren Device Kontext ab, um den Bereich unseres Multisamples herauszufinden. Wieder... schauen wir uns einfach den Code an.

bool InitMultisample(HINSTANCE hInstance,HWND hWnd,PIXELFORMATDESCRIPTOR pfd)
{
    // Schaue, ob String in WGL existiert!
    if (!WGLisExtensionSupported("WGL_ARB_multisample "))
    {
        arbMultisampleSupported=false;
        return false;
    }

    // ermittle unser Pixel Format
    PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB =
        (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");

    if (!wglChoosePixelFormatARB)
    {
        // Wir haben keine Unterstützung für Multisampling gefunden, setze unser Flag und beende.
        arbMultisampleSupported=false;
        return false;
    }

    // ermittle unseren aktuellen Device Kontext. Wir benötigen das, um das OpenGL Fenster zu fragen, welche Attribute wir haben
    HDC hDC = GetDC(hWnd);

    int pixelFormat;
    bool valid;
    UINT numFormats;
    float fAttributes[] = {0,0};

    // Diese Attribute sind die Bits, die wir in unserem Sample testen wollen
    // alles ist ziemlicher Standard, das einzige worauf wir wirklich 
    // achten wollen, sind SAMPLE BUFFERS ARB und WGL SAMPLES
    // Diese beiden werden das hauptsächliche Testen vornehmen, ob wir
    // Unterstützung für Multisampling von der Hardware haben oder nicht
    int iAttributes[] = { WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
        WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
        WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
        WGL_COLOR_BITS_ARB,24,
        WGL_ALPHA_BITS_ARB,8,
        WGL_DEPTH_BITS_ARB,16,
        WGL_STENCIL_BITS_ARB,0,
        WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
        WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
        WGL_SAMPLES_ARB, 4 ,                        // Überprüfe für 4x Multisampling
        0,0};

    // Als erstes überprüfen wir, ob wir ein Pixel Format für 4 Samples bekommen können
    valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);

    // wenn wir True zurückgeliefert bekommen haben und unsere Anzahl an Formaten größer als 1 ist
    if (valid && numFormats >= 1)
    {
        arbMultisampleSupported    = true;
        arbMultisampleFormat    = pixelFormat;
        return arbMultisampleSupported;
    }

    // Unser Pixel Format mit 4 Samples schlug fehl, teste für 2 Samples
    iAttributes[19] = 2;
    valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
    if (valid && numFormats >= 1)
    {
        arbMultisampleSupported    = true;
        arbMultisampleFormat    = pixelFormat;
        return arbMultisampleSupported;
    }

    // gebe das gültige Format zurück
    return  arbMultisampleSupported;
}

Nun, da wir unseren Abfrage-Code fertig haben, müssen wir das Erzeugen unseres Fensters modifzieren. Als erstes müssen wir unsere arb_multisample.h Datei inkludieren, neben den Prototypen für destroywindow und createwindow am Anfang der Datei.

#include <windows.h>                                // Header Datei für die Windows Library
#include <gl/gl.h>                                // Header Datei für die OpenGL32 Library
#include <gl/glu.h>                                // Header Datei für die GLu32 Library
#include "NeHeGL.h"    // Header Datei für den NeHeGL Basecode

// ROACH
#include "ARB_MULTISAMPLE.h"

BOOL DestroyWindowGL (GL_Window* window);
BOOL CreateWindowGL (GL_Window* window);
// ENDROACH

Das nächste Stück muss in die CreateWindowGL Funktion eingefügt werden und zwar zwischen den folgenden original Code. Eigentlich übertreiben wir es hier ein wenig, um die Aufgabe zu erledigen. Wir können nicht das Pixel Format anfordern (um Multisampling abzufragen), bis wir das Fenster erzeugt haben. Aber wir können keinen FSAA Screen erzeugen, bevor wir wissen, dass das Pixel Format es unterstützt. Ähnlich der Henne-Ei Frage. Was ich gemacht habe, ist ein 2 Durchläufe-System; Wir erzeugen das Fenster, fragen das PixelFormat ab, zerstören/erzeugen erneut das Fenster, wenn Multisampling erlaubt ist. Irgendwie prima...

    window->hDC = GetDC (window->hWnd);                    // hole ein Device Kontext für dieses Fenster
    if (window->hDC == 0)                            // Haben wir einDevice Kontext bekommen?
    {
        // fehlgeschlagen
        DestroyWindow (window->hWnd);                    // zerstöre das Fenster 
        window->hWnd = 0;                        // Nulle das Fenster Handle
        return FALSE;                            // gebe False zurück
    }

// ROACH
    // Unser erster Durchlauf, Multisampling wurd noch nicht erzeugt, deshalb erzeugen wir ganz normal ein Fenster
    // Wenn es unterstützt wird, dann sind wir in unserem zweiten Durchlauf
    // das bedeutet, dass wir unser Pixel Format für Sampling benutzen
    // deshalb setze PixelFormat stattdessen auf arbMultiSampleformat
    if(!arbMultisampleSupported)
    {
        PixelFormat = ChoosePixelFormat (window->hDC, &pfd);        // Finde ein kompatibles Pixel Format
        if (PixelFormat == 0)                        // Haben wir ein kompatibles Format gefunden?
        {
            // fehlgeschlagen
            ReleaseDC (window->hWnd, window->hDC);            // gebe unserenDevice Kontext frei
            window->hDC = 0;                    // Nulle den Device Kontext
            DestroyWindow (window->hWnd);                // zerstöre das Fenster
            window->hWnd = 0;                    // Nulle das Fenster Handle
            return FALSE;                        // gebe False zurück
        }
    }
    else
    {
        PixelFormat = arbMultisampleFormat;
    }
//ENDROACH

    if (SetPixelFormat (window->hDC, PixelFormat, &pfd) == FALSE)        // Versuche das Pixel Format zu setzen
    {
        // fehlgeschlagen
        ReleaseDC (window->hWnd, window->hDC);                // geben unserDevice Kontext frei
        window->hDC = 0;                        // Nulle den Device Kontext
        DestroyWindow (window->hWnd);                    // Zerstöre das Fenster
        window->hWnd = 0;                        // Nulle das Fenster Handle
        return FALSE;                            // gebe False zurück
    }

Nun, da das Fenster erzeugt wurde, haben wir ein gültiges Handle, welches wir auf Multisample Unterstützung abfragen können. Wenn es unterstützt wird, zerstören wir das Fenster und rufen CreateWindowGL erneut auf, diesmal aber mit dem neuen Pixel-Format.

    // mache den Rendering Kontext zu unserem aktuellen Rendering Kontext
    if (wglMakeCurrent (window->hDC, window->hRC) == FALSE)
    {
        // fehlgeschlagen
        wglDeleteContext (window->hRC);                    // Lösche den Rendering Kontext
        window->hRC = 0;                        // Nulle Rendering Kontext
        ReleaseDC (window->hWnd, window->hDC);                // gebe unseren Device Kontext frei
        window->hDC = 0;                        // Nulle den Device Kontext
        DestroyWindow (window->hWnd);                    // zerstöre das Fenster
        window->hWnd = 0;                        // Nulle das FensterHandle
        return FALSE;                            // gebe False zurück
    }


// ROACH
    // Nun, da unser Fenster erzeugt wurde, wollen wir abfragen, ob Samples verfügbar sind
    // Wir rufen unser auf
    // wenn wir einen gültigen Kontext zurückgeliefert bekommen, wollen wir unser aktuelles Fenster zerstören
    // und ein neues mit dem Multisample Interface erzeugen.
    if(!arbMultisampleSupported && CHECK_FOR_MULTISAMPLE)
    {
        if(InitMultisample(window->init.application->hInstance,window->hWnd,pfd))
        {
            DestroyWindowGL (window);
            return CreateWindowGL(window);
        }
    }
// ENDROACH

    ShowWindow (window->hWnd, SW_NORMAL);                    // mach das Fenster sichtbar
    window->isVisible = TRUE;

Ok, die Initialisierung ist komplett. Wir haben unseren Screen initialisiert, um die passende Tiefe für Multi Sampling zu erlauben. Nun kommt der spaßige Teil, es tatsächlich zu tun. Glücklicherweise hat das ARB entschieden Multisampling dynamisch zu machen, was es uns erlaubt, es mit einem glEnable / glDisable Befehl an- und auszuschalten.

glEnable(GL_MULTISAMPLE_ARB);

// Render die Szene

glDisable(GL_MULTISAMPLE_ARB);

Und das war's! Was den Rest des Codes der Demo betrifft, ist es ein einfacher Rotations-Effekt, der zeigt, wie prima diese Methode funktioniert. ENJOY!

Colt "MainRoach" McAnlis

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

* DOWNLOAD Borland C++ Builder 6 Code für diese Lektion. ( Conversion by Le Thanh Cong )
* 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 Neil Roy )
* DOWNLOAD Visual Studio .NET Code für diese Lektion. ( Conversion by Joachim Rohde )



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