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

Bufor głębokości

[lekcja] Rozdział 12. Zasada działania i przeznaczenie bufora głębokości; testy bufora głębokości; czyszczenie bufora głębokości; zakres wartości bufora głębokości; przesunięcie wartości głębi; wycinanie fragmentów sceny przy pomocy bufora głębokości; trzy przykładowe programy.
Bufor głębokości to obok bufora koloru podstawowy element bufora ramki czyli pamięci obrazu. Bufor głębokości, nazywany także z-buforem lub buforem głębi, uczestniczy w procesie usuwania niewidocznych elementów renderowanej sceny.

Działanie bufora głębokości

Działanie bufora głębokości polega w uproszczeniu na przechowywaniu wartości współrzędnej z (głębokości) dla każdego piksela obrazu. Przy rysowaniu kolejnych pikseli danego obiektu obliczana jest wartość współrzędnej z i porównywana z wartością znajdującą się w buforze. Jeżeli porównanie wskazuje, że rysowany piksel „przesłania” to, co zostało narysowane wcześniej, zmieniana jest zarówno zawartość bufora głębokości jak i bufora koloru.
Algorytm z-bufora nie jest oczywiście jedynym znanym algorytmem ukrywania niewidocznych obiektów. O jego zastosowaniu w bibliotece OpenGL (i wielu innych bibliotekach grafiki 3D) zdecydowała łatwość sprzętowej implementacji oraz stosunkowo wysoka efektywność. Trzeba w tym miejscu dodać, że zaawansowane aplikacje stosujące bibliotekę OpenGL (czyli głównie gry) stosują szereg dodatkowych technik eliminujących niewidoczne powierzchnie obiektów przed etapem rysowana sceny. Pozwala to na czasami znaczącą redukcję ilości operacji na buforze głębokości.

Włączenie bufora głębokości

Standardowo bufor głębokości nie jest używany przez bibliotekę OpenGL. W pierwszej kolejności trzeba bufor dodać do bufora ramki. Jeżeli korzystamy z biblioteki GLUT, wymaga to dodania stałej GLUT DEPTH przy wywołaniu funkcji glutInitDisplayMode na początku działania programu.
Standardowo OpenGL nie wykonuje operacji na buforze głębokości. Włączenie testów bufora głębokości wymaga wywołania funkcji glEnable z parametrem GL_DEPTH_TEST. Biblioteka OpenGL umożliwia także wyłączenie zapisu wartości współrzędnych z do bufora głębokości. Realizuje to funkcja:
C/C++
void glDepthMask( GLboolean flag )
której parametr flag przyjmuje dwie wartości: GL_FALSE - wyłączenie zapisu do bufora głębokości i GL_TRUE - włączenie zapisu do bufora głębokości.

Test bufora głębokości

Biblioteka OpenGL umożliwia wybór rodzaju testu wykonywanego przy użyciu danych zawartych w buforze głębokości. Wybór rodzaju testu umożliwia funkcja:
C/C++
void glDepthFunc( GLenum func )
Parametr func przyjmuje jedną z poniższych wartości:
  • GL_NEVER - wartość testu zawsze negatywna,
  • GL_LESS - wartość testu pozytywna jeżeli testowana wartość współrzędnej z jest mniejsza od wartości znajdującej się w buforze; wartość domyślna,
  • GL_EQUAL - wartość testu pozytywna jeżeli testowana wartość współrzędnej z jest równa wartości znajdującej się w buforze,
  • GL_LEQUAL - wartość testu pozytywna jeżeli testowana wartość współrzędnej z jest mniejsza lub równa wartości znajdującej się w buforze,
  • GL_GREATER - wartość testu pozytywna jeżeli testowana wartość współrzędnej z jest większa od wartości znajdującej się w buforze,
  • GL_NOTEQUAL - wartość testu pozytywna jeżeli testowana wartość współrzędnej z jest różna od wartości znajdującej się w buforze,
  • GL_GEQUAL - wartość testu pozytywna jeżeli testowana wartość współrzędnej z jest większa lub równa wartości znajdującej się w buforze,
  • GL_ALWAYS - wartość testu zawsze pozytywna.
Przy wyborze rodzaju testu z-bufora trzeba uwzględnić, jaki model współrzędnych stosuje biblioteka OpenGL i jaką orientacje posiada oś OZ . W typowym przypadku rodzaj testu inny niż GL_LESS spowoduje niepoprawne wyświetlenie elementów sceny 3D.

Czyszczenie bufora głębokości

Przez rozpoczęciem rysowania elementów sceny 3D konieczne jest wyczyszczenie zawartości bufora głębokości. Wymaga to dodania stałej GL_DEPTH_BUFFER_BIT przy wywołaniu funkcji glClear.
Standardowo bufor głębokości zawiera liczby z przedziału [0, 1] i jest czyszczony liczbą 1. Biblioteka OpenGL daje możliwość wyboru wartości, którą czyszczony jest bufor głębokości. Wymaga to wywołania funkcji:
C/C++
void glClearDepth( GLclampd depth )
której parametr depth jest liczbą z przedziału [0, 1] (wartości przekraczające ten przedział są odpowiednio przycinane).

Zakres wartości bufora głębokości

Jak wcześniej powiedzieliśmy bufor głębokości zawiera standardowo liczby z przedziału [0, 1]. Biblioteka OpenGL umożliwia zmianę tego przedziału poprzez użycie funkcji:
C/C++
void glDepthRange( GLclampd zNear, GLclampd zFar )
Parametr zNear określą minimalna wartość w buforze głębokości, a zFar maksymalną wartość w buforze głębokości. Wartości domyślne wynoszą oczywiście 0 i 1. Oba parametry powinny należeć do przedziału [0, 1] (w razie potrzeby OpenGL wykona odpowiednie przycięcie). Nie ma wymogu aby wartość parametru zNear była mniejsza od wartości parametru zFar.
Warto pamiętać, że ograniczenie zakresu wartości przechowywanych w buforze głębokości prowadzi do zmniejszenia jego dokładności i może spowodować błędy przy usuwaniu niewidocznych obiektów.

Przesunięcie wartości głębi

W wersji 1.1 biblioteki OpenGL, a wcześniej w rozszerzeniu EXT polygon offset, wprowadzono mechanizm pozwalający na przesuwanie wartości głębi pikseli przy rysowaniu wielokątów. Przesunięcie wyliczane jest na podstawie wzoru:
(m * factor) + (r * units)
którego współczynnik m oznacza maksymalne nachylenie głębokości wielokąta (obliczenia wykonuje OpenGL), a współczynnik r jest zależną od implementacji najmniejszą różnicą wartości przechowywanych w buforze głębokości. Określenie wartości współczynników skalowania factor i units wymaga wywołania funkcji:
C/C++
void glPolygonOffset( GLfloat factor, GLfloat units )
Wartości początkowe obu współczynników wynoszą 0.
Przesuwanie wartości głębi jest domyślnie wyłączone. Włączenie tego mechanizmu wymaga wywołania funkcji glEnable z jednym z poniższych parametrów:
  • GL_POLYGON_OFFSET_POINT - przesuwanie wartości głębi, gdy rysowane są tylko wierzchołki wielokątów,
  • GL_POLYGON_OFFSET_LINE - przesuwanie wartości głębi, gdy rysowane są tylko krawędzie wielokątów,
  • GL_POLYGON_OFFSET_FILL - przesuwanie wartości głębi, gdy rysowane są wypełnione wielokąty.

Programy przykładowe

Pierwszy program przykładowy (plik szescian5.cpp) prezentuje podstawowe zadanie bufora głębokości, czyli ukrywanie niewidocznych krawędzi obiektu 3D. Tym obiektem jest już po raz kolejny sześcian, ale tym razem o zupełnie innym wyglądzie - jest to bowiem sześcian przedstawiający wszystkie barwy RGB (patrz rysunek 1). Efekt równomiernego rozłożenia barw na bokach sześcianu zapewnia gładkie cieniowanie połączone z określeniem odpowiedniego koloru odrębnie dla każdego wierzchołka.
Na potrzeby tego i przyszłych programów przygotowany został plik colors.h, który zawiera definicje składowych RGBA 141 kolorów. Definicje oraz nazwy kolorów pochodzą z pakietu ASP.NET. Część angielskich nazw kolorów została przetłumaczona - nazwy te umieszczono w komentarzach.
Rysunek 1. Program Sześcian 5
Rysunek 1. Program Sześcian 5
Poza rysowaniem sześcianu barw RGB program umożliwia wybór rodzaju testu bufora głębokości. Część z testów generuje pusty ekran, a testy GL_ALWAYS i GL_NOTEQUAL dają całkowicie błędny wygląd obiektu (patrz rysunek 2). Obracając sześcian (prawy przycisk myszki lub klawisze kursora) można zauważyć, kiedy następuje błędne rysowanie kolejnych trójkątów, z których składają się ściany obiektu.
Rysunek 2. Program Sześcian 5 - testy GL_ALWAYS i GL_NOTEQUAL
Rysunek 2. Program Sześcian 5 - testy GL_ALWAYS i GL_NOTEQUAL

Plik colors.h

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

#ifndef __COLORS__H__
#define __COLORS__H__

#include <GL/gl.h>

// definicje kolorów RGB w ASP.NET (System.Drawing.Color)
// źródło:
// http://www.opinionatedgeek.com/DotNet/Tools/Colors/default.aspx


const GLfloat AliceBlue[ 4 ] =
{
    0.941176, 0.972549, 1.000000, 1.000000
};

const GLfloat AntiqueWhite[ 4 ] =
{
    0.980392, 0.921569, 0.843137, 1.000000
};

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

const GLfloat Aquamarine[ 4 ] =
{
    0.498039, 1.000000, 0.831373, 1.000000
};

// lazurowy

const GLfloat Azure[ 4 ] =
{
    0.941176, 1.000000, 1.000000, 1.000000
};

// beżowy

const GLfloat Beige[ 4 ] =
{
    0.960784, 0.960784, 0.862745, 1.000000
};

const GLfloat Bisque[ 4 ] =
{
    1.000000, 0.894118, 0.768627, 1.000000
};

// czarny

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

const GLfloat BlanchedAlmond[ 4 ] =
{
    1.000000, 0.921569, 0.803922, 1.000000
};

// niebieski

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

// niebieskofiletowy

const GLfloat BlueViolet[ 4 ] =
{
    0.541176, 0.168627, 0.886275, 1.000000
};

// brązowy

const GLfloat Brown[ 4 ] =
{
    0.647059, 0.164706, 0.164706, 1.000000
};

const GLfloat BurlyWood[ 4 ] =
{
    0.870588, 0.721569, 0.529412, 1.000000
};

const GLfloat CadetBlue[ 4 ] =
{
    0.372549, 0.619608, 0.627451, 1.000000
};

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

// czekoladowy

const GLfloat Chocolate[ 4 ] =
{
    0.823529, 0.411765, 0.117647, 1.000000
};

// koralowy

const GLfloat Coral[ 4 ] =
{
    1.000000, 0.498039, 0.313725, 1.000000
};

const GLfloat CornflowerBlue[ 4 ] =
{
    0.392157, 0.584314, 0.929412, 1.000000
};

const GLfloat Cornsilk[ 4 ] =
{
    1.000000, 0.972549, 0.862745, 1.000000
};

// karmazynowy (purpurowy)

const GLfloat Crimson[ 4 ] =
{
    0.862745, 0.078431, 0.235294, 1.000000
};

// niebieskozielony

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

// ciemnoniebieski

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

// ciemnoniebieskozielony

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

const GLfloat DarkGoldenrod[ 4 ] =
{
    0.721569, 0.525490, 0.043137, 1.000000
};

// ciemnoszary

const GLfloat DarkGray[ 4 ] =
{
    0.662745, 0.662745, 0.662745, 1.000000
};

// ciemnozielony

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

// ciemny khaki

const GLfloat DarkKhaki[ 4 ] =
{
    0.741176, 0.717647, 0.419608, 1.000000
};

// ciemny purpurowy

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

const GLfloat DarkOliveGreen[ 4 ] =
{
    0.333333, 0.419608, 0.184314, 1.000000
};

// ciemnopomarańczowy

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

const GLfloat DarkOrchid[ 4 ] =
{
    0.600000, 0.196078, 0.800000, 1.000000
};

// ciemnoczerwony

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

const GLfloat DarkSalmon[ 4 ] =
{
    0.913725, 0.588235, 0.478431, 1.000000
};

const GLfloat DarkSeaGreen[ 4 ] =
{
    0.560784, 0.737255, 0.545098, 1.000000
};

const GLfloat DarkSlateBlue[ 4 ] =
{
    0.282353, 0.239216, 0.545098, 1.000000
};

const GLfloat DarkSlateGray[ 4 ] =
{
    0.184314, 0.309804, 0.309804, 1.000000
};

const GLfloat DarkTurquoise[ 4 ] =
{
    0.000000, 0.807843, 0.819608, 1.000000
};

// ciemnofioletowy

const GLfloat DarkViolet[ 4 ] =
{
    0.580392, 0.000000, 0.827451, 1.000000
};

const GLfloat DeepPink[ 4 ] =
{
    1.000000, 0.078431, 0.576471, 1.000000
};

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

const GLfloat DimGray[ 4 ] =
{
    0.411765, 0.411765, 0.411765, 1.000000
};

const GLfloat DodgerBlue[ 4 ] =
{
    0.117647, 0.564706, 1.000000, 1.000000
};

const GLfloat Firebrick[ 4 ] =
{
    0.698039, 0.133333, 0.133333, 1.000000
};

const GLfloat FloralWhite[ 4 ] =
{
    1.000000, 0.980392, 0.941176, 1.000000
};

const GLfloat ForestGreen[ 4 ] =
{
    0.133333, 0.545098, 0.133333, 1.000000
};

// fuksja

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

const GLfloat Gainsboro[ 4 ] =
{
    0.862745, 0.862745, 0.862745, 1.000000
};

const GLfloat GhostWhite[ 4 ] =
{
    0.972549, 0.972549, 1.000000, 1.000000
};

// złoty

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

const GLfloat Goldenrod[ 4 ] =
{
    0.854902, 0.647059, 0.125490, 1.000000
};

// szary

const GLfloat Gray[ 4 ] =
{
    0.501961, 0.501961, 0.501961, 1.000000
};

// zielony

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

// zielonożółty

const GLfloat GreenYellow[ 4 ] =
{
    0.678431, 1.000000, 0.184314, 1.000000
};

// spadziowy

const GLfloat Honeydew[ 4 ] =
{
    0.941176, 1.000000, 0.941176, 1.000000
};

// intensywny różowy

const GLfloat HotPink[ 4 ] =
{
    1.000000, 0.411765, 0.705882, 1.000000
};

const GLfloat IndianRed[ 4 ] =
{
    0.803922, 0.360784, 0.360784, 1.000000
};

// indygo

const GLfloat Indigo[ 4 ] =
{
    0.294118, 0.000000, 0.509804, 1.000000
};

// kość słoniowa

const GLfloat Ivory[ 4 ] =
{
    1.000000, 1.000000, 0.941176, 1.000000
};

// khaki

const GLfloat Khaki[ 4 ] =
{
    0.941176, 0.901961, 0.549020, 1.000000
};

// lawendowy

const GLfloat Lavender[ 4 ] =
{
    0.901961, 0.901961, 0.980392, 1.000000
};

const GLfloat LavenderBlush[ 4 ] =
{
    1.000000, 0.941176, 0.960784, 1.000000
};

const GLfloat LawnGreen[ 4 ] =
{
    0.486275, 0.988235, 0.000000, 1.000000
};

const GLfloat LemonChiffon[ 4 ] =
{
    1.000000, 0.980392, 0.803922, 1.000000
};

const GLfloat LightBlue[ 4 ] =
{
    0.678431, 0.847059, 0.901961, 1.000000
};

const GLfloat LightCoral[ 4 ] =
{
    0.941176, 0.501961, 0.501961, 1.000000
};

const GLfloat LightCyan[ 4 ] =
{
    0.878431, 1.000000, 1.000000, 1.000000
};

const GLfloat LightGoldenrodYellow[ 4 ] =
{
    0.980392, 0.980392, 0.823529, 1.000000
};

const GLfloat LightGray[ 4 ] =
{
    0.827451, 0.827451, 0.827451, 1.000000
};

const GLfloat LightGreen[ 4 ] =
{
    0.564706, 0.933333, 0.564706, 1.000000
};

const GLfloat LightPink[ 4 ] =
{
    1.000000, 0.713725, 0.756863, 1.000000
};

const GLfloat LightSalmon[ 4 ] =
{
    1.000000, 0.627451, 0.478431, 1.000000
};

const GLfloat LightSeaGreen[ 4 ] =
{
    0.125490, 0.698039, 0.666667, 1.000000
};

const GLfloat LightSkyBlue[ 4 ] =
{
    0.529412, 0.807843, 0.980392, 1.000000
};

const GLfloat LightSlateGray[ 4 ] =
{
    0.466667, 0.533333, 0.600000, 1.000000
};

const GLfloat LightSteelBlue[ 4 ] =
{
    0.690196, 0.768627, 0.870588, 1.000000
};

const GLfloat LightYellow[ 4 ] =
{
    1.000000, 1.000000, 0.878431, 1.000000
};

// cytrynowy

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

// cytrynowozielony

const GLfloat LimeGreen[ 4 ] =
{
    0.196078, 0.803922, 0.196078, 1.000000
};

// lniany

const GLfloat Linen[ 4 ] =
{
    0.980392, 0.941176, 0.901961, 1.000000
};

// purpurowy

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

// kasztanowy, brązowoczerwony

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

const GLfloat MediumAquamarine[ 4 ] =
{
    0.400000, 0.803922, 0.666667, 1.000000
};

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

const GLfloat MediumOrchid[ 4 ] =
{
    0.729412, 0.333333, 0.827451, 1.000000
};

const GLfloat MediumPurple[ 4 ] =
{
    0.576471, 0.439216, 0.858824, 1.000000
};

const GLfloat MediumSeaGreen[ 4 ] =
{
    0.235294, 0.701961, 0.443137, 1.000000
};

const GLfloat MediumSlateBlue[ 4 ] =
{
    0.482353, 0.407843, 0.933333, 1.000000
};

const GLfloat MediumSpringGreen[ 4 ] =
{
    0.000000, 0.980392, 0.603922, 1.000000
};

const GLfloat MediumTurquoise[ 4 ] =
{
    0.282353, 0.819608, 0.800000, 1.000000
};

const GLfloat MediumVioletRed[ 4 ] =
{
    0.780392, 0.082353, 0.521569, 1.000000
};

const GLfloat MidnightBlue[ 4 ] =
{
    0.098039, 0.098039, 0.439216, 1.000000
};

const GLfloat MintCream[ 4 ] =
{
    0.960784, 1.000000, 0.980392, 1.000000
};

const GLfloat MistyRose[ 4 ] =
{
    1.000000, 0.894118, 0.882353, 1.000000
};

const GLfloat Moccasin[ 4 ] =
{
    1.000000, 0.894118, 0.709804, 1.000000
};

const GLfloat NavajoWhite[ 4 ] =
{
    1.000000, 0.870588, 0.678431, 1.000000
};

// granatowy

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

const GLfloat OldLace[ 4 ] =
{
    0.992157, 0.960784, 0.901961, 1.000000
};

// oliwkowy

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

// khaki, szarozielony


const GLfloat OliveDrab[ 4 ] =
{
    0.419608, 0.556863, 0.137255, 1.000000
};

// oranż, pomarańczowy

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

// pomarańczowoczerwony

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

const GLfloat Orchid[ 4 ] =
{
    0.854902, 0.439216, 0.839216, 1.000000
};

const GLfloat PaleGoldenrod[ 4 ] =
{
    0.933333, 0.909804, 0.666667, 1.000000
};

// bladozielony

const GLfloat PaleGreen[ 4 ] =
{
    0.596078, 0.984314, 0.596078, 1.000000
};

// bladoturkusowy

const GLfloat PaleTurquoise[ 4 ] =
{
    0.686275, 0.933333, 0.933333, 1.000000
};

const GLfloat PaleVioletRed[ 4 ] =
{
    0.858824, 0.439216, 0.576471, 1.000000
};

const GLfloat PapayaWhip[ 4 ] =
{
    1.000000, 0.937255, 0.835294, 1.000000
};

const GLfloat PeachPuff[ 4 ] =
{
    1.000000, 0.854902, 0.725490, 1.000000
};

const GLfloat Peru[ 4 ] =
{
    0.803922, 0.521569, 0.247059, 1.000000
};

// różowy

const GLfloat Pink[ 4 ] =
{
    1.000000, 0.752941, 0.796078, 1.000000
};

// śliwkowy

const GLfloat Plum[ 4 ] =
{
    0.866667, 0.627451, 0.866667, 1.000000
};

const GLfloat PowderBlue[ 4 ] =
{
    0.690196, 0.878431, 0.901961, 1.000000
};

// purpurowy

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

// czerwony

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

const GLfloat RosyBrown[ 4 ] =
{
    0.737255, 0.560784, 0.560784, 1.000000
};

const GLfloat RoyalBlue[ 4 ] =
{
    0.254902, 0.411765, 0.882353, 1.000000
};

// brunatny

const GLfloat SaddleBrown[ 4 ] =
{
    0.545098, 0.270588, 0.074510, 1.000000
};

// łososiowy

const GLfloat Salmon[ 4 ] =
{
    0.980392, 0.501961, 0.447059, 1.000000
};

const GLfloat SandyBrown[ 4 ] =
{
    0.956863, 0.643137, 0.376471, 1.000000
};

const GLfloat SeaGreen[ 4 ] =
{
    0.180392, 0.545098, 0.341176, 1.000000
};

const GLfloat SeaShell[ 4 ] =
{
    1.000000, 0.960784, 0.933333, 1.000000
};

// sjena

const GLfloat Sienna[ 4 ] =
{
    0.627451, 0.321569, 0.176471, 1.000000
};

// srebrny

const GLfloat Silver[ 4 ] =
{
    0.752941, 0.752941, 0.752941, 1.000000
};

// błękitny, jasnoniebieski, lazurowy

const GLfloat SkyBlue[ 4 ] =
{
    0.529412, 0.807843, 0.921569, 1.000000
};

// łupkowoniebieski

const GLfloat SlateBlue[ 4 ] =
{
    0.415686, 0.352941, 0.803922, 1.000000
};

// łupkowoszary

const GLfloat SlateGray[ 4 ] =
{
    0.439216, 0.501961, 0.564706, 1.000000
};

// śnieżny

const GLfloat Snow[ 4 ] =
{
    1.000000, 0.980392, 0.980392, 1.000000
};

// zielony wiosenny

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

// stalowoniebieski, szaroniebieski

const GLfloat SteelBlue[ 4 ] =
{
    0.274510, 0.509804, 0.705882, 1.000000
};

// jasnobrązowy

const GLfloat Tan[ 4 ] =
{
    0.823529, 0.705882, 0.549020, 1.000000
};

// cyrankowy

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

// ostowy

const GLfloat Thistle[ 4 ] =
{
    0.847059, 0.749020, 0.847059, 1.000000
};

// pomidorowy

const GLfloat Tomato[ 4 ] =
{
    1.000000, 0.388235, 0.278431, 1.000000
};

// przezroczysty

const GLfloat Transparent[ 4 ] =
{
    1.000000, 1.000000, 1.000000, 1.000000
};

// turkusowy

const GLfloat Turquoise[ 4 ] =
{
    0.250980, 0.878431, 0.815686, 1.000000
};

// fioletowy

const GLfloat Violet[ 4 ] =
{
    0.933333, 0.509804, 0.933333, 1.000000
};

const GLfloat Wheat[ 4 ] =
{
    0.960784, 0.870588, 0.701961, 1.000000
};

// biały

const GLfloat White[ 4 ] =
{
    1.000000, 1.000000, 1.000000, 1.000000
};

const GLfloat WhiteSmoke[ 4 ] =
{
    0.960784, 0.960784, 0.960784, 1.000000
};

// źółty

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

// żółtozielony

const GLfloat YellowGreen[ 4 ] =
{
    0.603922, 0.803922, 0.196078, 1.000000
};

#endif // __COLORS_H__

Plik szescian5.cpp

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

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

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

enum
{
    FULL_WINDOW = GL_ALWAYS + 100, // 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;

// rodzaj testu bufora głębokości

GLenum depth_test = GL_LESS;

// 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 testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // wybór funkcji do testu bufora głębokości
    glDepthFunc( depth_test );
   
    // rysowanie sześcianu RGB - 12 trójkątów
    glBegin( GL_TRIANGLES );
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
   
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
    glColor3fv( Magenta );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glColor3fv( Magenta );
    glVertex3f( - 1.0, 1.0, 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
   
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
    glColor3fv( Lime );
    glVertex3f( 1.0, - 1.0, - 1.0 );
   
    glColor3fv( Lime );
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glColor3fv( Magenta );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    glColor3fv( Lime );
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    // koniec definicji sześcianu RGB
    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 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;
       
        // test bufora głębokości: GL_NEVER
    case GL_NEVER:
        depth_test = GL_NEVER;
        Display();
        break;
       
        // test bufora głębokości: GL_LESS
    case GL_LESS:
        depth_test = GL_LESS;
        Display();
        break;
       
        // test bufora głębokości: GL_EQUAL
    case GL_EQUAL:
        depth_test = GL_EQUAL;
        Display();
        break;
       
        // test bufora głębokości: GL_LEQUAL
    case GL_LEQUAL:
        depth_test = GL_LEQUAL;
        Display();
        break;
       
        // test bufora głębokości: GL_GREATER
    case GL_GREATER:
        depth_test = GL_GREATER;
        Display();
        break;
       
        // test bufora głębokości: GL_NOTEQUAL
    case GL_NOTEQUAL:
        depth_test = GL_NOTEQUAL;
        Display();
        break;
       
        // test bufora głębokości: GL_GEQUAL
    case GL_GEQUAL:
        depth_test = GL_GEQUAL;
        Display();
        break;
       
        // test bufora głębokości: GL_ALWAYS
    case GL_ALWAYS:
        depth_test = GL_ALWAYS;
        Display();
        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( "Sześcian 5" );
    #else
   
    glutCreateWindow( "Szescian 5" );
    #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 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 );
   
    // utworzenie podmenu - Test z-bufora
    int MenuZbuffer = glutCreateMenu( Menu );
    glutAddMenuEntry( "GL_NEVER", GL_NEVER );
    glutAddMenuEntry( "GL_LESS", GL_LESS );
    glutAddMenuEntry( "GL_EQUAL", GL_EQUAL );
    glutAddMenuEntry( "GL_LEQUAL", GL_LEQUAL );
    glutAddMenuEntry( "GL_GREATER", GL_GREATER );
    glutAddMenuEntry( "GL_NOTEQUAL", GL_NOTEQUAL );
    glutAddMenuEntry( "GL_GEQUAL", GL_GEQUAL );
    glutAddMenuEntry( "GL_ALWAYS", GL_ALWAYS );
   
    // menu główne
    glutCreateMenu( Menu );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddSubMenu( "Test z-bufora", MenuZbuffer );
    #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 zbufor.cpp) wykorzystuje bufor głębokości do wycięcia wybranego fragmentu sceny 3D. Wycinanie następuje, gdy wyłączone jest rysowanie w buforze kolorów, ale jednocześnie działa bufor głębokości, eliminujący „niewidoczne” elementy sceny 3D. W programie rysowana jest kula wewnątrz sześcianu, a wycinany fragment sceny stanowi kwadratowe okno w jednej ze ścian sześcianu (patrz rysunek 3).
Rysunek 3. Program Z-bufor - obiekt z włączonym przesuwaniem wartości głębi
Rysunek 3. Program Z-bufor - obiekt z włączonym przesuwaniem wartości głębi
Selektywny wybór, który z buforów składających się na bufor koloru wykorzystywany jest do renderingu umożliwia opisana wcześniej funkcja glDrawBuffer. Domyślnie w trybach z pojedynczym buforowaniem biblioteka OpenGL rysuje w przednim buforze (GL_FRONT), a przy włączonym podwójnym buforowaniu w tylnym buforze (GL_BACK).
W programie wykorzystano technikę przesuwania wartości głębi zarówno przy rysowaniu sześcianu jak i kuli. Jeżeli wyłączymy przesuwanie wartości głębi przy rysowaniu obiektów położonych w tej samej płaszczyźnie pojawią się artefakty przypominające „szwy” (ang. stitching). Są one wyraźnie widoczne na rysunku 4.
Rysunek 4. Program Z-bufor - obiekt z wyłączonym przesuwaniem wartości głębi
Rysunek 4. Program Z-bufor - obiekt z wyłączonym przesuwaniem wartości głębi
Do rysowania niektórych elementów sceny 3D wykorzystane zostały do tej pory nieopisane funkcje biblioteki GLUT. Kulę, o środku położonym w początku układu współrzędnych, rysuje funkcja:
C/C++
void glutSolidSphere( GLdouble radius, GLint slices, GLint stacks )
której parametry oznaczają:
  • radius - promień kuli,
  • slices - ilość południków,
  • stacks - ilość równoleżników.
Sześcian o środku położonym w początku układu współrzędnych i boku długości size rysuje funkcja:
C/C++
void glutSolidCube( GLdouble size )
Wyjaśnienia wymaga jeszcze sposób realizacji obrotu kuli znajdującej się wewnątrz sześcianu. Kąt obrotu kuli (zmienna angle) zmieniany jest za przy każdym wywołaniu funkcji Display. Standardowo funkcja ta jest wywoływana tylko w razie potrzeby odrysowania całości lub części okna. Aby funkcja Display była wywoływana stale, dając efekt obrotu kuli, wykorzystamy każdą bezczynność systemu, czyli tryb oczekiwania przez system na komunikaty. W bibliotece GLUT wskazanie funkcji wywoływanej podczas bezczynności systemu umożliwia funkcja:
C/C++
void glutIdleFunc( void( * func )( void ) )

Plik zbufor.cpp

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

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

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

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

enum
{
    CUTTING_PLANE, // płaszczyzna przekroju
    POLYGON_OFFSET, // przesunięcie wartości głębi
    EXIT // wyjście
};

// kąt obrotu kuli

GLfloat angle = 0.0;

// kąty obrotu sześcianu

GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;

// wskaźnik rysowania płaszczyzna przekroju

bool cutting_plane = true;

// wskaźnik przesunięcia wartości głębi

bool polygon_offset = true;

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

int button_state = GLUT_UP;

// położenie kursora myszki

int button_x, button_y;

// 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 testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // najpierw rysujemy kulę obracającą się wewnątrz sześcianu;
    // z uwagi na celowy brak efektów oświetlenia, obrót kuli
    // podkreśla druga kula w wersji "szkieletowej"
    glPushMatrix();
    angle += 0.2;
    glRotatef( angle, 1.0, 1.0, 0.0 );
    glColor3fv( Yellow );
    if( polygon_offset )
         glEnable( GL_POLYGON_OFFSET_FILL );
   
    glPolygonOffset( 1.0, 1.0 );
    glutSolidSphere( 0.5, 10, 10 );
    glColor3fv( Black );
    glutWireSphere( 0.5, 10, 10 );
    if( polygon_offset )
         glDisable( GL_POLYGON_OFFSET_FILL );
   
    glPopMatrix();
   
    // w drugiej kolejności rysujemy wnętrze sześcianu;
    // rysowane są tylko przednie strony wewnętrznych ścian
    // no i nie jest rysowana ściana przednia, w której będzie otwór
    glEnable( GL_CULL_FACE );
    glBegin( GL_QUADS );
    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 );
   
    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 );
   
    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 );
   
    glColor3fv( Green );
    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 );
    glEnd();
    glDisable( GL_CULL_FACE );
   
    // rysowanie płaszczyzny otworu w sześcianie
    if( cutting_plane )
    {
        // wyłączenie rysowania w buforze kolorów
        glDrawBuffer( GL_NONE );
       
        // rysowanie kwadratu częściowo odsłaniającego wnętrze sześcianu
        // (kwadrat jest położony o 0,001 jednostki nad bokiem sześcianu)
        glBegin( GL_QUADS );
        glVertex3f( - 0.6, - 0.6, 1.001 );
        glVertex3f( 0.6, - 0.6, 1.001 );
        glVertex3f( 0.6, 0.6, 1.001 );
        glVertex3f( - 0.6, 0.6, 1.001 );
        glEnd();
       
        // włączenie rysowania w buforze kolorów
        glDrawBuffer( GL_BACK );
    }
   
    // właściwy sześcian z obramowaniem, którego rysowanie wymusza brak oświetlenia
    glColor3fv( Red );
    if( polygon_offset )
         glEnable( GL_POLYGON_OFFSET_FILL );
   
    glPolygonOffset( 1.0, 1.0 );
    glutSolidCube( 2.0 );
    glColor3fv( Black );
    glutWireCube( 2.0 );
    if( polygon_offset )
         glDisable( GL_POLYGON_OFFSET_FILL );
   
    // 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
    glFrustum( left, right, bottom, top, near, far );
   
    // generowanie sceny 3D
    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 )
    {
        // płaszczyzna przekroju
    case CUTTING_PLANE:
        cutting_plane = !cutting_plane;
        Display();
        break;
       
        // przesunięcie wartości głębi
    case POLYGON_OFFSET:
        polygon_offset = !polygon_offset;
        Display();
        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
    glutCreateWindow( "Z-bufor" );
   
    // 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 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 );
   
    // menu główne
    glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Płaszczyzna przekroju: rysowana/nierysowana", CUTTING_PLANE );
    glutAddMenuEntry( "Przesunięcie wartości głębi: włącz/wyłącz", POLYGON_OFFSET );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Plaszczyzna przekroju: rysowana/nierysowana", CUTTING_PLANE );
    glutAddMenuEntry( "Przesuniecie wartosci glebi: wlacz/wylacz", POLYGON_OFFSET );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // dołączenie funkcji wywoływanej podczas "bezczynności" systemu
    glutIdleFunc( Display );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
W ostatnim programie przykładowym (plik linie_z_halo.cpp) przedstawimy ciekawy efekt halo, czyli swoistą otoczkę lub poświatę widoczną przy rysowaniu krawędzi obiektów. Program ten pozwala lepiej zrozumieć zasadę działania bufora głębokości.
Rysowanie krawędzi obiektu z efektem halo przebiega w dwóch krokach. Pierwszy z nich polega na narysowaniu pogrubionych krawędzi obiektu przy włączonym buforze głębokości, ale przy wyłączonym zapisie składowych RGBA w buforze koloru. Na tak utworzony „obraz” w buforze głębokości nakładane są już zawężone krawędzie obiektu, przy czym zmianie ulega rodzaj testu na GL_LEQUAL (mniejszy lub równy), a samo rysowanie przebiega przy włączonym zapisie składowych RGBA do bufora koloru.
Efekt halo przedstawia rysunek 5. Dla porównania obiekt ze zwykłymi krawędziami - ryunek 6.
Wyłączenie i/lub włączenie zapisu składowych RGBA do bufora koloru realizuje funkcja:
C/C++
void glColorMask( GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha )
której parametry logiczne red, green, blue i alpha pozwalają na selektywną wybór składowych RGBA bufora kolorów. Przy okazji poznajmy funkcję:
C/C++
void glIndexMask( GLuint mask )
która jest działającym w trybie indeksowym odpowiednikiem glColorMask. Parametr mask jest maską bitową sterującą zapisem poszczególnych bitów indeksów bufora (buforów) kolorów. Wartość 1 bitu maski oznacza możliwość zapisu danego bitu bufora, wartość 0 blokuje zapis. Domyślnie wszystkie bity maski mają wartość 1.
Rysunek 5. Program Linie z halo - efekt halo
Rysunek 5. Program Linie z halo - efekt halo

Plik linie_z_halo.cpp

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

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

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

enum
{
    FILL, // obiekt wypełniony
    WIRE, // krawędzie obiektu
    HALO, // efekt halo
    EXIT // wyjście
};

// rozmiary bryły obcinania

const GLint left = - 5;
const GLint right = 5;
const GLint bottom = - 5;
const GLint top = 5;
const GLint near = - 5;
const GLint far = 5;

// kąty obrotu sceny

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;

// tryb rysowania

int render_mode = WIRE;

// 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();
   
    // obroty całej sceny
    glRotatef( rotatex, 1.0, 0.0, 0.0 );
    glRotatef( rotatey, 0.0, 1.0, 0.0 );
   
    // włączenie bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // kolor obiektu
    glColor4fv( Green );
   
    // narysowanie wybranych elementów sceny
    switch( render_mode )
    {
        // obiekt wypełniony
    case FILL:
        glutSolidSphere( 3.0, 15, 15 );
        break;
       
        // rysowanie tylko krawędzi obiektu
    case WIRE:
       
        // wyłączenie bufora głębokości
        glDisable( GL_DEPTH_TEST );
       
        // tryb rysowania wielokątów
        glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
       
        // szerokość linii - linia normalna
        glLineWidth( 3.0 );
       
        // narysowanie obiektu
        glutSolidSphere( 3.0, 15, 15 );
       
        // tryb rysowania wielokątów
        glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
       
        // włączenie testu bufora głębokości
        glEnable( GL_DEPTH_TEST );
        break;
       
        // efekt halo
    case HALO:
        // tryb rysowania wielokątów
        glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
       
        // wyłączenie zapisu do bufora kolorów
        glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
       
        // szerokość linii - linia pogrubiona
        glLineWidth( 9.0 );
       
        // narysoanie obiektu
        glutSolidSphere( 3.0, 15, 15 );
       
        // włączenie zapisu do bufora głębokości
        glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
       
        // szerokość linii - linia normalna
        glLineWidth( 3.0 );
       
        // rodzaj testu bufora głębokości
        glDepthFunc( GL_LEQUAL );
       
        // narysowanie obiektu
        glutSolidSphere( 3.0, 15, 15 );
       
        // rodzaj testu bufora głębokości
        glDepthFunc( GL_LESS );
       
        // tryb rysowania wielokątów
        glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
        break;
    }
   
    // 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
    glOrtho( left, right, bottom, top, near, far );
   
    // generowanie sceny 3D
    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 += 50 *( right - left ) /( float ) glutGet( GLUT_WINDOW_WIDTH ) *( x - button_x );
        button_x = x;
        rotatex -= 50 *( top - bottom ) /( float ) glutGet( GLUT_WINDOW_HEIGHT ) *( button_y - y );
        button_y = y;
        glutPostRedisplay();
    }
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // wybrany tryb rysowania
    case FILL:
    case WIRE:
    case HALO:
        render_mode = value;
        DisplayScene();
        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
    glutCreateWindow( "Linie z halo" );
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie podmenu - Tryb rysowania
    int MenuRenderMode = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Obiekt wypełniony", FILL );
    glutAddMenuEntry( "Krawędzie obiektu", WIRE );
    glutAddMenuEntry( "Efekt halo", HALO );
    #else
   
    glutAddMenuEntry( "Obiekt wypelniony", FILL );
    glutAddMenuEntry( "Krawedzie obiektu", WIRE );
    glutAddMenuEntry( "Efekt halo", HALO );
    #endif
   
    // menu główne
    glutCreateMenu( Menu );
    glutAddSubMenu( "Tryb rysowania", MenuRenderMode );
   
    #ifdef WIN32
   
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Mapy pikselowe Światła i materiały