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).
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:
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:
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:
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:
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:
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:
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:
Te zmienne stanu w wersji 1.2 biblioteki OpenGL zostały zastąpione poniższymi:
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:
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:
Wspomniana funkcja:
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
#include <GL/glut.h>
#include <GL/glext.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
enum
{
SMOOTH,
CLEAR_POINTS,
EXIT
};
std::vector < GLint > vertex_x;
std::vector < GLint > vertex_y;
GLfloat point_size = 10.0;
GLboolean point_smooth = true;
GLint point_smooth_hint = GL_DONT_CARE;
void DrawString( int x, int y, char * string )
{
glRasterPos2i( x, y );
int len = strlen( string );
for( int i = 0; i < len; i++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
}
void DisplayScene()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3f( 1.0, 0.0, 0.0 );
glPointSize( point_size );
glHint( GL_POINT_SMOOTH_HINT, point_smooth_hint );
if( point_smooth )
{
glEnable( GL_POINT_SMOOTH );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
}
else
{
glDisable( GL_POINT_SMOOTH );
glDisable( GL_BLEND );
}
glBegin( GL_POINTS );
for( unsigned int i = 0; i < vertex_x.size(); i++ )
glVertex2i( vertex_x[ i ], vertex_y[ i ] );
glEnd();
char string[ 100 ];
GLfloat tab[ 2 ];
glColor3f( 0.0, 0.0, 0.0 );
glGetFloatv( GL_SMOOTH_POINT_SIZE_RANGE, tab );
sprintf( string, "GL_SMOOTH_POINT_SIZE_RANGE: %f %f", tab[ 0 ], tab[ 1 ] );
DrawString( 0, 1, string );
glGetFloatv( GL_SMOOTH_POINT_SIZE_GRANULARITY, tab );
sprintf( string, "GL_SMOOTH_POINT_SIZE_GRANULARITY: %f", tab[ 0 ] );
DrawString( 0, 16, string );
glGetFloatv( GL_ALIASED_POINT_SIZE_RANGE, tab );
sprintf( string, "GL_ALIASED_POINT_SIZE_RANGE: %f %f", tab[ 0 ], tab[ 1 ] );
DrawString( 0, 31, string );
#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
glGetFloatv( GL_POINT_SIZE, tab );
sprintf( string, "GL_POINT_SIZE: %f", tab[ 0 ] );
DrawString( 0, 61, string );
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 );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0, width, 0, height );
DisplayScene();
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN )
{
vertex_x.insert( vertex_x.end(), x );
vertex_y.insert( vertex_y.end(), glutGet( GLUT_WINDOW_HEIGHT ) - y );
DisplayScene();
}
}
void Keyboard( unsigned char key, int x, int y )
{
if( key == '+' )
point_size += 0.25;
else
if( key == '-' && point_size > 0 )
point_size -= 0.25;
DisplayScene();
}
void Menu( int value )
{
switch( value )
{
case GL_FASTEST:
point_smooth_hint = GL_FASTEST;
DisplayScene();
break;
case GL_NICEST:
point_smooth_hint = GL_NICEST;
DisplayScene();
break;
case GL_DONT_CARE:
point_smooth_hint = GL_DONT_CARE;
DisplayScene();
break;
case SMOOTH:
point_smooth = !point_smooth;
DisplayScene();
break;
case CLEAR_POINTS:
vertex_x.erase( vertex_x.begin(), vertex_x.end() );
vertex_y.erase( vertex_y.begin(), vertex_y.end() );
DisplayScene();
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Antyaliasing punktów" );
#else
glutCreateWindow( "Antyaliasing punktow" );
#endif
glutDisplayFunc( DisplayScene );
glutReshapeFunc( Reshape );
glutMouseFunc( MouseButton );
glutKeyboardFunc( Keyboard );
int MenuHint = glutCreateMenu( Menu );
glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
glutAddMenuEntry( "GL_NICEST", GL_NICEST );
glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
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
glutAttachMenu( GLUT_RIGHT_BUTTON );
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.
Plik antyaliasing_linii.cpp
#include <GL/glut.h>
#include <GL/glext.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
enum
{
STIPPLE_1111111111111111,
STIPPLE_0000000011111111,
STIPPLE_1111111100000000,
STIPPLE_0000111100001111,
STIPPLE_1111000011110000,
STIPPLE_0011001100110011,
STIPPLE_1100110011001100,
SMOOTH,
CLEAR_LINES,
EXIT
};
std::vector < GLint > vertex_x;
std::vector < GLint > vertex_y;
std::vector < GLfloat > lines_width;
GLfloat line_width = 10.0;
std::vector < GLfloat > lines_stipple;
GLushort line_stipple = 0xFFFF;
int button_state = GLUT_UP;
GLboolean line_smooth = true;
GLint line_smooth_hint = GL_DONT_CARE;
void DrawString( int x, int y, char * string )
{
glRasterPos2i( x, y );
int len = strlen( string );
for( int i = 0; i < len; i++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
}
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3f( 1.0, 0.0, 0.0 );
glEnable( GL_LINE_STIPPLE );
if( line_smooth )
{
glEnable( GL_LINE_SMOOTH );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
}
else
{
glDisable( GL_LINE_SMOOTH );
glDisable( GL_BLEND );
}
glHint( GL_LINE_SMOOTH_HINT, line_smooth_hint );
for( unsigned int i = 0; i < vertex_x.size(); i += 2 )
{
glLineWidth( lines_width[ i / 2 ] );
glLineStipple( 1, lines_stipple[ i / 2 ] );
glBegin( GL_LINES );
glVertex2i( vertex_x[ i ], vertex_y[ i ] );
glVertex2i( vertex_x[ i + 1 ], vertex_y[ i + 1 ] );
glEnd();
}
char string[ 100 ];
GLfloat tab[ 2 ];
glColor3f( 0.0, 0.0, 0.0 );
glGetFloatv( GL_SMOOTH_LINE_WIDTH_RANGE, tab );
sprintf( string, "GL_SMOOTH_LINE_WIDTH_RANGE: %f %f", tab[ 0 ], tab[ 1 ] );
DrawString( 0, 1, string );
glGetFloatv( GL_SMOOTH_LINE_WIDTH_GRANULARITY, tab );
sprintf( string, "GL_SMOOTH_LINE_WIDTH_GRANULARITY: %f", tab[ 0 ] );
DrawString( 0, 16, string );
glGetFloatv( GL_ALIASED_LINE_WIDTH_RANGE, tab );
sprintf( string, "GL_ALIASED_LINE_WIDTH_RANGE: %f %f", tab[ 0 ], tab[ 1 ] );
DrawString( 0, 31, string );
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 );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0, width, 0, height );
Display();
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
if( state == GLUT_DOWN && button_state == GLUT_UP )
{
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 );
lines_width.insert( lines_width.end(), line_width );
lines_stipple.insert( lines_stipple.end(), line_stipple );
button_state = GLUT_DOWN;
Display();
}
else
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;
button_state = GLUT_UP;
Display();
}
}
}
void MouseMotion( int x, int y )
{
if( button_state == GLUT_DOWN )
{
vertex_x[ vertex_x.size() - 1 ] = x;
vertex_y[ vertex_y.size() - 1 ] = glutGet( GLUT_WINDOW_HEIGHT ) - y;
Display();
}
}
void Keyboard( unsigned char key, int x, int y )
{
if( key == '+' )
line_width += 0.25;
else
if( key == '-' && line_width > 0 )
line_width -= 0.25;
Display();
}
void Menu( int value )
{
switch( value )
{
case GL_FASTEST:
line_smooth_hint = GL_FASTEST;
Display();
break;
case GL_NICEST:
line_smooth_hint = GL_NICEST;
Display();
break;
case GL_DONT_CARE:
line_smooth_hint = GL_DONT_CARE;
Display();
break;
case SMOOTH:
line_smooth = !line_smooth;
Display();
break;
case STIPPLE_1111111111111111:
line_stipple = 0xFFFF;
Display();
break;
case STIPPLE_0000000011111111:
line_stipple = 0x00FF;
Display();
break;
case STIPPLE_1111111100000000:
line_stipple = 0xFF00;
Display();
break;
case STIPPLE_0000111100001111:
line_stipple = 0x0F0F;
Display();
break;
case STIPPLE_1111000011110000:
line_stipple = 0xF0F0;
Display();
break;
case STIPPLE_0011001100110011:
line_stipple = 0x3333;
Display();
break;
case STIPPLE_1100110011001100:
line_stipple = 0xCCCC;
Display();
break;
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;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( 500, 500 );
glutCreateWindow( "Antyaliasing linii" );
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
glutKeyboardFunc( Keyboard );
int MenuHint = glutCreateMenu( Menu );
glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
glutAddMenuEntry( "GL_NICEST", GL_NICEST );
glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
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 );
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
glutAttachMenu( GLUT_RIGHT_BUTTON );
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
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <algorithm>
enum
{
SMOOTH,
FULL_WINDOW,
ASPECT_1_1,
EXIT
};
int aspect = FULL_WINDOW;
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;
const int polygon_count = 50;
GLboolean polygon_smooth = true;
GLint polygon_smooth_hint = GL_DONT_CARE;
struct Triangle
{
GLfloat v1[ 3 ];
GLfloat v2[ 3 ];
GLfloat v3[ 3 ];
GLfloat c1[ 3 ];
GLfloat c2[ 3 ];
GLfloat c3[ 3 ];
};
std::vector < Triangle > Polygons;
bool operator <( const Triangle & t1, const Triangle & t2 )
{
return t1.v1[ 2 ] < t2.v1[ 2 ];
}
void DrawString( GLfloat x, GLfloat y, char * string )
{
glRasterPos2f( x, y );
int len = strlen( string );
for( int i = 0; i < len; i++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
}
void Display()
{
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -( near + far ) / 2 );
if( polygon_smooth )
{
GLint alpha;
glGetIntegerv( GL_ALPHA_BITS, & alpha );
if( alpha != 0 )
glClearColor( 0.0, 0.0, 0.0, 0.0 );
else
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glDisable( GL_DEPTH_TEST );
glEnable( GL_POLYGON_SMOOTH );
glEnable( GL_BLEND );
if( alpha != 0 )
glBlendFunc( GL_SRC_ALPHA_SATURATE, GL_ONE );
else
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glHint( GL_POLYGON_SMOOTH_HINT, polygon_smooth_hint );
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
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();
if( alpha != 0 )
{
glColor4f( 1.0, 1.0, 1.0, 1.0 );
glEnable( GL_DEPTH );
glDepthFunc( GL_EQUAL );
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();
glDepthFunc( GL_LESS );
}
}
else
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glEnable( GL_DEPTH_TEST );
glDisable( GL_POLYGON_SMOOTH );
glDisable( GL_BLEND );
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();
}
glLoadIdentity();
glTranslatef( 0, 0, - near );
glDisable( GL_BLEND );
char string[ 100 ];
GLfloat tab[ 2 ];
glColor3f( 0.0, 0.0, 0.0 );
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 );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left, right, bottom * height / width, top * height / width, near, far );
else
if( width >= height && height > 0 )
glFrustum( left * width / height, right * width / height, bottom, top, near, far );
}
else
glFrustum( left, right, bottom, top, near, far );
Display();
}
void Menu( int value )
{
switch( value )
{
case GL_FASTEST:
polygon_smooth_hint = GL_FASTEST;
Display();
break;
case GL_NICEST:
polygon_smooth_hint = GL_NICEST;
Display();
break;
case GL_DONT_CARE:
polygon_smooth_hint = GL_DONT_CARE;
Display();
break;
case SMOOTH:
polygon_smooth = !polygon_smooth;
Display();
break;
case FULL_WINDOW:
aspect = FULL_WINDOW;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case ASPECT_1_1:
aspect = ASPECT_1_1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case EXIT:
exit( 0 );
}
}
void GeneratePolygons()
{
srand( time( NULL ) );
for( int i = 0; i < polygon_count; i++ )
{
Triangle t;
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 ];
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 );
Polygons.insert( Polygons.end(), t );
}
std::sort( Polygons.begin(), Polygons.end() );
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_ALPHA );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Antyaliasing wielokątów" );
#else
glutCreateWindow( "Antyaliasing wielokatow" );
#endif
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
int MenuHint = glutCreateMenu( Menu );
glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
glutAddMenuEntry( "GL_NICEST", GL_NICEST );
glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
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 );
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
glutAttachMenu( GLUT_RIGHT_BUTTON );
GeneratePolygons();
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
#include <GL/glut.h>
#include <GL/glext.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "colors.h"
enum
{
SMOOTH_ACCUM_BUFFER_3,
SMOOTH_ACCUM_BUFFER_5,
SMOOTH_ACCUM_BUFFER_7,
SMOOTH_MULTISAMPLE,
SMOOTH_NONE,
FULL_WINDOW,
ASPECT_1_1,
EXIT
};
int aspect = FULL_WINDOW;
#ifdef near
#undef near
#endif
#ifdef far
#undef far
#endif
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;
GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;
int button_state = GLUT_UP;
int button_x, button_y;
int scene_smooth = SMOOTH_ACCUM_BUFFER_3;
int smooth_quality = 1;
int frames = 0;
long start_time = 0;
char time_string[ 100 ] = "FPS:";
void DrawString( GLfloat x, GLfloat y, char * string )
{
glRasterPos2f( x, y );
int len = strlen( string );
for( int i = 0; i < len; i++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
}
void DisplayScene()
{
if( !frames++ )
start_time = clock();
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glEnable( GL_DEPTH_TEST );
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
glEnable( GL_NORMALIZE );
int width = glutGet( GLUT_WINDOW_WIDTH );
int height = glutGet( GLUT_WINDOW_HEIGHT );
if( scene_smooth == SMOOTH_MULTISAMPLE || scene_smooth == SMOOTH_NONE )
{
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left, right, bottom * height / width, top * height / width, near, far );
else
if( width >= height && height > 0 )
glFrustum( left * width / height, right * width / height, bottom, top, near, far );
}
else
glFrustum( left, right, bottom, top, near, far );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -( near + far ) / 2 );
glRotatef( rotatex, 1.0, 0, 0 );
glRotatef( rotatey, 0, 1.0, 0 );
if( scene_smooth == SMOOTH_MULTISAMPLE )
glEnable( GL_MULTISAMPLE );
glColor4fv( Green );
glutSolidTorus( 0.5, 1.5, 50, 40 );
if( scene_smooth == SMOOTH_MULTISAMPLE )
glDisable( GL_MULTISAMPLE );
}
else
{
int min = - smooth_quality;
int max = - min + 1;
GLfloat count =( max - min ) *( max - min );
GLfloat scale = 0.9 /( GLfloat ) smooth_quality;
GLint viewport[ 4 ];
glGetIntegerv( GL_VIEWPORT, viewport );
GLfloat pointx =( right - left ) /( GLfloat ) viewport[ 2 ];
GLfloat pointy =( top - bottom ) /( GLfloat ) viewport[ 3 ];
glClear( GL_ACCUM_BUFFER_BIT );
for( int y = min; y < max; y++ )
for( int x = min; x < max; x++ )
{
GLfloat dx = pointx * scale * x;
GLfloat dy = pointy * scale * y;
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left + dx, right + dx,
bottom * height / width + dy, top * height / width + dy,
near, far );
else
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 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -( near + far ) / 2 );
glRotatef( rotatex, 1.0, 0, 0 );
glRotatef( rotatey, 0, 1.0, 0 );
glColor4fv( Green );
glutSolidTorus( 0.5, 1.5, 50, 40 );
glAccum( GL_ACCUM, 1.0 / count );
}
glAccum( GL_RETURN, 1.0 );
}
glDisable( GL_LIGHTING );
glDisable( GL_DEPTH_TEST );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, - near );
glColor4fv( Black );
if( frames == 500 )
{
frames = 0;
sprintf( time_string, "FPS: %i",( int )( 500 * CLOCKS_PER_SEC /( float )( clock() - start_time ) ) );
}
DrawString( left + 0.02, bottom + 0.02, time_string );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
DisplayScene();
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
button_state = state;
if( state == GLUT_DOWN )
{
button_x = x;
button_y = y;
}
}
}
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();
}
}
void Menu( int value )
{
switch( value )
{
case SMOOTH_ACCUM_BUFFER_3:
scene_smooth = SMOOTH_ACCUM_BUFFER_3;
smooth_quality = 1;
sprintf( time_string, "FPS:" );
DisplayScene();
break;
case SMOOTH_ACCUM_BUFFER_5:
scene_smooth = SMOOTH_ACCUM_BUFFER_5;
smooth_quality = 2;
sprintf( time_string, "FPS:" );
DisplayScene();
break;
case SMOOTH_ACCUM_BUFFER_7:
scene_smooth = SMOOTH_ACCUM_BUFFER_7;
smooth_quality = 3;
sprintf( time_string, "FPS:" );
DisplayScene();
break;
case SMOOTH_MULTISAMPLE:
scene_smooth = SMOOTH_MULTISAMPLE;
DisplayScene();
break;
case SMOOTH_NONE:
scene_smooth = SMOOTH_NONE;
DisplayScene();
break;
case FULL_WINDOW:
aspect = FULL_WINDOW;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case ASPECT_1_1:
aspect = ASPECT_1_1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH |
GLUT_ACCUM | GLUT_MULTISAMPLE );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Antyaliasing pełnoekranowy" );
#else
glutCreateWindow( "Antyaliasing pelnoekranowy" );
#endif
glutDisplayFunc( DisplayScene );
glutReshapeFunc( Reshape );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
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
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 );
glutCreateMenu( Menu );
glutAddSubMenu( "Antyaliasing", MenuSmooth );
#ifdef WIN32
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutIdleFunc( DisplayScene );
glutMainLoop();
return 0;
}