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

Definiowanie sceny 3D

[lekcja] Rozdział 3. Modyfikacja obszaru renderingu, rzutowanie prostokątne, rzutowanie perspektywiczne, położenie obserwatora, 5 przykładowych programów.
W kolejnych programach korzystajacych z biblioteki OpenGL zajmiemy sie podstawowymi zagadnieniami, zwiazanymi ze scena 3D: obszarem renderingu, rzutowaniem i połozeniem obserwatora. Informacje tu zawarte stanowia podstawe do wszystkich nastepnych programów.

Obszar renderingu

W pierwszym programie obszar renderingu, który zajmował początkowo całe okno, nie był modyfikowany podczas zmiany rozmiarów tego okna. W efekcie jedyny element sceny 3D - kwadrat - zawsze znajdował sie w tym samym miejscu wzgledem lewego dolnego naroznika okna. W aplikacjach pracujacych w systemach okienkowych problem zmiany rozmiaru okna jest jednak tak powszechny, ze wymaga specjalnego potraktowania. Jednym z mozliwych sposobów jego rozwiazania jest dynamiczna
modyfikacja obszaru renderingu. Słuzy to tego funkcja:
C/C++
void glViewport( GLint x, GLint y, GLsizei width, GLsizei height )
której parametry oznaczaja:
  • x, y - współrzedne lewego dolnego naroznika obszaru renderingu względem lewego dolnego naroznika okna,
  • width - szerokosc okna renderingu,
  • height - wysokosc okna renderingu.
Domyślnie obszar renderingu zajmuje całe okno udostepnione dla aplikacji OpenGL. W naszym drugim programie w trakcie zmiany rozmiaru okna (funkcja Reshape) bedziemy modyfikowac obszar renderingu na dwa sposoby.

Pierwszy polega na objeciu obszarem renderingu całego dostępnego okna, drugi na takim wyborze okna renderingu aby okno zachowało pierwotny aspekt obrazu, czyli stosunek szerokosci do wysokosci. Oczywiście przy zastosowaniu pierwszej metody kwadrat bedzie zazwyczaj zdeformowany (patrz rysunki 1 i 2). Zmiany sposobu definiowania okna renderingu mozna dokonac w dowolnym momencie, poprzez wybranie odpowiedniej opcji w menu podrecznym.

W funkcji Menu została uzyta do tej pory nieopisana funkcja z biblioteki GLUT:
C/C++
int glutGet( GLenum type )
O tym jakiego rodzaju informacje zwróci funkcja glutGet decyduje parametr type. W przykładowym programie sa to szerokosc i wysokosc okna, co odpowiada parametrom opisanym stałymi GLUT WINDOW WIDTH i GLUT WINDOW HEIGHT.
Warto jeszcze kilka słów poswiecic zagadnieniu aspektu obrazu. Typowe monitory komputerowe posiadaja aspekt 4:3, który jest zgodny z większością popularnych rozdzielczosci roboczych (np. 640 × 480, 800 × 600, 1.024 × 768, 1.600 × 1.200), ale inna popularna rozdzielczosc 1.280 × 1.024 pikseli odpowiada aspektowi 5:4.
Rysunek 1. Programu Kwadrat 2 - rendering na całym oknie
Rysunek 1. Programu Kwadrat 2 - rendering na całym oknie
Rysunek 2. Programu Kwadrat 2 - rendering z zachowaniem aspektu 1:1
Rysunek 2. Programu Kwadrat 2 - rendering z zachowaniem aspektu 1:1

Plik kwadrat2.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG(małpeczka)enter.net.pl
*/

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

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

enum
{
    FULL_WINDOW, // obszar renderingu - całe okno
    ASPECT_1_1, // obszar renderingu - aspekt 1:1
    EXIT // wyjście
};

// aspekt obrazu

int Aspect = FULL_WINDOW;

// 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 );
   
    // kolor kwadratu
    glColor3f( 1.0, 0.0, 0.0 );
   
    // początek definicji wielokąta
    glBegin( GL_POLYGON );
   
    // kolejne wierzchołki wielokąta
    glVertex3f( 0.0, 0.0, 0.0 );
    glVertex3f( 0.0, 1.0, 0.0 );
    glVertex3f( 1.0, 1.0, 0.0 );
    glVertex3f( 1.0, 0.0, 0.0 );
   
    // koniec definicji prymitywu
    glEnd();
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - aspekt 1:1
    if( Aspect == ASPECT_1_1 )
    {
        // szerokość okna większa od wysokości okna
        if( width > height )
             glViewport(( width - height ) / 2, 0, height, height );
        else
       
        // wysokość okna większa od wysokości okna
        if( width < height )
             glViewport( 0,( height - width ) / 2, width, width );
       
    }
    else
    // obszar renderingu - całe okno (także, gdy aspekt wynosi 1:1)
         glViewport( 0, 0, width, height );
   
    // generowanie sceny 3D
    Display();
}

// 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 );
    }
}

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( 400, 400 );
   
    // utworzenie głównego okna programu
    glutCreateWindow( "Kwadrat 2" );
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // dodanie pozycji do menu podręcznego
    #ifdef WIN32
   
    glutAddMenuEntry( "Obszar renderingu - całe okno", FULL_WINDOW );
    glutAddMenuEntry( "Obszar renderingu - aspekt 1:1", ASPECT_1_1 );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Obszar renderingu - cale okno", FULL_WINDOW );
    glutAddMenuEntry( "Obszar renderingu - aspekt 1:1", ASPECT_1_1 );
    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;
}

Rzutowanie prostokątne

Rzutowaniem okreslamy odwzorowanie zawartosci trójwymiarowej sceny graficznej na płaskim ekranie monitora. Biblioteka OpenGL oferuje standardowo dwie metody rzutowania: rzutowanie prostokatne i rzutowanie perspektywiczne. Domyslnie stosowane jest rzutowanie prostokatne. W rzutowaniu prostokatnym (lub ortogonalnym) proste rzutowania sa prostopadłe do rzutni, która jest reprezentowana przez obszar renderingu. Z rzutowaniem prostokatnym scisle zwiazane jest pojęcie bryły odcinania - prostopadłoscianu, który stanowi ograniczenie sceny 3D. Obiekty znajdujące sie poza bryła odcinania nie sa rysowane, a obiekty ja przecinajace rysowane sa tylko czesciowo. Rozmiar bryły odcinania w rzutowaniu prostokatnym okresla funkcja:
C/C++
void glOrtho( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far )
której parametry okreslaja współrzedne punktów przeciecia płaszczyzn tworzących bryłe odcinania z osiami układu współrzednych kartezjanskich. Płaszczyzny te opisane sa nastepujacymi równaniami:
x = right
x = left
y = top
y = bottm
z = ?near
z = ?far
Obszar renderingu zawiera sie w płaszczyznie o równaniu z = ?near. Połozenie poszczególnych płaszczyzn tworzacych bryłe odcinania przedstawia rysunek 3. Trzeba jednak wyraznie zwrócic uwage, ze poczatek układu współrzednych nie musi znajdowac sie wewnatrz bryły odcinania ? rozmiary i połozenie bryły ograniczone sa jedynie zakresem stosowanych liczb.
Rysunek 3. Połozenie płaszczyzn bryły odcinania w rzutowaniu prostokatnym
Rysunek 3. Połozenie płaszczyzn bryły odcinania w rzutowaniu prostokatnym
Domyslnie bryła odcinania ma postac szescianu o bokach równych 2, którego srodek pokrywa sie z poczatkiem układu współrzednych, co odpowiada wywołaniu funkcji glOrtho (-1,1,-1,1,-1,1). Os OZ jest prostopadła do płaszczyzny obszaru renderingu i przechodzi przez srodek tego obszaru. Dlatego rysowany w pierwszym i drugim programie kwadrat zajmował poczatkowo czwarta czesc okna.

Funkcja glOrtho tworzy macierz rzutu prostokatnego:
która jest nastepnie mnozona przez biezaca macierz i umieszczona na szczycie stosu z biezaca macierza. OpenGL zawiera kilka stosów macierzy, z których w przykładowym programie wykorzystamy stos macierzy rzutowania oraz stos macierzy modelowania. Wybór biezacej macierzy umozliwia funkcja:
C/C++
void glMatrixMode( GLenum mode )
gdzie parametr mode moze przyjac jedna z wartosci:
  • GL_MODELVIEW - macierz modelowania,
  • GL_PROJECTION - macierz rzutowania,
  • GL_TEXTURE - macierz tekstury (omówiona pózniej).
Poniewaz poczatkowa wartosc wybranej macierzy jest nieokreslona, przed wywołaniem glOrtho nalezy biezacej macierzy przyporzadkowac macierz jednostkowa. Najłatwiej mozna to zrobic uzywajac funkcji:
C/C++
void glLoadIdentity( void )
Analogiczne postepowanie dotyczy macierzy modelowania. Po jej wyborze przykładowym programie (plik szescian1.cpp) w funkcji Display macierzy modelowania takze przyporzadkowywana jest macierz jednostkowa. Jezeli renderowana scena jest dwuwymiarowa, do ustawienia parametrów bryły odcinania w rzutowaniu prostokatnym mozna uzyc funkcji z biblioteki GLU:
C/C++
void gluOrtho2D( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top )
której parametry left, right, bottom i top odpowiadaja parametrom funkcji glOrtho, a przednia (near) i tylna (far) płaszczyzna obcinania maja wartosci odpowiednio -1 i 1. W przykładowym programie poczatkowa bryła odcinania ma postac sześcianu o krawedziach długosci 4, a rysowana figura - takze szescian ? ma krawedzie o długosci 2. Centralne umieszczenie rysowanego szescianu w połaczeniu z zastosowanym rzutowaniem prostokatnym daje w efekcie kwadrat (patrz rysunek 4). Podobnie jak w poprzednim programie mozliwy jest wybór, czy scena ma byc rysowana z zachowaniem poczatkowego aspektu obrazu czy tez bez zachowania tej proporcji. Jednak w tym przypadku nie jest modyfikowany obszar renderingu ale współrzedne bryły odcinania (funkcja Reshape).
Rysunek 4. Poczatkowe okno programu Szescian 1
Rysunek 4. Poczatkowe okno programu Szescian 1

Plik szescian1.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG(małpeczka)enter.net.pl
*/

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

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

enum
{
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int Aspect = FULL_WINDOW;

// funkcja generująca scenę 3D

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // kolor krawędzi sześcianu
    glColor3f( 0.0, 0.0, 0.0 );
   
    // początek definicji krawędzi sześcianu
    glBegin( GL_LINES );
   
    // współrzędne kolejnych krawędzi sześcianu
    glVertex3f( 1.0, 1.0, 1.0 );
    glVertex3f( 1.0, - 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, 1.0 );
    glVertex3f( 1.0, - 1.0, - 1.0 );
   
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    glVertex3f( 1.0, 1.0, - 1.0 );
    glVertex3f( 1.0, 1.0, 1.0 );
   
    glVertex3f( - 1.0, 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glVertex3f( 1.0, 1.0, 1.0 );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glVertex3f( 1.0, 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    // koniec definicji prymitywu
    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 )
             glOrtho( - 2.0, 2.0, - 2.0 * height / width, 2.0 * height / width, - 2.0, 2.0 );
        else
       
        // szerokość okna większa lub równa wysokości okna
        if( width >= height && height > 0 )
             glOrtho( - 2.0 * width / height, 2.0 * width / height, - 2.0, 2.0, - 2.0, 2.0 );
       
    }
    else
         glOrtho( - 2.0, 2.0, - 2.0, 2.0, - 2.0, 2.0 );
   
    // generowanie sceny 3D
    Display();
}

// 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 );
    }
}

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( 400, 400 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
    glutCreateWindow( "Sześcian 1" );
    #else
    glutCreateWindow( "Szescian 1" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // dodanie pozycji do menu podręcznego
    #ifdef WIN32
    glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
    glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
    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;
}

Rzutowanie perspektywiczne

Rzutowanie perspektywiczne daje bardziej realistyczne efekty niz rzutowanie prostokatne, stad jest szeroko stosowane np. w grach. Parametry bryły odcinania, która przy rzutowaniu perspektywicznym ma postac ostrosłupa scietego o wierzchołku znajdujacym sie w poczatku układu współrzędnych (patrz rysunek 5), okresla funkcja:
C/C++
void glFrustum( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far )
Parametry left, right, bottom i top wyznaczaja rozmiary górnej podstawy bryły odcinania (jest to obszar bezposrednio odwzorowywany na obszar renderingu), a near i far wyznaczaja połozenie odpowiednio górnej i dolnej podstawy ostrosłupa (przedniej i tylnej płaszczyzny odcinania), które zawieraja sie w płaszczyznach o równaniach: z = ?near i z = ?far. Parametry near i far musza miec wartosci dodatnie.
Macierz rzutowania perspektywicznego, tworzona przez funkcje glFrustum i mnozona przez aktualnie wybrana macierz, ma postac:
Warto zauwazyc, ze precyzja działania jeszcze przez nas nieużywanego z-bufora, zalezy od wartosci stosunku parametrów near i far:
Rysunek 5. Połozenie płaszczyzn bryły odcinania w rzutowaniu perspektywicznym
Rysunek 5. Połozenie płaszczyzn bryły odcinania w rzutowaniu perspektywicznym
Im wieksza wartosc r, tym mniej efektywne jest działanie z-bufora. Oczywiście near nigdy nie moze przyjac wartosci równej 0, bowiem przednia płaszczyzna odcinania przechodziła by wówczas przez srodek perspektywy. Alternatywny sposób okreslania rzutu perspektywicznego umozliwia funkcja z biblioteki GLU:
C/C++
void gluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar )
gdzie parametr fovy okresla w stopniach kat widzenia obserwatora zawarty w płaszczyznie YZ (6 (top, 0, bottom)), a aspect jest stosunkiem szerokości do wysokosci przedniej płaszczyzny odcinania, czyli górnej podstawy ostrosłupa ograniczajacego scene 3D. Parametry zNear i zFar odpowiadaja parametrom near i far funkcji glFrustum. Macierz rzutowania perspektywicznego, tworzona przez funkcje gluPerspective
i mnozona przez aktualnie wybrana macierz, ma postac:
Wewnetrznie funkcja gluPerspective wykorzystuje do ustawienia macierzy rzutowania perspektywicznego funkcje glFrustum. Oto wzory przekształcenia parametrów funkcji gluPerspective na parametry glFrustum:
W kolejnym przykładowym programie (plik szescian2.cpp) do utworzenia macierzy rzutowania perspektywicznego wykorzystamy funkcje glFrustum. Przednia płaszczyzna odcinania bedzie miała takie same rozmiary jak w poprzednim programie. Zmianie ulegna natomiast współrzedne przedniej i tylnej płaszczyzny obcinania - poprzednio jedna z tych płaszczyzn miała wartosc ujemna, której nie akceptuje funkcja glFrustum. Rysowanym obiektem ponownie bedzie szescian ale próba narysowania go w tym samym miejscu jak w poprzednim programie spowoduje, ze będzie widoczna tylko jedna jego sciana. Wszystko dlatego, ze pozostałe sciany szescianu znajduja sie poza obszarem bryły odcinania. Mozliwe sa trzy sposoby rozwiazania tego problemu. Pierwszy polega na zmianie współrzędnych wierzchołków szescianu w taki sposób, aby szescian zmiescił sie w zmienionej bryle obcinania. Rozwiazanie to ma jedna zasadnicza wade ? wierzchołki szescianu trzeba bedzie modyfikowac przy kazdej zmianie parametrów sceny 3D. W przypadku jednego obiektu nie stanowi to specjalnego problemu, ale czyni pomysł niewykonalnym przy kazdej bardziej skomplikowanej scenie 3D.

Drugie, zastosowane w programie rozwiazanie, polega na przesunieciu wierzchołków szescianu o wektor [0, 0,?3], czyli o -3 jednostki wzdłuz osi OZ. Realizuje to funkcja glTranslatef, która wywoływana jest bezpośrednio po zainicjowaniu macierzy modelowania macierza jednostkowa (patrz funkcja Display). W efekcie otrzymamy szescian przedstawiony na rysunku 6. Nalezy dodac, ze taka metoda jest czesto stosowana praktyka. Obiekty 3D definiowane sa z róznymi współrzednymi, a nastepnie odpowiednio transformowane do docelowego połozenia w scenie 3D. Funkcje umożliwiające takie przekształcenia poznamy blizej w nastepnym odcinku kursu. Trzecia metoda jest modyfikacja połozenia obserwatora sceny 3D - zapoznamy się z ta technika jeszcze w tym odcinku.
Rysunek 6. Poczatkowe okno programu Szescian 2
Rysunek 6. Poczatkowe okno programu Szescian 2

Plik szescian2.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG(małpeczka)enter.net.pl
*/

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

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

enum
{
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int Aspect = FULL_WINDOW;

// funkcja generująca scenę 3D

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // przesunięcie obiektu o wektor [0,0,-3]
    glTranslatef( 0, 0, - 3.0 );
   
    // kolor krawędzi sześcianu
    glColor3f( 0.0, 0.0, 0.0 );
   
    // początek definicji krawędzi sześcianu
    glBegin( GL_LINES );
   
    // wspólrzędne kolejnych krawędzi sześcianu
    glVertex3f( 1.0, 1.0, 1.0 );
    glVertex3f( 1.0, - 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, 1.0 );
    glVertex3f( 1.0, - 1.0, - 1.0 );
   
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    glVertex3f( 1.0, 1.0, - 1.0 );
    glVertex3f( 1.0, 1.0, 1.0 );
   
    glVertex3f( - 1.0, 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glVertex3f( 1.0, 1.0, 1.0 );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glVertex3f( 1.0, 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    // koniec definicji prymitywu
    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( - 2.0, 2.0, - 2.0 * height / width, 2.0 * height / width, 1.0, 5.0 );
        else
       
        // szerokość okna większa lub równa wysokości okna
        if( width >= height && height > 0 )
             glFrustum( - 2.0 * width / height, 2.0 * width / height, - 2.0, 2.0, 1.0, 5.0 );
       
    }
    else
         glFrustum( - 2.0, 2.0, - 2.0, 2.0, 1.0, 5.0 );
   
    // generowanie sceny 3D
    Display();
}

// 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 );
    }
}

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( 400, 400 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Sześcian 2" );
    #else
   
    glutCreateWindow( "Sześcian 2" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // dodanie pozycji do menu podręcznego
    #ifdef WIN32
   
    glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
    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 przedstawiajacy rzutowanie perspektywiczne (plik szescian3.cpp) stosuje funkcje gluPerspective. Aby jednak nie powielac rozwiązań z poprzedniego programu dodamy mechanizm pokazujacy jaki wpływ na wyglad obiektów 3D ma zmiana połozenie srodka perspektywy, realizowana
poprzez zmiane kata widzenia obserwatora (parametr fovy funkcji gluPerspective). W tym celu potrzebna jest obsługa klawiatury. Podstawowa funkcja obsługi klawiatury (w przykładowym programie jest to funkcja Keyboard) ma trzy parametry:
  • key - kod ASCII klawisza,
  • x, y - współrzedne kursora myszki w chwili nacisniecia przycisku klawiatury.
Aby obsługa klawiatury działała, w czesci głównej programu nalezy włączyć funkcje obsługi klawiatury wywołujac funkcje:
void glutKeyboardFunc( void( * func )( unsigned char key, int x, int y ) )
Poczatkowe okno programu Szescian 3 zawiera rysunek 7. Przyciskając klawisze ?+? i ?-? mozna modyfikowac kat patrzenia obserwatora, który poczatkowo wynosi 90 stopni.
Rysunek 7. Poczatkowe okno programu Szescian 3
Rysunek 7. Poczatkowe okno programu Szescian 3

Plik szescian3.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG(małpeczka)enter.net.pl
*/

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

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

enum
{
    EXIT // wyjście
};

// pionowy kąt pola widzenia

GLdouble fovy = 90;

// funkcja generująca scenę 3D

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // przesunięcie obiektu o wektor [0,0,-3]
    glTranslatef( 0, 0, - 3.0 );
   
    // kolor krawędzi sześcianu
    glColor3f( 0.0, 0.0, 0.0 );
   
    // początek definicji krawędzi sześcianu
    glBegin( GL_LINES );
   
    // wspólrzędne kolejnych krawędzi sześcianu
    glVertex3f( 1.0, 1.0, 1.0 );
    glVertex3f( 1.0, - 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, 1.0 );
    glVertex3f( 1.0, - 1.0, - 1.0 );
   
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    glVertex3f( 1.0, 1.0, - 1.0 );
    glVertex3f( 1.0, 1.0, 1.0 );
   
    glVertex3f( - 1.0, 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glVertex3f( 1.0, 1.0, 1.0 );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glVertex3f( 1.0, 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    // koniec definicji prymitywu
    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();
   
    // obliczenie aspektu obrazu z uwzględnieniem
    // przypadku, gdy wysokość obrazu wynosi 0
    GLdouble aspect = 1;
    if( height > 0 )
         aspect = width /( GLdouble ) height;
   
    // rzutowanie perspektywiczne
    gluPerspective( fovy, aspect, 1.0, 5.0 );
   
    // generowanie sceny 3D
    Display();
}

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // klawisz +
    if( key == '+' && fovy < 180 )
         fovy++;
    else
   
    // klawisz -
    if( key == '-' && fovy > 0 )
         fovy--;
   
    // odrysowanie okna
    Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}

// 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( 400, 400 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Sześcian 3" );
    #else
   
    glutCreateWindow( "Szescian 3" );
    #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 );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // dodanie pozycji do menu podręcznego
    #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;
}

Położenie obserwatora

Ostatnim z podstawowych elementów wymagajacych omówienia przy tworzeniu sceny 3D jest połozenie obserwatora, nazywane takze położeniem kamery lub ?oka?. Domyslnie obserwator w OpenGL połozony jest w poczatku układu współrzednych i skierowany jest w strone ujemnej półosi OZ. Obserwator jest tak zorientowany w przestrzeni, ze kierunek ?do góry? pokrywa sie z kierunkiem osi OY. Zasadniczo OpenGL nie umozliwia zmiany połozenia obserwatora. Wszystkie przekształcenia połozenia obserwatora faktycznie realizowane SA jako odpowiednie przekształcenia układu współrzednych. Aby jednak ułatwic prace zwiazane z definiowaniem tych przekształcen, biblioteka GLU zawiera funkcje gluLookAt, która pozwala na jednorazowe zdefiniowanie wszystkich parametrów opisujacych obserwatora:
C/C++
void gluLookAt( GLdouble eyex, GLdouble eyey, GLdouble eyez,
GLdouble centerx, GLdouble centery,
GLdouble centerz,
GLdouble upx, GLdouble upy, GLdouble upz )
Kolejne trójki parametrów funkcji gluLookAt oznaczaja:
  • eyex, eyey, eyez - współrzedne połozenia obserwatora,
  • centerx, centery, centerz - współrzedne punktu, w którego kierunku
  • jest zwrócony obserwator,
  • upx, upy, upz - współrzedne wektora okreslajacego kierunek ?do góry?.
Domyślne połozenie obserwatora odpowiada wywołaniu
C/C++
gluLookat( 0.0, 0.0, 0.0, 0.0, 0.0, - 100.0, 0.0, 1.0, 0.0 )
W kolejnym przykładowym programie (plik szescian4.cpp) będziemy modyfikowac tylko współrzedne połozenia obserwatora. Przy niezmiennych współrzednych punktu, w którego kierunku patrzy obserwator, daje to ciekawy efekt obserwacji sceny z pewnej odległosci. Zmiana połozenia obserwatora realizowana jest w funkcjach Keyboard (przyciski ?+? i ?-?) oraz SpecialKeys (klawisze kursora).Warto zauwazyc, ze zmiany współrzednych obserwatora, które reprezentuja zmienne eyex, eyey i eyez, sa odwrotne niz mozna by sie spodziewac. Przykładowo nacisniecie strzałki w dół powoduje zwiekszenie o 0,1 zmiennej eyey, która okresla współrzedna Y połozenia obserwatora. Jest to spowodowane tym, ze macierz modelowania, modyfikowana przy wywołaniu funkcji gluLookAt, odgrywa podwójna role. Z jednej strony umozliwia przekształcenia współrzędnych obiektu (patrz poprzedni przykład), a z drugiej przekształcenia współrzednych obserwatora. Przykładowo, to co z punktu widzenia obiektu jest przesunieciem o wektor [1, 0, 0], dla obserwatora jest przesunieciem o wektor przeciwny tj. [?1, 0, 0]. Dobre poznanie opisanego mechanizmy wymaga eksperymentów, do których goraco zachecam Czytelników.

Do omówienia pozostała wprowadzona w ostatnim przykładzie obsługa klawiszy kursora. Jest ona realizowana odrebnie od obsługi przycisków, które reprezentowane sa bezposrednio przez kody ASCII (funkcja Keyboard). Funkcja obsługujaca klawisze kursora oraz przyciski funkcyjne (w przykładowym programie jest to funkcja SpecialKeys) ma trzy parametry:
key - kod przycisku; zwracana jest jedna z ponizszych wartości:
  • GLUT_KEY F1 - przycisk F1,
  • GLUT_KEY_F2 - przycisk F2,
  • GLUT_KEY_F3 - przycisk F3,
  • GLUT_KEY_F4 - przycisk F4,
  • GLUT_KEY_F5 - przycisk F5,
  • GLUT_KEY_F6 - przycisk F6,
  • GLUT_KEY_F7 - przycisk F7,
  • GLUT_KEY_F8 - przycisk F8,
  • GLUT_KEY_F9 - przycisk F9,
  • GLUT_KEY_F10 - przycisk F10,
  • GLUT_KEY_F11 - przycisk F11,
  • GLUT_KEY_F12 - przycisk F12,
  • GLUT_KEY_LEFT - kursor w lewo,
  • GLUT_KEY_UP - kursor do góry,
  • GLUT_KEY_RIGHT - kursor w prawo,
  • GLUT_KEY_DOWN - kursor w dół,]
  • GLUT_KEY_PAGE_UP - przycisk Page Up
  • GLUT_KEY_PAGE_DOWN - przycisk Page Down,
  • GLUT_KEY_HOME - przycisk Home,
  • GLUT_KEY_END - przycisk End,
  • GLUT_KEY_INSERT - przycisk Insert.
x, y - współrzedne kursora myszki w chwili nacisniecia przycisku klawiatury.

Podobnie jak w przypadku poprzedniej funkcji obsługujacej klawiature, w głównym programie nalezy właczyc obsługe klawiszy kursora i klawiszy funkcyjnych wywołujac funkcje:
C/C++
void glutSpecialFunc( void( * func )( int key, int x, int y ) )
Rysunek 8 przedstawia przykładowe okno programu Szescian 4, którego kod zródłowy znajduje sie poniżej.
Rysunek 8. Przykładowe okno programu Szescian 4
Rysunek 8. Przykładowe okno programu Szescian 4

Plik szescian4.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG(małpeczka)enter.net.pl
*/

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

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

enum
{
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int Aspect = FULL_WINDOW;

// wpółrzędne położenia obserwatora

GLdouble eyex = 0;
GLdouble eyey = 0;
GLdouble eyez = 3;

// współrzędne punktu w którego kierunku jest zwrócony obserwator,

GLdouble centerx = 0;
GLdouble centery = 0;
GLdouble centerz = - 100;

// funkcja generująca scenę 3D

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // ustawienie obserwatora
    gluLookAt( eyex, eyey, eyez, centerx, centery, centerz, 0, 1, 0 );
   
    // kolor krawędzi sześcianu
    glColor3f( 0.0, 0.0, 0.0 );
   
    // początek definicji krawędzi sześcianu
    glBegin( GL_LINES );
   
    // wspólrzędne kolejnych krawędzi sześcianu
    glVertex3f( 1.0, 1.0, 1.0 );
    glVertex3f( 1.0, - 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, 1.0 );
    glVertex3f( 1.0, - 1.0, - 1.0 );
   
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    glVertex3f( 1.0, 1.0, - 1.0 );
    glVertex3f( 1.0, 1.0, 1.0 );
   
    glVertex3f( - 1.0, 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glVertex3f( 1.0, 1.0, 1.0 );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, 1.0 );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glVertex3f( 1.0, 1.0, - 1.0 );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    // koniec definicji prymitywu
    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( - 2.0, 2.0, - 2.0 * height / width, 2.0 * height / width, 1.0, 5.0 );
        else
       
        // szerokość okna większa lub równa wysokości okna
        if( width >= height && height > 0 )
             glFrustum( - 2.0 * width / height, 2.0 * width / height, - 2.0, 2.0, 1.0, 5.0 );
       
    }
    else
         glFrustum( - 2.0, 2.0, - 2.0, 2.0, 1.0, 5.0 );
   
    // generowanie sceny 3D
    Display();
}

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // klawisz +
    if( key == '+' )
         eyez -= 0.1;
    else
   
    // klawisz -
    if( key == '-' )
         eyez += 0.1;
   
    // odrysowanie okna
    Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}

// obsługa klawiszy funkcyjnych i klawiszy kursora

void SpecialKeys( int key, int x, int y )
{
    switch( key )
    {
        // kursor w lewo
    case GLUT_KEY_LEFT:
        eyex += 0.1;
        break;
       
        // kursor w górę
    case GLUT_KEY_UP:
        eyey -= 0.1;
        break;
       
        // kursor w prawo
    case GLUT_KEY_RIGHT:
        eyex -= 0.1;
        break;
       
        // kursor w dół
    case GLUT_KEY_DOWN:
        eyey += 0.1;
        break;
    }
   
    // odrysowanie okna
    Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}

// 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 );
    }
}

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( 400, 400 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Sześcian 4" );
    #else
   
    glutCreateWindow( "Szescian 4" );
    #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 );
   
    // dołączenie funkcji obsługi klawiszy funkcyjnych i klawiszy kursora
    glutSpecialFunc( SpecialKeys );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // dodanie pozycji do menu podręcznego
    #ifdef WIN32
   
    glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
    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
Pierwszy program Przekształcenia geometryczne