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

Światła i materiały

[lekcja] Rozdział 13. Model oświetlenia w bibliotece OpenGL; parametry źródeł światła; parametry modelu oświetlenia; właściwości materiałów; śledzenie kolorów; wektory normalne; kwadryki; pięć przykładowych programów.
Model oświetlenia zastosowany w bibliotece OpenGL opiera się na trzech rodzajach światła. Pierwszy rodzaj to światło otaczające (ang. ambient light), które nie pochodzi z żadnego konkretnego kierunku i równomiernie oświetla wszystkie elementy sceny 3D. Drugim rodzajem światła jest światło rozproszone (ang. diffuse light), które pada na obiekt z określonego kierunku, ale jest na nim rozpraszane we wszystkich kierunkach. Ostatnim rodzajem światła obsługiwanego przez OpenGL jest światło odbite (ang. specular light), zwane także światłem kierunkowym, które pada na obiekt w określonym kierunku i odbijane jest także w ściśle określonym kierunku.
W bibliotece OpenGL ze światłem ściśle związane jest pojęcia materiałów określające właściwości oświetlanych obiektów. Właściwości materiału, poza reakcją na opisane wyżej trzy rodzaje światła, uwzględniają także możliwość emitowania światła.

Włączanie oświetlenia

Domyślnie biblioteka OpenGL nie wykonuje obliczeń związanych z oświetleniem. Uruchomienie oświetlenia wymaga wywołania funkcji glEnable z parametrem GL_LIGHTING. Ponadto należy włączyć bufor głębokości. Nie jest to wprawdzie konieczne ale znacząco wpływa na realizm generowanej sceny 3D. Oczywiście wyłączenie oświetlenia sprowadza się do wywołania funkcji glDisable z parametrem GL_LIGHTING.

Włączanie źródła światła

Specyfikacja OpenGL określa, że minimalną ilość źródeł światła, którą musi obsługiwać każda implementacja biblioteki wynosi 8. Każde źródło światła ma swój unikatowy numer oznaczony stałą GL_LIGHTi, gdzie i zawiera się w przedziale [0, 7]. Ilość źródeł światła obsługiwanych przez daną implementację biblioteki OpenGL zwraca wywołanie funkcji glGetIntegerv z parametrem GL_MAX_LIGHTS. Alternatywnie numery źródeł światła można określić przy pomocy wyrażenia: GL_LIGHTi = GL_LIGHT0 + i. Jest to szczególnie przydatne przy źródłach światła o numerach wyższych niż 7, bowiem pliki nagłówkowe biblioteki OpenGL standardowo nie zawierają stosownych stałych. Zasada ta odnosi się do każdej funkcji korzystającej z numeru źródła światła.
Poszczególne źródła światła włączamy i wyłączamy przy użyciu funkcji glEnable/glDisable z parametrem określającym numer źródła światła.

Parametry źródła światła

Parametry każdego ze źródła światła można modyfikować niezależnie od pozostałych źródeł światła. Służą do tego funkcje z grupy glLight, które można podzielić na dwie podgrupy. Pierwsza pozwala na modyfikację parametrów źródła światła określanych pojedynczą wartością; druga grupa funkcji przyjmuje wskaźniki na tablice z wartościami parametrów, przy czym można ich także używać do modyfikacji parametrów źródła światła określanych pojedynczą wartością.
Zmianę parametru źródła światła określanego pojedynczą wartością umożliwiają funkcje:
C/C++
void glLightf( GLenum light, GLenum pname, GLfloat param )
void glLighti( GLenum light, GLenum pname, GLint param )
Natomiast zmianę parametru źródła światła określanego tablicą wartości umożliwiają funkcje:
C/C++
void glLightfv( GLenum light, GLenum pname, const GLfloat * params )
void glLightiv( GLenum light, GLenum pname, const GLint * params )
Parametr light określa numer źródła światła, którego parametry chcemy zmienić. Numer źródła światła określają omówione już stałe GL_LIGHTi. Parametr pname określa parametr źródła światła, który chcemy zmodyfikować. Dopuszczalne są następujące wartości tego parametru:
  • GL_AMBIENT - wartości składowych RGBA światła otaczającego,
  • GL_DIFFUSE - wartości składowych RGBA światła rozproszonego,
  • GL_SPECULAR - wartości składowych RGBA światła odbitego,
  • GL_POSITION – czteroelementowy wektor (x, y, z, w), którego interpretacja zależy od wartości w; jeżeli w ma wartość 1, oznacza to, że trzy pierwsze współrzędne wektora określają położenie źródła światła; jeżeli natomiast w ma wartość 0, to źródło światła jest światłem kierunkowym, a jego promienie padają w kierunku zdefiniowanym przez trzy pierwsze współrzędne wektora,
  • GL_SPOT_DIRECTION - znormalizowany (długości 1) trójwspółrzędny wektor określający kierunek reflektora,
  • GL_SPOT_EXPONENT - wykładnik tłumienia kątowego reflektora; dopuszczalne są wartości z przedziału [0, 128],
  • GL_SPOT_CUTOFF - kąt odcięcia reflektora; dopuszczalne są wartości z przedziału [0, 90] oraz 180,
  • GL_CONSTANT_ATTENUATION - stały współczynnik tłumienia światła,
  • GL_LINEAR_ATTENUATION - liniowy współczynnik tłumienia światła,
  • GL_QUADRATIC_ATTENUATION - kwadratowy współczynnik tłumienia światła.
Wspomniane wyżej reflektory to po prostu światło kierunkowe, którego promienie rozchodzą się w przestrzeni ograniczonej stożkiem o wierzchołku znajdującym się w źródle światła. Połowę kąta rozwarcia stożka światła określa parametr GL_SPOT_CUTOFF (patrz rysunek 1). Wszystkie obiekty sceny znajdujące się poza stożkiem światła reflektora pozostają nieoświetlone jego światłem.
Rysunek 1. Parametr GL_SPOT_CUTOFF
Rysunek 1. Parametr GL_SPOT_CUTOFF
Parametr GL_SPOT_EXPONENT umożliwia regulację stopnia skupienia światła reflektora. Jest to wykładnik potęgi wyrażenia stanowiącego kosinus kąta pomiędzy kierunkiem padania światła, a kierunkiem od źródła światła do wierzchołka oświetlanego elementu sceny. Domyślna wartość 0 oznacza brak tłumienia kątowego, czyli równomierne rozchodzenie się światła we wszystkich kierunkach.
Trzy ostatnie parametry źródła światła określają współczynnik tłumienia światła dany wzorem:
Wzór
Wzór
gdzie d oznacza odległość wierzchołka od źródła światła, a stałe ac, al i aq odpowiednio stały (GL_CONSTANT_ATTENUATION), liniowy (GL_LINEAR_ATTENUATION) i kwadratowy (GL_QUADRATIC_ATTENUATION) współczynnik tłumienia. Przy domyślnych ustawieniach parametrów źródeł światła wartość współczynnika tłumienia wynosi 1, co oznacza, że intensywność strumienie światła nie zależy od odległości pomiędzy jego źródłem a wierzchołkiem oświetlanego obiektu.
Zestawienie domyślnych wartości parametrów źródła światła GL_LIGHT0 zawiera tabela 1. Te same dane dla pozostałych źródeł światła przedstawia tabela 2.

Tabela 1: Domyślne parametry źródła światła GL_LIGHT0

parametr źródła światławartość
GL_AMBIENT(0, 0, 0, 1)
GL_DIFFUSE(1, 1, 1, 1)
GL_SPECULAR(1, 1, 1, 1)
GL_POSITION(0, 0, 1, 0)
GL_SPOT_DIRECTION(0, 0, −1)
GL_SPOT_EXPONENT0
GL_SPOT_CUTOFF180
GL_CONSTANT_ATTENUATION1
GL_LINEAR_ATTENUATION0
GL_QUADRATIC_ATTENUATION0

Tabela 2: Domyślne parametry źródeł światła GL_LIGHT1 - GL_LIGHT7

parametr źródła światławartość
GL_AMBIENT(0, 0, 0, 1)
GL_DIFFUSE(0, 0, 0, 1)
GL_SPECULAR(0, 0, 0, 1)
GL_POSITION(0, 0, 1, 0)
GL_SPOT_DIRECTION(0, 0, −1)
GL_SPOT_EXPONENT0
GL_SPOT_CUTOFF180
GL_CONSTANT_ATTENUATION1
GL_LINEAR_ATTENUATION0
GL_QUADRATIC_ATTENUATION0
Jak widzimy na powyższych tabelach światło GL_LIGHT0 jest światłem białym kierunkowym położonym w punkcie o współrzędnych (0, 0, 1). Światło skierowane jest zgodnie z kierunkiem wyznaczonym przez wektor o współrzędnych [0, 0, −1]. Pomimo kierunkowego charakteru stożek światła GL - LIGHT0 nie jest w żaden sposób ograniczony (parametr GL_SPOT_CUTOFF ma wartość 180◦ ) i światło rozchodzi się każdym kierunku. Przy domyślnych początkowych ustawieniach układu współrzędnych oraz bryły odcinania źródło światła GL_LIGHT0 znajduje się na środku płaszczyzny monitora (okna) i jest skierowane prostopadle w głąb monitora.
Źródła światła GL_LIGHT1 - GL_LIGHT7 różną się do światła GL_LIGHT0 tylko barwą światła rozproszonego i odbitego.

Parametry modelu oświetlenia

Poza zdefiniowaniem właściwości poszczególnych źródeł światła do oświetlenia sceny w bibliotece OpenGL trzeba jeszcze określić parametry stosowanego modelu oświetlenia. Służą do tego funkcje z grupy glLightModel:
C/C++
void glLightModelf( GLenum pname, GLfloat param )
void glLightModeli( GLenum pname, GLint param )
void glLightModelfv( GLenum pname, const GLfloat * params )
void glLightModeliv( GLenum pname, const GLint * params )
Funkcje te, podobnie jak funkcje z grupy glLight, występują w dwóch wersjach różniących się sposobem przekazywania parametrów.
Parametr pname określa właściwość modelu oświetlenia, który chcemy zmienić. Dopuszczalne są następujące wartości tego parametru:
  • GL_LIGHT_MODEL_LOCAL_VIEWER - określenie sposobu obliczania kąta odbicia światła; wartość 0 oznacza, że kąt obliczany jest na podstawie kierunku od początku układu współrzędnych do oświetlanego wierzchołka obiektu; wartość różna od 0 oznacza, że kąt odbicia światła obliczany jest na podstawie kierunku ujemnej osi OZ,
  • GL_LIGHT_MODEL_TWO_SIDE - określenie czy będą oświetlane obie strony wielokątów (wartość różna od 0) czy też tylko przednie strony wielokątów (wartość 0),
  • GL_LIGHT_MODEL_AMBIENT - określenie składowych RGBA globalnego światła otaczającego,
  • GL_LIGHT_MODEL_COLOR_CONTROL - określenie czy efekt oświetlenia światłem odbitym będzie nakładany dopiero po nałożeniu tekstury (wartość GL_SEPARATE_SPECULAR_COLOR) lub też razem z teksturą (wartość GL_SINGLE_COLOR).
Ustawienie wartości składowych RGBA globalnego światła otaczającego wymaga oczywiście użycia funkcji glLightModelfv lub glLightModeliv. Parametr GL_LIGHT_MODEL_COLOR_CONTROL, będący jednym z elementów techniki nazywanej drugorzędnym kolorem odbicia (ang. secondary specular color), wprowadzono w wersji 1.2 biblioteki OpenGL. Wcześniej technika ta opisana była w rozszerzeniu EXT separate specular color. Zostanie to bliżej zaprezentowane w jednym z kolejnych odcinków kursu OpenGL.
Tabela 3 przedstawia wartości domyślne parametrów modelu oświetlenia w bibliotece OpenGL.

Tabela 3: Domyślne parametry modelu oświetlenia

parametr modelu oświetleniawartość
GL_LIGHT_MODEL_LOCAL_VIEWER0
GL_LIGHT_MODEL_TWO_SIDE0
GL_LIGHT_MODEL_AMBIENT(2/10, 2/10, 2/10, 1)
GL_LIGHT_MODEL_COLOR_CONTROLGL_SINGLE_COLOR

Materiały

Integralnym elementem modelu oświetlenia przyjętego w bibliotece OpenGL jest opis sposobu zachowania się powierzchni obiektów na poszczególne rodzaje światła, czyli opis właściwości materiałów. Modyfikowanie właściwości materiałów umożliwiają funkcje z grupy glMaterial:
C/C++
void glMaterialf( GLenum face, GLenum pname, GLfloat para )
void glMateriali( GLenum face, GLenum pname, GLint para )
void glMaterialfv( GLenum face, GLenum pname, const GLfloat * params )
void glMaterialiv( GLenum face, GLenum pname, const GLint * params )
Jak Czytelnik zauważył także i te funkcje występują w dwóch wersjach różniących się sposobem przekazywania parametrów.
Parametr face ustala, którą stronę wielokąta dotyczy modyfikowany parametr. Dopuszczalne są poznane już wcześniej wartości: GL_FRONT - przednia strona wielokąta, GL_BACK - tylna strona wielokąta i GL_FRONT_AND_BACK - obie strony wielokąta.
Parametr pname określa zmienianą wartość parametru materiału. Dopuszczalne są poniższe wartości:
  • GL_AMBIENT - składowe RGBA określające stopień odbicia światła otaczającego,
  • GL_DIFFUSE - składowe RGBA określające stopień rozproszenia światła rozproszonego,
  • GL_AMBIENT_AND_DIFFUSE - składowe RGBA określające jednocześnie stopień odbicia światła otaczającego i stopień rozproszenia światła rozproszonego; jest to wartość domyślna,
  • GL_SPECULAR - składowe RGBA określające stopień odbicia światła odbitego,
  • GL_SHININESS - stała z przedziału [0, 128] określająca wykładnik odbłysku światła (ang. specular exponent) czyli regulację stopnia występowania efektu rozbłysku obiektu; im większa wartość parametru, tym większe skupienie rozbłysku światła na obiekcie,
  • GL_EMISSION - składowe RGBA światła emitowanego przez obiekt; taki obiekt nie staje się źródłem światła i nie oświetla innych obiektów sceny, wymaga to także utworzenia źródła światła,
  • GL_COLOR_INDEXES - w indeksowym trybie kolorów trzy indeksy do tablicy kolorów określające kolejno składowe RGBA określające reakcję na światło otaczające, rozproszone i odbite.
Zestawienie domyślnych własności materiałów zawiera tabela 4. Zwróćmy uwagę, że parametr GL_AMBIENT_AND_DIFFUSE nie posiada domyślnych wartości.

Tabela 4: Domyślne parametry materiałów

parametr materiałuwartość
GL_AMBIENT(2/10, 2/10, 2/10, 1)
GL_DIFFUSE(8/10, 8/10, 8/10, 1)
GL_AMBIENT_AND_DIFFUSE-
GL_SPECULAR(0,0,0,1)
GL_SHININESS0
GL_EMISSION(0,0,0,1)
GL_COLOR_INDEXES(0,1,1)
Największy wpływ na kolor obiektu mają składowe GL_DIFFUSE.
Dobranie materiału o odpowiednich właściwościach nie jest zadaniem łatwym, choćby z uwagi na dużą ilość niezależnych od siebie parametrów. Podamy jednak kilka praktycznych rad przy dobieraniu materiałów. Przy materiałach matowych duże wartości powinny mieć składowe GL_DIFFUSE przy małych wartościach składowych GL_SPECULAR. Materiały imitujące tworzywa sztuczne także mają wysokie wartości składowych GL_DIFFUSE przy neutralnych (białych) składowych GL_SPECULAR. Powierzchnie metaliczne to małe wartości składowych GL_DIFFUSE i jednocześnie duże wartości składowych GL_SPECULAR. Stosownie do rodzaju powierzchni trzeba także dobrać odpowiednią wartość współczynnika GL_SHININESS.
Przykładowe definicje materiałów Czytelnik znajdzie w jednym z programów testowych.

Śledzenie kolorów

Biblioteka OpenGL umożliwia także definiowanie parametrów materiałów na podstawie kolorów wierzchołków określanych, przypomnijmy, przy pomocy funkcji z grupy glColor. Technika ta nazywana jest śledzeniem kolorów (ang. color tracking). Wybór parametrów materiału, które będą określane poprzez kolory wierzchołków, określa funkcja:
C/C++
void glColorMaterial( GLenum face, GLenum mode )
Parametr face ustala, którą stronę wielokąta dotyczy modyfikowany parametr. Dopuszczalne są wszystkie opisane wcześniej wartości: GL_FRONT, GL_BACK i GL_FRONT_AND_BACK. Wartością domyślną jest GL_FRONT_AND_BACK.
Parametr mode wskazuje, która z właściwości materiału ma być definiowana zgodnie z bieżącym kolorem wierzchołka. Dopuszczalne są niemal wszystkie opisane wcześniej własności materiałów: GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR i GL_AMBIENT_AND_DIFFUSE. Wartością domyślną jest GL_AMBIENT_AND_DIFFUSE.
Śledzenie kolorów jest domyślne wyłączone, stąd przed użyciem funkcji glColorMaterial trzeba wywołać funkcję glEnable z parametrem GL_COLOR_MATERIAL. Oczywiście wyłączenie śledzenia kolorów wymaga wywołania funkcji glDisable z parametrem GL_COLOR_MATERIAL.
Używanie techniki śledzenie kolorów jest szczególnie użyteczne, gdy na każdy wierzchołek obiektu zmieniamy tylko jedną właściwość materiału.

Wektory normalne

Do prawidłowego generowania efektów oświetlenia, a w szczególności określenia orientacji wierzchołków obiektów względem źródeł światła, biblioteka OpenGL wymaga definiowania wektorów normalnych (prostopadłych). Ponieważ obliczenia związane z oświetleniem OpenGL wykonuje dla każdego wierzchołka, stąd każdy wierzchołek ma przypisany swój wektor normalny.
Wektor normalny definiują funkcje z grupy glNormal3, które dzielą się na dwie grupy. Pierwsza grupa posiada trzy parametry określające składowe wektora normalnego:
C/C++
void glNormal3b( GLbyte nx, GLbyte ny, GLbyte nz )
void glNormal3d( GLdouble nx, GLdouble ny, GLdouble nz )
void glNormal3f( GLfloat nx, GLfloat ny, GLfloat nz )
void glNormal3i( GLint nx, GLint ny, GLint nz )
void glNormal3s( GLshort nx, GLshort ny, GLshort nz )
Druga grupa funkcji posiada jeden parametr będący wskaźnikiem na tablicę ze składowymi wektora normalnego:
C/C++
void glNormal3bv( const GLbyte * v )
void glNormal3dv( const GLdouble * v )
void glNormal3fv( const GLfloat * v )
void glNormal3iv( const GLint * v )
void glNormal3sv( const GLshort * v )
Podobnie jak w przypadku kolorów czy własności materiału dany wektor normalny obowiązuje do czasu zdefiniowania kolejnego. W szczególności można użyć jednego wektora normalnego do grupy wierzchołków opisujących płaski obiekt. Początkowa wartość wektora normalnego wynosi [0, 0, 1].
Do wyznaczania wektora normalnego można użyć iloczynu wektorowego. Przy obliczeniach wykorzystujemy wektory utworzone z krawędzi ściany obiektu, pamiętając o zachowaniu odpowiedniej orientacji. Jeżeli ściany mają sprawiać wrażenie płaskich, wówczas obliczamy wektor normalny tylko dla pierwszego wierzchołka. Pozostałe wierzchołki będą korzystały z tego samego wektora.
Jeżeli natomiast zależy nam na uzyskaniu wrażenia gładkości oświetlanego obiektu wykorzystujemy technikę uśredniania wektorów normalnych. Polega ona na obliczaniu wektora normalnego dla każdego wierzchołka obiektu 3D. Tym razem wektor ten obliczamy nie na podstawie krawędzi jednej ściany ale wszystkich krawędzi wychodzących z wierzchołka. Można do tego celu wykorzystać złożenie (sumę) wektorów normalnych wszystkich ścian zawierających dany wierzchołek. Technika ta pozwala na zwiększenie realizmu sceny bez radykalnego zwiększania złożoności renderowanych obiektów.
Aby obliczenia oświetlenia były wykonywane poprawne wektor normalny musi mieć długość jednostkową, inaczej mówiąc musi być znormalizowany (jednostkowy). Normalizację wektorów można wykonać programowo, przy czym należy uwzględniać przekształcenia macierzy modelowania, które modyfikują także wektory normalne i mogą przy okazji zmienić ich długość. W szczególności dotyczy to operacji skalowania macierzy modelowania.
Drugim sposobem normalizacji wektorów normalnych jest skorzystanie z mechanizmu udostępnianego przez OpenGL, który automatycznie normalizuje wektory normalne. W tym celu trzeba wywołać funkcję glEnable z parametrem GL_NORMALIZE. Domyślnie mechanizm ten jest nieaktywny. Wyłączenie automatycznej normalizacji wektorów normalnych wymaga wywołania funkcji glDisable z parametrem GL_NORMALIZE.
Bardzo pomocnym przy stosowaniu wektorów normalnych jest wprowadzony w wersji 1.2 biblioteki OpenGL mechanizm automatycznego skalowania jednostkowych wektorów normalnych. Przy jego zastosowaniu normalizacja wektora normalnego jest wykonywana tylko raz, a późniejsze jego modyfikacje wykonywane są wyłącznie przy skalowaniu macierzy modelowania, które, przypomnijmy, zmienia długość wektora normalnego.
Technikę automatycznego skalowania jednostkowych wektorów normalnych wprowadzono wcześniej w rozszerzeniu EXT rescale normal, a jej wykorzystanie sprowadza się do wywołania funkcji glEnable z parametrem GL_RESCALE_NORMAL (lub GL_RESCALE_NORMAL_EXT). Wyłączenie automatycznego skalowania jednostkowych wektorów normalnych wymaga wywołania funkcji glDisable z parametrem GL_RESCALE_NORMAL lub GL_RESCALE_NORMAL_EXT.

Odczyt parametrów źródła światła i materiałów

Odczyt ustawień wybranego źródła światła umożliwiają funkcje z grupy glGetLight:
C/C++
void glGetLightfv( GLenum light, GLenum pname, GLfloat * params )
void glGetLightiv( GLenum light, GLenum pname, GLint * params )
Znaczenie parametrów light i pname jest takie same jak analogicznych parametrów funkcji z grupy glLight. Wartości pobieranych ustawień źródła światła umieszczane są w tablicy, do której wskaźnik należy podać w parametrze params.
Analogicznie wygląda pobieranie ustawień bieżącego materiału, do czego służą funkcje z grupy glGetMaterial:
C/C++
void glGetMaterialfv( GLenum face, GLenum pname, GLfloat * params )
void glGetMaterialiv( GLenum face, GLenum pname, GLint * params )
Parametry face i pname są odpowiednikami parametrów funkcji z grupy glMaterial, a wartości pobieranych ustawień bieżącego materiału umieszczane są w tablicy wskazywanej przez parametr params.
Oczywiście przy korzystaniu z powyższych funkcji program musi zapewnić wystarczającą ilość miejsca w tablicy, która będzie zawierała pobierane parametry źródła światła lub materiału.

Programy przykładowe

Pierwszy przykładowy program (patrz plik globalne swiatlo otaczajace.cpp) oświetla scenę wyłącznie przy pomocy globalnego światła otaczającego i pozwala na modyfikację wartości poszczególnych składowych RGB tego światła. Jak już powiedzieliśmy na wstępie światło otaczające nie pochodzi z żadnego konkretnego kierunku i równomiernie oświetla wszystkie elementy sceny 3D. Przedstawione wcześniej funkcje z grupy glLightModel umożliwiają modyfikację globalnego światła otaczającego dla całej sceny 3D.
Pozwala to, przy braku innych źródeł światła, na równomierne oświetlenie całej sceny.
Wartości początkowe składowych tego światła przyjęte w programie odpowiadają wartościom domyślnym przyjmowanym przez bibliotekę OpenGL. Początkowy wygląd ścian: czerwonej, niebieskiej i zielonej sześcianu RGB przedstawia rysunek 2. Jak widać domyślne światło otaczające jest stosunkowo ciemne. Dla porównania rysunek 3 przedstawia te same ściany sześcianu ale przy maksymalnych wartościach składowych RGB globalnego światła otaczającego. Natomiast efekty oświetlenia obiektu sceny globalnych światłem otaczającym z wybranymi składowymi RGB o wartościach ujemnych przedstawiają rysunki 4 i 5.
Rysunek 2. Program Globalne światło otaczające - ściany RGB przy początkowych (domyślnych) wartościach składowych RGB globalnego światła otaczającego
Rysunek 2. Program Globalne światło otaczające - ściany RGB przy początkowych (domyślnych) wartościach składowych RGB globalnego światła otaczającego
Warto także zwrócić uwagę, że przy definiowaniu sześcianu nie były wykorzystywane wektory normalne. Nie ma takiej potrzeby, bowiem jak wcześniej napisaliśmy, globalne światło otaczające nie pochodzi z żadnego konkretnego kierunku.
W ramach ćwiczeń Czytelnik może łatwo sprawdzić, że wygląd sześcianu RGB przedstawiony na rysunku 3, odpowiada wyglądowi tego samego sześcianu ale przy wyłączonych efektach oświetlenia. Oznacza to, że domyślnie biblioteka OpenGL faktycznie nie wykonuje żadnych obliczeń związanych z oświetleniem, a obiekty są bezpośrednio rasteryzowane zgodnie z wartościami składowych RGB.
Rysunek 3. Program Globalne światło otaczające - ściany RGB przy maksymalnych wartościach składowych RGB globalnego światła otaczającego
Rysunek 3. Program Globalne światło otaczające - ściany RGB przy maksymalnych wartościach składowych RGB globalnego światła otaczającego
Rysunek 4. Program Globalne światło otaczające - ściany RGB przy minimalnej wartości składowej czerwonej globalnego światła otaczającego
Rysunek 4. Program Globalne światło otaczające - ściany RGB przy minimalnej wartości składowej czerwonej globalnego światła otaczającego

Plik globalne_swiatlo_otaczajace.cpp

Rysunek 5. Program Globalne światło otaczające - ściany RGB przy minimalnej wartości składowych czerwonej i niebieskiej globalnego światła otaczającego
Rysunek 5. Program Globalne światło otaczające - ściany RGB przy minimalnej wartości składowych czerwonej i niebieskiej globalnego światła otaczającego
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "colors.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;

// 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

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;

// składowe globalnego światła otaczającego
// wartość początkowa odpowiada wartości domyślnej
// składowych tego światła

GLfloat ambient_light[ 4 ] =
{
    0.2, 0.2, 0.2, 1.0
};

// funkcja rysująca napis w wybranym miejscu

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

// funkcja generująca scenę 3D

void Display()
{
    // 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();
   
    // przesunięcie układu współrzędnych sześcianu do środka bryły odcinania
    glTranslatef( 0, 0, -( near + far ) / 2 );
   
    // obroty sześcianu
    glRotatef( rotatex, 1.0, 0, 0 );
    glRotatef( rotatey, 0, 1.0, 0 );
   
    // niewielkie powiększenie sześcianu
    glScalef( 1.15, 1.15, 1.15 );
   
    // włączenie oświetlenia
    glEnable( GL_LIGHTING );
   
    // parametry globalnego światła otaczającego
    glLightModelfv( GL_LIGHT_MODEL_AMBIENT, ambient_light );
   
    // włączenie obsługi właściwości materiałów
    glEnable( GL_COLOR_MATERIAL );
   
    // właściwości materiału określone przez kolor wierzchołków
    glColorMaterial( GL_FRONT, GL_AMBIENT );
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // rysowanie sześcianu - 12 trójkątów
    glBegin( GL_TRIANGLES );
    glColor3fv( Red );
    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 );
   
    glColor3fv( Magenta );
    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 );
   
    glColor3fv( Cyan );
    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 );
   
    glColor3fv( Lime );
    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 );
   
    glColor3fv( Blue );
    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 );
   
    glColor3fv( Yellow );
    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 sześcianu
    glEnd();
   
    // wyłączenie oświetlenia
    glDisable( GL_LIGHTING );
   
    // wyłączenie obsługi właściwości materiałów
    glDisable( GL_COLOR_MATERIAL );
   
    // wyświetlenie składowych globalnego światła otaczającego
    char string[ 100 ];
    GLfloat rgba[ 4 ];
    glColor3fv( Black );
   
    // pobranie wartości składowych światła otaczającego
    // (oczywiście wartości te odpowiadają tablicy ambient_light)
    glGetFloatv( GL_LIGHT_MODEL_AMBIENT, rgba );
    sprintf( string, "AMBIENT: R=%f G=%f B=%f A=%f", rgba[ 0 ], rgba[ 1 ], rgba[ 2 ], rgba[ 3 ] );
   
    // trzeba odpowiednio przekształcić układ współrzędnych
    // aby napis znajdował się na samej "górze" bryły obcinania
    glLoadIdentity();
    glTranslatef( 0, 0, - near );
   
    // narysowanie napisu
    DrawString( left, bottom, string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

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

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // zmiana wartości składowej R
    if( key == 'R' && ambient_light[ 0 ] < 1.0 )
         ambient_light[ 0 ] += 0.05;
    else
    if( key == 'r' && ambient_light[ 0 ] > - 1.0 )
         ambient_light[ 0 ] -= 0.05;
   
    // zmiana wartości składowej G
    if( key == 'G' && ambient_light[ 1 ] < 1.0 )
         ambient_light[ 1 ] += 0.05;
    else
    if( key == 'g' && ambient_light[ 1 ] > - 1.0 )
         ambient_light[ 1 ] -= 0.05;
   
    // zmiana wartości składowej B
    if( key == 'B' && ambient_light[ 2 ] < 1.0 )
         ambient_light[ 2 ] += 0.05;
    else
    if( key == 'b' && ambient_light[ 2 ] > - 1.0 )
         ambient_light[ 2 ] -= 0.05;
   
    // narysowanie sceny
    Display();
}

// obsługa klawiszy funkcyjnych i klawiszy kursora

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

// 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 )
    {
        // 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( "Globalne światło otaczające" );
    #else
   
    glutCreateWindow( "Globalne swiatlo otaczajace" );
    #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 );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // 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( "Aspekt obrazu", MenuAspect );
    #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 przykładowy program (plik materialy.cpp) prezentuje sposób wykorzystania materiałów. Definicje materiałów, umieszczone w pliku materials.h, pochodzą pochodzą z pracy „Advanced Graphics Programming Techniques Using OpenGL” (SIGGRAPH 1998), która dostępna jest pod adresem: http://www.opengl.org.
Do prezentacji materiałów wykorzystamy kwadryki, czyli powierzchnie stopnia drugiego, udostępniane przez bibliotekę pomocniczą GLU. Nową kwadrykę tworzymy wywołując funkcję:
C/C++
GLUquadric * gluNewQuadric()
Funkcja zwraca wskaźnik do struktury, lub klasy w przypadku użycia języka C++, typu GLUquadric, który jest później używany przy wszelkich operacjach na kwadrykach. Warto zauważyć, że specyfikacja biblioteki GLU używa przy opisie kwadryk równorzędnego typu GLUquadricObj. Po zakończeniu używania kwadryki trzeba zwolnić przydzieloną jej pamięć wywołując funkcję:
C/C++
void gluDeleteQuadric( GLUquadric * state )
Biblioteka GLU udostępnia cztery rodzaje kwadryk. Są to: kula (sfera), cylinder, dysk i niepełny dysk, przy czym dwie ostatnie są figurami płaskimi. Kulę tworzy wywołanie funkcji:
C/C++
void gluSphere( GLUquadric * quadobj,
GLdouble radius, GLint slices, GLint stacks )
której parametry radius, slices i stacks maję te same znaczenie jak parametry opisywanych wcześniej funkcji glutWireSphere i glutSolidSphere. Parametr quadobj to oczywiście wskaźnik na strukturę (klasę) GLUquadric, zwrócony przez funkcję gluNewQuadric przy tworzeniu kwadryki.
Cylinder tworzony jest poprzez wywołanie funkcji:
C/C++
void gluCylinder( GLUquadric * quadobj,
GLdouble baseRadius, GLdouble topRadius, GLdouble height, GLint slices, GLint stacks )
Parametry baseRadius i topRadius określają odpowiednio promień dolnej i górnej podstawy cylindra. Promienie mogą oczywiście mieć różną wielkość, co spowoduje utworzenie stożka ściętego. W szczególnym przypadku jeden z promieni może mieć wartość 0. Jak się Czytelnik domyśla, otrzymamy wówczas stożek. W każdym przypadku obiekt rysowany jest bez górnej i dolnej podstawy. Można je utworzyć korzystając z opisanego dalej dysku.
Parametry slices i stacks mają takie same znaczenie jak w przypadku tworzenia kuli. Są to odpowiedniki południków i równoleżników na kuli. W przypadku cylindra można je określić jako wzdłużne i poprzeczne segmenty.
Kolejną kwadryką dostępną w bibliotece GLU jest dysk (koło) tworzony wywołaniem funkcji:
C/C++
void gluDisk( GLUquadric * quadobj,
GLdouble innerRadius, GLdouble outerRadius, GLint slices, GLint loops )
Parametry innerRadius i outerRadius określają promień wewnętrzny i zewnętrzny dysku. Jeżeli promień wewnętrzny jest większy od 0, to dysk zawiera w środku okrągły otwór. Taką figurę nazywamy pierścieniem kołowym. Parametry slices i loops określają ilość sektorów w kierunku obwodowym i promieniowym, z których składa się dysk.
Ostatnią kwadryką dostępną w bibliotece GLU jest częściowy dysk (wycinek kołowy). Tworzy go wywołanie funkcji:
C/C++
void gluPartialDisk( GLUquadric * quadobj, GLdouble innerRadius, GLdouble outerRadius, GLint slices, GLint loops, GLdouble startAngle, GLdouble sweepAngle )
Parametry innerRadius, outerRadius, slices i loops mają takie same znaczenie jak przy tworzeniu dysku za pomocą funkcji gluDisk. Natomiast parametry startAngle i sweepAngle określają kąt początkowy i kąt środkowy dysku. Jeżeli promień wewnętrzny, określony parametrem outerRadius, jest większy od 0, otrzymamy figurę nazywaną wycinkiem pierścienia kołowego.
Biblioteka GLU zawiera cztery funkcje umożliwiające modyfikację sposobu rysowania kwadryk. Trzy z nich zostały wykorzystane w przykładowym programie, a czwartą obsługującą teksturowanie, wykorzystamy w jednym z następnych odcinków kursu.
Sposób prezentacji kwadryki określa funkcja:
C/C++
void gluQuadricDrawStyle( GLUquadric * quadobj, GLenum drawStyle )
której parametr drawStyle przyjmuje jedną z poniższych wartości:
  • GLU_POINT - punkty,
  • GLU_LINE - odcinki,
  • GLU_FILL - pełna figura (wartość domyślna),
  • GLU_SILHOUETTE - odcinki, przy czym rysowane są tylko odcinki zewnętrzne (brzegowe) figury.
Druga funkcja określa sposób generowania wektorów normalnych kwadryk:
C/C++
void gluQuadricNormals( GLUquadric * quadobj, GLenum normals )
Parametr normals przyjmuje jedną z wartości:
  • GLU_SMOOTH - wektor normalny określany jest dla każdego wierzchołka, czyli z zastosowaniem opisywanej wcześniej techniki uśredniania wektorów normalnych (wartość domyślna),
  • GLU_FLAT - wektor normalny określany jest dla każdej ściany,
  • GLU_NONE - wektory normalne nie są generowane.
Kolejna prezentowana funkcja określa orientację generowanych wektorów
normalnych:
C/C++
void gluQuadricOrientation( GLUquadric * quadobj, GLenum orientation )
Parametr orientation przyjmuje jedną z dwóch wartości:
  • GLU_OUTSIDE - wektory normalne skierowane na zewnątrz (wartość domyślna),
  • GLU_INSIDE - wektory normalne skierowane do wewnątrz.
Oczywiście pojęcia „na zewnątrz” i „do wewnątrz” nabierają różnego znaczenia w zależności od rodzaju rysowanej kwadryki. W przypadku figur płaskich będzie to po prostu jedna ze stron figury.
Zadaniem ostatniej z opisywanych funkcji jest nałożenie dwuwymiarowej tekstury na kwadrykę:
C/C++
void gluQuadricTexture( GLUquadric * quadobj, GLboolean texture )
Parametr quadobj to oczywiście wskaźnik do struktury (klasy) GLUquadric. Drugi parametr określa czy kwadryka ma być teksturowana bieżącą teksturą (GL_TRUE) czy też nie (GL_FALSE).
Ostatnim elementem obługi kwadryk w bibliotece GLU jest możliwość zdefiniowania funkcji zwrotnej wywoływanej w momencie wystąpienia błędu. Dołączenie funkcji zwrotnej realizuje funkcja:
C/C++
void gluQuadricCallback( GLUquadric * quadobj, GLenum which, void( * fn )() )
której parametr which określa rodzaj błędu obsługiwanego przez funkcję zwrotną wskazywaną w parametrze fn. Ponieważ jednak w przypadku kwadryk biblioteka GLU nie posiada specjalnego zestawu kodów błędów, jedyną dopuszczalną wartością parametru which jest stała GLU_ERROR.
Wróćmy teraz do możliwości naszego programu. Kwadryki oświetlane są światłem GL_LIGHT0 z parametrami domyślnymi. Program z założenia nie modyfikuje żadnych ustawień tego źródła światła, umożliwia natomiast wybór z poziomu menu podręcznego wszystkich opisanych wcześniej opcji rysowania kwadryk. Z poziomu menu użytkownik może także wybrać rodzaj materiału użytego do narysowania kwadryki. Dodatkowo przy pomocy przycisków PageUp i PageDown można zmieniać ilość elementów, z których będzie złożona kwadryka. Wyświetlany cały czas na ekranie wskaźnik FPS (ang. frames per second) ilości ramek obrazu rysowanych w ciągu jednej sekundy pozwala na jednoczesną weryfikację stosunku jakości generowanego obrazu do szybkości jego generowania. Wartość FPS jest obliczana po narysowaniu co tysięcznej ramki obrazu.
Przykładowe efekty działania programu przedstawiono na rysunkach 6 - 10. W celu ujednolicenia opisu pod każdym rysunkiem po nazwie rysowanej kwadryki umieszczono parametry z jaką została narysowana. Są to kolejno: wektory normalne, orientacja, styl oraz ilość elementów użytych do rysowania kwadryki (odpowiednio ilość sektorów lub południków i równoleżników figury). Nie należy tego mylić z ilością trójkątów lub innych prymitywów graficznych użytych do narysowania figury.
Rysunek 6. Program Materiały - kula: mosiądz, GLU_SMOOTH, GLU_OUTSIDE, GLU_FILL, 10
Rysunek 	6. Program 	Materiały	-	kula: 	mosiądz, 	GLU_SMOOTH, GLU_OUTSIDE, GLU_FILL, 10

Plik materials.h

Rysunek 7. Program Materiały - cylinder: brąz, GLU_SMOOTH, GLU_OUTSIDE, GLU_FILL, 30
Rysunek 	7. Program 	Materiały	-	cylinder: 	brąz, 	GLU_SMOOTH, GLU_OUTSIDE, GLU_FILL, 30
Rysunek 8. Program Materiały - dysk: grafit, GLU_SMOOTH, GLU_OUTSIDE, GLU_LINE, 20
Rysunek 	8. Program 	Materiały 	-	dysk: 	grafit, 	GLU_SMOOTH, GLU_OUTSIDE, GLU_LINE, 20
Rysunek 9. Program Materiały - kula: perła, GLU_FLAT, GLU_OUTSIDE, GLU_FILL, 40
Rysunek 9. Program Materiały - kula: perła, GLU_FLAT, GLU_OUTSIDE, GLU_FILL, 40
Rysunek 10. Program Materiały - częściowy dysk: rubin, GLU_FLAT, GLU_OUTSIDE, GLU_POINT, 200
Rysunek 10. Program Materiały - częściowy dysk: rubin, GLU_FLAT, GLU_OUTSIDE, GLU_POINT, 200
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#ifndef __MATERIALS__H__
#define __MATERIALS__H__

#include <GL/gl.h>

// mosiądz

const GLfloat BrassAmbient[ 4 ] =
{
    0.329412, 0.223529, 0.027451, 1.000000
};

const GLfloat BrassDiffuse[ 4 ] =
{
    0.780392, 0.568627, 0.113725, 1.000000
};

const GLfloat BrassSpecular[ 4 ] =
{
    0.992157, 0.941176, 0.807843, 1.000000
};

const GLfloat BrassShininess = 27.8974;

// brąz

const GLfloat BronzeAmbient[ 4 ] =
{
    0.212500, 0.127500, 0.054000, 1.000000
};

const GLfloat BronzeDiffuse[ 4 ] =
{
    0.714000, 0.428400, 0.181440, 1.000000
};

const GLfloat BronzeSpecular[ 4 ] =
{
    0.393548, 0.271906, 0.166721, 1.000000
};

const GLfloat BronzeShininess = 25.6;

// polerowany brąz

const GLfloat PolishedBronzeAmbient[ 4 ] =
{
    0.250000, 0.148000, 0.064750, 1.000000
};

const GLfloat PolishedBronzeDiffuse[ 4 ] =
{
    0.400000, 0.236800, 0.103600, 1.000000
};

const GLfloat PolishedBronzeSpecular[ 4 ] =
{
    0.774597, 0.458561, 0.200621, 1.000000
};

const GLfloat PolishedBronzeShininess = 76.8;

// chrom

const GLfloat ChromeAmbient[ 4 ] =
{
    0.250000, 0.250000, 0.250000, 1.000000
};

const GLfloat ChromeDiffuse[ 4 ] =
{
    0.400000, 0.400000, 0.400000, 1.000000
};

const GLfloat ChromeSpecular[ 4 ] =
{
    0.774597, 0.774597, 0.774597, 1.000000
};

const GLfloat ChromeShininess = 76.8;

// miedź

const GLfloat CopperAmbient[ 4 ] =
{
    0.191250, 0.073500, 0.022500, 1.000000
};

const GLfloat CopperDiffuse[ 4 ] =
{
    0.703800, 0.270480, 0.082800, 1.000000
};

const GLfloat CopperSpecular[ 4 ] =
{
    0.256777, 0.137622, 0.086014, 1.000000
};

const GLfloat CopperShininess = 12.8;

// polerowana miedź

const GLfloat PolishedCopperAmbient[ 4 ] =
{
    0.229500, 0.088250, 0.027500, 1.000000
};

const GLfloat PolishedCopperDiffuse[ 4 ] =
{
    0.550800, 0.211800, 0.066000, 1.000000
};

const GLfloat PolishedCopperSpecular[ 4 ] =
{
    0.580594, 0.223257, 0.069570, 1.000000
};

const GLfloat PolishedCopperShininess = 51.2;

// złoto

const GLfloat GoldAmbient[ 4 ] =
{
    0.247250, 0.199500, 0.074500, 1.000000
};

const GLfloat GoldDiffuse[ 4 ] =
{
    0.751640, 0.606480, 0.226480, 1.000000
};

const GLfloat GoldSpecular[ 4 ] =
{
    0.628281, 0.555802, 0.366065, 1.000000
};

const GLfloat GoldShininess = 52.2;

// polerowane złoto

const GLfloat PolishedGoldAmbient[ 4 ] =
{
    0.247250, 0.224500, 0.064500, 1.000000
};

const GLfloat PolishedGoldDiffuse[ 4 ] =
{
    0.346150, 0.314300, 0.090300, 1.000000
};

const GLfloat PolishedGoldSpecular[ 4 ] =
{
    0.797357, 0.723991, 0.208006, 1.000000
};

const GLfloat PolishedGoldShininess = 83.2;

// cyna z ołowiem (grafit)

const GLfloat PewterAmbient[ 4 ] =
{
    0.105882, 0.058824, 0.113725, 1.000000
};

const GLfloat PewterDiffuse[ 4 ] =
{
    0.427451, 0.470588, 0.541176, 1.000000
};

const GLfloat PewterSpecular[ 4 ] =
{
    0.333333, 0.333333, 0.521569, 1.000000
};

const GLfloat PewterShininess = 9.84615;

// srebro

const GLfloat SilverAmbient[ 4 ] =
{
    0.192250, 0.192250, 0.192250, 1.000000
};

const GLfloat SilverDiffuse[ 4 ] =
{
    0.507540, 0.507540, 0.507540, 1.000000
};

const GLfloat SilverSpecular[ 4 ] =
{
    0.508273, 0.508273, 0.508273, 1.000000
};

const GLfloat SilverShininess = 51.2;

// polerowane srebro

const GLfloat PolishedSilverAmbient[ 4 ] =
{
    0.231250, 0.231250, 0.231250, 1.000000
};

const GLfloat PolishedSilverDiffuse[ 4 ] =
{
    0.277500, 0.277500, 0.277500, 1.000000
};

const GLfloat PolishedSilverSpecular[ 4 ] =
{
    0.773911, 0.773911, 0.773911, 1.000000
};

const GLfloat PolishedSilverShininess = 89.6;

// szmaragd

const GLfloat EmeraldAmbient[ 4 ] =
{
    0.021500, 0.174500, 0.021500, 0.550000
};

const GLfloat EmeraldDiffuse[ 4 ] =
{
    0.075680, 0.614240, 0.075680, 0.550000
};

const GLfloat EmeraldSpecular[ 4 ] =
{
    0.633000, 0.727811, 0.633000, 0.550000
};

const GLfloat EmeraldShininess = 76.8;

// jadeit

const GLfloat JadeAmbient[ 4 ] =
{
    0.135000, 0.222500, 0.157500, 0.950000
};

const GLfloat JadeDiffuse[ 4 ] =
{
    0.540000, 0.890000, 0.630000, 0.950000
};

const GLfloat JadeSpecular[ 4 ] =
{
    0.316228, 0.316228, 0.316228, 0.950000
};

const GLfloat JadeShininess = 12.8;

// obsydian (szkło wulkaniczne)

const GLfloat ObsidianAmbient[ 4 ] =
{
    0.053750, 0.050000, 0.066250, 0.820000
};

const GLfloat ObsidianDiffuse[ 4 ] =
{
    0.182750, 0.170000, 0.225250, 0.820000
};

const GLfloat ObsidianSpecular[ 4 ] =
{
    0.332741, 0.328634, 0.346435, 0.820000
};

const GLfloat ObsidianShininess = 38.4;

// perła

const GLfloat PearlAmbient[ 4 ] =
{
    0.250000, 0.207250, 0.207250, 0.922000
};

const GLfloat PearlDiffuse[ 4 ] =
{
    1.000000, 0.829000, 0.829000, 0.922000
};

const GLfloat PearlSpecular[ 4 ] =
{
    0.296648, 0.296648, 0.296648, 0.922000
};

const GLfloat PearlShininess = 11.264;

// rubin

const GLfloat RubyAmbient[ 4 ] =
{
    0.174500, 0.011750, 0.011750, 0.550000
};

const GLfloat RubyDiffuse[ 4 ] =
{
    0.614240, 0.041360, 0.041360, 0.550000
};

const GLfloat RubySpecular[ 4 ] =
{
    0.727811, 0.626959, 0.626959, 0.550000
};

const GLfloat RubyShininess = 76.8;

// turkus

const GLfloat TurquoiseAmbient[ 4 ] =
{
    0.100000, 0.187250, 0.174500, 0.800000
};

const GLfloat TurquoiseDiffuse[ 4 ] =
{
    0.396000, 0.741510, 0.691020, 0.800000
};

const GLfloat TurquoiseSpecular[ 4 ] =
{
    0.297254, 0.308290, 0.306678, 0.800000
};

const GLfloat TurquoiseShininess = 12.8;

// czarny plastik

const GLfloat BlackPlasticAmbient[ 4 ] =
{
    0.000000, 0.000000, 0.000000, 1.000000
};

const GLfloat BlackPlasticDiffuse[ 4 ] =
{
    0.010000, 0.010000, 0.010000, 1.000000
};

const GLfloat BlackPlasticSpecular[ 4 ] =
{
    0.500000, 0.500000, 0.500000, 1.000000
};

const GLfloat BlackPlasticShininess = 32;

// czarna guma

const GLfloat BlackRubberAmbient[ 4 ] =
{
    0.020000, 0.020000, 0.020000, 1.000000
};

const GLfloat BlackRubberDiffuse[ 4 ] =
{
    0.010000, 0.010000, 0.010000, 1.000000
};

const GLfloat BlackRubberSpecular[ 4 ] =
{
    0.040000, 0.040000, 0.040000, 1.000000
};

const GLfloat BlackRubberShininess = 10;

#endif // __MATERIALS_H__

Plik materlaly.cpp

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 <string.h>
#include <stdio.h>
#include <time.h>
#include "colors.h"
#include "materials.h"

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

enum
{
    // obiekty
    SPHERE, // kula
    CYLINDER, // cylinder
    DISK, // dysk
    PARTIAL_DISK, // częściowy dysk
   
    // 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
   
    // wektory normalne
    NORMALS_SMOOTH, // GLU_SMOOTH
    NORMALS_FLAT, // GLU_FLAT
    NORMALS_NONE, // GLU_NONE
   
    // orientacja
    ORIENTATION_OUTSIDE, // GLU_OUTSIDE
    ORIENTATION_INSIDE, // GLU_INSIDE
   
    // styl
    STYLE_POINT, // GLU_POINT
    STYLE_LINE, // GLU_LINE
    STYLE_FILL, // GLU_FILL
    STYLE_SILHOUETTE, // GLU_SILHOUETTE
   
    // 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 = - 1.0;
const GLdouble right = 1.0;
const GLdouble bottom = - 1.0;
const GLdouble top = 1.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.05;

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

// wyświetlany obiekt 3D

int object = SPHERE;

// wektory normalne

int normals = NORMALS_SMOOTH;

// orientacja

int orientation = GLU_OUTSIDE;

// styl

int style = GLU_FILL;

// ilość segmantów obiektu

int segments = 10;

// licznik ramek (FPS)

int frames = 0;

// licznik czasu

long start_time = 0;

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

char time_string[ 100 ] = "FPS:";

// funkcja rysująca napis w wybranym miejscu

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

// funkcja generująca scenę 3D

void Display()
{
    // licznik czasu
    if( !frames++ )
         start_time = clock();
   
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru i bufora głębokości
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // włączenie oświetlenia
    glEnable( GL_LIGHTING );
   
    // włączenie światła GL_LIGHT0 z parametrami domyślnymi
    glEnable( GL_LIGHT0 );
   
    // włączenie automatycznej normalizacji wektorów normalnych
    glEnable( GL_NORMALIZE );
   
    // 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ł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 );
   
    // utworzenie kwadryki
    GLUquadricObj * quadobj = gluNewQuadric();
   
    // sposób generacji wektorów normalnych
    gluQuadricNormals( quadobj, normals );
   
    // styl ((wygląd) generowanej kwadryki
    gluQuadricDrawStyle( quadobj, style );
   
    // określenie orientacji wektorów normalnych kwadryki
    gluQuadricOrientation( quadobj, orientation );
   
    // wybór rysowanego obiektu 3D
    switch( object )
    {
    case SPHERE:
        gluSphere( quadobj, 1, segments, segments );
        break;
    case CYLINDER:
        gluCylinder( quadobj, 0.5, 0.5, 1, segments, segments );
        break;
    case DISK:
        gluDisk( quadobj, 0.5, 1.0, segments, segments );
        break;
    case PARTIAL_DISK:
        gluPartialDisk( quadobj, 0.5, 1.0, segments, segments, 90, 180 );
        break;
    }
   
    // usunięcie kwadryki
    gluDeleteQuadric( quadobj );
   
    // wyłączenie oświetlenia
    glDisable( GL_LIGHTING );
   
    // wyłączenie testu bufora głębokości
    glDisable( GL_DEPTH_TEST );
   
    // trzeba odpowiednio przekształcić układ współrzędnych
    // aby napis znajdował się na samej "górze" bryły obcinania
    glLoadIdentity();
    glTranslatef( 0, 0, - near );
   
    // komunikat o ilości ramek rysowanych na sekundę (FPS)
    glColor3fv( Black );
    if( frames == 1000 )
    {
        frames = 0;
        sprintf( time_string, "FPS: %i",( int )( 1000 * CLOCKS_PER_SEC /( float )( clock() - start_time ) ) );
    }
   
    // narysowanie napisu
    DrawString( left, bottom, time_string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // 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 klawiszy funkcyjnych i klawiszy kursora

void SpecialKeys( int key, int x, int y )
{
    switch( key )
    {
        // PageUp
    case GLUT_KEY_PAGE_UP:
        segments += 10;
        break;
       
        // PageDown
    case GLUT_KEY_PAGE_DOWN:
        if( segments > 10 )
             segments -= 10;
       
        break;
    }
   
    // odrysowanie okna
    Display();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // rysowany obiekt - kula
    case SPHERE:
        object = SPHERE;
        Display();
        break;
       
        // rysowany obiekt - cylinder
    case CYLINDER:
        object = CYLINDER;
        Display();
        break;
       
        // rysowany obiekt - dysk
    case DISK:
        object = DISK;
        Display();
        break;
       
        // rysowany obiekt - częściowy dysk
    case PARTIAL_DISK:
        object = PARTIAL_DISK;
        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;
       
        // wektory normalne - GLU_SMOOTH
    case NORMALS_SMOOTH:
        normals = GLU_SMOOTH;
        Display();
        break;
       
        // wektory normalne - GLU_FLAT
    case NORMALS_FLAT:
        normals = GLU_FLAT;
        Display();
        break;
       
        // wektory normalne - GLU_NONE
    case NORMALS_NONE:
        normals = GLU_NONE;
        Display();
        break;
       
        // orientacja - GLU_OUTSIDE
    case ORIENTATION_OUTSIDE:
        orientation = GLU_OUTSIDE;
        Display();
        break;
       
        // orientacja - GLU_INSIDE
    case ORIENTATION_INSIDE:
        orientation = GLU_INSIDE;
        Display();
        break;
       
        // styl - GLU_POINT
    case STYLE_POINT:
        style = GLU_POINT;
        Display();
        break;
       
        // styl - GLU_LINE
    case STYLE_LINE:
        style = GLU_LINE;
        Display();
        break;
       
        // styl - GLU_FILL
    case STYLE_FILL:
        style = GLU_FILL;
        Display();
        break;
       
        // styl - GLU_SILHOUETTE
    case STYLE_SILHOUETTE:
        style = GLU_SILHOUETTE;
        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( "Materiały" );
    #else
   
    glutCreateWindow( "Materialy" );
    #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 );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // utworzenie podmenu - obiekt
    int MenuObject = glutCreateMenu( Menu );
    glutAddMenuEntry( "Kula", SPHERE );
    glutAddMenuEntry( "Cylinder", CYLINDER );
    glutAddMenuEntry( "Dysk", DISK );
    #ifdef WIN32
   
    glutAddMenuEntry( "Częściowy dysk", PARTIAL_DISK );
    #else
   
    glutAddMenuEntry( "Czesciowy dysk", PARTIAL_DISK );
    #endif
   
    // 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 - Wektory normalne
    int MenuNormals = glutCreateMenu( Menu );
    glutAddMenuEntry( "GLU_SMOOTH", NORMALS_SMOOTH );
    glutAddMenuEntry( "GLU_FLAT", NORMALS_FLAT );
    glutAddMenuEntry( "GLU_NONE", NORMALS_NONE );
   
    // utworzenie podmenu - Orientacja
    int MenuOrientation = glutCreateMenu( Menu );
    glutAddMenuEntry( "GLU_OUTSIDE", ORIENTATION_OUTSIDE );
    glutAddMenuEntry( "GLU_INSIDE", ORIENTATION_INSIDE );
   
    // utworzenie podmenu - Styl
    int MenuStyle = glutCreateMenu( Menu );
    glutAddMenuEntry( "GLU_POINT", STYLE_POINT );
    glutAddMenuEntry( "GLU_LINE", STYLE_LINE );
    glutAddMenuEntry( "GLU_FILL", STYLE_FILL );
    glutAddMenuEntry( "GLU_SILHOUETTE", STYLE_SILHOUETTE );
   
    // 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( "Obiekt", MenuObject );
   
    #ifdef WIN32
   
    glutAddSubMenu( "Materiał", MenuMaterial );
    #else
   
    glutAddSubMenu( "Material", MenuMaterial );
    #endif
   
    glutAddSubMenu( "Wektory normalne", MenuNormals );
    glutAddSubMenu( "Orientacja", MenuOrientation );
    glutAddSubMenu( "Styl", MenuStyle );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    #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 );
   
    // funkcja bezczynności
    glutIdleFunc( Display );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Kolejny program przykładowy (plik wektory normalne.cpp) przedstawia dwa opisane wcześniej sposoby generowania wektorów normalnych. Ale zanim przejdziemy do opisu obliczania wektorów normalnych popatrzmy na sposób opisu bryły wyświetlanej przez program - dwudziestościanu foremnego.
Dwudziestościan opisany jest przez dwie tablice. Pierwsza tablica vertex opisuje współrzędne położenia wierzchołków figury. Druga tablica triangles zawiera opis poszczególnych trójkątów, z których składa się dwudziestościan. Jedną z cech takiego rodzaju opis bryły jest łatwość obliczania wektorów normalnych. W przypadku, gdy do obliczamy wektor normalny dla ściany, wystarczy skorzystać z iloczynu wektorowego, przy czym składniki tego iloczynu stanowią wektory utworzone z dwóch boków trójkąta. W przykładowym programie realizuje to funkcja Normal. Natomiast w przypadku, gdy obliczamy wektor normalny dla każdego wierzchołka, program przegląda listę ścian (tablica triangles) wybierając te ściany, w skład których wchodzi bieżący wierzchołek i dokonuje sumowania wektorów normalnych każdej z wybranych ścian. Wykorzystany do tego celu algorytm jest wprawdzie prosty, ale także bardzo czasochłonny. Najprostszym sposobem jego optymalizacji jest jednokrotne obliczenie wektora normalnego każdej ze ścian i zapamiętanie wyników w tablicy.
Normalizacja wektorów normalnych realizowana jest w programie na dwa spsoby. Jeżeli implementacja biblioteki OpenGL obsługuje rozszerzenie EXT rescale normal, lub jest to wersja co najmniej 1.2, program korzysta z opisanego wcześniej mechanizmu automatycznego skalowania jednostkowych wektorów normalnych. W przeciwnym wypadku całość obliczeń związanych z normalizacją wykonuje biblioteka OpenGL, a funkcja Normalize, która wykonuje normalizację, nie jest wykorzystywana.
Efekty działania programu przedstawiają dwa rysunki. Pierwszy z nich (rysunek nr 11) przedstawia obiekt z wektorami normalnymi generowanymi dla każdego wierzchołka. Na rysunku łatwo można dostrzec charakterystyczne linie pokrywające się z krawędziami obiektu. Nieciągłości te, zwane pasmami Macha, są jedną z wad cieniowania Gourauda zastosowanego w bibliotece OpenGL. Na rysunku nr 12 przedstawiono bryłę w tym samym powiększeniu i położeniu, ale z wektorami normalnymi generowanymi dla każdej ściany.
Rysunek 11. Program Wektory normalne - wektory normalne generowane dla każdego wierzchołka
Rysunek 11. Program Wektory normalne - wektory normalne generowane dla każdego wierzchołka

Plik wektory_normalne.cpp

Rysunek 12. Program Wektory normalne - wektory normalne generowane dla ściany
Rysunek 12. Program Wektory normalne - wektory normalne generowane dla ściany
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

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

// wskaźnik dostępności rozszerzenia EXT_rescale_normal

bool rescale_normal = false;

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

enum
{
    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
    NORMALS_SMOOTH, // jeden wektor normalny na wierzchołek
    NORMALS_FLAT, // jeden wektor normalny na ścianę
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int aspect = FULL_WINDOW;

// usunięcie definicji makr near i far

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

// rozmiary bryły obcinania

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

// kąty obrotu

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;

// wektory normalne

int normals = NORMALS_FLAT;

// współrzędne wierzchołków dwudziestościanu

GLfloat vertex[ 12 * 3 ] =
{
    0.000, 0.667, 0.500, // v0
    0.000, 0.667, - 0.500, // v1
    0.000, - 0.667, - 0.500, // v2
    0.000, - 0.667, 0.500, // v3
    0.667, 0.500, 0.000, // v4
    0.667, - 0.500, 0.000, // v5
    - 0.667, - 0.500, 0.000, // v6
    - 0.667, 0.500, 0.000, // v7
    0.500, 0.000, 0.667, // v8
    - 0.500, 0.000, 0.667, // v9
    - 0.500, 0.000, - 0.667, // v10
    0.500, 0.000, - 0.667 // v11
};

// opis ścian dwudziestościanu

int triangles[ 20 * 3 ] =
{
    2, 10, 11,
    1, 11, 10,
    1, 10, 7,
    1, 4, 11,
    0, 1, 7,
    0, 4, 1,
    0, 9, 8,
    3, 8, 9,
    0, 7, 9,
    0, 8, 4,
    3, 9, 6,
    3, 5, 8,
    2, 3, 6,
    2, 5, 3,
    2, 6, 10,
    2, 11, 5,
    6, 7, 10,
    6, 9, 7,
    4, 5, 11,
    4, 8, 5
};

// obliczanie wektora normalnego dla wybranej ściany

void Normal( GLfloat * n, int i )
{
    GLfloat v1[ 3 ], v2[ 3 ];
   
    // obliczenie wektorów na podstawie współrzędnych wierzchołków trójkątów
    v1[ 0 ] = vertex[ 3 * triangles[ 3 * i + 1 ] + 0 ] - vertex[ 3 * triangles[ 3 * i + 0 ] + 0 ];
    v1[ 1 ] = vertex[ 3 * triangles[ 3 * i + 1 ] + 1 ] - vertex[ 3 * triangles[ 3 * i + 0 ] + 1 ];
    v1[ 2 ] = vertex[ 3 * triangles[ 3 * i + 1 ] + 2 ] - vertex[ 3 * triangles[ 3 * i + 0 ] + 2 ];
    v2[ 0 ] = vertex[ 3 * triangles[ 3 * i + 2 ] + 0 ] - vertex[ 3 * triangles[ 3 * i + 1 ] + 0 ];
    v2[ 1 ] = vertex[ 3 * triangles[ 3 * i + 2 ] + 1 ] - vertex[ 3 * triangles[ 3 * i + 1 ] + 1 ];
    v2[ 2 ] = vertex[ 3 * triangles[ 3 * i + 2 ] + 2 ] - vertex[ 3 * triangles[ 3 * i + 1 ] + 2 ];
   
    // obliczenie waktora normalnego przy pomocy iloczynu wektorowego
    n[ 0 ] = v1[ 1 ] * v2[ 2 ] - v1[ 2 ] * v2[ 1 ];
    n[ 1 ] = v1[ 2 ] * v2[ 0 ] - v1[ 0 ] * v2[ 2 ];
    n[ 2 ] = v1[ 0 ] * v2[ 1 ] - v1[ 1 ] * v2[ 0 ];
}

// normalizacja wektora

void Normalize( GLfloat * v )
{
    // obliczenie długości wektora
    GLfloat d = sqrt( v[ 0 ] * v[ 0 ] + v[ 1 ] * v[ 1 ] + v[ 2 ] * v[ 2 ] );
   
    // normalizacja wektora
    if( d )
    {
        v[ 0 ] /= d;
        v[ 1 ] /= d;
        v[ 2 ] /= d;
    }
}

// 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();
   
    // 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 testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // włączenie oświetlenia
    glEnable( GL_LIGHTING );
   
    // włączenie światła GL_LIGHT0 z parametrami domyślnymi
    glEnable( GL_LIGHT0 );
   
    // właściwości materiału
    glMaterialfv( GL_FRONT, GL_AMBIENT, ambient );
    glMaterialfv( GL_FRONT, GL_DIFFUSE, diffuse );
    glMaterialfv( GL_FRONT, GL_SPECULAR, specular );
    glMaterialf( GL_FRONT, GL_SHININESS, shininess );
   
    // włączenie automatycznej normalizacji wektorów normalnych
    // lub automatycznego skalowania jednostkowych wektorów normalnych
    if( rescale_normal == true )
         glEnable( GL_RESCALE_NORMAL );
    else
         glEnable( GL_NORMALIZE );
   
    // początek definicji obiektu
    glBegin( GL_TRIANGLES );
   
    // generowanie obiektu gładkiego - jeden uśredniony
    // wektor normalny na wierzchołek
    if( normals == NORMALS_SMOOTH )
    for( int i = 0; i < 20; i++ )
    {
        // obliczanie wektora normalnego dla pierwszego wierzchołka
        GLfloat n[ 3 ];
        n[ 0 ] = n[ 1 ] = n[ 2 ] = 0.0;
       
        // wyszukanie wszystkich ścian posiadających bieżący wierzchołek
        for( int j = 0; j < 20; j++ )
        if( 3 * triangles[ 3 * i + 0 ] == 3 * triangles[ 3 * j + 0 ] ||
        3 * triangles[ 3 * i + 0 ] == 3 * triangles[ 3 * j + 1 ] ||
        3 * triangles[ 3 * i + 0 ] == 3 * triangles[ 3 * j + 2 ] )
        {
            // dodawanie wektorów normalnych poszczególnych ścian
            GLfloat nv[ 3 ];
            Normal( nv, j );
            n[ 0 ] += nv[ 0 ];
            n[ 1 ] += nv[ 1 ];
            n[ 2 ] += nv[ 2 ];
        }
       
        // uśredniony wektor normalny jest normalizowany tylko, gdy biblioteka
        // obsługuje automatyczne skalowania jednostkowych wektorów normalnych
        if( rescale_normal == true )
             Normalize( n );
       
        glNormal3fv( n );
        glVertex3fv( & vertex[ 3 * triangles[ 3 * i + 0 ] ] );
       
        // obliczanie wektora normalnego dla drugiego wierzchołka
        n[ 0 ] = n[ 1 ] = n[ 2 ] = 0.0;
       
        // wyszukanie wszystkich ścian posiadających bieżący wierzchołek
        for( int j = 0; j < 20; j++ )
        if( 3 * triangles[ 3 * i + 1 ] == 3 * triangles[ 3 * j + 0 ] ||
        3 * triangles[ 3 * i + 1 ] == 3 * triangles[ 3 * j + 1 ] ||
        3 * triangles[ 3 * i + 1 ] == 3 * triangles[ 3 * j + 2 ] )
        {
            // dodawanie wektorów normalnych poszczególnych ścian
            GLfloat nv[ 3 ];
            Normal( nv, j );
            n[ 0 ] += nv[ 0 ];
            n[ 1 ] += nv[ 1 ];
            n[ 2 ] += nv[ 2 ];
        }
       
        // uśredniony wektor normalny jest normalizowany tylko, gdy biblioteka
        // obsługuje automatyczne skalowania jednostkowych wektorów normalnych
        if( rescale_normal == true )
             Normalize( n );
       
        glNormal3fv( n );
        glVertex3fv( & vertex[ 3 * triangles[ 3 * i + 1 ] ] );
       
        // obliczanie wektora normalnego dla trzeciego wierzchołka
        n[ 0 ] = n[ 1 ] = n[ 2 ] = 0.0;
       
        // wyszukanie wszystkich ścian posiadających bieżący wierzchołek
        for( int j = 0; j < 20; j++ )
        if( 3 * triangles[ 3 * i + 2 ] == 3 * triangles[ 3 * j + 0 ] ||
        3 * triangles[ 3 * i + 2 ] == 3 * triangles[ 3 * j + 1 ] ||
        3 * triangles[ 3 * i + 2 ] == 3 * triangles[ 3 * j + 2 ] )
        {
            // dodawanie wektorów normalnych poszczególnych ścian
            GLfloat nv[ 3 ];
            Normal( nv, j );
            n[ 0 ] += nv[ 0 ];
            n[ 1 ] += nv[ 1 ];
            n[ 2 ] += nv[ 2 ];
        }
       
        // uśredniony wektor normalny jest normalizowany tylko, gdy biblioteka
        // obsługuje automatyczne skalowania jednostkowych wektorów normalnych
        if( rescale_normal == true )
             Normalize( n );
       
        glNormal3fv( n );
        glVertex3fv( & vertex[ 3 * triangles[ 3 * i + 2 ] ] );
    }
    else
   
    // generowanie obiektu "płaskiego" - jeden wektor normalny na ścianę
    for( int i = 0; i < 20; i++ )
    {
        GLfloat n[ 3 ];
        Normal( n, i );
       
        // uśredniony wektor normalny jest normalizowany tylko, gdy biblioteka
        // obsługuje automatyczne skalowania jednostkowych wektorów normalnych
        if( rescale_normal == true )
             Normalize( n );
       
        glNormal3fv( n );
        glVertex3fv( & vertex[ 3 * triangles[ 3 * i + 0 ] ] );
        glVertex3fv( & vertex[ 3 * triangles[ 3 * i + 1 ] ] );
        glVertex3fv( & vertex[ 3 * triangles[ 3 * i + 2 ] ] );
    }
   
    // koniec definicji obiektu
    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 klawiszy funkcyjnych i klawiszy kursora

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

// 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 )
    {
        // 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;
       
        // wektory normalne - GLU_SMOOTH
    case NORMALS_SMOOTH:
        normals = NORMALS_SMOOTH;
        Display();
        break;
       
        // wektory normalne - GLU_FLAT
    case NORMALS_FLAT:
        normals = NORMALS_FLAT;
        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 );
    }
}

// sprawdzenie i przygotowanie obsługi wybranych rozszerzeń

void ExtensionSetup()
{
    // pobranie numeru wersji biblioteki OpenGL
    const char * version =( char * ) glGetString( GL_VERSION );
   
    // odczyt wersji OpenGL
    int major = 0, minor = 0;
    if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
    {
        #ifndef WIN32
        printf( "Błędny format wersji OpenGL\n" );
        #else
       
        printf( "Bledny format wersji OpenGL\n" );
        #endif
       
        exit( 0 );
    }
   
    // sprawdzenie czy jest co najmniej wersja 1.2
    if( major > 1 || minor >= 2 )
         rescale_normal = true;
    else
    // sprawdzenie czy jest obsługiwane rozszerzenie EXT_rescale_normal
    if( glutExtensionSupported( "GL_EXT_rescale_normal" ) )
         rescale_normal = true;
   
}

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
    glutCreateWindow( "Wektory normalne" );
   
    // 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 );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // 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 - Wektory normalne
    int MenuNormals = glutCreateMenu( Menu );
    #ifndef WIN32
   
    glutAddMenuEntry( "Jeden wektor normalny na wierzcholek", NORMALS_SMOOTH );
    glutAddMenuEntry( "Jeden wektor normalny na sciane", NORMALS_FLAT );
    #else
   
    glutAddMenuEntry( "Jeden wektor normalny na wierzchołek", NORMALS_SMOOTH );
    glutAddMenuEntry( "Jeden wektor normalny na ścianę", NORMALS_FLAT );
    #endif
   
    // utworzenie podmenu - aspekt obrazu
    int MenuAspect = glutCreateMenu( Menu );
    #ifndef WIN32
   
    glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
    #else
   
    glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
    #endif
   
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
   
    // menu główne
    glutCreateMenu( Menu );
   
    #ifdef WIN32
   
    glutAddSubMenu( "Materiał", MenuMaterial );
    #else
   
    glutAddSubMenu( "Material", MenuMaterial );
    #endif
   
    glutAddSubMenu( "Wektory normalne", MenuNormals );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    #ifndef WIN32
   
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Czwarty program przykładowy (plik swiatlo kierunkowe.cpp) prezentuje podstawową właściwość światła kierunkowego, czyli możliwość zmiany kierunku, z którego padają promienie światła. Wektor kierunku źródła światła jest modyfikowany zgodnie z przekształceniami macierzy modelowania, stąd, aby zmiana kierunku źródła światła odbywała się w sposób niezależny od obrotu obiektu umieszczonego na scenie, macierz modelowania należy odłożyć na stos korzystając z funkcji glPushMatrix. Po wykonaniu niezbędnych przekształceń (w przypadku światła kierunkowego będą to oczywiście tylko obroty), przywracamy odłożoną na stos macierz modelowania, wywołując funkcję glPopMatrix. Aby przekształcenie kierunku światła zostały wykonane trzeba jeszcze dla wybranego źródła światła wywołać funkcję glLightfv (lub inną z tej grupy) podając przy tym tablicę zawierającą wektor z kierunkiem źródła światła.
W odróżnieniu od drugiego programu przykładowego do rysowania obiektów w omawianym programie użyto funkcji z biblioteki GLUT. Dwie z nich (rysujące kulę i sześcian) omówiliśmy już wcześniej. Pozostałe funkcje nie różną się parametrami od ich wcześniej opisywanych wersji rysujących obiekty w postaci siatek krawędzi.
Stożek (rysunek 15), składający się z równoległych do podstawy „południków” i tworzących biegnących od wierzchołka do krawędzi podstawy stożka, rysuje funkcja:
C/C++
void glutSolidCone( GLdouble base, GLdouble height, GLint slices, GLint stacks )
której parametry oznaczaja:
  • base - promień podstawy stożka,
  • height - wysokość stożka,
  • slices - ilość tworzących,
  • stacks - ilość „południków”.
Torus (rysunek 16), składający się z serii walców o nierównoległych podstawach, rysuje funkcja:
C/C++
void glutSolidTorus( GLdouble innerRadius, GLdouble outerRadius, GLint sides, GLint rings )
której parametry oznaczają odpowiednio:
  • innerRadius - promień koła tworzącego torus,
  • outerRadius - promień torusa,
  • sides - ilość ścian bocznych, z których składa się pojedynczy walec,
  • rings - ilość walców, z których składa sie torus.
Czajnik o wielkości regulowanej parametrem size rysuje funkcja:
C/C++
void glutSolidTeapot( GLdouble size )
Pozostałe użyte w programie funkcje rysują bryły Platońskie. Są to kolejno: dwunastościan (rysunek 17), ośmiościan, czworościan i dwudziestościan:
C/C++
void glutSolidDodecahedron()
void glutSolidOctahedron()
void glutSolidTetrahedron()
void glutSolidIcosahedron()
Ponadto w programie wykorzystano funkcję glWindowPos2i wprowadzoną w wersji 1.4 biblioteki OpenGL, a wcześniej dostępną w opisywanym już rozszerzeniu ARB window pos. Funkcja ta została zastosowana do pozycjonowania tekstów wyświetlanych w oknie programu. A są to współrzędne określające kierunek źródła światła oraz kąty obrotu kierunku źródła światła.
Rysunek 13. Program Światło kierunkowe - rubinowy czajnik
Rysunek 13. Program Światło kierunkowe - rubinowy czajnik

Plik swiatlo_kierunkowe.cpp

Rysunek 14. Program Światło kierunkowe - jadeitowa kula
Rysunek 14. Program Światło kierunkowe - jadeitowa kula
Rysunek 15. Program Światło kierunkowe - złoty stożek
Rysunek 15. Program Światło kierunkowe - złoty stożek
Rysunek 16. Program Światło kierunkowe - szmaragdowy torus
Rysunek 16. Program Światło kierunkowe - szmaragdowy torus
Rysunek 17. Program Światło kierunkowe - grafitowy dwunastościan
Rysunek 17. Program Światło kierunkowe - grafitowy dwunastościan
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#ifndef WIN32
#define GLX_GLXEXT_LEGACY
#include <GL/glx.h>
#define wglGetProcAddress glXGetProcAddressARB
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "colors.h"
#include "materials.h"

// wskaźnik na funkcję glWindowPos2i

PFNGLWINDOWPOS2IPROC glWindowPos2i = NULL;

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

enum
{
    // obiekty
    SPHERE, // kula
    TEAPOT, // czajnik
    CONE, // stożek
    TORUS, // torus
    CUBE, // sześcian
    DODECAHEDRON, // dwunastościan
    OCTAHEDRON, // ośmiościan
    TETRAHEDRON, // czworościan
    ICOSAHEDRON, // dwudziestościan
   
    // 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;

// usunięcie definicji makr near i far

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

// rozmiary bryły obcinania

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

// kąty obrotu obiektu

GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;

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

int button_state = GLUT_UP;

// położenie kursora myszki

int button_x, button_y;

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

// wyświetlany obiekt 3D

int object = SPHERE;

// kierunek źródła światła

GLfloat light_position[ 4 ] =
{
    0.0, 0.0, 2.0, 0.0
};

// kąty obrotu kierunku źródła światła

GLfloat light_rotatex = 0.0;
GLfloat light_rotatey = 0.0;

// funkcja rysująca napis w wybranym miejscu
// (wersja korzystająca z funkcji glWindowPos2i)

void DrawString( GLint x, GLint y, char * string )
{
    // położenie napisu
    glWindowPos2i( x, y );
   
    // wyświetlenie napisu
    int len = strlen( string );
    for( int i = 0; i < len; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}
// funkcja generująca scenę 3D

void DisplayScene()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru i bufora głębokości
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // włączenie oświetlenia
    glEnable( GL_LIGHTING );
   
    // włączenie światła GL_LIGHT0
    glEnable( GL_LIGHT0 );
   
    // włączenie automatycznej normalizacji wektorów normalnych
    glEnable( GL_NORMALIZE );
   
    // 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łaściwości materiału
    glMaterialfv( GL_FRONT, GL_AMBIENT, ambient );
    glMaterialfv( GL_FRONT, GL_DIFFUSE, diffuse );
    glMaterialfv( GL_FRONT, GL_SPECULAR, specular );
    glMaterialf( GL_FRONT, GL_SHININESS, shininess );
   
    // zmiana kierunku źródła światła jest wykonywana niezależnie
    // od obrotów obiektu, stąd odłożenie na stos macierzy modelowania
    glPushMatrix();
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // obroty kierunku źródła światła - klawisze kursora
    glRotatef( light_rotatex, 1.0, 0, 0 );
    glRotatef( light_rotatey, 0, 1.0, 0 );
   
    // ustalenie kierunku źródła światła
    glLightfv( GL_LIGHT0, GL_POSITION, light_position );
   
    // przywrócenie pierwotnej macierzy modelowania
    glPopMatrix();
   
    // rysowanie wybranego obiektu 3D
    switch( object )
    {
        // kula
    case SPHERE:
        glutSolidSphere( 1.0, 50, 40 );
        break;
       
        // czajnik
    case TEAPOT:
        glutSolidTeapot( 1 );
        break;
       
        // stożek
    case CONE:
        glutSolidCone( 1, 1, 50, 40 );
        break;
       
        // torus
    case TORUS:
        glutSolidTorus( 0.3, 1, 40, 50 );
        break;
       
        // sześcian
    case CUBE:
        glutSolidCube( 1 );
        break;
       
        // dwunastościan
    case DODECAHEDRON:
        glutSolidDodecahedron();
        break;
       
        // ośmiościan
    case OCTAHEDRON:
        glutSolidOctahedron();
        break;
       
        // czworościan
    case TETRAHEDRON:
        glutSolidTetrahedron();
        break;
       
        // dwudziestościan
    case ICOSAHEDRON:
        glutSolidIcosahedron();
        break;
    }
   
    // informacje o modyfikowanych wartościach
    // parametrów źródła światała GL_LIGHT0
    char string[ 200 ];
    GLfloat vec[ 4 ];
    glColor3fv( Black );
   
    // kierunek źródła światła
    glGetLightfv( GL_LIGHT0, GL_POSITION, vec );
    sprintf( string, "GL_POSITION = (%f,%f,%f,%f)", vec[ 0 ], vec[ 1 ], vec[ 2 ], vec[ 3 ] );
    DrawString( 2, 2, string );
   
    // kąty obrotu kierunku źródła światła
    sprintf( string, "light_rotatex = %f", light_rotatex );
    DrawString( 2, 16, string );
    sprintf( string, "light_rotatey = %f", light_rotatey );
    DrawString( 2, 30, string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

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

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    switch( key )
    {
        // klawisz "+" - powiększenie obiektu
    case '+':
        scale += 0.05;
        break;
       
        // klawisz "-" - zmniejszenie obiektu
    case '-':
        if( scale > 0.05 )
             scale -= 0.05;
       
        break;
    }
   
    // narysowanie sceny
    DisplayScene();
}

// obsługa przycisków myszki

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

// obsługa ruchu kursora myszki

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

// obsługa klawiszy funkcyjnych i klawiszy kursora

void SpecialKeys( int key, int x, int y )
{
    switch( key )
    {
        // kursor w lewo
    case GLUT_KEY_LEFT:
        light_rotatey -= 5;
        break;
       
        // kursor w prawo
    case GLUT_KEY_RIGHT:
        light_rotatey += 5;
        break;
       
        // kursor w dół
    case GLUT_KEY_DOWN:
        light_rotatex += 5;
        break;
       
        // kursor w górę
    case GLUT_KEY_UP:
        light_rotatex -= 5;
        break;
    }
   
    // odrysowanie okna
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // rysowany obiekt - kula
    case SPHERE:
        object = SPHERE;
        DisplayScene();
        break;
       
        // rysowany obiekt - czajnik
    case TEAPOT:
        object = TEAPOT;
        DisplayScene();
        break;
       
        // rysowany obiekt - stożek
    case CONE:
        object = CONE;
        DisplayScene();
        break;
       
        // rysowany obiekt - torus
    case TORUS:
        object = TORUS;
        DisplayScene();
        break;
       
        // rysowany obiekt - sześcian
    case CUBE:
        object = CUBE;
        DisplayScene();
        break;
       
        // rysowany obiekt - dwunastościan
    case DODECAHEDRON:
        object = DODECAHEDRON;
        DisplayScene();
        break;
       
        // rysowany obiekt - ośmiościan
    case OCTAHEDRON:
        object = OCTAHEDRON;
        DisplayScene();
        break;
       
        // rysowany obiekt - czworościan
    case TETRAHEDRON:
        object = TETRAHEDRON;
        DisplayScene();
        break;
       
        // rysowany obiekt - dwudziestościan
    case ICOSAHEDRON:
        object = ICOSAHEDRON;
        DisplayScene();
        break;
       
        // materiał - mosiądz
    case BRASS:
        ambient = BrassAmbient;
        diffuse = BrassDiffuse;
        specular = BrassSpecular;
        shininess = BrassShininess;
        DisplayScene();
        break;
       
        // materiał - brąz
    case BRONZE:
        ambient = BronzeAmbient;
        diffuse = BronzeDiffuse;
        specular = BronzeSpecular;
        shininess = BronzeShininess;
        DisplayScene();
        break;
       
        // materiał - polerowany brąz
    case POLISHED_BRONZE:
        ambient = PolishedBronzeAmbient;
        diffuse = PolishedBronzeDiffuse;
        specular = PolishedBronzeSpecular;
        shininess = PolishedBronzeShininess;
        DisplayScene();
        break;
       
        // materiał - chrom
    case CHROME:
        ambient = ChromeAmbient;
        diffuse = ChromeDiffuse;
        specular = ChromeSpecular;
        shininess = ChromeShininess;
        DisplayScene();
        break;
       
        // materiał - miedź
    case COPPER:
        ambient = CopperAmbient;
        diffuse = CopperDiffuse;
        specular = CopperSpecular;
        shininess = CopperShininess;
        DisplayScene();
        break;
       
        // materiał - polerowana miedź
    case POLISHED_COPPER:
        ambient = PolishedCopperAmbient;
        diffuse = PolishedCopperDiffuse;
        specular = PolishedCopperSpecular;
        shininess = PolishedCopperShininess;
        DisplayScene();
        break;
       
        // materiał - złoto
    case GOLD:
        ambient = GoldAmbient;
        diffuse = GoldDiffuse;
        specular = GoldSpecular;
        shininess = GoldShininess;
        DisplayScene();
        break;
       
        // materiał - polerowane złoto
    case POLISHED_GOLD:
        ambient = PolishedGoldAmbient;
        diffuse = PolishedGoldDiffuse;
        specular = PolishedGoldSpecular;
        shininess = PolishedGoldShininess;
        DisplayScene();
        break;
       
        // materiał - grafit (cyna z ołowiem)
    case PEWTER:
        ambient = PewterAmbient;
        diffuse = PewterDiffuse;
        specular = PewterSpecular;
        shininess = PewterShininess;
        DisplayScene();
        break;
       
        // materiał - srebro
    case SILVER:
        ambient = SilverAmbient;
        diffuse = SilverDiffuse;
        specular = SilverSpecular;
        shininess = SilverShininess;
        DisplayScene();
        break;
       
        // materiał - polerowane srebro
    case POLISHED_SILVER:
        ambient = PolishedSilverAmbient;
        diffuse = PolishedSilverDiffuse;
        specular = PolishedSilverSpecular;
        shininess = PolishedSilverShininess;
        DisplayScene();
        break;
       
        // materiał - szmaragd
    case EMERALD:
        ambient = EmeraldAmbient;
        diffuse = EmeraldDiffuse;
        specular = EmeraldSpecular;
        shininess = EmeraldShininess;
        DisplayScene();
        break;
       
        // materiał - jadeit
    case JADE:
        ambient = JadeAmbient;
        diffuse = JadeDiffuse;
        specular = JadeSpecular;
        shininess = JadeShininess;
        DisplayScene();
        break;
       
        // materiał - obsydian
    case OBSIDIAN:
        ambient = ObsidianAmbient;
        diffuse = ObsidianDiffuse;
        specular = ObsidianSpecular;
        shininess = ObsidianShininess;
        DisplayScene();
        break;
       
        // materiał - perła
    case PEARL:
        ambient = PearlAmbient;
        diffuse = PearlDiffuse;
        specular = PearlSpecular;
        shininess = PearlShininess;
        DisplayScene();
        break;
       
        // metariał - rubin
    case RUBY:
        ambient = RubyAmbient;
        diffuse = RubyDiffuse;
        specular = RubySpecular;
        shininess = RubyShininess;
        DisplayScene();
        break;
       
        // materiał - turkus
    case TURQUOISE:
        ambient = TurquoiseAmbient;
        diffuse = TurquoiseDiffuse;
        specular = TurquoiseSpecular;
        shininess = TurquoiseShininess;
        DisplayScene();
        break;
       
        // materiał - czarny plastik
    case BLACK_PLASTIC:
        ambient = BlackPlasticAmbient;
        diffuse = BlackPlasticDiffuse;
        specular = BlackPlasticSpecular;
        shininess = BlackPlasticShininess;
        DisplayScene();
        break;
       
        // materiał - czarna guma
    case BLACK_RUBBER:
        ambient = BlackRubberAmbient;
        diffuse = BlackRubberDiffuse;
        specular = BlackRubberSpecular;
        shininess = BlackRubberShininess;
        DisplayScene();
        break;
       
        // obszar renderingu - całe okno
    case FULL_WINDOW:
        aspect = FULL_WINDOW;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // obszar renderingu - aspekt 1:1
    case ASPECT_1_1:
        aspect = ASPECT_1_1;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

// sprawdzenie i przygotowanie obsługi wybranych rozszerzeń

void ExtensionSetup()
{
    // pobranie numeru wersji biblioteki OpenGL
    const char * version =( char * ) glGetString( GL_VERSION );
   
    // odczyt wersji OpenGL
    int major = 0, minor = 0;
    if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
    {
        #ifndef WIN32
        printf( "Błędny format wersji OpenGL\n" );
        #else
       
        printf( "Bledny format wersji OpenGL\n" );
        #endif
       
        exit( 0 );
    }
   
    // sprawdzenie czy jest co najmniej wersja 1.4
    if( major > 1 || minor >= 4 )
    {
        // pobranie wskaźnika wybranej funkcji OpenGL 1.4
        glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress( "glWindowPos2i" );
    }
    else
    // sprawdzenie czy jest obsługiwane rozszerzenie ARB_window_pos
    if( glutExtensionSupported( "GL_ARB_window_pos" ) )
    {
        // pobranie wskaźnika wybranej funkcji rozszerzenia ARB_window_pos
        glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress
        ( "glWindowPos2iARB" );
    }
    else
    {
        printf( "Brak rozszerzenia ARB_window_pos!\n" );
        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( "Światło kierunkowe" );
    #else
   
    glutCreateWindow( "Swiatlo kierunkowe" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // dołączenie funkcji obsługi klawiatury
    glutKeyboardFunc( Keyboard );
   
    // dołączenie funkcji obsługi klawiszy funkcyjnych i klawiszy kursora
    glutSpecialFunc( SpecialKeys );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // utworzenie podmenu - obiekt
    int MenuObject = glutCreateMenu( Menu );
    glutAddMenuEntry( "Kula", SPHERE );
    glutAddMenuEntry( "Czajnik", TEAPOT );
   
    #ifdef WIN32
   
    glutAddMenuEntry( "Stożek", CONE );
    glutAddMenuEntry( "Torus", TORUS );
    glutAddMenuEntry( "Sześcian", CUBE );
    glutAddMenuEntry( "Dwunastościan", DODECAHEDRON );
    glutAddMenuEntry( "Ośmiościan", OCTAHEDRON );
    glutAddMenuEntry( "Czworościan", TETRAHEDRON );
    glutAddMenuEntry( "Dwudziestościan", ICOSAHEDRON );
    #else
   
    glutAddMenuEntry( "Stozek", CONE );
    glutAddMenuEntry( "Torus", TORUS );
    glutAddMenuEntry( "Szescian", CUBE );
    glutAddMenuEntry( "Dwunastoscian", DODECAHEDRON );
    glutAddMenuEntry( "Osmioscian", OCTAHEDRON );
    glutAddMenuEntry( "Czworoscian", TETRAHEDRON );
    glutAddMenuEntry( "Dwudziestoscian", ICOSAHEDRON );
    #endif
   
    // 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( "Obiekt", MenuObject );
   
    #ifdef WIN32
   
    glutAddSubMenu( "Materiał", MenuMaterial );
    #else
   
    glutAddSubMenu( "Material", MenuMaterial );
    #endif
   
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    #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 );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Ostatni program przykładowy (plik reflektor.cpp) prezentuje sposób wykorzystania punktowego światła kierunkowego, tzw. reflektora. Ponadto program umożliwia zmianę wybranych parametrów pracy reflektora. Wartości wszystkich modyfikowanych parametrów źródła światła wyświetlane są na dole okna programu.
W celu łatwiejszej interpretacji uzyskanego efektu oświetlenia obiektu w miejscu położenia źródła światła program rysuje niewielką czerwoną kulę (barwa kuli nie ma nic wspólnego z kolorem źródła światła). Jednym ze sposobów aby kula otrzymała tę barwę jest chwilowe wyłączenie źródła światła GL_LIGHT0. Najłatwiej jest to zrealizować odkładając na stos zmienne stanu dotyczące oświetlenia, a następnie po modyfikacji wybranych parametrów materiału i narysowaniu kuli pobrać ze stosu wcześniej odłożone zmienne.
Odłożenie na stos wybranych grup zmiennych stanu realizuje funkcja:
C/C++
void glPushAttrib( GLbitfield mask )
Parametr mask to maska bitowa określająca jaka grupa lub grupy stanu mają zostać odłożone na stos. Grupy zmiennych stanu określają następujące stałe:
  • GL_ACCUM_BUFFER_BIT - wartość bufora akumulacyjnego,
  • GL_COLOR_BUFFER_BIT - ustawienia bufora koloru,
  • GL_CURRENT_BIT - aktualne kolory i współrzędne, w tym m.in. wektor normalny,
  • GL_DEPTH_BUFFER_BIT - ustawienia bufora głębokości,
  • GL_ENABLE_BIT - wartości znaczników,
  • GL_EVAL_BIT - ustawienia mechanizmu automatycznego obliczania wartości,
  • GL_FOG_BIT - ustawienia mgły,
  • GL_HINT_BIT - ustawienia wskazówek renderingu,
  • GL_LIGHTING_BIT - ustawienia oświetlenia (stała użyta w przykładowym programie),
  • GL_LINE_BIT - ustawienia renderingu linii,
  • GL_LIST_BIT - ustawienia list wyświetlania,
  • GL_MULTISAMPLE_BIT - ustawienia wielopróbkowania (multisamlingu),
  • GL_PIXEL_MODE_BIT - ustawienia przekształceń pikseli,
  • GL_POINT_BIT - ustawienia renderingu punktów,
  • GL_POLYGON_BIT - ustawienia renderingu wielokątów,
  • GL_POLYGON_STIPPLE_BIT - wzór wypełnienia wielokątów,
  • GL_SCISSOR_BIT - ustawienia obcinania obrazu,
  • GL_STENCIL_BUFFER_BIT - ustawienia bufora szablonowego,
  • GL_TEXTURE_BIT - ustawienia teksturowania,
  • GL_TRANSFORM_BIT - ustawienia przekształceń i bryły obcinania,
  • GL_VIEWPORT_BIT - ustawienia widoku,
  • GL_ALL_ATTRIB_BITS - wszystkie grupy zmiennych maszyny stanu biblioteki OpenGL.
Stałe te można w razie potrzeby łączyć przy pomocy bitowych operatorów logicznych. Zauważmy, że dwie spośród wymienionych stałych używamy już od dłuższego czasu w każdym przykładowym programie. Są to stałe: GL_COLOR_BUFFER_BIT i GL_DEPTH_BUFFER_BIT.
Przywrócenie wartości zmiennych stanu wcześniej odłożonych na stos realizuje funkcja:
C/C++
void glPopAttrib()
Dodajmy jeszcze, że stała GL_MULTISAMPLE_BIT została wprowadzona w wersji 1.3 biblioteki OpenGL. Wcześniej technikę wielopróbkowania obsługiwały rozszerzenia ARB multisample i SGIS multisample, przy czym opisywana stała miała odpowiednio oznaczenia: GL_MULTISAMPLE_BIT_ARB i GL_MULTISAMPLE_BIT_EXT.
Efekty działania programu ilustrują trzy rysunki. Pierwszy rysunek (18) przedstawia oświetlenie obiektu silnie skupioną i zarazem wąską wiązką światła. Efekt jest zgodny z oczekiwaniami. Także drugi rysunek (19), przedstawiający oświetlenie obiektu wąską ale słabo skupiona wiązką światła, daje spodziewany efekt. Jednak na trzecim rysunku (20) widzimy, że próba oświetlenia światłem kierunkowym obiektu posiadającego małą ilość wierzchołków może dać w efekcie brak oświetlenia. Jest to związane z modelem oświetlenia Gourauda przyjętym w bibliotece OpenGL, który wylicza oświetlenie wyłącznie dla wierzchołków. Jeżeli zatem w wiązce światła reflektora nie znajdzie się żaden wierzchołek oświetlanego obiektu, OpenGL nie wykona dla tego obiektu obliczeń związanych z oświetleniem.
Choć opisanej wady modelu oświetlenia biblioteki OpenGL nie można usunąć, to stosunkowo łatwo można uniknąć związanych z tym objawów. Najprostszą i najabardziej skuteczną metodą jest zwiększenie ilości wierzchołków, z których składa się obiekt.

Plik reflektor.cpp

Rysunek 18. Program Reflektor - silnie skupiona i wąska wiązka światła
Rysunek 18. Program Reflektor - silnie skupiona i wąska wiązka światła
Rysunek 19. Program Reflektor - wąska ale słabo skupiona wiązka światła
Rysunek 19. Program Reflektor - wąska ale słabo skupiona wiązka światła
Rysunek 20. Program Reflektor - efektu braku oświetlenia powierzchni
Rysunek 20. Program Reflektor - efektu braku oświetlenia powierzchni
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#ifndef WIN32
#define GLX_GLXEXT_LEGACY
#include <GL/glx.h>
#define wglGetProcAddress glXGetProcAddressARB
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "colors.h"
#include "materials.h"

// wskaźnik na funkcję glWindowPos2i

PFNGLWINDOWPOS2IPROC glWindowPos2i = NULL;

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

enum
{
    // obiekty
    SPHERE, // kula
    TEAPOT, // czajnik
    CONE, // stożek
    TORUS, // torus
    CUBE, // sześcian
    DODECAHEDRON, // dwunastościan
    OCTAHEDRON, // ośmiościan
    TETRAHEDRON, // czworościan
    ICOSAHEDRON, // dwudziestościan
   
    // 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;

// usunięcie definicji makr near i far

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

// rozmiary bryły obcinania

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

// kąty obrotu obiektu

GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;

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

int button_state = GLUT_UP;

// położenie kursora myszki

int button_x, button_y;

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

// wyświetlany obiekt 3D

int object = SPHERE;

// kierunek źródła światła

GLfloat light_position[ 4 ] =
{
    0.0, 0.0, 2.0, 1.0
};

// kąty obrotu położenia źródła światła

GLfloat light_rotatex = 0.0;
GLfloat light_rotatey = 0.0;

// kierunek reflektora

GLfloat spot_direction[ 3 ] =
{
    0.0, 0.0, - 1.0
};

// kąt odcięcia reflektora

GLfloat spot_cutoff = 180.0;

// wykładnik tłumienia kątowego reflektora

GLfloat spot_exponent = 128.0;

// stały współczynnik tłumienia światła

GLfloat constant_attenuation = 1.0;

// liniowy współczynnik tłumienia światła

GLfloat linear_attenuation = 0.0;

// kwadratowy współczynnik tłumienia światła

GLfloat quadratic_attenuation = 0.0;

// funkcja rysująca napis w wybranym miejscu
// (wersja korzystająca z funkcji glWindowPos2i)

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

// funkcja generująca scenę 3D

void DisplayScene()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru i bufora głębokości
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // włączenie oświetlenia
    glEnable( GL_LIGHTING );
   
    // włączenie światła GL_LIGHT0
    glEnable( GL_LIGHT0 );
   
    // włączenie automatycznej normalizacji wektorów normalnych
    glEnable( GL_NORMALIZE );
   
    // 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łaściwości materiału
    glMaterialfv( GL_FRONT, GL_AMBIENT, ambient );
    glMaterialfv( GL_FRONT, GL_DIFFUSE, diffuse );
    glMaterialfv( GL_FRONT, GL_SPECULAR, specular );
    glMaterialf( GL_FRONT, GL_SHININESS, shininess );
   
    // kąt odcięcia reflektora
    glLightf( GL_LIGHT0, GL_SPOT_CUTOFF, spot_cutoff );
   
    // wykładnik tłumienia kątowego reflektora
    glLightf( GL_LIGHT0, GL_SPOT_EXPONENT, spot_exponent );
   
    // stały współczynnik tłumienia światła,
    glLightf( GL_LIGHT0, GL_CONSTANT_ATTENUATION, constant_attenuation );
   
    // liniowy współczynnik tłumienia światła
    glLightf( GL_LIGHT0, GL_LINEAR_ATTENUATION, linear_attenuation );
   
    // kwadratowy współczynnik tłumienia światła
    glLightf( GL_LIGHT0, GL_QUADRATIC_ATTENUATION, quadratic_attenuation );
   
    // zmiana położenia źródła światła jest wykonywana niezależnie
    // od obrotów obiektu, stąd odłożenie na stos macierzy modelowania
    glPushMatrix();
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // przesunięcie układu współrzędnych źródła światła do środka bryły odcinania
    glTranslatef( 0, 0, -( near + far ) / 2 );
   
    // obroty położenia źródła światła - klawisze kursora
    glRotatef( light_rotatex, 1.0, 0, 0 );
    glRotatef( light_rotatey, 0, 1.0, 0 );
   
    // przesunięcie źródła światła
    glTranslatef( light_position[ 0 ], light_position[ 1 ], light_position[ 2 ] );
   
    // ustalenie pozycji źródła światła
    glLightfv( GL_LIGHT0, GL_POSITION, light_position );
   
    // ustalenie kierunku reflektora
    glLightfv( GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction );
   
    // odłożenie na stos zmiennych stanu związanych z oświetleniem sceny
    glPushAttrib( GL_LIGHTING_BIT );
   
    // wyłacznie źródła światła GL_LIGHT0
    glDisable( GL_LIGHT0 );
   
    // materiał imitujący świecenie kuli na czerwono
    glMaterialfv( GL_FRONT, GL_EMISSION, Red );
   
    // narysowanie kuli położonej w początku źródła światła
    glutSolidSphere( 0.1, 30, 20 );
   
    // przywrócenie zmiennych stanu związanych z oświetleniem sceny
    glPopAttrib();
   
    // przywrócenie pierwotnej macierzy modelowania
    glPopMatrix();
   
    // rysowanie wybranego obiektu 3D
    switch( object )
    {
        // kula
    case SPHERE:
        glutSolidSphere( 1.0, 50, 40 );
        break;
       
        // czajnik
    case TEAPOT:
        glutSolidTeapot( 1 );
        break;
       
        // stożek
    case CONE:
        glutSolidCone( 1, 1, 50, 40 );
        break;
       
        // torus
    case TORUS:
        glutSolidTorus( 0.3, 1, 40, 50 );
        break;
       
        // sześcian
    case CUBE:
        glutSolidCube( 1 );
        break;
       
        // dwunastościan
    case DODECAHEDRON:
        glutSolidDodecahedron();
        break;
       
        // ośmiościan
    case OCTAHEDRON:
        glutSolidOctahedron();
        break;
       
        // czworościan
    case TETRAHEDRON:
        glutSolidTetrahedron();
        break;
       
        // dwudziestościan
    case ICOSAHEDRON:
        glutSolidIcosahedron();
        break;
    }
   
    // informacje o wartościach modyfikowanych
    // parametrów źródła światała GL_LIGHT0
    char string[ 200 ];
    GLfloat vec[ 4 ];
    glColor3fv( Black );
   
    // kierunek źródła światła
    glGetLightfv( GL_LIGHT0, GL_POSITION, vec );
    sprintf( string, "GL_POSITION = (%f,%f,%f,%f)", vec[ 0 ], vec[ 1 ], vec[ 2 ], vec[ 3 ] );
    DrawString( 2, 2, string );
   
    glGetLightfv( GL_LIGHT0, GL_SPOT_DIRECTION, vec );
    sprintf( string, "GL_SPOT_DIRECTION = (%f,%f,%f)", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
    DrawString( 2, 16, string );
   
    // kąt odcięcia reflektora
    glGetLightfv( GL_LIGHT0, GL_SPOT_CUTOFF, vec );
    sprintf( string, "GL_SPOT_CUTOFF = %f", vec[ 0 ] );
    DrawString( 2, 30, string );
   
    // wykładnik tłumienia kątowego reflektora
    glGetLightfv( GL_LIGHT0, GL_SPOT_EXPONENT, vec );
    sprintf( string, "GL_SPOT_EXPONENT = %f", vec[ 0 ] );
    DrawString( 2, 44, string );
   
    // stały współczynnik tłumienia światła,
    glGetLightfv( GL_LIGHT0, GL_CONSTANT_ATTENUATION, vec );
    sprintf( string, "GL_CONSTANT_ATTENUATION = %f", vec[ 0 ] );
    DrawString( 2, 58, string );
   
    // liniowy współczynnik tłumienia światła
    glGetLightfv( GL_LIGHT0, GL_LINEAR_ATTENUATION, vec );
    sprintf( string, "GL_LINEAR_ATTENUATION = %f", vec[ 0 ] );
    DrawString( 2, 72, string );
   
    // kwadratowy współczynnik tłumienia światła
    glGetLightfv( GL_LIGHT0, GL_QUADRATIC_ATTENUATION, vec );
    sprintf( string, "GL_QUADRATIC_ATTENUATION = %f", vec[ 0 ] );
    DrawString( 2, 86, string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

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

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    switch( key )
    {
        // klawisz "+" - powiększenie obiektu
    case '+':
        scale += 0.05;
        break;
       
        // klawisz "-" - zmniejszenie obiektu
    case '-':
        if( scale > 0.05 )
             scale -= 0.05;
       
        break;
       
        // klawisz "S" - zwiększenie kąta obcięcia reflektora
    case 'S':
        if( spot_cutoff == 90 )
             spot_cutoff = 180;
        else
        if( spot_cutoff < 90 )
             spot_cutoff++;
       
        break;
       
        // klawisz "s" - zmniejszenie kąta obcięcia reflektora
    case 's':
        if( spot_cutoff == 180 )
             spot_cutoff = 90;
        else
        if( spot_cutoff > 0 )
             spot_cutoff--;
       
        break;
       
        // klawisz "E" - zwiększenie wykładnika tłumienia kątowego reflektora
    case 'E':
        if( spot_exponent < 128 )
             spot_exponent++;
       
        break;
       
        // klawisz "e" - zmniejszenie wykładnika tłumienia kątowego reflektora
    case 'e':
        if( spot_exponent > 0 )
             spot_exponent--;
       
        break;
       
        // klawisz "C" - zwiększenie stałego współczynnika tłumienia światła
    case 'C':
        constant_attenuation += 0.1;
        break;
       
        // klawisz "c" - zmniejszenie stałego współczynnika tłumienia światła
    case 'c':
        if( constant_attenuation > 0 )
             constant_attenuation -= 0.1;
       
        break;
       
        // klawisz "L" - zwiększenie liniowego współczynnika tłumienia światła
    case 'L':
        linear_attenuation += 0.1;
        break;
       
        // klawisz "l" - zmniejszenie liniowego współczynnika tłumienia światła
    case 'l':
        if( linear_attenuation > 0 )
             linear_attenuation -= 0.1;
       
        break;
       
        // klawisz "Q" - zwiększenie kwadratowego współczynnika tłumienia światła
    case 'Q':
        quadratic_attenuation += 0.1;
        break;
       
        // klawisz "q" - zmniejszenie kwadratowego współczynnika tłumienia światła
    case 'q':
        if( quadratic_attenuation > 0 )
             quadratic_attenuation -= 0.1;
       
        break;
    }
   
    // narysowanie sceny
    DisplayScene();
}

// obsługa przycisków myszki

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

// obsługa ruchu kursora myszki

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

// obsługa klawiszy funkcyjnych i klawiszy kursora

void SpecialKeys( int key, int x, int y )
{
    switch( key )
    {
        // kursor w lewo
    case GLUT_KEY_LEFT:
        light_rotatey -= 5;
        break;
       
        // kursor w prawo
    case GLUT_KEY_RIGHT:
        light_rotatey += 5;
        break;
       
        // kursor w dół
    case GLUT_KEY_DOWN:
        light_rotatex += 5;
        break;
       
        // kursor w górę
    case GLUT_KEY_UP:
        light_rotatex -= 5;
        break;
    }
   
    // odrysowanie okna
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // rysowany obiekt - kula
    case SPHERE:
        object = SPHERE;
        DisplayScene();
        break;
       
        // rysowany obiekt - czajnik
    case TEAPOT:
        object = TEAPOT;
        DisplayScene();
        break;
       
        // rysowany obiekt - stożek
    case CONE:
        object = CONE;
        DisplayScene();
        break;
       
        // rysowany obiekt - torus
    case TORUS:
        object = TORUS;
        DisplayScene();
        break;
       
        // rysowany obiekt - sześcian
    case CUBE:
        object = CUBE;
        DisplayScene();
        break;
       
        // rysowany obiekt - dwunastościan
    case DODECAHEDRON:
        object = DODECAHEDRON;
        DisplayScene();
        break;
       
        // rysowany obiekt - ośmiościan
    case OCTAHEDRON:
        object = OCTAHEDRON;
        DisplayScene();
        break;
       
        // rysowany obiekt - czworościan
    case TETRAHEDRON:
        object = TETRAHEDRON;
        DisplayScene();
        break;
       
        // rysowany obiekt - dwudziestościan
    case ICOSAHEDRON:
        object = ICOSAHEDRON;
        DisplayScene();
        break;
       
        // materiał - mosiądz
    case BRASS:
        ambient = BrassAmbient;
        diffuse = BrassDiffuse;
        specular = BrassSpecular;
        shininess = BrassShininess;
        DisplayScene();
        break;
       
        // materiał - brąz
    case BRONZE:
        ambient = BronzeAmbient;
        diffuse = BronzeDiffuse;
        specular = BronzeSpecular;
        shininess = BronzeShininess;
        DisplayScene();
        break;
       
        // materiał - polerowany brąz
    case POLISHED_BRONZE:
        ambient = PolishedBronzeAmbient;
        diffuse = PolishedBronzeDiffuse;
        specular = PolishedBronzeSpecular;
        shininess = PolishedBronzeShininess;
        DisplayScene();
        break;
       
        // materiał - chrom
    case CHROME:
        ambient = ChromeAmbient;
        diffuse = ChromeDiffuse;
        specular = ChromeSpecular;
        shininess = ChromeShininess;
        DisplayScene();
        break;
       
        // materiał - miedź
    case COPPER:
        ambient = CopperAmbient;
        diffuse = CopperDiffuse;
        specular = CopperSpecular;
        shininess = CopperShininess;
        DisplayScene();
        break;
       
        // materiał - polerowana miedź
    case POLISHED_COPPER:
        ambient = PolishedCopperAmbient;
        diffuse = PolishedCopperDiffuse;
        specular = PolishedCopperSpecular;
        shininess = PolishedCopperShininess;
        DisplayScene();
        break;
       
        // materiał - złoto
    case GOLD:
        ambient = GoldAmbient;
        diffuse = GoldDiffuse;
        specular = GoldSpecular;
        shininess = GoldShininess;
        DisplayScene();
        break;
       
        // materiał - polerowane złoto
    case POLISHED_GOLD:
        ambient = PolishedGoldAmbient;
        diffuse = PolishedGoldDiffuse;
        specular = PolishedGoldSpecular;
        shininess = PolishedGoldShininess;
        DisplayScene();
        break;
       
        // materiał - grafit (cyna z ołowiem)
    case PEWTER:
        ambient = PewterAmbient;
        diffuse = PewterDiffuse;
        specular = PewterSpecular;
        shininess = PewterShininess;
        DisplayScene();
        break;
       
        // materiał - srebro
    case SILVER:
        ambient = SilverAmbient;
        diffuse = SilverDiffuse;
        specular = SilverSpecular;
        shininess = SilverShininess;
        DisplayScene();
        break;
       
        // materiał - polerowane srebro
    case POLISHED_SILVER:
        ambient = PolishedSilverAmbient;
        diffuse = PolishedSilverDiffuse;
        specular = PolishedSilverSpecular;
        shininess = PolishedSilverShininess;
        DisplayScene();
        break;
       
        // materiał - szmaragd
    case EMERALD:
        ambient = EmeraldAmbient;
        diffuse = EmeraldDiffuse;
        specular = EmeraldSpecular;
        shininess = EmeraldShininess;
        DisplayScene();
        break;
       
        // materiał - jadeit
    case JADE:
        ambient = JadeAmbient;
        diffuse = JadeDiffuse;
        specular = JadeSpecular;
        shininess = JadeShininess;
        DisplayScene();
        break;
       
        // materiał - obsydian
    case OBSIDIAN:
        ambient = ObsidianAmbient;
        diffuse = ObsidianDiffuse;
        specular = ObsidianSpecular;
        shininess = ObsidianShininess;
        DisplayScene();
        break;
       
        // materiał - perła
    case PEARL:
        ambient = PearlAmbient;
        diffuse = PearlDiffuse;
        specular = PearlSpecular;
        shininess = PearlShininess;
        DisplayScene();
        break;
       
        // metariał - rubin
    case RUBY:
        ambient = RubyAmbient;
        diffuse = RubyDiffuse;
        specular = RubySpecular;
        shininess = RubyShininess;
        DisplayScene();
        break;
       
        // materiał - turkus
    case TURQUOISE:
        ambient = TurquoiseAmbient;
        diffuse = TurquoiseDiffuse;
        specular = TurquoiseSpecular;
        shininess = TurquoiseShininess;
        DisplayScene();
        break;
       
        // materiał - czarny plastik
    case BLACK_PLASTIC:
        ambient = BlackPlasticAmbient;
        diffuse = BlackPlasticDiffuse;
        specular = BlackPlasticSpecular;
        shininess = BlackPlasticShininess;
        DisplayScene();
        break;
       
        // materiał - czarna guma
    case BLACK_RUBBER:
        ambient = BlackRubberAmbient;
        diffuse = BlackRubberDiffuse;
        specular = BlackRubberSpecular;
        shininess = BlackRubberShininess;
        DisplayScene();
        break;
       
        // obszar renderingu - całe okno
    case FULL_WINDOW:
        aspect = FULL_WINDOW;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // obszar renderingu - aspekt 1:1
    case ASPECT_1_1:
        aspect = ASPECT_1_1;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

// sprawdzenie i przygotowanie obsługi wybranych rozszerzeń

void ExtensionSetup()
{
    // pobranie numeru wersji biblioteki OpenGL
    const char * version =( char * ) glGetString( GL_VERSION );
   
    // odczyt wersji OpenGL
    int major = 0, minor = 0;
    if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
    {
        #ifndef WIN32
        printf( "Błędny format wersji OpenGL\n" );
        #else
       
        printf( "Bledny format wersji OpenGL\n" );
        #endif
       
        exit( 0 );
    }
   
    // sprawdzenie czy jest co najmniej wersja 1.4
    if( major > 1 || minor >= 4 )
    {
        // pobranie wskaźnika wybranej funkcji OpenGL 1.4
        glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress( "glWindowPos2i" );
    }
    else
    // sprawdzenie czy jest obsługiwane rozszerzenie ARB_window_pos
    if( glutExtensionSupported( "GL_ARB_window_pos" ) )
    {
        // pobranie wskaźnika wybranej funkcji rozszerzenia ARB_window_pos
        glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress
        ( "glWindowPos2iARB" );
    }
    else
    {
        printf( "Brak rozszerzenia ARB_window_pos!\n" );
        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
    glutCreateWindow( "Reflektor" );
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // dołączenie funkcji obsługi klawiatury
    glutKeyboardFunc( Keyboard );
   
    // dołączenie funkcji obsługi klawiszy funkcyjnych i klawiszy kursora
    glutSpecialFunc( SpecialKeys );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // utworzenie podmenu - obiekt
    int MenuObject = glutCreateMenu( Menu );
    glutAddMenuEntry( "Kula", SPHERE );
    glutAddMenuEntry( "Czajnik", TEAPOT );
   
    #ifdef WIN32
   
    glutAddMenuEntry( "Stożek", CONE );
    glutAddMenuEntry( "Torus", TORUS );
    glutAddMenuEntry( "Sześcian", CUBE );
    glutAddMenuEntry( "Dwunastościan", DODECAHEDRON );
    glutAddMenuEntry( "Ośmiościan", OCTAHEDRON );
    glutAddMenuEntry( "Czworościan", TETRAHEDRON );
    glutAddMenuEntry( "Dwudziestościan", ICOSAHEDRON );
    #else
   
    glutAddMenuEntry( "Stozek", CONE );
    glutAddMenuEntry( "Torus", TORUS );
    glutAddMenuEntry( "Szescian", CUBE );
    glutAddMenuEntry( "Dwunastoscian", DODECAHEDRON );
    glutAddMenuEntry( "Osmioscian", OCTAHEDRON );
    glutAddMenuEntry( "Czworoscian", TETRAHEDRON );
    glutAddMenuEntry( "Dwudziestoscian", ICOSAHEDRON );
    #endif
   
    // 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( "Obiekt", MenuObject );
   
    #ifdef WIN32
   
    glutAddSubMenu( "Materiał", MenuMaterial );
    #else
   
    glutAddSubMenu( "Material", MenuMaterial );
    #endif
   
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    #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 );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Bufor głębokości Listy wyświetlania