Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Janusz Ganczarski
Biblioteki C++

Antyaliasing

[lekcja] Rozdział 19. Zjawisko aliasingu i metody antyaliasingu; ustawianie jakości renderingu; antyaliasing punktów, linii i wielokątów; antyaliasing z wykorzystaniem bufora akumulacyjnego; wielopróbkowanie (ang. multisampling); cztery programy przykładowe.
Aliasing jest zjawiskiem związanym z procesem rasteryzacji obiektów. Proces ten realizowany jest w dyskretnym urządzeniu wyjściowym (np. ekran monitora, plik graficzny), które posiada skończoną rozdzielczość. Stąd wszystkie rasteryzowane obiekty składają się z mniejszej lub większej ilości pikseli, i w zależności od rozdzielczości urządzenia widoczne są poszczególne punkty rastra. Efekt ten to aliasing, który potocznie bywa określany zjawiskiem „schodków” (dolna linia na rysunku 1).
Rysunek 1. Rasteryzacja linii z antyaliasingiem oraz bez antyaliasingu
Rysunek 1. Rasteryzacja linii z antyaliasingiem oraz bez antyaliasingu
Aliasing jest zjawiskiem niepożądanym nawet w przypadku, gdy urządzenie wyjściowe ma wysoką rozdzielczość. Techniki usuwające efekt aliasingu, nazywane antyaliasingiem lub wygładzaniem krawędzi, sprowadzają się najczęściej do rozbudowania rasteryzacji obiektów o dodatkowe piksele „uzupełniające” brzegi figury (górna linia na rysunku 1) lub procesie nadpróbkowania, czyli przeprowadzaniu rasteryzacji w większej niż docelowa rozdzielczości i następującym po tym odpowiednim skalowaniu. Biblioteka OpenGL udostępnia obie techniki antyaliasingu.

Ustawienie jakości renderingu

Jednym z elementów biblioteki OpenGL całkowicie zależnych od implementacji jest ustawianie jakości renderingu. Swoboda pozostawiona implementacji sięga tak daleko, że pozwala nawet na zupełne ignorowanie tej części specyfikacji OpenGL.
Wybór jakości renderingu umożliwia funkcja:
C/C++
void glHint( GLenum target, GLenum mode )
Parametr target określa, jakość którego z elementów renderingu będzie regulowana. Dopuszczalne są następujące wartości:
  • GL_FOG_HINT - jakość mgły; obliczanie mgły dla każdego wierzchołka (GL_FASTES) lub każdego piksela (GL_NICEST),
  • GL_LINE_SMOOTH_HINT - jakość antyaliasingu linii,
  • GL_PERSPECTIVE_CORRECTION_HINT - korekcja perspektywy przy renderingu tekstur,
  • GL_POINT_SMOOTH_HINT - jakość antyaliasingu punktów,
  • GL_POLYGON_SMOOTH_HINT - jakość antyaliasingu wielokątów,
  • GL_GENERATE_MIPMAP_HINT - jakość generowania mipmap,
  • GL_TEXTURE_COMPRESSION_HINT - jakość kompresji tekstur,
  • GL_FRAGMENT_SHADER_DERIVATIVE_HINT - dokładność wbudowanych funkcji dFdx, dFdy i fwidth procesora fragmentów.
Stała GL_GENERATE_MIPMAP_HINT została wprowadzona w wersji 1.4 biblioteki OpenGL. Wcześniej możliwość definiowania jakości mipmap definiowało rozszerzenie SGIS generate mipmap.
Stała GL_TEXTURE_COMPRESSION_HINT została wprowadzona w wersji 1.3 biblioteki OpenGL. Do tego czasu jakość kompresji tekstur określało rozszerzenie ARB texture compression.
Stałą GL_FRAGMENT_SHADER_DERIVATIVE_HINT, podobnie jak całość obsługi języka cieniowania fragmentów, wprowadzono w wersji 2.0 biblioteki
OpenGL. Wcześniej język ten opisywało rozszerzenie ARB fragment shader.
Parametr mode określa poziom jakości renderingu. Dopuszczalne są trzy wartości:
  • GL_FASTEST - najwyższa wydajność renderingu,
  • GL_NICEST - najwyższa jakość obrazu,
  • GL_DONT_CARE - ustawienia domyślne.

Antyaliasing punktów

Standardowo antyaliasing punktów jest wyłączony. W celu jego włączenia należy wywołać funkcję glEnable z parametrem GL_POINT_SMOOTH. Ponadto trzeba włączyć mieszanie kolorów oraz określić parametry równania mieszania kolorów przy użyciu funkcji glBlendFunc:
C/C++
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA )
Przy okazji popatrzmy na to jakie możliwości oferuje nam implementacja biblioteki OpenGL w przypadku rysowania punktów. Jest to tym ważniejsze, że jak już pisaliśmy, specyfikacja OpenGL wymaga jedynie obsługi punktów o wielkości równej 1.
Informacje o możliwościach implementacji biblioteki OpenGL w zakresie renderingu punktów określają następujące zmienne stanu, których wartości można pobrać używając funkcji z grupy glGet:
  • GL_POINT_SIZE_RANGE - minimalny i maksymalny rozmiar punktów określany przy pomocy funkcji glPointSize,
  • GL_POINT_SIZE_GRANULARITY - krok z jakim może być zmieniana wielkość punktu.
W wersji 1.2 biblioteki OpenGL rozdzielono zmienne stanu określające powyższe parametry renderingu punktów na dotyczące punktów rysowanych z włączonym i wyłączonym antyaliasingiem. Wprowadzono cztery nowe stałe:
  • GL_SMOOTH_POINT_SIZE_RANGE - minimalny i maksymalny rozmiar punktów z włączonym antyaliasingiem; stała ta ma taką samą wartość jak GL_POINT_SIZE_RANGE,
  • GL_SMOOTH_POINT_SIZE_GRANULARITY - krok z jakim może być zmieniana wielkość punktu z włączonym antyaliasingiem; stała ta ma taką samą wartość jak GL_POINT_SIZE_GRANULARITY,
  • GL_ALIASED_POINT_SIZE_RANGE - minimalny i maksymalny rozmiar punktów bez antyaliasingu.

Antyaliasing linii

Użycie antyaliasingu linii jest równie proste jak stosowanie antyaliasingu punktów i sprowadza się do wywołania funkcji glEnable z parametrem GL_LINE_SMOOTH oraz włączenia mechanizmu mieszania kolorów z takimi samymi ustawieniami jak przy antyaliasingu punktów. Domyślnie antyaliasing linii jest wyłączony.
Podobnie jak w przypadku punktów implementacja OpenGL określa dopuszczalne parametry rysowania linii, które można odczytać z następujących zmiennych stanu:
  • GL_LINE_WIDTH_RANGE - minimalna i maksymalna szerokość linii,
  • GL_LINE_WIDTH_GRANULARITY - dopuszczalny krok pomiędzy szerokością linii.
Te zmienne stanu w wersji 1.2 biblioteki OpenGL zostały zastąpione poniższymi:
  • GL_SMOOTH_LINE_WIDTH_RANGE - minimalna i maksymalna szerokość linii z antyaliasingiem; stała ta ma taką samą wartość jak GL_LINE_WIDTH_RANGE,
  • GL_SMOOTH_LINE_WIDTH_GRANULARITY - dopuszczalny krok pomiędzy szerokością linii z antyaliasingiem; stała ta ma taką samą wartość jak GL_LINE_WIDTH_GRANULARITY,
  • GL_ALIASED_LINE_WIDTH_RANGE - minimalna i maksymalna szerokość linii bez antyaliasingu.

Antyaliasing wielokątów

Antyaliasing wielokątów zależy od sposobu ich rysowania określanego przy użyciu funkcji glPolygonMode. W trybie GL_POINT i GL_LINE postępowanie nie różni się od antyaliasingu odpowiednio punktów i linii. Zdecydowanie trudniej jest uzyskać antyaliasing krawędzi wypełnionych wielokątów. Przede wszystkim nie każda implementacja biblioteki OpenGL musi obsługiwać antyaliasing wielokątów. Drugim niezbędnym warunkiem jest pełna obsługa kanału alfa w buforze koloru. Można to sprawdzić pobierając przy użyciu funkcji z grupy g lGet z parametrem GL_ALPHA_BITS liczbę bitów kanału alfa. Wartość 0 oznacza brak obsługi kanału alfa.
Gdy mam oba warunki spełnione włączamy antyaliasing wielokątów wywołując funkcję glEnable z parametrem GL_POLYGON_SMOOTH, a następnie ustawiamy parametry równania mieszania kolorów przy użyciu funkcji glBlendFunc:
C/C++
glBlendFunc( GL_SRC_ALPHA_SATURATE, GL_ONE )
Z uwagi na korzystanie z mieszania kolorów musimy także wyłączyć bufor głębokości oraz posortować wielokąty, tak aby były rysowane w kolejności od najbliższego (!) do najdalszego. Dodatkowo, przed przystąpieniem do rysowania wielokątów bufor koloru musi być wypełniony składowymi RGBA o wartości (0, 0, 0, 0), czyli kolorem czarnym.
Można także wykorzystać do antyaliasingu krawędzi wielokątów parametry równania mieszania kolorów takie same jak przy antyaliasingu punktów i linii, tj. GL_SRC_ALPHA dla koloru źródłowego i GL_ONE_MINUS_SRC_ALPHA dla koloru przeznaczenia. Wówczas wielokąty także należy rysować posortowane, ale w kolejności od najdalszego do najbliższego.
Właśnie ten ostatni warunek powoduje, że antyaliasing wielokątów jest trudny do praktycznego zastosowania, zwłaszcza przy bardziej rozbudowanych scenach. Także znane algorytmy sortujące wielokąty (np. algorytm malarski) mają dużą złożoność obliczeniową i mogą mieć problemy z prawidłową obsługą niektórych przypadków przecięć wielokątów.

Antyaliasing z użyciem bufora akumulacyjnego

Jednym z efektów, który wykorzystuje bufor akumulacyjny jest FSAA (ang. Full Scene Anti Aliasing) czyli antyaliasing pełnoekranowy (całej sceny). Idea tego pomysłu jest bardzo prosta i sprowadza się do wielokrotnego rysowania sceny, która jest za każdym razem nieznacznie przesunięta w stosunku do pierwotnego obrazu. Tak otrzymane wartości składowych bufora koloru są odpowiednio skalowane (mnożone) a następnie sumowane w buforze akumulacyjnym. Podstawową wadą tego rozwiązania jest bardzo duża ilość obliczeń wykonywanych przez bibliotekę OpenGL.

Wielopróbkowanie

Rozwiązaniem opisanych niedogodności związanych z antyaliasingiem w OpenGL jest wprowadzona w wersji 1.3 biblioteki technika wielopróbkowania (ang. multisampling). Wielopróbkowanie polega w uproszczeniu na dodaniu do bufora ramki kolejnego bufora, którego zadaniem jest wyznaczanie koloru piksela docelowego na podstawie próbek pikseli z przyległego obszaru (maski). Cały proces realizowany jest procesor karty graficznej i bezpośrednio z poziomu biblioteki OpenGL (bez dodatkowych rozszerzeń) nie ma możliwości kontroli sposobu doboru wielkości i kształtu maski, która ma oczywisty wpływ na jakość antyaliasingu oraz wydajność renderingu.
Wielopróbkowanie, na długo przed wprowadzeniem do specyfikacji biblioteki OpenGL, było dostępne w rozszerzeniu SGIS multisample. Rozszerzenie to stało się później podstawą do opracowania rozszerzenia ARB multisample, które następnie włączono do podstawowej specyfikacji OpenGL, oraz rozszerzeń EXT multisample i 3DFX multisample.
Włączenie wielopróbkowania jest bardzo proste i sprowadza się do wywołania funkcji glEnable z parametrem GL_MULTISAMPLE. Oczywiście wyłącznie wielopróbkowania wymaga wywołania funkcji glDisable z takim samym parametrem. Dodatkowo, przy tworzeniu bufora ramki musi zostać utworzony bufor wielopróbkowania, co jest operacją specyficzną dla danej platformy systemowej.
Trzeba pamiętać, że włączenie wielopróbkowania powoduje nieaktywność wszystkich opisanych metod antyaliasingu punktów, linii i wielokątów. Zatem korzystanie z tych metod wymaga wyodrębnienia odpowiedniego kodu. Może to mieć szczególne znaczenie, jeżeli w danej implementacji OpenGL antyaliasing figur podstawowych daje lepsze efekty od wielopróbkowania.
Domyślnie bufory wielopróbkowania nie obejmują składowych kanału alfa. Włączenie składowych alfa do operacji wielopróbkowania wymaga wywołania funkcji glEnable z jednym z poniższych parametrów:
  • GL_SAMPLE_ALPHA_TO_COVERAGE - używanie składowych alfa w wielopróbkowaniu,
  • GL_SAMPLE_ALPHA_TO_ONE - ustawia wartość składowej alfa na maksymalną wartość (czyli 1), która jest następnie używana w wielopróbkowaniu,
  • GL_SAMPLE_COVERAGE - używa wartości składowej alfa ustalonej przez funkcję glSampleCoverage.
Wspomniana funkcja:
C/C++
void glSampleCoverage( GLclampf value, GLboolean invert )
umożliwia zdefiniowanie wartości składowej alfa, która będzie łączona operacją iloczynu logicznego ze składową alfa piksela i następnie używana w wielopróbkowaniu. Parametr value, przyjmujący wartości z przedziału [0, 1], określa wartość tej składowej, a parametr invert określa czy bity składowej alfa mają być przez operacją odwrócone (GL_FALSE) czy pozostawione bez zmiany (GL_TRUE).

Programy przykładowe

Pierwszy przykładowy program (plik antyaliasing punktow.cpp) prezentuje antyaliasing punktów, i jest modyfikacją wcześniej prezentowanego programu rysującego punkty. Program umożliwia włączenie i wyłączenie antyaliasingu punktów oraz wybór jakości renderingu punktów. Zaskakujące, ale podstawową różnicą pomiędzy renderingiem punktów z włączonym antyaliasingiem i bez antyaliasingu jest to, że punkty z włączonym antyaliasingiem są ... okrągłe (rysunek 3), a bez antyaliasingu są kwadratowe (lub prostokątne).
Przy okazji popatrzmy jak realizuje ustawienia jakości renderingu punktów karta graficzna ATI Radeon X700 znajdująca się w komputerze Autora. Rysunek 2 przedstawia trzy punkty, każdy o tej samej wielkości, przy renderowaniu których kolejno od lewej użyto: GL_FASTEST, GL_DONT_CARE i GL_NICEST. Różnice w jakości renderingu, w tym przypadku objawiające się ilością pikseli obrazu użytych do uzyskania efektu antyaliasingu, są wyraźnie widoczne. Warto także zauważyć, że w dopuszczalny rozmiar punktu z antyaliasingiem jest znacznie mniejszy od maksymalnego rozmiaru punktu z aliasingiem.

Plik antyaliasing_punktow.cpp

Rysunek 2. Rasteryzacja punktu z antyaliasingiem, kolejno od lewej: GL_FASTEST, GL_DONT_CARE i GL_NICEST
Rysunek 2. Rasteryzacja punktu z antyaliasingiem, kolejno od lewej: GL_FASTEST, GL_DONT_CARE i GL_NICEST
Rysunek 3. Program Antyaliasing punktów
Rysunek 3. Program Antyaliasing punktów
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>

// stałe do obsługi menu podręcznego

enum
{
    SMOOTH, // antyaliasing włącz/wyłącz
    CLEAR_POINTS, // usuwanie punktów
    EXIT // wyjście
};

// współrzędne wierzchołków punktów

std::vector < GLint > vertex_x;
std::vector < GLint > vertex_y;

// początkowy rozmiar punktu

GLfloat point_size = 10.0;

// antyaliasing

GLboolean point_smooth = true;

// jakość renderingu

GLint point_smooth_hint = GL_DONT_CARE;

// funkcja rysująca napis w wybranym miejscu

void DrawString( int x, int y, char * string )
{
    // położenie napisu
    glRasterPos2i( x, y );
   
    // wyświetlenie napisu
    int len = strlen( string );
    for( int i = 0; i < len; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}

// funkcja generująca scenę 3D

void DisplayScene()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // kolor punktów
    glColor3f( 1.0, 0.0, 0.0 );
   
    // wielkość punktów
    glPointSize( point_size );
   
    // ustawienie jakości renderingu punktów
    glHint( GL_POINT_SMOOTH_HINT, point_smooth_hint );
   
    // włączenie/wyłączenie antyaliasingu
    if( point_smooth )
    {
        // włączenie antyaliasingu punktów
        glEnable( GL_POINT_SMOOTH );
       
        // włączenie mieszania kolorów
        glEnable( GL_BLEND );
       
        // równanie mieszania kolorów
        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    }
    else
    {
        // wyłączenie antyaliasingu punktów
        glDisable( GL_POINT_SMOOTH );
       
        // wyłączenie mieszania kolorów
        glDisable( GL_BLEND );
    }
   
    // rysowanie punktów
    glBegin( GL_POINTS );
    for( unsigned int i = 0; i < vertex_x.size(); i++ )
         glVertex2i( vertex_x[ i ], vertex_y[ i ] );
   
    glEnd();
   
    // wyświetlenie parametrów renderingu punktów
    char string[ 100 ];
    GLfloat tab[ 2 ];
   
    // kolor napisów
    glColor3f( 0.0, 0.0, 0.0 );
   
    // minimalna i maksymalna wielkość punktu z antyaliasingiem
    glGetFloatv( GL_SMOOTH_POINT_SIZE_RANGE, tab );
    sprintf( string, "GL_SMOOTH_POINT_SIZE_RANGE: %f %f", tab[ 0 ], tab[ 1 ] );
    DrawString( 0, 1, string );
   
    // dopuszczalny krok pomiędzy rozmiarami punktu z antyaliasingiem
    glGetFloatv( GL_SMOOTH_POINT_SIZE_GRANULARITY, tab );
    sprintf( string, "GL_SMOOTH_POINT_SIZE_GRANULARITY: %f", tab[ 0 ] );
    DrawString( 0, 16, string );
   
    // minimalna i maksymalna wielkość punktu bez antyaliasingu
    glGetFloatv( GL_ALIASED_POINT_SIZE_RANGE, tab );
    sprintf( string, "GL_ALIASED_POINT_SIZE_RANGE: %f %f", tab[ 0 ], tab[ 1 ] );
    DrawString( 0, 31, string );
   
    // dopuszczalny krok pomiędzy rozmiarami punktu bez antyaliasingu
    #ifdef GL_ALIASED_POINT_SIZE_GRANULARITY
   
    glGetFloatv( GL_ALIASED_POINT_SIZE_GRANULARITY, tab );
    sprintf( string, "GL_ALIASED_POINT_SIZE_GRANULARITY: %f", tab[ 0 ] );
    DrawString( 0, 46, string );
    #endif
   
    // bieżąca wielkość punktu
    glGetFloatv( GL_POINT_SIZE, tab );
    sprintf( string, "GL_POINT_SIZE: %f", tab[ 0 ] );
    DrawString( 0, 61, string );
   
    // jakość renderingu punktów
    glGetFloatv( GL_POINT_SMOOTH_HINT, tab );
    if( tab[ 0 ] == GL_FASTEST )
         sprintf( string, "GL_POINT_SMOOTH_HINT: GL_FASTEST" );
    else
    if( tab[ 0 ] == GL_NICEST )
         sprintf( string, "GL_POINT_SMOOTH_HINT: GL_NICEST" );
    else
    if( tab[ 0 ] == GL_DONT_CARE )
         sprintf( string, "GL_POINT_SMOOTH_HINT: GL_DONT_CARE" );
   
    DrawString( 0, 76, string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // rzutowanie prostokątne
    gluOrtho2D( 0, width, 0, height );
   
    // generowanie sceny 3D
    DisplayScene();
}

// obsługa przycisków myszki

void MouseButton( int button, int state, int x, int y )
{
    if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN )
    {
        // zapamiętanie współrzędnych punktu
        vertex_x.insert( vertex_x.end(), x );
        vertex_y.insert( vertex_y.end(), glutGet( GLUT_WINDOW_HEIGHT ) - y );
       
        // narysowanie sceny
        DisplayScene();
    }
}

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // zwiększenie rozmiaru punktu
    if( key == '+' )
         point_size += 0.25;
    else
   
    // zmniejszenie rozmiaru punktu
    if( key == '-' && point_size > 0 )
         point_size -= 0.25;
   
    // narysowanie sceny
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // jakość renderingu - GL_FASTEST
    case GL_FASTEST:
        point_smooth_hint = GL_FASTEST;
        DisplayScene();
        break;
       
        // jakość renderingu - GL_NICEST
    case GL_NICEST:
        point_smooth_hint = GL_NICEST;
        DisplayScene();
        break;
       
        // jakość renderingu - GL_DONT_CARE
    case GL_DONT_CARE:
        point_smooth_hint = GL_DONT_CARE;
        DisplayScene();
        break;
       
        // antyaliasing włącz/wyłącz
    case SMOOTH:
        point_smooth = !point_smooth;
        DisplayScene();
        break;
       
        // usuwanie punktów
    case CLEAR_POINTS:
        vertex_x.erase( vertex_x.begin(), vertex_x.end() );
        vertex_y.erase( vertex_y.begin(), vertex_y.end() );
        DisplayScene();
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Antyaliasing punktów" );
    #else
   
    glutCreateWindow( "Antyaliasing punktow" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // dołączenie funkcji obsługi klawiatury
    glutKeyboardFunc( Keyboard );
   
    // utworzenie podmenu - Jakość renderingu
    int MenuHint = glutCreateMenu( Menu );
    glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
    glutAddMenuEntry( "GL_NICEST", GL_NICEST );
    glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
   
    // menu główne
    glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddSubMenu( "Jakość renderingu", MenuHint );
    glutAddMenuEntry( "Antyaliasing włącz/wyłącz", SMOOTH );
    glutAddMenuEntry( "Usuń punkty", CLEAR_POINTS );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Jakosc renderingu", MenuHint );
    glutAddMenuEntry( "Antyaliasing wlacz/wylacz", SMOOTH );
    glutAddMenuEntry( "Usun punkty", CLEAR_POINTS );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Drugi program przykładowy (plik antyaliasing linii.cpp) także jest przeróbką wcześniejszego programu. Program umożliwia zmianę szerokości oraz wzoru każdej z rysowanych linii. Oczywiście jest także możliwość włączania i wyłączania antyaliasingu oraz kontrola jakości renderingu. Przykładowe efekt rysowania linii z włączonym antyaliasingiem przedstawia rysunek 5.
Sterownik używanej do testów karty graficznej ATI Radeon X700 nie różnicował jakości renderingu linii z włączonym antyaliasingiem ustawianych za pośrednictwem funkcji glHint. Wygląd powiększonej linii przerywanej z włączonym antyaliasingiem przedstawia lewa część rysunku 4. Warto zwrócić uwagę, że wewnętrzne części linii są renderowane bez antyaliasingu. Z kolei druga testowana implementacja OpenGL - biblioteka Mesa 3D w wersji 6.5 nie tylko występowały różnice w jakości renderingu, ale antyaliasing obejmował także wewnętrzne części przerywanych linii (prawa część rysunku 4). Oczywiście w innych implementacjach biblioteki OpenGL ta sama linia może zostać wyrenderowana jeszcze w inny sposób.
Rysunek 4. Rasteryzacja linii przerywanej z antyaliasingiem: po lewej ATI Radeon X700, po prawej Mesa 6.5 (GL_NICEST)
Rysunek 4. Rasteryzacja linii przerywanej z antyaliasingiem: po lewej ATI Radeon X700, po prawej Mesa 6.5 (GL_NICEST)

Plik antyaliasing_linii.cpp

Rysunek 5. Program Antyaliasing linii
Rysunek 5. Program Antyaliasing linii
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>

// stałe do obsługi menu podręcznego

enum
{
    STIPPLE_1111111111111111, // wzór linii 1111111111111111
    STIPPLE_0000000011111111, // wzór linii 0000000011111111
    STIPPLE_1111111100000000, // wzór linii 1111111100000000
    STIPPLE_0000111100001111, // wzór linii 0000111100001111
    STIPPLE_1111000011110000, // wzór linii 1111000011110000
    STIPPLE_0011001100110011, // wzór linii 0011001100110011
    STIPPLE_1100110011001100, // wzór linii 1100110011001100
    SMOOTH, // antyaliasing włącz/wyłącz
    CLEAR_LINES, // usuwanie linii
    EXIT // wyjście
};

// współrzędne końców linii

std::vector < GLint > vertex_x;
std::vector < GLint > vertex_y;

// szerokości linii

std::vector < GLfloat > lines_width;

// początkowa szerokość linii

GLfloat line_width = 10.0;

// wzory linii

std::vector < GLfloat > lines_stipple;

// początkowy wzór rysowanej linii

GLushort line_stipple = 0xFFFF;

// wskaźnik naciśnięcia lewego przycisku myszki

int button_state = GLUT_UP;

// antyaliasing

GLboolean line_smooth = true;

// jakość renderingu

GLint line_smooth_hint = GL_DONT_CARE;

// funkcja rysująca napis w wybranym miejscu

void DrawString( int x, int y, char * string )
{
    // położenie napisu
    glRasterPos2i( x, y );
   
    // wyświetlenie napisu
    int len = strlen( string );
    for( int i = 0; i < len; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}

// funkcja generująca scenę 3D

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // kolor linii
    glColor3f( 1.0, 0.0, 0.0 );
   
    // włączenie rysowania wzorów linii
    glEnable( GL_LINE_STIPPLE );
   
    // włączenie/wyłączenie antyaliasingu
    if( line_smooth )
    {
        // włączenie antyaliasingu linii
        glEnable( GL_LINE_SMOOTH );
       
        // włączenie mieszania kolorów
        glEnable( GL_BLEND );
       
        // równanie mieszania kolorów
        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    }
    else
    {
        // wyłączenie antyaliasingu punktów
        glDisable( GL_LINE_SMOOTH );
       
        // wyłączenie mieszania kolorów
        glDisable( GL_BLEND );
    }
   
    // ustawienie jakości renderingu linii
    glHint( GL_LINE_SMOOTH_HINT, line_smooth_hint );
   
    // rysowanie linii
    for( unsigned int i = 0; i < vertex_x.size(); i += 2 )
    {
        // szerokość linii
        glLineWidth( lines_width[ i / 2 ] );
       
        // wzór rysowanej linii
        glLineStipple( 1, lines_stipple[ i / 2 ] );
       
        // rysowanie pojedynczej linii
        glBegin( GL_LINES );
        glVertex2i( vertex_x[ i ], vertex_y[ i ] );
        glVertex2i( vertex_x[ i + 1 ], vertex_y[ i + 1 ] );
        glEnd();
    }
   
    // wyświetlenie parametrów renderingu linii
    char string[ 100 ];
    GLfloat tab[ 2 ];
   
    // kolor napisów
    glColor3f( 0.0, 0.0, 0.0 );
   
    // minimalna i maksymalna szerokość linii z antyaliasingiem
    glGetFloatv( GL_SMOOTH_LINE_WIDTH_RANGE, tab );
    sprintf( string, "GL_SMOOTH_LINE_WIDTH_RANGE: %f %f", tab[ 0 ], tab[ 1 ] );
    DrawString( 0, 1, string );
   
    // dopuszczalny krok pomiędzy szerokością linii z antyaliasingiem
    glGetFloatv( GL_SMOOTH_LINE_WIDTH_GRANULARITY, tab );
    sprintf( string, "GL_SMOOTH_LINE_WIDTH_GRANULARITY: %f", tab[ 0 ] );
    DrawString( 0, 16, string );
   
    // minimalna i maksymalna szerokość linii bez antyaliasingu
    glGetFloatv( GL_ALIASED_LINE_WIDTH_RANGE, tab );
    sprintf( string, "GL_ALIASED_LINE_WIDTH_RANGE: %f %f", tab[ 0 ], tab[ 1 ] );
    DrawString( 0, 31, string );
   
    // jakość renderingu punktów
    glGetFloatv( GL_LINE_SMOOTH_HINT, tab );
    if( tab[ 0 ] == GL_FASTEST )
         sprintf( string, "GL_LINE_SMOOTH_HINT: GL_FASTEST" );
    else
    if( tab[ 0 ] == GL_NICEST )
         sprintf( string, "GL_LINE_SMOOTH_HINT: GL_NICEST" );
    else
    if( tab[ 0 ] == GL_DONT_CARE )
         sprintf( string, "GL_LINE_SMOOTH_HINT: GL_DONT_CARE" );
   
    DrawString( 0, 46, string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // rzutowanie prostokątne
    gluOrtho2D( 0, width, 0, height );
   
    // generowanie sceny 3D
    Display();
}

// obsługa przycisków myszki

void MouseButton( int button, int state, int x, int y )
{
    if( button == GLUT_LEFT_BUTTON )
    {
        // początek rysowania linii
        if( state == GLUT_DOWN && button_state == GLUT_UP )
        {
            // zapamiętanie współrzędnych początku i końca linii
            vertex_x.insert( vertex_x.end(), x );
            vertex_y.insert( vertex_y.end(), glutGet( GLUT_WINDOW_HEIGHT ) - y );
            vertex_x.insert( vertex_x.end(), x );
            vertex_y.insert( vertex_y.end(), glutGet( GLUT_WINDOW_HEIGHT ) - y );
           
            // zapamiętanie bieżącej szerokości linii
            lines_width.insert( lines_width.end(), line_width );
           
            // zapamiętanie bieżącego wzoru linii
            lines_stipple.insert( lines_stipple.end(), line_stipple );
           
            // zmiana wartości wskaźnika
            button_state = GLUT_DOWN;
           
            // narysowanie sceny
            Display();
        }
        else
       
        // zakończenie rysowania linii
        if( state == GLUT_UP && button_state == GLUT_DOWN )
        {
            vertex_x[ vertex_x.size() - 1 ] = x;
            vertex_y[ vertex_y.size() - 1 ] = glutGet( GLUT_WINDOW_HEIGHT ) - y;
           
            // zmiana wartości wskaźnika
            button_state = GLUT_UP;
           
            // narysowanie sceny
            Display();
        }
    }
}

// obsługa ruchu kursora myszki

void MouseMotion( int x, int y )
{
    // zmiana końca położenia linii
    if( button_state == GLUT_DOWN )
    {
        // zmiana współrzędnych końca linii
        vertex_x[ vertex_x.size() - 1 ] = x;
        vertex_y[ vertex_y.size() - 1 ] = glutGet( GLUT_WINDOW_HEIGHT ) - y;
       
        // narysowanie sceny
        Display();
    }
}

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // zwiększenie szerokości linii
    if( key == '+' )
         line_width += 0.25;
    else
   
    // zmniejszenie szerokości linii
    if( key == '-' && line_width > 0 )
         line_width -= 0.25;
   
    // narysowanie sceny
    Display();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // jakość renderingu - GL_FASTEST
    case GL_FASTEST:
        line_smooth_hint = GL_FASTEST;
        Display();
        break;
       
        // jakość renderingu - GL_NICEST
    case GL_NICEST:
        line_smooth_hint = GL_NICEST;
        Display();
        break;
       
        // jakość renderingu - GL_DONT_CARE
    case GL_DONT_CARE:
        line_smooth_hint = GL_DONT_CARE;
        Display();
        break;
       
        // antyaliasing włącz/wyłącz
    case SMOOTH:
        line_smooth = !line_smooth;
        Display();
        break;
       
        // wzór linii 1111111111111111
    case STIPPLE_1111111111111111:
        line_stipple = 0xFFFF;
        Display();
        break;
       
        // wzór linii 0000000011111111
    case STIPPLE_0000000011111111:
        line_stipple = 0x00FF;
        Display();
        break;
       
        // wzór linii 1111111100000000
    case STIPPLE_1111111100000000:
        line_stipple = 0xFF00;
        Display();
        break;
       
        // wzór linii 0000111100001111
    case STIPPLE_0000111100001111:
        line_stipple = 0x0F0F;
        Display();
        break;
       
        // wzór linii 1111000011110000
    case STIPPLE_1111000011110000:
        line_stipple = 0xF0F0;
        Display();
        break;
       
        // wzór linii 0011001100110011
    case STIPPLE_0011001100110011:
        line_stipple = 0x3333;
        Display();
        break;
       
        // wzór linii 1100110011001100
    case STIPPLE_1100110011001100:
        line_stipple = 0xCCCC;
        Display();
        break;
       
        // usuwanie linii
    case CLEAR_LINES:
        vertex_x.erase( vertex_x.begin(), vertex_x.end() );
        vertex_y.erase( vertex_y.begin(), vertex_y.end() );
        lines_width.erase( lines_width.begin(), lines_width.end() );
        lines_stipple.erase( lines_stipple.begin(), lines_stipple.end() );
        Display();
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    glutCreateWindow( "Antyaliasing linii" );
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // dołączenie funkcji obsługi klawiatury
    glutKeyboardFunc( Keyboard );
   
    // utworzenie podmenu - Jakość renderingu
    int MenuHint = glutCreateMenu( Menu );
    glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
    glutAddMenuEntry( "GL_NICEST", GL_NICEST );
    glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
   
    // utworzenie podmenu - Wzór linii
    int MenuStipple = glutCreateMenu( Menu );
    glutAddMenuEntry( "1111111111111111", STIPPLE_1111111111111111 );
    glutAddMenuEntry( "0000000011111111", STIPPLE_0000000011111111 );
    glutAddMenuEntry( "1111111100000000", STIPPLE_1111111100000000 );
    glutAddMenuEntry( "0000111100001111", STIPPLE_0000111100001111 );
    glutAddMenuEntry( "1111000011110000", STIPPLE_1111000011110000 );
    glutAddMenuEntry( "0011001100110011", STIPPLE_0011001100110011 );
    glutAddMenuEntry( "1100110011001100", STIPPLE_1100110011001100 );
   
    // menu główne
    glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddSubMenu( "Jakość renderingu", MenuHint );
    glutAddSubMenu( "Wzór linii", MenuStipple );
    glutAddMenuEntry( "Antyaliasing włącz/wyłącz", SMOOTH );
    glutAddMenuEntry( "Usuń linie", CLEAR_LINES );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Jakosc renderingu", MenuHint );
    glutAddSubMenu( "Wzor linii", MenuStipple );
    glutAddMenuEntry( "Antyaliasing wlacz/wylacz", SMOOTH );
    glutAddMenuEntry( "Usun linie", CLEAR_LINES );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Kolejny program przykładowy (plik antyaliasing wielokatow.cpp) testuje implementację antyaliasingu krawędzi wypełnionych wielokątów w bibliotece OpenGL. Słowo „testuje” nie jest użyte przypadkowo, bowiem program stosuje jedną z dwóch opisanych wyżej metod uzyskania efektu antyaliasingu, przy czym wybór dokonywany jest po sprawdzeniu dostępności kanału alfa w buforze koloru.
Nieco zaskakujące jest to, że oba prezentowane zrzuty okna programu (rysunki 6 i 7) powstały przy użyciu biblioteki Mesa 3D w wersji 6.5, która obsługuje antyaliasing wielokątów, ale bufor koloru pozbawiony był kanału alfa. Z kolei posiadana przez Autora karta graficzna ATI Radeon X700 wprawdzie posiadała bufora alfa ale niestety nie obsługiwała antyaliasingu wielokątów. Biblioteka Mesa nie różnicowała jakości antyaliasingu wielokątów określanej przy użyciu funkcji glHint.
W programie nie został zaimplementowany żaden zaawansowany algorytm sortowania wielokątów. Aby uzyskać prawidłowe efekty antyaliasingu generowane losowo współrzędne trójkątów są tak dobierane, aby każdy trójkąt zawierał się w płaszczyźnie prostopadłej do osi Z układu współrzędnych, czy inaczej mówiąc był równoległy do płaszczyzny monitora (obiekty na scenie nie są obracane). Dzięki temu proces sortowania trójkątów sprowadza się do porównania wartości współrzędnych z. Dla uproszczenia programu do sortowania wykorzystano odpowiedni algorytm z biblioteki standardowej języka C++.
Tradycyjne białe tło okna programu uzyskano rysując biały czworokąt tak położony aby nie przecinał grupy wygenerowanych trójkątów. Zmieniono przy tym, korzystając z funkcji glDepthFunc, sposób działania bufora głębokości z domyślnego GL_LESS (mniejszy) na GL_EQUAL (równy), dzięki czemu tło nie zasłania narysowanych wcześniej trójkątów.
Warto jeszcze zwrócić uwagę, na modyfikację sposobu inicjalizacji bufora ramki w funkcji main omawianego programu. Przy wywołaniu funkcji glutInitDisplayMode została dodana stała GLUT ALPHA, której zadaniem jest aktywacja kanału alfa w buforze kolorów.

Plik antyaliasing_wielokatow.cpp

Rysunek 6. Program Antyaliasing wielokątów - trójkąty z antyaliasingiem (Mesa 6.5)
Rysunek 6. Program Antyaliasing wielokątów - trójkąty z antyaliasingiem (Mesa 6.5)
Rysunek 7. Program Antyaliasing wielokątów - trójkąty bez antyaliasingu
Rysunek 7. Program Antyaliasing wielokątów - trójkąty bez antyaliasingu
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <algorithm>

// stałe do obsługi menu podręcznego

enum
{
    SMOOTH, // antyaliasing włącz/wyłącz
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int aspect = FULL_WINDOW;

// rozmiary bryły obcinania

const GLdouble left = - 2.0;
const GLdouble right = 2.0;
const GLdouble bottom = - 2.0;
const GLdouble top = 2.0;
const GLdouble near = 3.0;
const GLdouble far = 7.0;

// ilość generowanych wielokątów

const int polygon_count = 50;

// antyaliasing

GLboolean polygon_smooth = true;

// jakość renderingu

GLint polygon_smooth_hint = GL_DONT_CARE;

// struktora opisująca współrzędne oraz kolory wierzchołków trójkąta

struct Triangle
{
    GLfloat v1[ 3 ];
    GLfloat v2[ 3 ];
    GLfloat v3[ 3 ];
    GLfloat c1[ 3 ];
    GLfloat c2[ 3 ];
    GLfloat c3[ 3 ];
};

// lista trójkątów

std::vector < Triangle > Polygons;

// funkcja sortująca trójkąty ze względu na współrzędną z;
// trójkąty są tak generowane, że wszystkie współrzędne z są równe,
// stąd wystarczy bardzo proste porównanie pojedynczej współrzędnej

bool operator <( const Triangle & t1, const Triangle & t2 )
{
    return t1.v1[ 2 ] < t2.v1[ 2 ];
}

// funkcja rysująca napis w wybranym miejscu

void DrawString( GLfloat x, GLfloat y, char * string )
{
    // położenie napisu
    glRasterPos2f( x, y );
   
    // wyświetlenie napisu
    int len = strlen( string );
    for( int i = 0; i < len; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}

// funkcja generująca scenę 3D

void Display()
{
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // przesunięcie układu współrzędnych obiektu do środka bryły odcinania
    glTranslatef( 0, 0, -( near + far ) / 2 );
   
    // włączenie/wyłączenie antyaliasingu
    if( polygon_smooth )
    {
        // sprawdzenie obsługi kanału alfa - pobranie ilości bitów
        GLint alpha;
        glGetIntegerv( GL_ALPHA_BITS, & alpha );
       
        // kolor tła - zawartość bufora koloru
        if( alpha != 0 )
             glClearColor( 0.0, 0.0, 0.0, 0.0 );
        else
             glClearColor( 1.0, 1.0, 1.0, 1.0 );
       
        // czyszczenie bufora koloru
        glClear( GL_COLOR_BUFFER_BIT );
       
        // wyłączenie testu bufora głębokości
        glDisable( GL_DEPTH_TEST );
       
        // włączenie antyaliasingu wielokątów
        glEnable( GL_POLYGON_SMOOTH );
       
        // włączenie mieszania kolorów
        glEnable( GL_BLEND );
       
        // równanie mieszania kolorów
        if( alpha != 0 )
             glBlendFunc( GL_SRC_ALPHA_SATURATE, GL_ONE );
        else
             glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
       
        // ustawienie jakości renderingu wielokątów
        glHint( GL_POLYGON_SMOOTH_HINT, polygon_smooth_hint );
       
        // rysowanie wielokątów - odwrotna kolejność
        glBegin( GL_TRIANGLES );
        if( alpha != 0 )
        for( unsigned int i = Polygons.size() - 1; i > 0; i-- )
        {
            glColor3fv( Polygons[ i ].c1 );
            glVertex3fv( Polygons[ i ].v1 );
            glColor3fv( Polygons[ i ].c2 );
            glVertex3fv( Polygons[ i ].v2 );
            glColor3fv( Polygons[ i ].c3 );
            glVertex3fv( Polygons[ i ].v3 );
        }
        else
        // rysowanie wielokątów - normalna kolejność
        for( unsigned int i = 0; i < Polygons.size(); i++ )
        {
            glColor3fv( Polygons[ i ].c1 );
            glVertex3fv( Polygons[ i ].v1 );
            glColor3fv( Polygons[ i ].c2 );
            glVertex3fv( Polygons[ i ].v2 );
            glColor3fv( Polygons[ i ].c3 );
            glVertex3fv( Polygons[ i ].v3 );
        }
        glEnd();
       
        // dodanie białego tła
        if( alpha != 0 )
        {
            // kolor tła
            glColor4f( 1.0, 1.0, 1.0, 1.0 );
           
            // włączenie testu bufora głębokości
            glEnable( GL_DEPTH );
           
            // zmiana sposobu działania bufora głębokości
            glDepthFunc( GL_EQUAL );
           
            // czworokąt
            glBegin( GL_QUADS );
            glVertex3f( 2 * left, 2 * bottom, 0 );
            glVertex3f( 2 * left, 2 * top, 0 );
            glVertex3f( 2 * right, 2 * top, 0 );
            glVertex3f( 2 * right, 2 * bottom, 0 );
            glEnd();
           
            // powrót do domyślnego sposobu działania bufora głębokości
            glDepthFunc( GL_LESS );
        }
    }
    else
    {
        // kolor tła - zawartość bufora koloru
        glClearColor( 1.0, 1.0, 1.0, 1.0 );
       
        // czyszczenie bufora koloru i bufora głębokości
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
       
        // włączenie testu bufora głębokości
        glEnable( GL_DEPTH_TEST );
       
        // wyłączenie antyaliasingu wielokątów
        glDisable( GL_POLYGON_SMOOTH );
       
        // wyłączenie mieszania kolorów
        glDisable( GL_BLEND );
       
        // rysowanie wielokątów (kolejność bez znaczenia)
        glBegin( GL_TRIANGLES );
        for( unsigned int i = 0; i < Polygons.size(); i++ )
        {
            glColor3fv( Polygons[ i ].c1 );
            glVertex3fv( Polygons[ i ].v1 );
            glColor3fv( Polygons[ i ].c2 );
            glVertex3fv( Polygons[ i ].v2 );
            glColor3fv( Polygons[ i ].c3 );
            glVertex3fv( Polygons[ i ].v3 );
        }
        glEnd();
    }
   
    // trzeba odpowiednio przekształcić układ współrzędnych
    // aby napis znajdował się na samej "górze" bryły obcinania
    glLoadIdentity();
    glTranslatef( 0, 0, - near );
   
    // wyłączenie mieszania kolorów
    glDisable( GL_BLEND );
   
    // wyświetlenie parametrów renderingu wielokątów
    char string[ 100 ];
    GLfloat tab[ 2 ];
   
    // kolor napisów
    glColor3f( 0.0, 0.0, 0.0 );
   
    // jakość renderingu wielokątów
    glGetFloatv( GL_POLYGON_SMOOTH_HINT, tab );
    if( tab[ 0 ] == GL_FASTEST )
         sprintf( string, "GL_POLYGON_SMOOTH_HINT: GL_FASTEST" );
    else
    if( tab[ 0 ] == GL_NICEST )
         sprintf( string, "GL_POLYGON_SMOOTH_HINT: GL_NICEST" );
    else
    if( tab[ 0 ] == GL_DONT_CARE )
         sprintf( string, "GL_POLYGON_SMOOTH_HINT: GL_DONT_CARE" );
   
    DrawString( left, bottom, string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania
    if( aspect == ASPECT_1_1 )
    {
        // wysokość okna większa od wysokości okna
        if( width < height && width > 0 )
             glFrustum( left, right, bottom * height / width, top * height / width, near, far );
        else
       
        // szerokość okna większa lub równa wysokości okna
        if( width >= height && height > 0 )
             glFrustum( left * width / height, right * width / height, bottom, top, near, far );
       
    }
    else
         glFrustum( left, right, bottom, top, near, far );
   
    // generowanie sceny 3D
    Display();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // jakość renderingu - GL_FASTEST
    case GL_FASTEST:
        polygon_smooth_hint = GL_FASTEST;
        Display();
        break;
       
        // jakość renderingu - GL_NICEST
    case GL_NICEST:
        polygon_smooth_hint = GL_NICEST;
        Display();
        break;
       
        // jakość renderingu - GL_DONT_CARE
    case GL_DONT_CARE:
        polygon_smooth_hint = GL_DONT_CARE;
        Display();
        break;
       
        // antyaliasing włącz/wyłącz
    case SMOOTH:
        polygon_smooth = !polygon_smooth;
        Display();
        break;
       
        // obszar renderingu - całe okno
    case FULL_WINDOW:
        aspect = FULL_WINDOW;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // obszar renderingu - aspekt 1:1
    case ASPECT_1_1:
        aspect = ASPECT_1_1;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

// generowanie losowych wielokątów

void GeneratePolygons()
{
    // określenie zarodka dla  ciągu liczb pseudolosowych
    srand( time( NULL ) );
   
    // generowanie położenia punktów (wierzchołków) figur oraz ich kolorów
    for( int i = 0; i < polygon_count; i++ )
    {
        Triangle t;
       
        // współrzędne wierzchołków trójkątów są dobierane tak aby mieściły
        // się w bryle odcinania, a wszystkie "z" leżały na jednej płaszczyźnie
        t.v1[ 0 ] =( rand() /( float ) RAND_MAX ) *( right - left + 1 ) - right - 0.5;
        t.v1[ 1 ] =( rand() /( float ) RAND_MAX ) *( top - bottom + 1 ) - top - 0.5;
        t.v1[ 2 ] =( rand() /( float ) RAND_MAX ) *( far - near ) / 2 -( far - near ) / 4;
        t.v2[ 0 ] =( rand() /( float ) RAND_MAX ) *( right - left + 1 ) - right - 0.5;
        t.v2[ 1 ] =( rand() /( float ) RAND_MAX ) *( top - bottom + 1 ) - top - 0.5;
        t.v2[ 2 ] = t.v1[ 2 ];
        t.v3[ 0 ] =( rand() /( float ) RAND_MAX ) *( right - left + 1 ) - right - 0.5;
        t.v3[ 1 ] =( rand() /( float ) RAND_MAX ) *( top - bottom + 1 ) - top - 0.5;
        t.v3[ 2 ] = t.v1[ 2 ];
       
        // kolory wierzchołków trójkątów
        t.c1[ 0 ] =( rand() /( float ) RAND_MAX );
        t.c1[ 1 ] =( rand() /( float ) RAND_MAX );
        t.c1[ 2 ] =( rand() /( float ) RAND_MAX );
        t.c2[ 0 ] =( rand() /( float ) RAND_MAX );
        t.c2[ 1 ] =( rand() /( float ) RAND_MAX );
        t.c2[ 2 ] =( rand() /( float ) RAND_MAX );
        t.c3[ 0 ] =( rand() /( float ) RAND_MAX );
        t.c3[ 1 ] =( rand() /( float ) RAND_MAX );
        t.c3[ 2 ] =( rand() /( float ) RAND_MAX );
       
        // dodanie trójkąta na listę
        Polygons.insert( Polygons.end(), t );
    }
   
    // sortowanie trójkątów
    std::sort( Polygons.begin(), Polygons.end() );
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_ALPHA );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Antyaliasing wielokątów" );
    #else
   
    glutCreateWindow( "Antyaliasing wielokatow" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // utworzenie podmenu - Jakość renderingu
    int MenuHint = glutCreateMenu( Menu );
    glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
    glutAddMenuEntry( "GL_NICEST", GL_NICEST );
    glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
   
    // utworzenie podmenu - Aspekt obrazu
    int MenuAspect = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
    #else
   
    glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
    #endif
   
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
   
    // menu główne
    glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddSubMenu( "Jakość renderingu", MenuHint );
    glutAddMenuEntry( "Antyaliasing włącz/wyłącz", SMOOTH );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Jakosc renderingu", MenuHint );
    glutAddMenuEntry( "Antyaliasing wlacz/wylacz", SMOOTH );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // generowanie losowych wielokątów
    GeneratePolygons();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Czwarty i ostatni program przykładowy (plik antyaliasing pelnoekranowy.cpp) przedstawia antyaliasing pełnoekranowy uzyskany z wykorzystaniem obu opisanych wyżej technik, tj. wielopróbkowanie oraz przy zastosowaniu bufora akumulacyjnego.
W przypadku wykorzystania bufora akumulacyjnego zastosowano jeden z najpopularniejszych w literaturze algorytm obliczania współczynników przesunięcia - fluktacji (ang. jitter), który opisany jest m.in. w pracy [6]. W algorytmie tym parametry bryły odcinania, definiowane za pomocą funkcji glFrustum, modyfikowane są 9 (3 × 3), 25 (5 × 5) lub 49 (7 × 7) razy. Oczywiście taką samą ilość razy rysowane są obiekty sceny przy tworzeniu pojedynczej ramki obrazu.
Przesunięcia obiektów we współrzędnych lewej, prawej, dolnej i górnej bryły odcinania (parametry left, right, bottom i top funkcji glFrustum) obliczane są tak, aby nie przekraczały jednego piksela okna obrazu. Przy większych przesunięciach można uzyskać niepożądany w tym momencie efekt rozmycia obiektów sceny. Po każdym narysowaniu sceny dane z bufora koloru dodawane są do bufora akumulacyjnego, ale wcześniej dzielone przez ilość rysowanych scen dla każdej ramki obrazu (normalizacja). Dzięki temu dane znajdujące się w buforze akumulacyjnym będą zawierały poprawne informacje o kolorach obiektów sceny. Po wykonaniu wszystkich przesunięć, dane z bufora akumulacyjnego kopiowane są do bufora kolorów.
Subiektywne odczucia jakości antyaliasingu otrzymanego przy użyciu bufora akumulacyjnego (rysunki 8 i 9) w porównaniu do wielopróbkowania (rysunek 10) wypadają zdecydowanie na korzyść techniki korzystającej z bufora akumulacyjnego. Niestety uzyskanie wysokiej jakość obrazu zostało okupione drastycznym spadkiem szybkości wyświetlanego obrazu. Co ciekawe spadek ten jest znacznie większy niż wynikający ze zwielokrotnienia rysowania obiektów sceny. W tej sytuacji wielopróbkowanie wydaje się być w typowych aplikacjach bezkonkurencyjne, bowiem spadek szybkości renderingu w stosunku do sceny bez antyaliasingu jest minimalny (patrz rysunki 10 i 11).
Na koniec dodajmy, że program po raz kolejny modyfikuje sposób inicjalizacji bufora ramki w funkcji main. Przy wywołaniu funkcji glutInitDisplayMode zostały dodane stałe: GLUT ACCUM i GLUT MULTISAMPLE włączające obsługę bufora akumulacyjnego i bufora wielopróbkowania. Nie jest także sprawdzana dostępność wielopróbkowania w danej implementacji biblioteki OpenGL. Nie powinno to jednak być przeszkodą do działania pozostałych opcji programu.

Plik antyaliasing_pelnoekranowy.cpp

Rysunek 8. Program Antyaliasing pełnoekranowy - bufor akumulacyjny 3 × 3
Rysunek 8. Program Antyaliasing pełnoekranowy - bufor akumulacyjny 3 × 3
Rysunek 9. Program Antyaliasing pełnoekranowy - bufor akumulacyjny 7 × 7
Rysunek 9. Program Antyaliasing pełnoekranowy - bufor akumulacyjny 7 × 7
Rysunek 10. Program Antyaliasing pełnoekranowy - wieloprókowanie
Rysunek 10. Program Antyaliasing pełnoekranowy - wieloprókowanie
Rysunek 11. Program Antyaliasing pełnoekranowy - antyaliasing wyłączony
Rysunek 11. Program Antyaliasing pełnoekranowy - antyaliasing wyłączony
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "colors.h"

// stałe do obsługi menu podręcznego

enum
{
    SMOOTH_ACCUM_BUFFER_3, // antyaliasing - bufor akumulacyjny 3x3
    SMOOTH_ACCUM_BUFFER_5, // antyaliasing - bufor akumulacyjny 5x5
    SMOOTH_ACCUM_BUFFER_7, // antyaliasing - bufor akumulacyjny 7x7
    SMOOTH_MULTISAMPLE, // antyaliasing - wielopróbkowanie
    SMOOTH_NONE, // antyaliasing - wyłączony
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int aspect = FULL_WINDOW;

// usunięcie definicji makr near i far

#ifdef near
#undef near
#endif
#ifdef far
#undef far
#endif

// rozmiary bryły obcinania

const GLdouble left = - 2.0;
const GLdouble right = 2.0;
const GLdouble bottom = - 2.0;
const GLdouble top = 2.0;
const GLdouble near = 3.0;
const GLdouble far = 7.0;

// kąty obrotu obiektu

GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;

// wskaźnik naciśnięcia lewego przycisku myszki

int button_state = GLUT_UP;

// położenie kursora myszki

int button_x, button_y;

// rodzaj antyaliasingu

int scene_smooth = SMOOTH_ACCUM_BUFFER_3;

// jakość antyaliasingu z użyciem bufora akumulacyjnego

int smooth_quality = 1;

// licznik ramek (FPS)

int frames = 0;

// licznik czasu

long start_time = 0;

// tablica znaków ze wartością FPS

char time_string[ 100 ] = "FPS:";

// funkcja rysująca napis w wybranym miejscu

void DrawString( GLfloat x, GLfloat y, char * string )
{
    // położenie napisu
    glRasterPos2f( x, y );
   
    // wyświetlenie napisu
    int len = strlen( string );
    for( int i = 0; i < len; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}

// funkcja generująca scenę 3D

void DisplayScene()
{
    // licznik czasu
    if( !frames++ )
         start_time = clock();
   
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // włączenie oświetlenia
    glEnable( GL_LIGHTING );
   
    // włączenie światła GL_LIGHT0
    glEnable( GL_LIGHT0 );
   
    // włączenie obsługi właściwości materiałów
    glEnable( GL_COLOR_MATERIAL );
   
    // właściwości materiału określone przez kolor wierzchołków
    glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
   
    // włączenie automatycznej normalizacji wektorów normalnych
    glEnable( GL_NORMALIZE );
   
    // pobranie rozmiaru okna
    int width = glutGet( GLUT_WINDOW_WIDTH );
    int height = glutGet( GLUT_WINDOW_HEIGHT );
   
    // scena bez antyaliasingu lub z wieloprókowaniem
    if( scene_smooth == SMOOTH_MULTISAMPLE || scene_smooth == SMOOTH_NONE )
    {
        // wybór macierzy rzutowania
        glMatrixMode( GL_PROJECTION );
       
        // macierz rzutowania = macierz jednostkowa
        glLoadIdentity();
       
        // parametry bryły obcinania
        if( aspect == ASPECT_1_1 )
        {
            // wysokość okna większa od wysokości okna
            if( width < height && width > 0 )
                 glFrustum( left, right, bottom * height / width, top * height / width, near, far );
            else
           
            // szerokość okna większa lub równa wysokości okna
            if( width >= height && height > 0 )
                 glFrustum( left * width / height, right * width / height, bottom, top, near, far );
           
        }
        else
             glFrustum( left, right, bottom, top, near, far );
       
        // czyszczenie bufora koloru i bufora głębokości
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
       
        // wybór macierzy modelowania
        glMatrixMode( GL_MODELVIEW );
       
        // macierz modelowania = macierz jednostkowa
        glLoadIdentity();
       
        // przesunięcie układu współrzędnych obiektu do środka bryły odcinania
        glTranslatef( 0, 0, -( near + far ) / 2 );
       
        // obroty obiektu
        glRotatef( rotatex, 1.0, 0, 0 );
        glRotatef( rotatey, 0, 1.0, 0 );
       
        // włączenie wielopróbkowania
        if( scene_smooth == SMOOTH_MULTISAMPLE )
             glEnable( GL_MULTISAMPLE );
       
        // narysowanie obiektu
        glColor4fv( Green );
        glutSolidTorus( 0.5, 1.5, 50, 40 );
       
        // wyłączenie wielopróbkowania
        if( scene_smooth == SMOOTH_MULTISAMPLE )
             glDisable( GL_MULTISAMPLE );
       
    }
   
    // scena z antyalisingiem z użyciem bufora akumulacyjnego
    else
    {
        // zakresy pętli rysujących kolejne przesunięte ramki sceny
        int min = - smooth_quality;
        int max = - min + 1;
       
        // ilość rysowanych ramek sceny, wartość używana przy
        // mnożeniu danych dodawanych do bufora akumulacyjnego
        GLfloat count =( max - min ) *( max - min );
       
        // współczynnik o jaki przesuwany jest układ współrzędnch
        // przy rysowaniu kolejnych ramek sceny; współczynnik 0.9
        // dobrany eksperymentalnie, jego zwiększenie powoduje widoczne
        // rozmycie obrazu, a zmniejszenie pogorszenie jakości antyaliasingu
        GLfloat scale = 0.9 /( GLfloat ) smooth_quality;
       
        // pobranie rozmiaru obszaru renderingu
        GLint viewport[ 4 ];
        glGetIntegerv( GL_VIEWPORT, viewport );
       
        // obliczenie rozmiaru piksela ekranu
        GLfloat pointx =( right - left ) /( GLfloat ) viewport[ 2 ];
        GLfloat pointy =( top - bottom ) /( GLfloat ) viewport[ 3 ];
       
        // czyszczenie bufora akumulacyjnego
        glClear( GL_ACCUM_BUFFER_BIT );
       
        // wyświetlenie sceny (max-min-1)^2 razy
        for( int y = min; y < max; y++ )
        for( int x = min; x < max; x++ )
        {
            // obliczenie współczynników przesunięcia - fluktuacji
            // (ang. jitter) układu współrzędnych
            GLfloat dx = pointx * scale * x;
            GLfloat dy = pointy * scale * y;
           
            // wybór macierzy rzutowania
            glMatrixMode( GL_PROJECTION );
           
            // macierz rzutowania = macierz jednostkowa
            glLoadIdentity();
           
            // parametry bryły obcinania
            if( aspect == ASPECT_1_1 )
            {
                // wysokość okna większa od wysokości okna
                if( width < height && width > 0 )
                glFrustum( left + dx, right + dx,
                bottom * height / width + dy, top * height / width + dy,
                     near, far );
                else
                // szerokość okna większa lub równa wysokości okna
                if( width >= height && height > 0 )
                glFrustum( left * width / height + dx,
                right * width / height + dy, bottom + dy, top + dy,
                     near, far );
               
            }
            else
                 glFrustum( left + dx, right + dx, bottom + dy, top + dy, near, far );
           
            // czyszczenie bufora koloru i bufora głębokości
            glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
           
            // wybór macierzy modelowania
            glMatrixMode( GL_MODELVIEW );
           
            // macierz modelowania = macierz jednostkowa
            glLoadIdentity();
           
            // przesunięcie układu współrzędnych obiektu do środka bryły odcinania
            glTranslatef( 0, 0, -( near + far ) / 2 );
           
            // obroty obiektu
            glRotatef( rotatex, 1.0, 0, 0 );
            glRotatef( rotatey, 0, 1.0, 0 );
           
            // narysowanie obiektu
            glColor4fv( Green );
            glutSolidTorus( 0.5, 1.5, 50, 40 );
           
            // dodanie danych do bufora akumulacyjnego
            glAccum( GL_ACCUM, 1.0 / count );
        }
       
        // przeniesienie danych z bufora akumulacyjnego do bufora koloru
        glAccum( GL_RETURN, 1.0 );
    }
   
    // wyłączenie oświetlenia
    glDisable( GL_LIGHTING );
   
    // wyłączenie testu bufora głębokości
    glDisable( GL_DEPTH_TEST );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // trzeba odpowiednio przekształcić układ współrzędnych
    // aby napis znajdował się na samej "górze" bryły obcinania
    glLoadIdentity();
    glTranslatef( 0, 0, - near );
   
    // komunikat o ilości ramek rysowanych na sekundę (FPS)
    glColor4fv( Black );
    if( frames == 500 )
    {
        frames = 0;
        sprintf( time_string, "FPS: %i",( int )( 500 * CLOCKS_PER_SEC /( float )( clock() - start_time ) ) );
    }
   
    // narysowanie napisu
    DrawString( left + 0.02, bottom + 0.02, time_string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // generowanie sceny 3D
    DisplayScene();
}

// obsługa przycisków myszki

void MouseButton( int button, int state, int x, int y )
{
    if( button == GLUT_LEFT_BUTTON )
    {
        // zapamiętanie stanu lewego przycisku myszki
        button_state = state;
       
        // zapamiętanie położenia kursora myszki
        if( state == GLUT_DOWN )
        {
            button_x = x;
            button_y = y;
        }
    }
}

// obsługa ruchu kursora myszki

void MouseMotion( int x, int y )
{
    if( button_state == GLUT_DOWN )
    {
        rotatey += 30 *( right - left ) / glutGet( GLUT_WINDOW_WIDTH ) *( x - button_x );
        button_x = x;
        rotatex -= 30 *( top - bottom ) / glutGet( GLUT_WINDOW_HEIGHT ) *( button_y - y );
        button_y = y;
        glutPostRedisplay();
    }
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // antyaliasing - bufor akumulacyjny 3x3
    case SMOOTH_ACCUM_BUFFER_3:
        scene_smooth = SMOOTH_ACCUM_BUFFER_3;
        smooth_quality = 1;
        sprintf( time_string, "FPS:" );
        DisplayScene();
        break;
       
        // antyaliasing - bufor akumulacyjny 5x5
    case SMOOTH_ACCUM_BUFFER_5:
        scene_smooth = SMOOTH_ACCUM_BUFFER_5;
        smooth_quality = 2;
        sprintf( time_string, "FPS:" );
        DisplayScene();
        break;
       
        // antyaliasing - bufor akumulacyjny 7x7
    case SMOOTH_ACCUM_BUFFER_7:
        scene_smooth = SMOOTH_ACCUM_BUFFER_7;
        smooth_quality = 3;
        sprintf( time_string, "FPS:" );
        DisplayScene();
        break;
       
        // antyaliasing - wielopróbkowanie
    case SMOOTH_MULTISAMPLE:
        scene_smooth = SMOOTH_MULTISAMPLE;
        DisplayScene();
        break;
       
        // antyaliasing - wyłączony
    case SMOOTH_NONE:
        scene_smooth = SMOOTH_NONE;
        DisplayScene();
        break;
       
        // obszar renderingu - całe okno
    case FULL_WINDOW:
        aspect = FULL_WINDOW;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // obszar renderingu - aspekt 1:1
    case ASPECT_1_1:
        aspect = ASPECT_1_1;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH |
    GLUT_ACCUM | GLUT_MULTISAMPLE );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Antyaliasing pełnoekranowy" );
    #else
   
    glutCreateWindow( "Antyaliasing pelnoekranowy" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie podmenu - Antyaliasing
    int MenuSmooth = glutCreateMenu( Menu );
    glutAddMenuEntry( "Bufor akumulacyjny 3x3", SMOOTH_ACCUM_BUFFER_3 );
    glutAddMenuEntry( "Bufor akumulacyjny 5x5", SMOOTH_ACCUM_BUFFER_5 );
    glutAddMenuEntry( "Bufor akumulacyjny 7x7", SMOOTH_ACCUM_BUFFER_7 );
   
    #ifdef WIN32
   
    glutAddMenuEntry( "Wielopróbkowanie", SMOOTH_MULTISAMPLE );
    glutAddMenuEntry( "Wyłączony", SMOOTH_NONE );
    #else
   
    glutAddMenuEntry( "Wieloprobkowanie", SMOOTH_MULTISAMPLE );
    glutAddMenuEntry( "Wylaczony", SMOOTH_NONE );
    #endif
   
    // utworzenie podmenu - Aspekt obrazu
    int MenuAspect = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
    #else
   
    glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
    #endif
   
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
   
    // menu główne
    glutCreateMenu( Menu );
    glutAddSubMenu( "Antyaliasing", MenuSmooth );
    #ifdef WIN32
   
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // funkcja bezczynności
    glutIdleFunc( DisplayScene );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Bufor akumulacyjny Tekstury