« Bufor akumulacyjny, lekcja »
Rozdział 18. Budowa i działanie bufora akumulacyjnego; sterowanie buforem akumulacyjnym; czyszczenie bufora akumulacyjnego; dwa programy przykładowe z efektami: rozmycia w ruchu (ang. motion blur) oraz głębią ostrości (ang. depth of field) oraz program z cyfrową filtracją obrazów. (lekcja)
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!
Autor: Janusz Ganczarski
Kurs OpenGL, C++

Bufor akumulacyjny

[lekcja] Rozdział 18. Budowa i działanie bufora akumulacyjnego; sterowanie buforem akumulacyjnym; czyszczenie bufora akumulacyjnego; dwa programy przykładowe z efektami: rozmycia w ruchu (ang. motion blur) oraz głębią ostrości (ang. depth of field) oraz program z cyfrową filtracją obrazów.
Bufor akumulacyjny umożliwia łączenie kilku obrazów w celu uzyskania określonego efektu końcowego. W swojej budowie bufor akumulacyjny przypomina bufor koloru, przechowuje bowiem informacje o składowych RGBA kolorów. Bufor akumulacyjny nie jest dostępny, gdy biblioteka OpenGL pracuje w trybie indeksu kolorów.
Bufor akumulacyjny używany jest do uzyskiwania wielu efektów specjalnych. Najpopularniejsze z nich to rozmycie w ruchu (ang. motion blur), głębia ostrości (ang. depth of field), antyaliasing pełnoekranowy (ang. FSAA - Full Scene Anti Aliasing) oraz miękkie cienie (ang. soft shadows). Dwie pierwsze z wymienionych technik przedstawimy w niniejszym odcinku kursu. Pozostałe zostaną zaprezentowane wraz z innymi technikami antyaliasingu i cieni.
Niestety użycie bufora akumulacyjnego do uzyskania efektów specjalnych wiąże się najczęściej z bardzo dużą ilością dodatkowych obliczeń. Jednak największym problemem jest brak sprzętowej obsługi bufora akumulacyjnego przez popularne procesory graficzne.

Sterowanie buforem akumulacyjnym

Działaniem bufora akumulacyjnego steruje funkcja:
C/C++
void glAccum( GLenum op, GLfloat value )
której parametr op określa rodzaj operacji wykonywanej na buforze:
  • GL_ACCUM - dodawanie do bufora akumulacyjnego wartości bufora kolorów przemnożonych (przeskalowanych) przez wartość parametru value:
  • GL_LOAD - zapisanie do bufora akumulacyjnego wartości bufora kolorów przemnożonych (przeskalowanych) przez wartość parametru value:
  • GL_RETURN - kopiowanie wartości bufora akumulacyjnego przemnożonych
    (przeskalowanych) przez wartość parametru value do bufora kolorów:
  • GL_MULT - mnożenie (skalowanie) wartości zawartych w buforze akumulacyjnym przez wartość parametru value:
  • GL_ADD - dodawanie wartość parametru value do wartości zawartych w buforze akumulacyjnym:
Jak Czytelnik zauważył, w powyższych równaniach (Rc, Gc, Bc, Ac) oznaczają składowe bufora kolorów, a (Ra , Ga , Ba , Aa ) wartości bufora szablonowego

Czyszczenie bufora akumulacyjnego

W razie potrzeby bufor akumulacyjny może zostać wypełniony określonymi wartościami składowych RGBA. Służy to tego dobrze znana funkcja glClear wraz z parametrem GL_ACCUM_BUFFER_BIT. Domyślnie bufor akumulacyjny wypełniany jest wartościami (0, 0, 0, 0), ale funkcja:
C/C++
void glClearAccum( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha )
umożliwia zmianę tego stanu. Wartości parametrów red, green, blue i alpha w razie potrzeby obcinane są do przedziału [−1, 1].

Programy przykładowe

Zgodnie z wcześniejszą zapowiedzią pierwszy program przykładowy (plik rozmycie_w_ruchu.cpp) przedstawia efekt rozmycia w ruchu. Efekt uzyskiwany jest poprzez odpowiednie operacje na buforze akumulacyjnym. Z wielu możliwości uzyskania efektu rozmycia program przedstawia trzy metody.
Pierwsze dwie dają bardzo podobny efekt (patrz rysunki 1 i 2) i sprowadzają się jedynie do wykonywania odpowiednich operacji na buforze akumulacyjnym, przy czym bufor ten czyszczony jest tylko przy rysowaniu pierwszej ramki obrazu.
Trzeci zastosowany algorytm jest bardziej klasyczny i sprowadza się wielokrotnego rysowania sceny za każdym razem przesuniętej o odpowiedni kąt. Kolejne rysowane ramki są po skalowaniu sumowane w buforze akumulacyjnym. Efekt uzyskany przy 16 rysowanych ramkach przedstawia rysunek 3. Metoda ta daje pełną kontrolę nad jakością uzyskanego efektu rozmycia w ruchu, ale zwielokrotnienie rysowania sceny bezpośrednio wpływa na spadek szybkości renderingu.
Aby udostępnić bufor akumulacyjny trzeba go dodać przy inicjalizacji bufora ramki. W przypadku biblioteki GLUT sprowadza się to do dodania stałej GLUT ACCUM przy wywołaniu funkcji glutInitDisplayMode.
Rysunek 1. Program Rozmycie w ruchu - pierwszy algorytm
Rysunek 1. Program Rozmycie w ruchu - pierwszy algorytm
Rysunek 2. Program Rozmycie w ruchu - drugi algorytm
Rysunek 2. Program Rozmycie w ruchu - drugi algorytm

Plik rozmycie_w_ruchu.cpp

Rysunek 3. Program Rozmycie w ruchu - trzeci algorytm
Rysunek 3. Program Rozmycie w ruchu - trzeci algorytm
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

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

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

enum
{
    MOTION_BLUR_1, // pierwszy algorytm rozmycia w ruchu
    MOTION_BLUR_2, // drugi algorytm rozmycia w ruchu
    MOTION_BLUR_3, // trzeci algorytm rozmycia w ruchu
    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;

// kąty obrotu obiektów

GLfloat rotatex = 20.0;
GLfloat rotatey = 20.0;
GLfloat rotatez = 20.0;

// identyfikator listy wyświetlania

GLint WORLD_LIST;

// wybrany algorytm rozmycia w ruchu

int motion_blur = MOTION_BLUR_1;

// 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 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();
   
    // 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 automatycznej normalizacji wektorów normalnych
    glEnable( GL_NORMALIZE );
   
    // 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 );
   
    // przesunięcie układu współrzędnych obiektów do środka bryły odcinania
    glTranslatef( 0, 0, -( near + far ) / 2 );
   
    // obroty obiektów sceny
    glRotatef( rotatex, 1.0, 0.0, 0.0 );
    glRotatef( rotatey, 0.0, 1.0, 0.0 );
    glRotatef( rotatez, 0.0, 0.0, - 1.0 );
   
    // wyświetlenie obiektów sceny
    glCallList( WORLD_LIST );
   
    // wybór algorytmu realizującego efekt rozmycia w ruchu
    switch( motion_blur )
    {
        // pierwszy algorytm rozmycia w ruchu
    case MOTION_BLUR_1:
       
        // przeskalowanie wartości znajdujących się w buforze akumulacyjnym
        glAccum( GL_MULT, 0.9 );
       
        // dodanie przeskalowanych wartości z bufora kolorów do bufora akumulacynego
        glAccum( GL_ACCUM, 0.1 );
       
        // kopiowanie wartości z bufora akumulacyjnego do bufora kolorów
        glAccum( GL_RETURN, 1.0 );
        break;
       
        // drugi algorytm rozmycia w ruchu
    case MOTION_BLUR_2:
       
        // dodanie przeskalowanych wartości z bufora kolorów do bufora akumulacynego
        glAccum( GL_ACCUM, 0.1 );
       
        // kopiowanie wartości z bufora akumulacyjnego do bufora kolorów
        glAccum( GL_RETURN, 1.0 );
       
        // załadowanie przeskalowanych wartości z bufora kolorów do bufora akumulacyjnego
        glAccum( GL_LOAD, 0.9 );
        break;
       
        // trzeci algorytm rozmycia w ruchu
    case MOTION_BLUR_3:
       
        // dodanie przeskalowanych wartości z bufora kolorów do bufora akumulacynego
        glAccum( GL_LOAD, 0.5 );
       
        // rysowanie wybranej ilości poprzednich ramek animacji
        int count = 15;
        for( int i = 1; i <= count; i++ )
        {
            // czyszczenie bufora koloru i bufora głębokości
            glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
           
            // obroty obiektów sceny
            glRotatef( - 1.0, 1.0, 0.0, 0.0 );
            glRotatef( 1.0, 0.0, 1.0, 0.0 );
            glRotatef( - 1.0, 0.0, 0.0, - 1.0 );
           
            // wyświetlenie obiektów sceny
            glCallList( WORLD_LIST );
           
            // dodanie przeskalowanych wartości z bufora kolorów do bufora akumulacynego
            // współczynnik skalowania jest obliczany na podstawie ilości rysowanych
            // wcześniejszych ramek animacji
            glAccum( GL_ACCUM, 0.5 /( float ) count );
        }
       
        // kopiowanie wartości z bufora akumulacyjnego do bufora kolorów
        glAccum( GL_RETURN, 1.0 );
        break;
       
    }
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// funkcja timera

void Timer( int value )
{
    // zwiększenie kąta obrotu obiektu sceny
    rotatex++;
    rotatey--;
    rotatez++;
   
    // wyświetlenie sceny
    DisplayScene();
   
    // następne wywołanie funkcji timera
    glutTimerFunc( 20, Timer, 0 );
}

// 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 );
   
    // wartość czyszcząca bufor akumulacyjny
    glClearAccum( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora akumulacyjnego
    glClear( GL_ACCUM_BUFFER_BIT );
   
    // generowanie sceny 3D
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // 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 );
    }
}

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    switch( key )
    {
        // pierwszy algorytm rozmycia w ruchu
    case '1':
        glClear( GL_ACCUM_BUFFER_BIT );
        motion_blur = MOTION_BLUR_1;
        break;
       
        // drugi algorytm rozmycia w ruchu
    case '2':
        glClear( GL_ACCUM_BUFFER_BIT );
        motion_blur = MOTION_BLUR_2;
        break;
       
        // trzeci algorytm rozmycia w ruchu
    case '3':
        glClear( GL_ACCUM_BUFFER_BIT );
        motion_blur = MOTION_BLUR_3;
        break;
    }
   
    // narysowanie sceny
    DisplayScene();
}

// utworzenie listy wyświetlania

void GenerateDisplayList()
{
    // generowanie identyfikatora listy wyświetlania
    WORLD_LIST = glGenLists( 1 );
   
    // lista wyświetlania - scena
    glNewList( WORLD_LIST, GL_COMPILE );
   
    // niebieski torus
    glColor4fv( Blue );
    glutSolidTorus( 0.5, 1.5, 50, 40 );
   
    // koniec drugiej listy wyświetlania
    glEndList();
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_ACCUM );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    glutCreateWindow( "Rozmycie w ruchu" );
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // dołączenie funkcji obsługi klawiatury
    glutKeyboardFunc( Keyboard );
   
    // 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( "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 );
   
    // utworzenie listy wyświetlania
    GenerateDisplayList();
   
    // wywołanie funkcji timera
    glutTimerFunc( 20, Timer, 0 );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Drugi program przykładowy (plik glebia_ostrosci.cpp) przedstawia jedną z możliwych realizacji efektu głębi ostrości (ang. depth of field). Idea pomysłu jest bardzo prosta i sprowadza się do wielokrotnego rysowania obiektów sceny, za każdym razem nieco przesuniętych w stosunku do podstawowego obrazu. Odpowiedni dobór przesunięcia powoduje, że obiekty położone bliżej (rysunek 4) są bardziej „ostre” od obiektów położonych dalej (rysunek 4). Program umożliwia wybór jakości uzyskanego efektu, przy czym lepsza jakość związana jest niestety z większą ilością wykonywanych obliczeń.
Rysunek 4. Program Głębia ostrości - obiekt w małej odległości
Rysunek 4. Program Głębia ostrości - obiekt w małej odległości
Podobna technika może zostać wykorzystania do uzyskania efektu antyaliasingu całej sceny, ale to przedstawimy w następnym odcinku kursu.

Plik glebia_ostrosci.cpp

Rysunek 5. Program Głębia ostrości - obiekt w dużej odległości
Rysunek 5. Program Głębia ostrości - obiekt w dużej odległości
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

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

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

enum
{
    DOF_3, // głębia ostrości - bufor akumulacyjny 3x3
    DOF_5, // głębia ostrości - bufor akumulacyjny 5x5
    DOF_7, // głębia ostrości - bufor akumulacyjny 7x7
    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 = 1.0;
const GLdouble far = 11;

// współrzędna z położenia obiektu

GLfloat z = 0;

// rodzaj efektu głębi ostrości

int depth_of_field = DOF_3;

// jakość efektu głębi ostrości

int dof_quality = 1;

// funkcja generująca scenę 3D

void DisplayScene()
{
    // 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 z antyalisingiem z użyciem bufora akumulacyjnego
    // zakresy pętli rysujących kolejne przesunięte ramki sceny
    int min = - dof_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 );
   
    // 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; współczynnik
        // 0.02 dobrany eksperymentalnie
        GLfloat dx = x * 0.02;
        GLfloat dy = y * 0.02;
       
        // 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 );
       
        // drobne przesunięcie obiektu poprawiające jakość
        // renderingu obiektów w bliższej odległości;
        // współczynnik 0.02 dobrany eksperymentalnie
        glTranslatef( x * 0.02, y * 0.02, 0.0 );
       
        // kolor obiektu
        glColor4fv( Green );
       
        // przesunięcie obiektu względem osi OZ
        glTranslatef( 0.0, 0.0, z );
       
        // narysowanie obiektu
        glutSolidTorus( 1.0, 2.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 );
   
    // 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 klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // przesunięcie obiektu względem osi OZ
    if( key == '+' )
         z += 0.1;
    else
    if( key == '-' )
         z -= 0.1;
   
    // narysowanie sceny
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // głębia ostrości - bufor akumulacyjny 3x3
    case DOF_3:
        depth_of_field = DOF_3;
        dof_quality = 1;
        DisplayScene();
        break;
       
        // głębia ostrości - bufor akumulacyjny 5x5
    case DOF_5:
        depth_of_field = DOF_5;
        dof_quality = 2;
        DisplayScene();
        break;
       
        // głębia ostrości - bufor akumulacyjny 7x7
    case DOF_7:
        depth_of_field = DOF_7;
        dof_quality = 3;
        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 );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Głębia ostrości" );
    #else
   
    glutCreateWindow( "Glebia ostrosci" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // dołączenie funkcji obsługi klawiatury
    glutKeyboardFunc( Keyboard );
   
    // utworzenie podmenu - Antyaliasing
    int MenuDOF = glutCreateMenu( Menu );
    glutAddMenuEntry( "Bufor akumulacyjny 3x3", DOF_3 );
    glutAddMenuEntry( "Bufor akumulacyjny 5x5", DOF_5 );
    glutAddMenuEntry( "Bufor akumulacyjny 7x7", DOF_7 );
   
    // 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( "Głębia ostrości", MenuDOF );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Glebia ostrosci", MenuDOF );
    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;
}
Trzeci i ostatni program przykładowy (plik filtracja_obrazow.cpp) przedstawia sposób wykorzystania bufora akumulacyjngo do filtracji obrazów przy użyciu filtrów splotowych. Zbiór filtrów jest taki sam jak używany w programie prezentującym opcjonalny podzbiór przetwarzania obrazów (plik filters.h).
Efekt działania filtra splotowego uzyskiwany jest poprzez wielokrotne rysowanie filtrowanego obrazu przy czym za każdym razem zmieniane jest położenie rysowanego obrazu i odpowiednio modyfikowany współczynnik skalowania składowych RGB obrazu. Z uwagi na specyfikę działania bufora akumulacyjnego maski filtra muszą być tak przeskalowane, aby ich wartość bezwzględna nie była większa o jednego. Drugim ważnym elementem, jest kolejność nakładania poszczególnych obrazów, bowiem w przypadku przekroczenia przez składową bufora akumulacyjnego przedziału wartości [−1, 1], nastąpią przekłamania w końcowym wyniku filtracji. Obie powyższe operacje wykonywane są każdorazowo przy wyborze filtra (funkcja Menu).
Efekty działania programu nie różną się w żaden sposób od działania wspomnianego wyżej programu wykorzystującego opcjonalny podzbiór funkcji przetwarzania obrazów. Efekt działania wybranych filtrów przedstawiono na rysunkach 6 - 10.

Plik filtracja_obrazow.cpp

Rysunek 6. Program Filtracja obrazów - filtr pionowy Laplace’a
Rysunek 6. Program Filtracja obrazów - filtr pionowy Laplace’a
Rysunek 7. Program Filtracja obrazów - filtr pionowy Sobela
Rysunek 7. Program Filtracja obrazów - filtr pionowy Sobela
Rysunek 8. Program Filtracja obrazów - filtr pionowy Prewitta
Rysunek 8. Program Filtracja obrazów - filtr pionowy Prewitta
Rysunek 9. Program Filtracja obrazów - filtr HP1
Rysunek 9. Program Filtracja obrazów - filtr HP1
Rysunek 10. Program Filtracja obrazów - filtr HP2
Rysunek 10. Program Filtracja obrazów - filtr HP2
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "targa.h"
#include "filters.h"

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

enum
{
    // filtry splotowe
    NONE, // brak filtracji
    AVERAGE, // filtr uśredniający
    LP1, // filtr LP1
    LP2, // filtr LP2
    LP3, // filtr LP3
    GAUSS, // filtr Gaussa
    MEAN_REMOVAL, // filtr usuwający średnią
    HP1, // filtr HP1
    HP2, // filtr HP2
    HP3, // filtr HP3
    HORIZONTAL, // filtr poziomy
    VERTICAL, // filtr pionowy
    HORIZONTAL_VERTICAL, // filtr poziomy/pionowy
    GRADIENT_EAST, // filtr gradientowy wschód
    GRADIENT_SOUTH_EAST, // filtr gradientowy południowy wschód
    GRADIENT_SOUTH, // filtr gradientowy południe
    GRADIENT_SOUTH_WEST, // filtr gradientowy południowy zachód
    GRADIENT_WEST, // filtr gradientowy zachód
    GRADIENT_NORTH_WEST, // filtr gradientowy północny zachód
    GRADIENT_NORTH, // filtr gradientowy północ
    GRADIENT_NORTH_EAST, // filtr gradientowy północny wschód
    EMBOSS_EAST, // filtr uwypuklający wschód
    EMBOSS_SOUTH_EAST, // filtr uwypuklający południowy wschód
    EMBOSS_SOUTH, // filtr uwypuklający południe
    EMBOSS_SOUTH_WEST, // filtr uwypuklający południowy zachód
    EMBOSS_WEST, // filtr uwypuklający zachód
    EMBOSS_NORTH_WEST, // filtr uwypuklający północny zachód
    EMBOSS_NORTH, // filtr uwypuklający północ
    EMBOSS_NORTH_EAST, // filtr uwypuklający północny wschód
    LAPLACIAN_LAPL1, // filtr Laplace'a LAPL1
    LAPLACIAN_LAPL2, // filtr Laplace'a LAPL2
    LAPLACIAN_LAPL3, // filtr Laplace'a LAPL3
    LAPLACIAN_DIAGONAL, // filtr Laplace'a skośny
    LAPLACIAN_HORIZONTAL, // filtr Laplace'a poziomy
    LAPLACIAN_VERTICAL, // filtr Laplace'a pionowy
    SOBEL_HORIZONTAL, // filtr poziomy Sobela
    SOBEL_VERTICAL, // filtr pionowy Sobela
    PREWITT_HORIZONTAL, // filtr poziomy Prewitta
    PREWITT_VERTICAL, // filtr pionowy Prewitta
   
    SAVE_FILE, // zapis pliku TARGA
    EXIT // wyjście
};

// dane opisujące obraz odczytany z pliku TARGA

GLsizei width; // szerokość obrazu
GLsizei height; // wysokość obrazu
GLenum format; // format danych obrazu
GLenum type; // format danych pikseli obrazu
GLvoid * pixels; // wskaźnik na tablicę z danymi obrazu

// neutralna maska filtra splotowego

GLfloat neutral[ 9 ] =
{
    0, 0, 0,
    0, 1, 0,
    0, 0, 0
};

// tablica z bieżącą maską filtra splotowego

GLfloat filter[ 9 ] =
{
    0, 0, 0,
    0, 1, 0,
    0, 0, 0
};

// wartość skalująca składowe bufora akumulacyjnego

GLfloat scale = 1.0;

// numery kolejnych przetwarzanych elementów maski filtra

int dir_h[ 9 ] =
{
    2, 0, 0, 2, 1, 0, 1, 2, 1
};

int dir_w[ 9 ] =
{
    0, 0, 2, 2, 0, 1, 2, 1, 1
};

// wczytanie pliku graficznego w formacie TARGA

void LoadTARGA( int argc, char * argv[] )
{
    // sprawdzenie czy jest parametr
    if( argc < 2 )
    {
        printf( "Brak nazwy pliku TARGA\n" );
        exit( 1 );
    }
   
    // odczyt pliku TARGA i ewentualny komunikat o błędzie
    if( !load_targa( argv[ 1 ], width, height, format, type, pixels ) )
    {
        #ifdef WIN32
        printf( "Błąd odczytu lub błędny format pliku: %s\n", argv[ 1 ] );
        #else
       
        printf( "Blad odczytu lub bledny format pliku: %s\n", argv[ 1 ] );
        #endif
       
        exit( 1 );
    }
}

// sprawdzenie czy dany format pliku TARGA jest obsługiwany

void CheckImageFormat()
{
    // obraz w formacie BGR i BGRA
    if( format == GL_BGR || format == GL_BGRA )
    {
        // odczyt wersji OpenGL
        const char * version =( char * ) glGetString( GL_VERSION );
        int major = 0, minor = 0;
        if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
        {
            #ifdef WIN32
            printf( "Błędny format wersji OpenGL\n" );
            #else
           
            printf( "Bledny format wersji OpenGL\n" );
            #endif
           
            exit( 1 );
        }
       
        // sprawdzenie czy jest co najmniej wersja 1.2 OpenGL
        if( major <= 1 && minor < 2 )
        {
            // jeżeli jest starsza wersja OpenGL sprawdzenie
            // czy jest obsługa rozszerzenia GL_EXT_bgra
            if( !glutExtensionSupported( "GL_EXT_bgra" ) )
            {
                // komunikat o błędzie - w tym miejscu można wykonać
                // konwersję danych z formatu BGR/BGRA na RGB/RGBA
                printf( "Brak rozszerzenia GL_EXT_bgra\n" );
                exit( 1 );
            }
        }
    }
}

// zapis pliku graficznego w formacie TARGA

void SaveTARGA( GLenum format )
{
    // wyrównywanie wiersza mapy do pojedyńczego bajta
    glPixelStorei( GL_PACK_ALIGNMENT, 1 );
   
    // wskaźnik na bufor mapy pikselowej
    GLvoid * pixels;
   
    // szerokość i wysokość mapy pikselowej
    GLsizei width = glutGet( GLUT_WINDOW_WIDTH );
    GLsizei height = glutGet( GLUT_WINDOW_HEIGHT );
   
    // utworzenie bufora mapy pikselowej
    if( format == GL_BGRA )
         pixels = new unsigned char[ width * height * 4 ];
    else
    if( format == GL_BGR )
         pixels = new unsigned char[ width * height * 3 ];
    else
    if( format == GL_LUMINANCE )
         pixels = new unsigned char[ width * height ];
    else
         return;
   
    // tylko plik w odcieniach szarości
    if( format == GL_LUMINANCE )
    {
        // określenie przekształcenia składowych RGB na odcienie szarości
        glPixelTransferf( GL_RED_SCALE, 0.229 );
        glPixelTransferf( GL_GREEN_SCALE, 0.587 );
        glPixelTransferf( GL_BLUE_SCALE, 0.114 );
    }
   
    // skopiowanie zawartości bufora koloru do bufora mapy pikselowej
    glReadPixels( 0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels );
   
    // tylko plik w odcieniach szarości
    if( format == GL_LUMINANCE )
    {
        // powrót do neutralnego przekształcenia składowych
        glPixelTransferf( GL_RED_SCALE, 1.0 );
        glPixelTransferf( GL_GREEN_SCALE, 1.0 );
        glPixelTransferf( GL_BLUE_SCALE, 1.0 );
    }
   
    // zapis mapy pikselowej do pliku TARGA
    if( format == GL_BGRA )
         save_targa( "test_BGRA.tga", width, height, format, type, pixels );
    else
    if( format == GL_BGR )
         save_targa( "test_BGR.tga", width, height, format, type, pixels );
    else
    if( format == GL_LUMINANCE )
         save_targa( "test_LUMINANCE.tga", width, height, format, type, pixels );
   
    // porządki
    delete[]( unsigned char * ) pixels;
}

// wyświetlenie i skalowanie obrazu

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // wartość czyszcząca bufor akumulacyjny
    glClearAccum( 0.0, 0.0, 0.0, 1.0 );
   
    // czyszczenie bufora koloru i bufora akumulacyjnego
    glClear( GL_COLOR_BUFFER_BIT | GL_ACCUM_BUFFER_BIT );
   
    // pobranie rozmiarów okna
    int win_width = glutGet( GLUT_WINDOW_WIDTH );
    int win_height = glutGet( GLUT_WINDOW_HEIGHT );
   
    // rozmiary maski filtra
    const int whmax = 3;
   
    // filtracja obrazu
    for( int i = 0; i < whmax * whmax; i++ )
    {
        // obszar renderingu - odpowiedni przesunięte okno
        glViewport( - dir_w[ i ], - dir_h[ i ], win_width - dir_w[ i ], win_height - dir_h[ i ] );
       
        // pozycja mapy pikselowej
        glRasterPos2i( 0, 0 );
       
        // wyrównywanie wiersza mapy pikselowej do pojedyńczego bajta
        glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
       
        // skalowanie mapy pikselowej do rozmiarów okna
        glPixelZoom(( float ) win_width /( float ) width,( float ) win_height /( float ) height );
       
        // wyświetlenie obrazu zawartego w mapie pikselowej
        glDrawPixels( width, height, format, type, pixels );
       
        // dodanie danych do bufora akumulacyjnego
        glAccum( GL_ACCUM, filter[ dir_w[ i ] + dir_h[ i ] * whmax ] );
    }
   
    // obszar renderingu - całe okno
    glViewport( 0, 0, win_width, win_height );
   
    // przeniesienie danych z bufora akumulacyjnego do bufora koloru
    glAccum( GL_RETURN, scale );
   
    // skierowanie poleceń do wykonania
    glFinish();
   
    // 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
    gluOrtho2D( 0.0, Width, 0.0, Height );
   
    // generowanie sceny 3D
    Display();
}

// obsługa menu podręcznego

void Menu( int value )
{
    // wskaźniki na maski poszczególnych filtrów splotowych
    GLfloat * filters[ 39 ] =
    {
        neutral, average, lp1, lp2, lp3, gauss, mean_removal, hp1, hp2,
        hp3, horizontal, vertical, horizontal_vertical, gradient_east,
        gradient_south_east, gradient_south, gradient_south_west,
        gradient_west, gradient_north_west, gradient_north,
        gradient_north_east, emboss_east, emboss_south_east, emboss_south,
        emboss_south_west, emboss_west, emboss_north_west, emboss_north,
        emboss_north_east, laplacian_lapl1, laplacian_lapl2,
        laplacian_lapl3, laplacian_diagonal, laplacian_horizontal,
        laplacian_vertical, sobel_horizontal, sobel_vertical,
        prewitt_horizontal, prewitt_vertical
    };
   
    // stałe określające kolejność nakładania kolejnych obrazów
    enum
    {
        HW, HW_REV, CROSS, CROSS_REV
    };
   
    // oznaczenia numerów kolejnych elementów maski filtra
    // | 00 10 20 |
    // | 10 11 21 |
    // | 20 21 22 |
   
    // kolejność - poziome
    const int hw_h[ 9 ] =
    {
        0, 0, 0, 1, 1, 1, 2, 2, 2
    };
    const int hw_w[ 9 ] =
    {
        0, 1, 2, 0, 1, 2, 0, 1, 2
    };
   
    // kolejność - poziome odwrócone
    const int hw_rev_h[ 9 ] =
    {
        2, 2, 2, 1, 1, 1, 0, 0, 0
    };
    const int hw_rev_w[ 9 ] =
    {
        2, 1, 0, 2, 1, 0, 2, 1, 0
    };
   
    // kolejność - skrajne elementy przekątne oraz krzyż (odwrócone)
    const int cross_rev_h[ 9 ] =
    {
        2, 0, 0, 2, 1, 0, 1, 2, 1
    };
    const int cross_rev_w[ 9 ] =
    {
        0, 0, 2, 2, 0, 1, 2, 1, 1
    };
   
    // kolejność - skrajne elementy przekątne oraz krzyż
    const int cross_h[ 9 ] =
    {
        0, 2, 2, 0, 1, 2, 1, 0, 1
    };
    const int cross_w[ 9 ] =
    {
        0, 0, 2, 2, 0, 1, 2, 1, 1
    };
   
    // kolejności nakładania obrazów dla wybranych filtrów splotowych
    int directions[ 39 ] =
    {
        CROSS, CROSS, CROSS, // neutral, average, lp1,
        CROSS, CROSS, CROSS, // lp2, lp3, gauss,
        CROSS, CROSS, CROSS, // mean_removal, hp1, hp2,
        CROSS, CROSS, CROSS, // hp3, horizontal, vertical,
        CROSS, CROSS, CROSS, // horizontal_vertical, gradient_east,
        // gradient_south_east,
        CROSS, CROSS, CROSS, // gradient_south, gradient_south_west,
        // gradient_west,
        CROSS, CROSS, CROSS, // gradient_north_west, gradient_north,
        // gradient_north_east,
        HW, CROSS, CROSS, // emboss_east, emboss_south_east, emboss_south,
        CROSS_REV, HW_REV, CROSS, // emboss_south_west, emboss_west,
        // emboss_north_west,
        CROSS, CROSS_REV, CROSS, // emboss_north, emboss_north_east, laplacian_lapl1,
        CROSS, CROSS, CROSS, // laplacian_lapl2, laplacian_lapl3,
        // laplacian_diagonal,
        CROSS, CROSS, CROSS, // laplacian_horizontal, laplacian_vertical,
        // sobel_horizontal,
        CROSS, CROSS, CROSS // sobel_vertical, prewitt_horizontal,
        // prewitt_vertical
    };
   
    switch( value )
    {
        // zapis pliku w formacie TARGA
    case SAVE_FILE:
        SaveTARGA( format );
        break;
       
        // filtry splotowe
    case NONE:
    case AVERAGE:
    case LP1:
    case LP2:
    case LP3:
    case GAUSS:
    case MEAN_REMOVAL:
    case HP1:
    case HP2:
    case HP3:
    case HORIZONTAL:
    case VERTICAL:
    case HORIZONTAL_VERTICAL:
    case GRADIENT_EAST:
    case GRADIENT_SOUTH_EAST:
    case GRADIENT_SOUTH:
    case GRADIENT_SOUTH_WEST:
    case GRADIENT_WEST:
    case GRADIENT_NORTH_WEST:
    case GRADIENT_NORTH:
    case GRADIENT_NORTH_EAST:
    case EMBOSS_EAST:
    case EMBOSS_SOUTH_EAST:
    case EMBOSS_SOUTH:
    case EMBOSS_SOUTH_WEST:
    case EMBOSS_WEST:
    case EMBOSS_NORTH_WEST:
    case EMBOSS_NORTH:
    case EMBOSS_NORTH_EAST:
    case LAPLACIAN_LAPL1:
    case LAPLACIAN_LAPL2:
    case LAPLACIAN_LAPL3:
    case LAPLACIAN_DIAGONAL:
    case LAPLACIAN_HORIZONTAL:
    case LAPLACIAN_VERTICAL:
    case SOBEL_HORIZONTAL:
    case SOBEL_VERTICAL:
    case PREWITT_HORIZONTAL:
    case PREWITT_VERTICAL:
       
        // kolejność nakładania obrazów
        switch( directions[ value ] )
        {
        case CROSS:
            memcpy( dir_h, cross_h, 9 * sizeof( int ) );
            memcpy( dir_w, cross_w, 9 * sizeof( int ) );
            break;
        case CROSS_REV:
            memcpy( dir_h, cross_rev_h, 9 * sizeof( int ) );
            memcpy( dir_w, cross_rev_w, 9 * sizeof( int ) );
            break;
        case HW:
            memcpy( dir_h, hw_h, 9 * sizeof( int ) );
            memcpy( dir_w, hw_w, 9 * sizeof( int ) );
            break;
        case HW_REV:
            memcpy( dir_h, hw_rev_h, 9 * sizeof( int ) );
            memcpy( dir_w, hw_rev_w, 9 * sizeof( int ) );
            break;
        }
       
        // przygotowanie maski filtra
        memcpy( filter, filters[ value ], 9 * sizeof( GLfloat ) );
       
        // wyszukanie elemenu maksymalnego maski splotu filtra
        GLfloat absmax = fabs( filter[ 0 ] );
        for( int i = 1; i < 9; i++ )
        if( fabs( filter[ i ] ) > absmax )
             absmax = fabs( filter[ i ] );
       
        // modyfikacja maski splotu filtra
        if( absmax > 1.0 )
        for( int i = 0; i < 9; i++ )
             filter[ i ] /= absmax;
       
        // współczynnik skalowania składowych bufora akumulacyjnego
        if( absmax > 1.0 )
             scale = absmax;
        else
             scale = 1.0;
       
        // wyświetlenie sceny
        Display();
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
        break;
    }
}

int main( int argc, char * argv[] )
{
    // odczyt pliku graficznego TARGA
    LoadTARGA( argc, argv );
   
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_ACCUM );
   
    // rozmiary głównego okna programu - równe wymiarom
    // odczytanego pliku graficznego TARGA
    glutInitWindowSize( width, height );
   
    // utworzenie głównego okna programu - nazwa okna taka
    // sama jak nazwa odczytanego pliku graficznego TARGA
    glutCreateWindow( argv[ 1 ] );
   
    // dołączenie funkcji generującej scenę 3D - obraz zawarty w pliku TARGA
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // sprawdzenie czy dany format pliku TARGA jest obsługiwany
    CheckImageFormat();
   
    // podmenu "Filtry"
    int MenuFilters = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Filtr uśredniający", AVERAGE );
    glutAddMenuEntry( "Filtr LP1", LP1 );
    glutAddMenuEntry( "Filtr LP2", LP2 );
    glutAddMenuEntry( "Filtr LP3", LP3 );
    glutAddMenuEntry( "Filtr Gaussa", GAUSS );
    glutAddMenuEntry( "Filtr usuwający średnią", MEAN_REMOVAL );
    glutAddMenuEntry( "Filtr HP1", HP1 );
    glutAddMenuEntry( "Filtr HP2", HP2 );
    glutAddMenuEntry( "Filtr HP3", HP3 );
    glutAddMenuEntry( "Filtr poziomy", HORIZONTAL );
    glutAddMenuEntry( "Filtr pionowy", VERTICAL );
    glutAddMenuEntry( "Filtr poziomy/pionowy", HORIZONTAL_VERTICAL );
    glutAddMenuEntry( "Filtr gradientowy wschód", GRADIENT_EAST );
    glutAddMenuEntry( "Filtr gradientowy południowy wschód", GRADIENT_SOUTH_EAST );
    glutAddMenuEntry( "Filtr gradientowy południe", GRADIENT_SOUTH );
    glutAddMenuEntry( "Filtr gradientowy południowy zachód", GRADIENT_SOUTH_WEST );
    glutAddMenuEntry( "Filtr gradientowy zachód", GRADIENT_WEST );
    glutAddMenuEntry( "Filtr gradientowy północny zachód", GRADIENT_NORTH_WEST );
    glutAddMenuEntry( "Filtr gradientowy północ", GRADIENT_NORTH );
    glutAddMenuEntry( "Filtr gradientowy północny wschód", GRADIENT_NORTH_EAST );
    glutAddMenuEntry( "Filtr uwypuklający wschód", EMBOSS_EAST );
    glutAddMenuEntry( "Filtr uwypuklający południowy wschód", EMBOSS_SOUTH_EAST );
    glutAddMenuEntry( "Filtr uwypuklający południe", EMBOSS_SOUTH );
    glutAddMenuEntry( "Filtr uwypuklający południowy zachód", EMBOSS_SOUTH_WEST );
    glutAddMenuEntry( "Filtr uwypuklający zachód", EMBOSS_WEST );
    glutAddMenuEntry( "Filtr uwypuklający północny zachód", EMBOSS_NORTH_WEST );
    glutAddMenuEntry( "Filtr uwypuklający północ", EMBOSS_NORTH );
    glutAddMenuEntry( "Filtr uwypuklający północny wschód", EMBOSS_NORTH_EAST );
    glutAddMenuEntry( "Filtr Laplace'a LAPL1", LAPLACIAN_LAPL1 );
    glutAddMenuEntry( "Filtr Laplace'a LAPL2", LAPLACIAN_LAPL2 );
    glutAddMenuEntry( "Filtr Laplace'a LAPL3", LAPLACIAN_LAPL3 );
    glutAddMenuEntry( "Filtr Laplace'a skośny", LAPLACIAN_DIAGONAL );
    #else
   
    glutAddMenuEntry( "Filtr uśredniający", AVERAGE );
    glutAddMenuEntry( "Filtr LP1", LP1 );
    glutAddMenuEntry( "Filtr LP2", LP2 );
    glutAddMenuEntry( "Filtr LP3", LP3 );
    glutAddMenuEntry( "Filtr Gaussa", GAUSS );
    glutAddMenuEntry( "Filtr usuwajacy srednia", MEAN_REMOVAL );
    glutAddMenuEntry( "Filtr HP1", HP1 );
    glutAddMenuEntry( "Filtr HP2", HP2 );
    glutAddMenuEntry( "Filtr HP3", HP3 );
    glutAddMenuEntry( "Filtr poziomy", HORIZONTAL );
    glutAddMenuEntry( "Filtr pionowy", VERTICAL );
    glutAddMenuEntry( "Filtr poziomy/pionowy", HORIZONTAL_VERTICAL );
    glutAddMenuEntry( "Filtr gradientowy wschod", GRADIENT_EAST );
    glutAddMenuEntry( "Filtr gradientowy poludniowy wschod", GRADIENT_SOUTH_EAST );
    glutAddMenuEntry( "Filtr gradientowy poludnie", GRADIENT_SOUTH );
    glutAddMenuEntry( "Filtr gradientowy poludniowy zachod", GRADIENT_SOUTH_WEST );
    glutAddMenuEntry( "Filtr gradientowy zachod", GRADIENT_WEST );
    glutAddMenuEntry( "Filtr gradientowy polnocny zachod", GRADIENT_NORTH_WEST );
    glutAddMenuEntry( "Filtr gradientowy polnoc", GRADIENT_NORTH );
    glutAddMenuEntry( "Filtr gradientowy polnocny wschod", GRADIENT_NORTH_EAST );
    glutAddMenuEntry( "Filtr uwypuklajacy wschod", EMBOSS_EAST );
    glutAddMenuEntry( "Filtr uwypuklajacy poludniowy wschod", EMBOSS_SOUTH_EAST );
    glutAddMenuEntry( "Filtr uwypuklajacy poludnie", EMBOSS_SOUTH );
    glutAddMenuEntry( "Filtr uwypuklajacy poludniowy zachod", EMBOSS_SOUTH_WEST );
    glutAddMenuEntry( "Filtr uwypuklajacy zachod", EMBOSS_WEST );
    glutAddMenuEntry( "Filtr uwypuklajacy polnocny zachod", EMBOSS_NORTH_WEST );
    glutAddMenuEntry( "Filtr uwypuklajacy polnoc", EMBOSS_NORTH );
    glutAddMenuEntry( "Filtr uwypuklajacy polnocny wschod", EMBOSS_NORTH_EAST );
    glutAddMenuEntry( "Filtr Laplace'a LAPL1", LAPLACIAN_LAPL1 );
    glutAddMenuEntry( "Filtr Laplace'a LAPL2", LAPLACIAN_LAPL2 );
    glutAddMenuEntry( "Filtr Laplace'a LAPL3", LAPLACIAN_LAPL3 );
    glutAddMenuEntry( "Filtr Laplace'a skosny", LAPLACIAN_DIAGONAL );
    #endif
   
    glutAddMenuEntry( "Filtr Laplace'a poziomy", LAPLACIAN_HORIZONTAL );
    glutAddMenuEntry( "Filtr Laplace'a pionowy", LAPLACIAN_VERTICAL );
    glutAddMenuEntry( "Filtr poziomy Sobela", SOBEL_HORIZONTAL );
    glutAddMenuEntry( "Filtr pionowy Sobela", SOBEL_VERTICAL );
    glutAddMenuEntry( "Filtr poziomy Prewitta", PREWITT_HORIZONTAL );
    glutAddMenuEntry( "Filtr pionowy Prewitta", PREWITT_VERTICAL );
    glutAddMenuEntry( "Brak", NONE );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
    glutAddSubMenu( "Filtry", MenuFilters );
    glutAddMenuEntry( "Zapisz", SAVE_FILE );
   
    #ifdef WIN32
   
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    //PrepareFilters ();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokumentNastępny dokument
Bufor szablonowyAntyaliasing