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:
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:
void glDepthFunc( GLenum func )
Parametr func przyjmuje jedną z poniższych wartości:
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:
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:
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:
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:
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.
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.
Plik colors.h
#ifndef __COLORS__H__
#define __COLORS__H__
#include <GL/gl.h>
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
};
const GLfloat Azure[ 4 ] =
{
0.941176, 1.000000, 1.000000, 1.000000
};
const GLfloat Beige[ 4 ] =
{
0.960784, 0.960784, 0.862745, 1.000000
};
const GLfloat Bisque[ 4 ] =
{
1.000000, 0.894118, 0.768627, 1.000000
};
const GLfloat Black[ 4 ] =
{
0.000000, 0.000000, 0.000000, 1.000000
};
const GLfloat BlanchedAlmond[ 4 ] =
{
1.000000, 0.921569, 0.803922, 1.000000
};
const GLfloat Blue[ 4 ] =
{
0.000000, 0.000000, 1.000000, 1.000000
};
const GLfloat BlueViolet[ 4 ] =
{
0.541176, 0.168627, 0.886275, 1.000000
};
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
};
const GLfloat Chocolate[ 4 ] =
{
0.823529, 0.411765, 0.117647, 1.000000
};
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
};
const GLfloat Crimson[ 4 ] =
{
0.862745, 0.078431, 0.235294, 1.000000
};
const GLfloat Cyan[ 4 ] =
{
0.000000, 1.000000, 1.000000, 1.000000
};
const GLfloat DarkBlue[ 4 ] =
{
0.000000, 0.000000, 0.545098, 1.000000
};
const GLfloat DarkCyan[ 4 ] =
{
0.000000, 0.545098, 0.545098, 1.000000
};
const GLfloat DarkGoldenrod[ 4 ] =
{
0.721569, 0.525490, 0.043137, 1.000000
};
const GLfloat DarkGray[ 4 ] =
{
0.662745, 0.662745, 0.662745, 1.000000
};
const GLfloat DarkGreen[ 4 ] =
{
0.000000, 0.392157, 0.000000, 1.000000
};
const GLfloat DarkKhaki[ 4 ] =
{
0.741176, 0.717647, 0.419608, 1.000000
};
const GLfloat DarkMagenta[ 4 ] =
{
0.545098, 0.000000, 0.545098, 1.000000
};
const GLfloat DarkOliveGreen[ 4 ] =
{
0.333333, 0.419608, 0.184314, 1.000000
};
const GLfloat DarkOrange[ 4 ] =
{
1.000000, 0.549020, 0.000000, 1.000000
};
const GLfloat DarkOrchid[ 4 ] =
{
0.600000, 0.196078, 0.800000, 1.000000
};
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
};
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
};
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
};
const GLfloat Gold[ 4 ] =
{
1.000000, 0.843137, 0.000000, 1.000000
};
const GLfloat Goldenrod[ 4 ] =
{
0.854902, 0.647059, 0.125490, 1.000000
};
const GLfloat Gray[ 4 ] =
{
0.501961, 0.501961, 0.501961, 1.000000
};
const GLfloat Green[ 4 ] =
{
0.000000, 0.501961, 0.000000, 1.000000
};
const GLfloat GreenYellow[ 4 ] =
{
0.678431, 1.000000, 0.184314, 1.000000
};
const GLfloat Honeydew[ 4 ] =
{
0.941176, 1.000000, 0.941176, 1.000000
};
const GLfloat HotPink[ 4 ] =
{
1.000000, 0.411765, 0.705882, 1.000000
};
const GLfloat IndianRed[ 4 ] =
{
0.803922, 0.360784, 0.360784, 1.000000
};
const GLfloat Indigo[ 4 ] =
{
0.294118, 0.000000, 0.509804, 1.000000
};
const GLfloat Ivory[ 4 ] =
{
1.000000, 1.000000, 0.941176, 1.000000
};
const GLfloat Khaki[ 4 ] =
{
0.941176, 0.901961, 0.549020, 1.000000
};
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
};
const GLfloat Lime[ 4 ] =
{
0.000000, 1.000000, 0.000000, 1.000000
};
const GLfloat LimeGreen[ 4 ] =
{
0.196078, 0.803922, 0.196078, 1.000000
};
const GLfloat Linen[ 4 ] =
{
0.980392, 0.941176, 0.901961, 1.000000
};
const GLfloat Magenta[ 4 ] =
{
1.000000, 0.000000, 1.000000, 1.000000
};
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
};
const GLfloat Navy[ 4 ] =
{
0.000000, 0.000000, 0.501961, 1.000000
};
const GLfloat OldLace[ 4 ] =
{
0.992157, 0.960784, 0.901961, 1.000000
};
const GLfloat Olive[ 4 ] =
{
0.501961, 0.501961, 0.000000, 1.000000
};
const GLfloat OliveDrab[ 4 ] =
{
0.419608, 0.556863, 0.137255, 1.000000
};
const GLfloat Orange[ 4 ] =
{
1.000000, 0.647059, 0.000000, 1.000000
};
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
};
const GLfloat PaleGreen[ 4 ] =
{
0.596078, 0.984314, 0.596078, 1.000000
};
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
};
const GLfloat Pink[ 4 ] =
{
1.000000, 0.752941, 0.796078, 1.000000
};
const GLfloat Plum[ 4 ] =
{
0.866667, 0.627451, 0.866667, 1.000000
};
const GLfloat PowderBlue[ 4 ] =
{
0.690196, 0.878431, 0.901961, 1.000000
};
const GLfloat Purple[ 4 ] =
{
0.501961, 0.000000, 0.501961, 1.000000
};
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
};
const GLfloat SaddleBrown[ 4 ] =
{
0.545098, 0.270588, 0.074510, 1.000000
};
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
};
const GLfloat Sienna[ 4 ] =
{
0.627451, 0.321569, 0.176471, 1.000000
};
const GLfloat Silver[ 4 ] =
{
0.752941, 0.752941, 0.752941, 1.000000
};
const GLfloat SkyBlue[ 4 ] =
{
0.529412, 0.807843, 0.921569, 1.000000
};
const GLfloat SlateBlue[ 4 ] =
{
0.415686, 0.352941, 0.803922, 1.000000
};
const GLfloat SlateGray[ 4 ] =
{
0.439216, 0.501961, 0.564706, 1.000000
};
const GLfloat Snow[ 4 ] =
{
1.000000, 0.980392, 0.980392, 1.000000
};
const GLfloat SpringGreen[ 4 ] =
{
0.000000, 1.000000, 0.498039, 1.000000
};
const GLfloat SteelBlue[ 4 ] =
{
0.274510, 0.509804, 0.705882, 1.000000
};
const GLfloat Tan[ 4 ] =
{
0.823529, 0.705882, 0.549020, 1.000000
};
const GLfloat Teal[ 4 ] =
{
0.000000, 0.501961, 0.501961, 1.000000
};
const GLfloat Thistle[ 4 ] =
{
0.847059, 0.749020, 0.847059, 1.000000
};
const GLfloat Tomato[ 4 ] =
{
1.000000, 0.388235, 0.278431, 1.000000
};
const GLfloat Transparent[ 4 ] =
{
1.000000, 1.000000, 1.000000, 1.000000
};
const GLfloat Turquoise[ 4 ] =
{
0.250980, 0.878431, 0.815686, 1.000000
};
const GLfloat Violet[ 4 ] =
{
0.933333, 0.509804, 0.933333, 1.000000
};
const GLfloat Wheat[ 4 ] =
{
0.960784, 0.870588, 0.701961, 1.000000
};
const GLfloat White[ 4 ] =
{
1.000000, 1.000000, 1.000000, 1.000000
};
const GLfloat WhiteSmoke[ 4 ] =
{
0.960784, 0.960784, 0.960784, 1.000000
};
const GLfloat Yellow[ 4 ] =
{
1.000000, 1.000000, 0.000000, 1.000000
};
const GLfloat YellowGreen[ 4 ] =
{
0.603922, 0.803922, 0.196078, 1.000000
};
#endif
Plik szescian5.cpp
#include <GL/glut.h>
#include <stdlib.h>
#include "colors.h"
enum
{
FULL_WINDOW = GL_ALWAYS + 100,
ASPECT_1_1,
EXIT
};
int aspect = FULL_WINDOW;
const GLdouble left = - 2.0;
const GLdouble right = 2.0;
const GLdouble bottom = - 2.0;
const GLdouble top = 2.0;
const GLdouble near = 3.0;
const GLdouble far = 7.0;
GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;
int button_state = GLUT_UP;
int button_x, button_y;
GLenum depth_test = GL_LESS;
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -( near + far ) / 2 );
glRotatef( rotatex, 1.0, 0, 0 );
glRotatef( rotatey, 0, 1.0, 0 );
glScalef( 1.15, 1.15, 1.15 );
glEnable( GL_DEPTH_TEST );
glDepthFunc( depth_test );
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 );
glEnd();
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left, right, bottom * height / width, top * height / width, near, far );
else
if( width >= height && height > 0 )
glFrustum( left * width / height, right * width / height, bottom, top, near, far );
}
else
glFrustum( left, right, bottom, top, near, far );
Display();
}
void SpecialKeys( int key, int x, int y )
{
switch( key )
{
case GLUT_KEY_LEFT:
rotatey -= 1;
break;
case GLUT_KEY_UP:
rotatex -= 1;
break;
case GLUT_KEY_RIGHT:
rotatey += 1;
break;
case GLUT_KEY_DOWN:
rotatex += 1;
break;
}
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
button_state = state;
if( state == GLUT_DOWN )
{
button_x = x;
button_y = y;
}
}
}
void MouseMotion( int x, int y )
{
if( button_state == GLUT_DOWN )
{
rotatey += 30 *( right - left ) / glutGet( GLUT_WINDOW_WIDTH ) *( x - button_x );
button_x = x;
rotatex -= 30 *( top - bottom ) / glutGet( GLUT_WINDOW_HEIGHT ) *( button_y - y );
button_y = y;
glutPostRedisplay();
}
}
void Menu( int value )
{
switch( value )
{
case FULL_WINDOW:
aspect = FULL_WINDOW;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case ASPECT_1_1:
aspect = ASPECT_1_1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case GL_NEVER:
depth_test = GL_NEVER;
Display();
break;
case GL_LESS:
depth_test = GL_LESS;
Display();
break;
case GL_EQUAL:
depth_test = GL_EQUAL;
Display();
break;
case GL_LEQUAL:
depth_test = GL_LEQUAL;
Display();
break;
case GL_GREATER:
depth_test = GL_GREATER;
Display();
break;
case GL_NOTEQUAL:
depth_test = GL_NOTEQUAL;
Display();
break;
case GL_GEQUAL:
depth_test = GL_GEQUAL;
Display();
break;
case GL_ALWAYS:
depth_test = GL_ALWAYS;
Display();
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Sześcian 5" );
#else
glutCreateWindow( "Szescian 5" );
#endif
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutSpecialFunc( SpecialKeys );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
glutCreateMenu( Menu );
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 );
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 );
glutCreateMenu( Menu );
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddSubMenu( "Test z-bufora", MenuZbuffer );
#ifdef WIN32
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
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).
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.
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:
void glutSolidSphere( GLdouble radius, GLint slices, GLint stacks )
której parametry oznaczają:
Sześcian o środku położonym w początku układu współrzędnych i boku długości size rysuje funkcja:
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:
void glutIdleFunc( void( * func )( void ) )
Plik zbufor.cpp
#include <GL/glut.h>
#include <stdlib.h>
#include "colors.h"
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;
enum
{
CUTTING_PLANE,
POLYGON_OFFSET,
EXIT
};
GLfloat angle = 0.0;
GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;
bool cutting_plane = true;
bool polygon_offset = true;
int button_state = GLUT_UP;
int button_x, button_y;
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -( near + far ) / 2 );
glRotatef( rotatex, 1.0, 0, 0 );
glRotatef( rotatey, 0, 1.0, 0 );
glScalef( 1.15, 1.15, 1.15 );
glEnable( GL_DEPTH_TEST );
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();
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 );
if( cutting_plane )
{
glDrawBuffer( GL_NONE );
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();
glDrawBuffer( GL_BACK );
}
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 );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glFrustum( left, right, bottom, top, near, far );
Display();
}
void SpecialKeys( int key, int x, int y )
{
switch( key )
{
case GLUT_KEY_LEFT:
rotatey -= 1;
break;
case GLUT_KEY_UP:
rotatex -= 1;
break;
case GLUT_KEY_RIGHT:
rotatey += 1;
break;
case GLUT_KEY_DOWN:
rotatex += 1;
break;
}
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
button_state = state;
if( state == GLUT_DOWN )
{
button_x = x;
button_y = y;
}
}
}
void MouseMotion( int x, int y )
{
if( button_state == GLUT_DOWN )
{
rotatey += 30 *( right - left ) / glutGet( GLUT_WINDOW_WIDTH ) *( x - button_x );
button_x = x;
rotatex -= 30 *( top - bottom ) / glutGet( GLUT_WINDOW_HEIGHT ) *( button_y - y );
button_y = y;
glutPostRedisplay();
}
}
void Menu( int value )
{
switch( value )
{
case CUTTING_PLANE:
cutting_plane = !cutting_plane;
Display();
break;
case POLYGON_OFFSET:
polygon_offset = !polygon_offset;
Display();
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
glutInitWindowSize( 500, 500 );
glutCreateWindow( "Z-bufor" );
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutSpecialFunc( SpecialKeys );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
glutCreateMenu( Menu );
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
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutIdleFunc( Display );
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:
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ę:
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.
Plik linie_z_halo.cpp
#include <GL/glut.h>
#include <stdlib.h>
#include <math.h>
#include "colors.h"
enum
{
FILL,
WIRE,
HALO,
EXIT
};
const GLint left = - 5;
const GLint right = 5;
const GLint bottom = - 5;
const GLint top = 5;
const GLint near = - 5;
const GLint far = 5;
GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;
int button_state = GLUT_UP;
int button_x, button_y;
int render_mode = WIRE;
void DisplayScene()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glRotatef( rotatex, 1.0, 0.0, 0.0 );
glRotatef( rotatey, 0.0, 1.0, 0.0 );
glEnable( GL_DEPTH_TEST );
glColor4fv( Green );
switch( render_mode )
{
case FILL:
glutSolidSphere( 3.0, 15, 15 );
break;
case WIRE:
glDisable( GL_DEPTH_TEST );
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
glLineWidth( 3.0 );
glutSolidSphere( 3.0, 15, 15 );
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
glEnable( GL_DEPTH_TEST );
break;
case HALO:
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
glLineWidth( 9.0 );
glutSolidSphere( 3.0, 15, 15 );
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
glLineWidth( 3.0 );
glDepthFunc( GL_LEQUAL );
glutSolidSphere( 3.0, 15, 15 );
glDepthFunc( GL_LESS );
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
break;
}
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( left, right, bottom, top, near, far );
DisplayScene();
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
button_state = state;
if( state == GLUT_DOWN )
{
button_x = x;
button_y = y;
}
}
}
void MouseMotion( int x, int y )
{
if( button_state == GLUT_DOWN )
{
rotatey += 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();
}
}
void Menu( int value )
{
switch( value )
{
case FILL:
case WIRE:
case HALO:
render_mode = value;
DisplayScene();
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
glutInitWindowSize( 500, 500 );
glutCreateWindow( "Linie z halo" );
glutDisplayFunc( DisplayScene );
glutReshapeFunc( Reshape );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
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
glutCreateMenu( Menu );
glutAddSubMenu( "Tryb rysowania", MenuRenderMode );
#ifdef WIN32
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutMainLoop();
return 0;
}