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

Krzywe i powierzchnie Beziera

[lekcja] Rozdział 26. Ewaluacja krzywych i powierzchni Béziera, pobieranie danych map; dwa programy przykładowe.
Poza opisanymi i wielokrotnie wykorzystanymi prymitywami graficznymi biblioteka OpenGL dysponuje także alternatywnymi mechanizmami odwzorowania krzywych i powierzchni w postaci krzywych i powierzchni B´eziera.
Krzywe B´eziera zostały opracowane w latach 60 ubiegłego wieku niezależnie przez Pierre B´eziera, francuskiego inżyniera pracującego w firmie Renault oraz Paula de Casteljau pracującego dla konkurencyjnej firmy Citro¨en.

Krzywe B´eziera

Krzywe B´eziera są krzywymi parametrycznymi co oznacza, że każda współrzędna P (x, y, z) punktu krzywej określona jest funkcją parametryczną parametru u z przedziału [0, 1]:
Kształt krzywej określają tzw. punkty kontrolne, których ilość określa jednocześnie stopień krzywej. W praktycznych zastosowaniach wystarczają krzywe trzeciego stopnia, oparte na czterech punktach kontrolnych. Niezależnie od ilości użytych punktów kontrolnych krzywa B´eziera zawsze przechodzi przez pierwszy i ostatni punkt kontrolny.
Przykładowo krzywą B´eziera dla czterech punktów kontrolnych o współrzędnych (x0, y0, z0), (x1 , y1, z1), (x2, y2, z2) i (x3, y3 , z3 ), określają następujące równania:

Tworzenie ewaluatorów

Do generowania krzywych i powierzchni B´eziera biblioteka OpenGL używa tzw. ewaluatorów (ang. evaluators). Ewaluator jednowymiarowy definiują funkcje z grupy glMap1:
C/C++
void glMap1d( GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble * points )
void glMap1f( GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat * points )
Parametr target określa rodzaj wartości generowanych przez ewaluator. Dopuszczalne są następujące wartości tego parametru:
  • GL_MAP1_VERTEX_3 - współrzędne (x, y, z) punktów krzywej,
  • GL_MAP1_VERTEX_4 - współrzędne (x, y, z, w) punktów krzywej,
  • GL_MAP1_INDEX - numery indeksów kolorów,
  • GL_MAP1_COLOR_4 - składowe RGBA kolorów,
  • GL_MAP1_NORMAL - współrzędne (x, y, z) wektorów normalnych,
  • GL_MAP1_TEXTURE_COORD_1 - współrzędne s tekstury,
  • GL_MAP1_TEXTURE_COORD_2 - współrzędne s i t tekstury,
  • GL_MAP1_TEXTURE_COORD_3 - współrzędne s, t i r tekstury,
  • GL_MAP1_TEXTURE_COORD_4 - współrzędne s, t, r i q tekstury.
Współrzędne tekstur generowane są wyłącznie dla pierwszej jednostki teksturującej (GL_TEXTURE0). Jeżeli aktywną jednostką teksturującą (GL_ACTIVE_TEXTURE) nie jest GL_TEXTURE0 zostanie wygenerowany błąd GL_INVALID_OPERATION.
Parametry u1 i u2 określają przedział wartości parametru u funkcji parametrycznej P (u). Typowo wykorzystywany jest cały przedział wartości parametru u czyli [0, 1].
Parametr stride określa ilość wartości zmiennoprzecinkowych (odpowiednio GLfloat lub GLdouble) zawartych pomiędzy danymi poszczególnych punktów kontrolnych. Umożliwia to umieszczenie w tablicy points innych danych poza współrzędnymi punktów kontrolnych.
Kolejny parametr order określa ilość punktów kontrolnych krzywej zawartych w tablicy points. Maksymalna obsługiwana ilość punktów kontrolnych zależy od implementacji biblioteki OpenGL, ale nie może być mniejsza niż 8. Wielkość tę zawiera zmienna stanu GL_MAX_EVAL_ORDER.
Jak łatwo zauważyć ewaluator pozwala na wygenerowanie czterech rodzajów wartości. Są to współrzędne punktów krzywej, składowe (lub indeksy) kolorów, współrzędne wektorów normalnych oraz współrzędne tekstury. Przed wywołaniem funkcji ewaluatora trzeba uaktywnić wybrany ewaluator używając funkcji glEnable z parametrem odpowiadającym parametrowi target funkcji z grupy glMap1. Jeżeli jednocześnie aktywne będzie więcej niż jeden ewaluator należący do tej samej grupy, biblioteka OpenGL wygeneruje wartości ewaluatora zawierającego większą ilość współrzędnych.

Renderowanie krzywej

Biblioteka OpenGL udostępnia kilka sposobów wykorzystania danych wygenerowanych przez wybrany ewaluator jednowymiarowy. Najbardziej elementarną metodą jest użycie funkcji z grupy glEvalCoord1:
C/C++
void glEvalCoord1d( GLdouble u )
void glEvalCoord1f( GLfloat u )
void glEvalCoord1dv( const GLdouble * u )
void glEvalCoord1fv( const GLfloat * u )
które zwracają wartości wygenerowane przez ewaluator dla danego parametru u funkcji parametrycznej P (u) (przypomnijmy, że typowo dziedziną tej funkcji są liczby z przedziału [0, 1]).
Wywołanie funkcji z grupy glEvalCoord1 jest równoważne odpowiednim wywołaniom funkcji z grup: glVertex, glColor, glIndex, glNormal i glTexCoord, przy czym jeżeli aktywnych jest jednocześnie kilka ewaluatorów zawsze jako ostatni będą generowane współrzędne wierzchołków. Jak się Czytelnik domyśla użycie funkcji z grupy glEvalCoord1 nastąpi najczęściej pomiędzy wywołaniami funkcji glBegin/glEnd.
Jeżeli nie chcemy samodzielnie renderować krzywej z danych wygenerowanych przez ewaluator trzeba skorzystać z funkcji z grupy glMapGrid1:
C/C++
void glMapGrid1d( GLint un, GLdouble u1, GLdouble u2 )
void glMapGrid1f( GLint un, GLfloat u1, GLfloat u2 )
których zadaniem jest utworzenie mapy (siatki) punktów odwzorowujących krzywą. Ilość generowanych punktów określa parametr un, a u1 i u2 wyznaczają przedział wartości funkcji parametrycznej P (u), dla których generowane są punkty mapy.
Tak uzyskane punkty mapy można renderować korzystając z funkcji glEvalMesh1:
C/C++
void glEvalMesh1( GLenum mode, GLint i1, GLint i2 )
gdzie parametr mode określa tryb renderingu (możliwe są GL_POINT - punkty i GL_LINE - linia), a parametry i1 i i2 określają numery renderowanych punktów mapy.
Alternatywnie można także narysować pojedynczy punkt mapy korzystając z funkcji glEvalPoint1:
C/C++
void glEvalPoint1( GLint i )

Powierzchnie B´eziera

Powierzchnie B´eziera są parametrycznymi określonymi funkcją dwuparametrową P (u, v):
gdzie u i v przyjmują wartości z przedziału [0, 1].
Zasady tworzenia powierzchni B´eziera są identyczne jak tworzenie krzywych. Opisane wyżej funkcje mają swoje odpowiedniki, które mają w nazwie cyfrę „2”, ale oczywiście różnią się także ilością parametrów.

Ewaluatory dwuwymiarowe

Ewaluator dwuwymiarowy definiują funkcje z grupy glMap2:
C/C++
void glMap2d( GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble * points )
void glMap2f( GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat * points )
Parametr target określa rodzaj wartości generowanych przez ewaluator. Dopuszczalne są następujące wartości tego parametru:
  • GL_MAP2_VERTEX_3 - współrzędne (x, y, z) punktów powierzchni,
  • GL_MAP2_VERTEX_4 - współrzędne (x, y, z, w) punktów powierzchni,
  • GL_MAP2_INDEX - numery indeksów kolorów,
  • GL_MAP2_COLOR_4 - składowe RGBA kolorów,
  • GL_MAP2_NORMAL - współrzędne (x, y, z) wektorów normalnych,
  • GL_MAP2_TEXTURE_COORD_1 - współrzędne s tekstury,
  • GL_MAP2_TEXTURE_COORD_2 - współrzędne s i t tekstury,
  • GL_MAP2_TEXTURE_COORD_3 - współrzędne s, t i r tekstury,
  • GL_MAP2_TEXTURE_COORD_4 - współrzędne s, t, r i q tekstury.
Podobnie jak w przypadku ewaluatorów jednowymiarowych Współrzędne tekstur generowane są wyłącznie dla pierwszej jednostki teksturującej (GL_TEXTURE0). Jeżeli aktywną jednostką teksturującą nie jest GL_TEXTURE0 zostanie wygenerowany błąd GL_INVALID_OPERATION.
Parametry u1, u2 oraz v1, v2 określają odpowiednio przedziały wartości parametrów u i v funkcji parametrycznej P (u, v). Typowo wykorzystywany jest cały przedział wartości obu parametrów czyli [0, 1].
Parametry ustride i vstride określają ilości wartości zmiennoprzecinkowych (odpowiednio GLfloat lub GLdouble) zawartych pomiędzy danymi poszczególnych punktów kontrolnych odpowiednio dla współrzędnych u i v. Umożliwia to umieszczenie w tablicy points innych danych poza współrzędnymi punktów kontrolnych.
Kolejne parametry uorder i vorder określają ilości punktów kontrolnych krzywej dla współrzędnych u i v zawartych w tablicy points. Dla każdego z tych parametrów obowiązuje opisane już ograniczenie maksymalnej ilości punktów kontrolnych obsługiwanych przez daną implementację OpenGL, określone w zmiennej stanu GL_MAX_EVAL_ORDER.
Korzystanie z ewaluatorów dwuwymiarowych wymaga ich wcześniejszej aktywacji przy użyciu funkcji glEnable. Podobnie jak w przypadku ewaluatorów jednowymiarowych, jednoczesna aktywacja dwóch lub większej ilości ewaluatorów należących do tej samej grupy, spowoduje, że biblioteka OpenGL wygeneruje wartości ewaluatora zawierającego większą ilość współrzędnych.
Dodajmy, że biblioteka OpenGL umożliwia włączenie automatycznego generowania wektorów normalnych zarówno dla powierzchni jak i krzywych B´eziera. Wymaga to wywołania funkcji glEnable z parametrem GL_AUTO_NORMAL.

Rendering powierzchni

Podobnie jak w przypadku krzywych B´eziera także w przypadku powierzchni biblioteka OpenGL udostępnia kilka sposobów wykorzystania danych wygenerowanych przez wybrany ewaluator dwuwymiarowy. Elementarną metodą jest użycie funkcji z grupy glEvalCoord2:
C/C++
void glEvalCoord2dv( const GLdouble * u )
void glEvalCoord2fv( const GLfloat * u )
void glEvalCoord2d( GLdouble u, GLdouble v )
void glEvalCoord2f( GLfloat u, GLfloat v )
które zwracają wartości wygenerowane przez ewaluator dla danej pary parametrów (u, v) funkcji parametrycznej P (u, v). Tablice wskazywane w parametrze u funkcji glEvalCoord2dv i glEvalCoord2fv zawierają parami wartości u i v.
Ponieważ wywołanie funkcji z grupy glEvalCoord2 jest równoważne odpowiednim wywołaniom funkcji z grup: glVertex, glColor, glIndex, glNormal i glTexCoord (jako ostatnie generowane są współrzędne wierzchołków), ich użycie nastąpi najczęściej pomiędzy funkcjami glBegin/glEnd.
Alternatywnie można utworzyć mapę (siatkę) punktów odwzorowujących powierzchnię B´eziera, co wymaga użycia funkcji z grupy glMapGrid2:
C/C++
void glMapGrid2d( GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2 )
void glMapGrid2f( GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2 )
Parametry un i vn określają ilość generowanych punktów mapy odpowiednio dla parametrów u i v funkcji parametrycznej P (u, v), natomiast pary parametrów u1, u2 i v1, v2 wyznaczają przedziały wartości u oraz v, dla których generowane są punkty mapy.
Uzyskane w powyższy sposób punkty mapy można bezpośrednio renderować korzystając z funkcji glEvalMesh2:
C/C++
void glEvalMesh2( GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2 )
której parametr mode określa tryb renderingu (możliwe są: GL_POINT - punkty, GL_LINE - linia i GL_FILL - wypełnienie), a pary parametrów i1, i2 oraz j1, j2 określają numery renderowanych punktów mapy, odpowiednio dla parametrów u i v funkcji P (u, v).
Można także narysować pojedynczy punkt mapy korzystając z funkcji glEvalPoint2:
C/C++
void glEvalPoint2( GLint i, GLint j )

Pobieranie danych map

Pobranie parametrów odwzorowania (map) określonych funkcjami z grupy glMap1 i glMap2 umożliwiają funkcje glGetMap:
C/C++
void glGetMapdv( GLenum target, GLenum query, GLdouble * v )
void glGetMapfv( GLenum target, GLenum query, GLfloat * v )
void glGetMapiv( GLenum target, GLenum query, GLint * v )
Parametr target określa rodzaj odwzorowania, którego dane będą pobierane. Możliwe są następujące wartości:
  • GL_MAP1_VERTEX_3 - współrzędne (x, y, z) punktów krzywej ewaluatora jednowymiarowego,
  • GL_MAP1_VERTEX_4 - współrzędne (x, y, z, w) punktów krzywej ewaluatora jednowymiarowego,
  • GL_MAP1_INDEX - numery indeksów kolorów ewaluatora jednowymiarowego,
  • GL_MAP1_COLOR_4 - składowe RGBA kolorów ewaluatora jednowymiarowego,
  • GL_MAP1_NORMAL - współrzędne (x, y, z) wektorów normalnych ewaluatora jednowymiarowego,
  • GL_MAP1_TEXTURE_COORD_1 - współrzędne s tekstury ewaluatora jednowymiarowego,
  • GL_MAP1_TEXTURE_COORD_2 - współrzędne s i t tekstury ewaluatora jednowymiarowego,
  • GL_MAP1_TEXTURE_COORD_3 - współrzędne s, t i r tekstury ewaluatora jednowymiarowego,
  • GL_MAP1_TEXTURE_COORD_4 - współrzędne s, t, r i q tekstury ewaluatora jednowymiarowego,
  • GL_MAP2_VERTEX_3 - współrzędne (x, y, z) punktów powierzchni ewaluatora dwuwymiarowego,
  • GL_MAP2_VERTEX_4 - współrzędne (x, y, z, w) punktów powierzchni ewaluatora dwuwymiarowego,
  • GL_MAP2_INDEX - numery indeksów kolorów ewaluatora dwuwymiarowego,
  • GL_MAP2_COLOR_4 - składowe RGBA kolorów ewaluatora dwuwymiarowego,
  • GL_MAP2_NORMAL - współrzędne (x, y, z) wektorów normalnych ewaluatora dwuwymiarowego,
  • GL_MAP2_TEXTURE_COORD_1 - współrzędne s tekstury ewaluatora dwuwymiarowego,
  • GL_MAP2_TEXTURE_COORD_2 - współrzędne s i t tekstury ewaluatora dwuwymiarowego,
  • GL_MAP2_TEXTURE_COORD_3 - współrzędne s, t i r tekstury ewaluatora dwuwymiarowego,
  • GL_MAP2_TEXTURE_COORD_4 - współrzędne s, t, r i q tekstury ewaluatora dwuwymiarowego.
Drugi parametr query określa jakie parametry odwzorowania mają zostać zwrócone w tablicy v. Możliwe są trzy wartości tego parametru:
  • GL_COEFF - punkty kontrolne mapy,
  • GL_ORDER - rząd funkcji obliczeniowej (tablica jednoelementowa dla ewaluatorów jednowymiarowych i dwuwymiarowa dla ewaluatorów dwuwymiarowych),
  • GL_DOMAIN - przedziały wartości (dziedzina) funkcji parametrycznej P (u) lub P (u, v).

Programy przykładowe

Pierwszy program przykładowy (plik krzywa beziera.cpp) przedstawia krzywą B´eziera opartą o cztery punkty kontrolne. Początkowy wygląd krzywej, przedstawiony na rysunku 1, można modyfikować przemieszczając punkty kontrolne. Przykładowy efekt takiej modyfikacji przedstawia rysunek 1. Do przesuwania punktów kontrolnych wykorzystano oczywiście bufor selekcji.
Rysunek 1. Program Krzywa B´eziera - początkowy wygląd krzywej
Rysunek 1. Program Krzywa B´eziera - początkowy wygląd krzywej

Plik krzywa_beziera.cpp

Rysunek 2. Program Krzywa B´eziera - jedna z przykładowych krzywych
Rysunek 2. Program Krzywa B´eziera - jedna z przykładowych krzywych
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 <string.h>
#include "colors.h"

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

enum
{
    EXIT // wyjście
};

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

int button_state = GLUT_UP;

// położenie kursora myszki

int button_x, button_y;

// punkty kontrolne krzywej

GLfloat points[ 12 ] =
{
    50.0, 50.0, 0.0, 50.0, 450.0, 0.0, 450.0, 50.0, 0.0, 450.0, 450.0, 0.0
};

// identyfikatory wyświetlanych obiektów

enum
{
    NONE,
    CURVE,
    POINT_0,
    POINT_1,
    POINT_2,
    POINT_3
};

// identyfikator wybranego obiektu

int select_object = NONE;

// 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 );
   
    // inicjalizacja stosu nazw obiektów
    glInitNames();
   
    // umieszczenie nazwy na stosie nazw, aby nie był on pusty
    glPushName( NONE );
   
    // obiekt CURVE
    glLoadName( CURVE );
   
    // włączenie generowana współrzędnych (x,y,z) punktów krzywej
    glEnable( GL_MAP1_VERTEX_3 );
   
    // ewaluator jednowymiarowy dla czterech punktów kontronych
    glMap1f( GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, points );
   
    // ustawienie grubości linii
    glLineWidth( 2.0 );
   
    // kolor krzywej
    glColor3fv( Black );
   
    // narysowanie krzywej złożonej z 25 odcinków
    glBegin( GL_LINE_STRIP );
    for( int i = 0; i <= 25; i++ )
         glEvalCoord1f(( GLfloat ) i / 25.0 );
   
    glEnd();
   
    // kolor punktów
    glColor3fv( Red );
   
    // obiekt - pierwszy punkt kontrolny
    glLoadName( POINT_0 );
   
    // narysowanie pierwszego punktu kontrolnego
    glRectf( points[ 0 ] - 5, points[ 1 ] - 5, points[ 0 ] + 5, points[ 1 ] + 5 );
   
    // obiekt - drugi punkt kontrolny
    glLoadName( POINT_1 );
   
    // narysowanie drugiego punktu kontrolnego
    glRectf( points[ 3 ] - 5, points[ 4 ] - 5, points[ 3 ] + 5, points[ 4 ] + 5 );
   
    // obiekt - trzeci punkt kontrolny
    glLoadName( POINT_2 );
   
    // narysowanie trzeciego punktu kontrolnego
    glRectf( points[ 6 ] - 5, points[ 7 ] - 5, points[ 6 ] + 5, points[ 7 ] + 5 );
   
    // obiekt - czwarty punkt kontrolny
    glLoadName( POINT_3 );
   
    // narysowanie czwartego punktu kontrolnego
    glRectf( points[ 9 ] - 5, points[ 10 ] - 5, points[ 9 ] + 5, points[ 10 ] + 5 );
   
    // 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 selekcji obietków

void Selection( int x, int y )
{
    // wielkość bufora selekcji
    const int BUFFER_LENGTH = 64;
   
    // bufor selekcji
    GLuint select_buffer[ BUFFER_LENGTH ];
   
    // przygotowanie bufora selekcji
    glSelectBuffer( BUFFER_LENGTH, select_buffer );
   
    // pobranie obszaru rozmiaru renderingu
    int viewport[ 4 ];
    glGetIntegerv( GL_VIEWPORT, viewport );
   
    // szerokość i wysokość obszaru renderingu
    int width = viewport[ 2 ];
    int height = viewport[ 3 ];
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // odłożenie macierzy rzutowania na stos
    glPushMatrix();
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania - jednostkowa kostka dookoła punktu wskaźnika
    // myszy (x,y) rozciągającej się na dwa piksele w poziomie i w pionie
    gluPickMatrix( x, height - y, 2, 2, viewport );
   
    // rzutowanie prostokątne
    gluOrtho2D( 0, width, 0, height );
   
    // włączenie trybu selekcji
    glRenderMode( GL_SELECT );
   
    // generowanie sceny 3D
    Display();
   
    // zliczenie ilości rekordów trafień, powrót do domyślnego trybu renderingu
    GLint hits = glRenderMode( GL_RENDER );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // zdjęcie macierzy rzutowania ze stosu
    glPopMatrix();
   
    // domyślnie w wyniku selekcji nie wybrano żadnego punktu kontrolnego
    select_object = NONE;
   
    // w wyniku selekcji wybrano co najmniej jeden obiekt
    // dla uproszczenia sprawdzamy tylko pierwszy obiekt na stosie
    if( hits > 0 )
    if( select_buffer[ 3 ] > CURVE )
         select_object = select_buffer[ 3 ];
   
}

// obsługa przycisków myszki

void MouseButton( int button, int state, int x, int y )
{
    if( button == GLUT_LEFT_BUTTON )
    {
        // obsługa selekcji obiektów
        Selection( x, y );
        glutPostRedisplay();
       
        // 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 )
{
    // sprawdzenie czy wskaźnik myszy nie znajduje się poza obszarem okna
    if( x > 0 & x < glutGet( GLUT_WINDOW_WIDTH ) & y > 0 & y < glutGet( GLUT_WINDOW_HEIGHT ) )
    if( button_state == GLUT_DOWN )
    {
        // zmiana współrzędnych punktów
        switch( select_object )
        {
        case POINT_0:
            points[ 0 ] += x - button_x;
            points[ 1 ] += button_y - y;
            break;
        case POINT_1:
            points[ 3 ] += x - button_x;
            points[ 4 ] += button_y - y;
            break;
        case POINT_2:
            points[ 6 ] += x - button_x;
            points[ 7 ] += button_y - y;
            break;
        case POINT_3:
            points[ 9 ] += x - button_x;
            points[ 10 ] += button_y - y;
            break;
        }
       
        // aktualizacja położenia myszki
        button_x = x;
        button_y = y;
       
        // wyświetlenie sceny
        glutPostRedisplay();
    }
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // 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( "Krzywa Béziera" );
    #else
   
    glutCreateWindow( "Krzywa Beziera" );
    #endif
   
    // 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 );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // menu główne
    glutCreateMenu( Menu );
    #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 );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Drugi program przykładowy (plik powierzchnia beziera.cpp) rysuje powierzchnię B´eziera opartą na 12 punktach kontrolnych. W odróżnieniu od poprzedniego programu nie ma możliwości bezpośredniej zmiany płożena punktów kontrolnych, ale za to powierzchnię można renderować w różny sposób: w formie punktów siatki, linii siatki (rysunek 3), wypełnienia (rysunek 4) oraz pokrytej teksturą (rysunek 5). W przypadku powierzchni wypełnionej program korzysta dodatkowo z oświetlenia (standardowe źródło światła GL_LIGHT0) oraz znanych już z wcześniejszych programów definicji materiałów.
Szczególną uwagę warto zwrócić na sposób teksturowania powierzchni B´eziera. Wymaga to bowiem zdefiniowania dodatkowej serii punktów kontrolnych, tym razem zawierających współrzędne tekstury. Trzeba także uaktywnić odpowiedni ewaluator dwuwymiarowy.
Rysunek 3. Program Powierzchnia B´eziera - powierzchnia w formie siatki
Rysunek 3. Program Powierzchnia B´eziera - powierzchnia w formie siatki

Plik powierzchnia_beziera.cpp

Rysunek 4. Program Powierzchnia B´eziera - powierzchnia wypełniona (materiał miedź)
Rysunek 4. Program Powierzchnia B´eziera - powierzchnia wypełniona (materiał miedź)
Rysunek 5. Program Powierzchnia B´eziera - powierzchnia z teksturą polskiej flagi
Rysunek 5. Program Powierzchnia B´eziera - powierzchnia z teksturą polskiej flagi
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

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

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

enum
{
    // materiały
    BRASS, // mosiądz
    BRONZE, // brąz
    POLISHED_BRONZE, // polerowany brąz
    CHROME, // chrom
    COPPER, // miedź
    POLISHED_COPPER, // polerowana miedź
    GOLD, // złoto
    POLISHED_GOLD, // polerowane złoto
    PEWTER, // grafit (cyna z ołowiem)
    SILVER, // srebro
    POLISHED_SILVER, // polerowane srebro
    EMERALD, // szmaragd
    JADE, // jadeit
    OBSIDIAN, // obsydian
    PEARL, // perła
    RUBY, // rubin
    TURQUOISE, // turkus
    BLACK_PLASTIC, // czarny plastik
    BLACK_RUBBER, // czarna guma
   
    // obszar renderingu
    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 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;

// współczynnik skalowania

GLfloat scale = 1.0;

// właściwości materiału - domyślnie mosiądz

const GLfloat * ambient = BrassAmbient;
const GLfloat * diffuse = BrassDiffuse;
const GLfloat * specular = BrassSpecular;
GLfloat shininess = BrassShininess;

// styl

int style = GL_FILL;

// współrzędne punktów kontrolnych powierzchni

GLfloat points[ 12 * 3 ] =
{
    - 1.0, - 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0,
    - 1.0, - 1.0, 0.0, - 1.0, 1.0, 0.0, 1.0, - 1.0, 0.0, 1.0, 1.0, 0.0,
    - 1.0, - 1.0, - 1.0, - 1.0, 1.0, - 1.0, 1.0, - 1.0, - 1.0, 1.0, 1.0, - 1.0
};

// współrzędne tekstury nałożonej na powierzchnię

GLfloat texture_coord[ 4 * 2 ] =
{
    0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0
};

// tekstura zawierająca polską flagę państwową, żródło:
// http://pl.wikipedia.org/wiki/Grafika:800px-Flaga_Polski_wzor_ustawowy.png

GLubyte polish_flag[ 2 * 2 * 3 ] =
{
    0xD9, 0x1E, 0x41, 0xD9, 0x1E, 0x41,
    0xF2, 0xE6, 0xEF, 0xF2, 0xE6, 0xEF
};

// 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 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 );
   
    // 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 );
   
    // skalowanie obiektu - klawisze "+" i "-"
    glScalef( scale, scale, scale );
   
    // włączenie generowana współrzędnych (x,y,z) punktów powierzchni
    glEnable( GL_MAP2_VERTEX_3 );
   
    // ewaluator dwuwymiarowy dla dwunastu punktów kontronych powierzchni
    glMap2f( GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 3, points );
   
    // utworzenie mapy (siatki) punktów reprezentujących powierzchnię
    glMapGrid2f( 25, 0.0, 1.0, 25, 0.0, 1.0 );
   
    // styl rysowania powierzchni - punkty
    if( style == GL_POINT )
    {
        // kolor punktów
        glColor3fv( Black );
       
        // rozmiar punktów
        glPointSize( 2.0 );
       
        // narysowanie siatki (mapy) powierzchni
        glEvalMesh2( GL_POINT, 0, 25, 0, 25 );
    }
   
    // styl rysowania powierzchni - linie
    if( style == GL_LINE )
    {
        // kolor linii
        glColor3fv( Black );
       
        // szerokość linii
        glLineWidth( 2.0 );
       
        // narysowanie siatki (mapy) powierzchni
        glEvalMesh2( GL_LINE, 0, 25, 0, 25 );
    }
   
    // styl rysowania powierzchni - wypełnienie
    if( style == GL_FILL )
    {
        // włączenie oświetlenia
        glEnable( GL_LIGHTING );
       
        // włączenie światła GL_LIGHT0 z parametrami domyślnymi
        glEnable( GL_LIGHT0 );
       
        // włączenie automatycznej normalizacji wektorów normalnych
        glEnable( GL_NORMALIZE );
       
        // właściwości materiału
        glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient );
        glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse );
        glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular );
        glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, shininess );
       
        // włączenie automatycznego generowania wektorów normalnych
        glEnable( GL_AUTO_NORMAL );
       
        // narysowanie siatki (mapy) powierzchni
        glEvalMesh2( GL_FILL, 0, 25, 0, 25 );
       
        // wyłączenie automatycznego generowania wektorów normalnych
        glDisable( GL_AUTO_NORMAL );
       
        // wyłączenie automatycznej normalizacji wektorów normalnych
        glDisable( GL_NORMALIZE );
       
        // wyłaczenie oświetlenia
        glDisable( GL_LIGHTING );
    }
   
    // styl rysowania powierzchni - wypełnienie
    if( style == GL_TEXTURE )
    {
        // tryb upakowania bajtów danych tekstury
        glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
       
        // filtr powiększający
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
       
        // filtr pomniejszający
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
       
        // parametry środowiska tesktur
        glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
       
        // tekstura dwuwymiarowa
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, polish_flag );
       
        // włączenie teksturowania dwuwymiarowego
        glEnable( GL_TEXTURE_2D );
       
        // włączenie generowana współrzędnych (s,t) tekstury
        glEnable( GL_MAP2_TEXTURE_COORD_2 );
       
        // ewaluator dwuwymiarowy dla czterech punktów kontronych powierzchni
        glMap2f( GL_MAP2_TEXTURE_COORD_2, 0.0, 1.0, 2, 2, 0.0, 1.0, 4, 2, texture_coord );
       
        // utworzenie mapy (siatki) punktów reprezentujących powierzchnię
        glMapGrid2f( 25, 0.0, 1.0, 25, 0.0, 1.0 );
       
        // narysowanie siatki (mapy) powierzchni
        glEvalMesh2( GL_FILL, 0, 25, 0, 25 );
       
        // wyłączenie generowana współrzędnych (s,t) tekstury
        glDisable( GL_MAP2_TEXTURE_COORD_2 );
       
        // wyłączenie teksturowania dwuwymiarowego
        glDisable( GL_TEXTURE_2D );
    }
   
    // kolor punktów
    glColor3fv( Blue );
   
    // rozxmiar punktów
    glPointSize( 6.0 );
   
    // narysowanie punktów kontrolnych
    glBegin( GL_POINTS );
    for( int i = 0; i < 12; i++ )
         glVertex3fv( points + i * 3 );
   
    glEnd();
   
    // 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 klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // klawisz +
    if( key == '+' )
         scale += 0.05;
    else
   
    // klawisz -
    if( key == '-' && scale > 0.05 )
         scale -= 0.05;
   
    // narysowanie sceny
    Display();
}

// 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 )
    {
        // styl
    case GL_POINT:
    case GL_LINE:
    case GL_FILL:
    case GL_TEXTURE:
        style = value;
        Display();
        break;
       
        // materiał - mosiądz
    case BRASS:
        ambient = BrassAmbient;
        diffuse = BrassDiffuse;
        specular = BrassSpecular;
        shininess = BrassShininess;
        Display();
        break;
       
        // materiał - brąz
    case BRONZE:
        ambient = BronzeAmbient;
        diffuse = BronzeDiffuse;
        specular = BronzeSpecular;
        shininess = BronzeShininess;
        Display();
        break;
       
        // materiał - polerowany brąz
    case POLISHED_BRONZE:
        ambient = PolishedBronzeAmbient;
        diffuse = PolishedBronzeDiffuse;
        specular = PolishedBronzeSpecular;
        shininess = PolishedBronzeShininess;
        Display();
        break;
       
        // materiał - chrom
    case CHROME:
        ambient = ChromeAmbient;
        diffuse = ChromeDiffuse;
        specular = ChromeSpecular;
        shininess = ChromeShininess;
        Display();
        break;
       
        // materiał - miedź
    case COPPER:
        ambient = CopperAmbient;
        diffuse = CopperDiffuse;
        specular = CopperSpecular;
        shininess = CopperShininess;
        Display();
        break;
       
        // materiał - polerowana miedź
    case POLISHED_COPPER:
        ambient = PolishedCopperAmbient;
        diffuse = PolishedCopperDiffuse;
        specular = PolishedCopperSpecular;
        shininess = PolishedCopperShininess;
        Display();
        break;
       
        // materiał - złoto
    case GOLD:
        ambient = GoldAmbient;
        diffuse = GoldDiffuse;
        specular = GoldSpecular;
        shininess = GoldShininess;
        Display();
        break;
       
        // materiał - polerowane złoto
    case POLISHED_GOLD:
        ambient = PolishedGoldAmbient;
        diffuse = PolishedGoldDiffuse;
        specular = PolishedGoldSpecular;
        shininess = PolishedGoldShininess;
        Display();
        break;
       
        // materiał - grafit (cyna z ołowiem)
    case PEWTER:
        ambient = PewterAmbient;
        diffuse = PewterDiffuse;
        specular = PewterSpecular;
        shininess = PewterShininess;
        Display();
        break;
       
        // materiał - srebro
    case SILVER:
        ambient = SilverAmbient;
        diffuse = SilverDiffuse;
        specular = SilverSpecular;
        shininess = SilverShininess;
        Display();
        break;
       
        // materiał - polerowane srebro
    case POLISHED_SILVER:
        ambient = PolishedSilverAmbient;
        diffuse = PolishedSilverDiffuse;
        specular = PolishedSilverSpecular;
        shininess = PolishedSilverShininess;
        Display();
        break;
       
        // materiał - szmaragd
    case EMERALD:
        ambient = EmeraldAmbient;
        diffuse = EmeraldDiffuse;
        specular = EmeraldSpecular;
        shininess = EmeraldShininess;
        Display();
        break;
       
        // materiał - jadeit
    case JADE:
        ambient = JadeAmbient;
        diffuse = JadeDiffuse;
        specular = JadeSpecular;
        shininess = JadeShininess;
        Display();
        break;
       
        // materiał - obsydian
    case OBSIDIAN:
        ambient = ObsidianAmbient;
        diffuse = ObsidianDiffuse;
        specular = ObsidianSpecular;
        shininess = ObsidianShininess;
        Display();
        break;
       
        // materiał - perła
    case PEARL:
        ambient = PearlAmbient;
        diffuse = PearlDiffuse;
        specular = PearlSpecular;
        shininess = PearlShininess;
        Display();
        break;
       
        // metariał - rubin
    case RUBY:
        ambient = RubyAmbient;
        diffuse = RubyDiffuse;
        specular = RubySpecular;
        shininess = RubyShininess;
        Display();
        break;
       
        // materiał - turkus
    case TURQUOISE:
        ambient = TurquoiseAmbient;
        diffuse = TurquoiseDiffuse;
        specular = TurquoiseSpecular;
        shininess = TurquoiseShininess;
        Display();
        break;
       
        // materiał - czarny plastik
    case BLACK_PLASTIC:
        ambient = BlackPlasticAmbient;
        diffuse = BlackPlasticDiffuse;
        specular = BlackPlasticSpecular;
        shininess = BlackPlasticShininess;
        Display();
        break;
       
        // materiał - czarna guma
    case BLACK_RUBBER:
        ambient = BlackRubberAmbient;
        diffuse = BlackRubberDiffuse;
        specular = BlackRubberSpecular;
        shininess = BlackRubberShininess;
        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 );
    }
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Powierzchnia Béziera" );
    #else
   
    glutCreateWindow( "Powierzchnia Beziera" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // dołączenie funkcji obsługi klawiatury
    glutKeyboardFunc( Keyboard );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // utworzenie podmenu - Styl
    int MenuStyle = glutCreateMenu( Menu );
    glutAddMenuEntry( "GL_POINT", GL_POINT );
    glutAddMenuEntry( "GL_LINE", GL_LINE );
    glutAddMenuEntry( "GL_FILL", GL_FILL );
    glutAddMenuEntry( "GL_FILL + tekstura", GL_TEXTURE );
   
    // utworzenie podmenu - Materiał
    int MenuMaterial = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Mosiądz", BRASS );
    glutAddMenuEntry( "Brąz", BRONZE );
    glutAddMenuEntry( "Polerowany brąz", POLISHED_BRONZE );
    glutAddMenuEntry( "Chrom", CHROME );
    glutAddMenuEntry( "Miedź", COPPER );
    glutAddMenuEntry( "Polerowana miedź", POLISHED_COPPER );
    glutAddMenuEntry( "Złoto", GOLD );
    glutAddMenuEntry( "Polerowane złoto", POLISHED_GOLD );
    glutAddMenuEntry( "Grafit (cyna z ołowiem)", PEWTER );
    glutAddMenuEntry( "Srebro", SILVER );
    glutAddMenuEntry( "Polerowane srebro", POLISHED_SILVER );
    glutAddMenuEntry( "Szmaragd", EMERALD );
    glutAddMenuEntry( "Jadeit", JADE );
    glutAddMenuEntry( "Obsydian", OBSIDIAN );
    glutAddMenuEntry( "Perła", PEARL );
    glutAddMenuEntry( "Rubin", RUBY );
    glutAddMenuEntry( "Turkus", TURQUOISE );
    glutAddMenuEntry( "Czarny plastik", BLACK_PLASTIC );
    glutAddMenuEntry( "Czarna guma", BLACK_RUBBER );
    #else
   
    glutAddMenuEntry( "Mosiadz", BRASS );
    glutAddMenuEntry( "Braz", BRONZE );
    glutAddMenuEntry( "Polerowany braz", POLISHED_BRONZE );
    glutAddMenuEntry( "Chrom", CHROME );
    glutAddMenuEntry( "Miedz", COPPER );
    glutAddMenuEntry( "Polerowana miedz", POLISHED_COPPER );
    glutAddMenuEntry( "Zloto", GOLD );
    glutAddMenuEntry( "Polerowane zloto", POLISHED_GOLD );
    glutAddMenuEntry( "Grafit (cyna z ołowiem)", PEWTER );
    glutAddMenuEntry( "Srebro", SILVER );
    glutAddMenuEntry( "Polerowane srebro", POLISHED_SILVER );
    glutAddMenuEntry( "Szmaragd", EMERALD );
    glutAddMenuEntry( "Jadeit", JADE );
    glutAddMenuEntry( "Obsydian", OBSIDIAN );
    glutAddMenuEntry( "Perla", PEARL );
    glutAddMenuEntry( "Rubin", RUBY );
    glutAddMenuEntry( "Turkus", TURQUOISE );
    glutAddMenuEntry( "Czarny plastik", BLACK_PLASTIC );
    glutAddMenuEntry( "Czarna guma", BLACK_RUBBER );
    #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( "Styl", MenuStyle );
   
    #ifdef WIN32
   
    glutAddSubMenu( "Materiał", MenuMaterial );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Material", MenuMaterial );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Systemy cząstek NURBS