01 opengl 4 2 wprowadzenie

background image

OpenGL 4.2

Wprowadzenie

Janusz Ganczarski

www.januszg.hg.pl

background image

Wstęp

W tym odcinku kursu poznamy niezbędne wiadomości wstępne dotyczące historii oraz

podstaw budowy i działania biblioteki OpenGL. Czytelnik znajdzie tutaj także uzasadnienie do
konwencji przyjętej w dalszej części kursu.

Historia OpenGL

Specyfikacja pierwszej wersji biblioteki OpenGL (ang. Open Graphics Library) została

opublikowana 1 czerwca 1992 roku na bazie języka GL opracowanego przez firmę Silicon Graphics
Inc. (SGI) na potrzeby stacji graficznych IRIS. Początkiem sukcesu OpenGL stała się jednak dopiero
wersja 1.1, opublikowana 4 marca 1997 roku, którą zaimplementowano m.in. w systemach z rodziny
Microsoft Windows. Kolejne wersje biblioteki (1.2, 1.3, 1.4, 1.5, 2.0, 2.1, 3.0, 3.1, 3.2, 3.3, 4.0, 4.1 i
najnowsza 4.2 z 8 sierpnia 2011 roku) są znakomitą ilustracją rozwoju sprzętu graficznego i tendencji
w programowym generowaniu grafiki.

Przez tych kilkadziesiąt lat jedną z głównych idei twórców OpenGL była zgodność wstecz

biblioteki. W praktyce programy przygotowane dla wcześniejszych wersji biblioteki działały
prawidłowo także na jej najnowszych implementacjach. Drugą zasadą OpenGL jest jej
wieloplatformowość – programy z niej korzystające działają praktycznie na wszystkich współczesnych
systemach operacyjnych. Cechy te są miarą sukcesu OpenGL, która jest obecnie podstawową
niskopoziomową biblioteką graficzną 3D, standardem powszechnie wykorzystywanym przez
producentów oprogramowania użytkowego i gier.

Od początku rozwojem OpenGL zajmowała się ARB (ang. Architecture Review Board), w skład

której wchodzili przedstawiciele najważniejszych firm z branży komputerowej. Taki sposób
wprowadzania zmian w bibliotece zapewnia OpenGL zachowanie niezależności od jednej platformy
sprzętowej lub programowej przy jednoczesnym uwzględnieniu najnowszych osiągnięć w dziedzinie
grafiki komputerowej. Oczywiście współpraca wielu firm, o często sprzecznych interesach, nie zawsze
przebiegała idealnie. Producenci sprzętu graficznego promowali własne rozwiązania,
niekompatybilne z rozwiązaniami konkurencji, a spory wewnątrz ARB powodowały opóźnienia w
przygotowywaniu nowych wersji specyfikacji.

Od 2006 roku rolę opiekuna OpenGL przejął Khronos Group, który rozwija także inne otwarte

standardy programistyczne. Początkowo krytykowany za opóźnienia, Khronos Group w drugiej
połowie 2008 roku wprowadził specyfikację wersji 3.0 i krótko po tym opublikowane zostały kolejne
wersje OpenGL oznaczone numerami 3.1 i 3.2. Ewolucję OpenGL zaproponowaną przez Khronos
Group opiszemy nieco dalej, z kilkuletniej perspektywy wydaje się jednak, że zmiana ta wywarła
pozytywny wpływ na tempo i kierunek rozwoju biblioteki. Tempo zmian widać także na przykładzie
szybkości dalszego rozwoju OpenGL. Wersje 3.3 i 4.0 opublikowano, po raz pierwszy w historii
OpenGL, w tym samym czasie – 11 marca 2010 roku. Krótko potem pojawiła się wersja 4.1 biblioteki.
Natomiast na kolejną i obecnie najnowszą wersję OpenGL 4.2 trzeba było czekać ponad rok.

Rozszerzenia

Jednym z najczęściej dyskutowanych mechanizmów OpenGL są rozszerzenia. Rozszerzenia

określają dodatkowe możliwości implementacji biblioteki i mogą dotyczyć najróżnorodniejszych jej
elementów. Zaletą tego mechanizmu jest dostępność z poziomu OpenGL najnowszych możliwości
sprzętu graficznego, ale jest jednocześnie to także wada. Rozszerzenia proponowane przez niektóre
firmy nie były implementowane przez inne (czasami wynikało to jednak z braku możliwości
technicznych), co czyniło rozszerzenia mechanizmem niepewnym w zastosowaniach komercyjnych.
Mimo tych wad rozszerzenia na trwałe wpisały się w bibliotekę OpenGL i stanowią ważny mechanizm
wspomagający jej rozwój.

OpenGL 3.0 - rewolucja przez ewolucję

Wspomniana wcześniej wersja 3.0 biblioteki OpenGL zapoczątkowała zupełnie nowe

podejście do dotychczas obowiązujących zasad. Twórcy standardu zauważyli bowiem, że dalsze

background image

utrzymywanie tak rozbudowanej i w znacznej części przestarzałej funkcjonalności nie znajduje
praktycznego uzasadnienie. Wyodrębniono liczną grupę tzw. funkcji przestarzałych, która została
wytypowana do usunięcia w przyszłych wersjach specyfikacji. Wskazuje to na generalną tendencję
ewolucji OpenGL – stopniowe usuwanie przestarzałych jej elementów i zastępowanie ich
nowoczesnymi programowalnymi mechanizmami.

Kolejną nowością w bibliotece OpenGL 3.0 jest wymuszenie na twórcach systemów

okienkowych utworzenia funkcji obsługujących nowy kontekst renderingu. Związane jest to z
zupełnie nową w bibliotece OpenGL koncepcją profilowania kontekstów renderingu oraz
planowanym cyklem usuwania z biblioteki przestarzałych mechanizmów. W przyszłości planowane są
profile przeznaczone do rozrywki, tworzenia dokumentów i stron WWW, obsługi aplikacji
desktopowych, czy też dedykowane do urządzeń mobilnych. Warto zauważyć, że ARB zastrzegła
sobie wyłączność do tworzenia profili OpenGL. Sposób obsługi nowego kontekstu renderingu musi
umożliwiać aplikacji wskazanie konkretnego profilu niezbędnego do działania programu. Zauważmy,
że brak nowego kontekstu renderingu przy redukcji funkcjonalności OpenGL, mógłby doprowadzić w
przyszłości do błędów przy uruchamianiu starszych programów.

Początkowo wersja 3.0 OpenGL spotkała się z dość chłodnym przyjęciem ze strony

środowiska programistycznego. Wśród głównych zarzutów pojawiały się tezy o braku pełnej
rezygnacji z przestarzałych funkcji oraz braku modelu obiektowego. Wszystko to było związane z
oczekiwaniami na zupełnie nowe API biblioteki. Całkiem słusznie niezadowolenie wzbudzało także
wielomiesięczne opóźnienie w prezentacji specyfikacji biblioteki.

Analiza decyzji Khronos Group wskazuje jednak, że podjęto bardzo racjonalne decyzje. Na

obecnym etapie rozwoju biblioteki nie była możliwa rezygnacja z całej jej dotychczasowej
funkcjonalności. Zbyt dużo jest bowiem na rynku oprogramowania z tego korzystającego – w grę
wchodzą choćby niezwykle skomplikowane aplikacje inżynierskie i naukowe. Wprowadzenie nowego
kontekstu renderingu, mechanizmu profili oraz redukcji funkcjonalności ułatwi stopniowe
przechodzenie do przyszłych wersji biblioteki.

OpenGL 3.1 – dalszy etap rewolucji przez ewolucję

Kolejna odsłona biblioteki OpenGL pojawiła się bardzo szybko (oczywiście w porównaniu do

poprzednich standardów w tym zakresie) i stanowi kolejny istotny krok w jej rozwoju. Zdecydowano
się na usunięcie wszystkich funkcji określonych w wersji 3.0 jako przestarzałe, choć pozostawiono
jeszcze dodatkową furtkę w postaci rozszerzenia GL_ARB_compatibility, które zawiera wszystkie
usunięte funkcje. Dzięki temu API stał się znacznie mniejsze, co widać choćby po objętości
specyfikacji. Pełna jej wersja obejmująca opis funkcji przestarzałych to blisko 500 stron, a właściwa
zajmuje nieco ponad 350 stron.

Usunięcie przestarzałej funkcjonalności znaczne uprościło bibliotekę i jednocześnie ułatwiło

jej stosowanie. Jednak z ułatwień tych w pełni skorzystają jedynie twórcy nowych aplikacji, którzy od
początku zdecydują się korzystanie wyłącznie z nowoczesnych rozwiązań oferowanych przez OpenGL
3.0/3.1.

OpenGL 3.2 – szybka ewolucja

OpenGL 3.2 to kolejny etap rewolucji przez ewolucję. Specyfikacja została opublikowana

nieco ponad cztery miesiące po upublicznieniu wersji 3.1 i wnosi szereg nowych możliwości do
biblioteki, w tym: tekstury wielopróbkowania, shadery geometrii, obiekty synchronizacji, restart
prymitywu i wierzchołki główne prymitywów. Jedną z bardziej praktycznych zmian jest zdefiniowanie
dwóch profili: podstawowego, obejmującego tylko nieusuniętą funkcjonalność i kompatybilnego,
zgodnego ze wszystkim poprzednimi wersjami OpenGL. Nie zmieniono przy tym praktycznie
funkcjonalności określonych jako przestarzałe, co wskazuje na pewną stabilizację w dalszym kierunku
rozwoju biblioteki.

OpenGL w wersji 3.2 bez rozszerzeń można w pełni porównywać z ówczesnym jej

odpowiednikiem, tj. biblioteką DirectX 10.0, a wraz z zaaprobowanymi przez ARB rozszerzeniami:

background image

GL_ARB_draw_buffers_blend,

GL_ARB_sample_shading,

GL_ARB_texture_cube_map_array,

GL_ARB_texture_gather i GL_ARB_texture_query_lod z biblioteką DirectX w wersji 10.1.

OpenGL 3.3 – ostatnie podejście do zgodności z DirectX 10.0

Specyfikacja biblioteki OpenGL 3.3 to ostatnie spojrzenie na możliwości oferowane prze karty

graficzne zgodne w biblioteką DirectX 10.0. Wprowadzone zamiany, jak bezpośrednia adresacja
atrybutów wierzchołków w języku GLSL, zamiana składowych tekstury przed próbkowaniem, liczniki
czasu, czy też nowe formaty atrybutów wierzchołków i formatów tekstur, służą głownie ułatwieniu
przenoszenia programów pomiędzy DirectX a OpenGL. Znamienna jest także data opublikowania
specyfikacji OpenGL 3.3, która zbiegła się z prezentacją OpenGL 4.0. W ten sposób dokonano
podziału wprowadzonych modyfikacji biblioteki na te obsługiwane przez karty graficzne zgodne z
DirectX 10.0 i te wymagające kart następnej generacji, obsługujących DirectX 11.0.

OpenGL 4.0 – obsługa kolejnej generacji sprzętu

Wersja 4.0 biblioteki OpenGL to kolejny duży krok w jej rozwoju. Autorzy nadrobili zaległości

w stosunku do bezpośredniego konkurenta i szybko przedstawili odpowiednik DirectX 11.0, z
najważniejsza nowością: sprzętową teselacją (kafelkowaniem) prymitywów. Wśród innych nowości
można wymienić dodanie pełnej obsługi 64-bitowych typów danych, nowe formaty tekstur
buforowych, nowe formaty kompresji tekstur oraz rozbudowę mechanizmów transformacji
sprzężonych zwrotnie. Jednocześnie odpowiednio zmodyfikowano GLSL dodając dwa nowe rodzaje
programów cieniowania kontrolujące teselację, które operują na ścieżkach (zbiorach) wierzchołków
wraz z ich atrybutami: shader kontroli teselacji (ang. tesselation control shader) i shader ewaluacji
teselacji (ang. tessellation evaluation shader). Równolegle z wprowadzeniem technik dostępnych w
DirectX 11.0 w OpenGL 4.0 dodano mechanizmy znane z DirectX 10.1, m.in. tablice tekstur
sześciennych oraz rozszerzone tryby mieszania kolorów.

OpenGL 4.1 – integracja z OpenGL ES 2.0

Wersja 4.1 biblioteki OpenGL została opublikowana w krótkim odstępie po wersji 4.0.

Najważniejsza zmianą jest wprowadzenie kompatybilności z biblioteką OpenGL ES 2.0, czyli wersją
OpenGL przeznaczoną na platformy mobilne. Ten nieco zaskakujący kierunek zmian nie jest jednak
przypadkowy – OpenGL ES 2.0 stało się podstawą technologii WebGL udostępniającej trójwymiarowe
API graficzne w przeglądarkach internetowych. Zatem integracja OpenGL ES 2.0 w OpenGL 4.1 ułatwi
zadanie twórcom implementacji WebGL w przeglądarkach internetowych. Ponadto wersja 4.1
OpenGL zawiera m.in. obsługę 64-bitowych danych atrybutów wierzchołków i możliwość
przechowywania shaderów w postaci binarnej.

OpenGL 4.2 – szereg nowych funkcjonalności

Najnowsza obecnie wersja biblioteki OpenGL 4.2 została opublikowana nieco ponad rok od

wersji ją poprzedzającej. Wprowadzono w niej kilka istotnych nowości: liczniki atomowe, atomowe
operacje zapisu/odczytu na wybranych elementach określonej warstwy tekstury, rozbudowa
transformacji sprzężonych zwrotnie o możliwość przechwytywania danych wierzchołków po wyjściu z
teselatora, możliwość modyfikacji wybranego podzbioru danych skompresowanej tekstury bez
konieczności jej ponownego ładowania, ładowanie 8- i 16-bitowych danych do 32-bitowych
zmiennych w celu przyspieszenia transferu danych między shaderami, możliwość pobrania
wewnętrznego formatu danych tekstury oraz umożliwienie współpracy shaderów modyfikujących
głębię z wcześniejszym testem głębokości. Całość wprowadzonych zmian została ujęta w
rozszerzenia, których część może być także obsługiwana przez karty graficzne starszej generacji.

GLSL

Język programów cieniowania OpenGL GLSL (ang. OpenGL Shading Language), zwanymi

najczęściej shaderami, został wprowadzony w wersji 2.0 biblioteki, a od wersji 3.1 stanowi
podstawowy element potoku renderingu zarówno na etapie przetwarzania wierzchołków jak i

background image

fragmentów. Sam język GLSL sukcesywnie ewoluował wraz z kolejnymi wersjami OpenGL. Aktualnie
jego najnowszą wersją jest 4.20, która odpowiada możliwościom tzw. Shader Model 5.0 języka HLSL z
biblioteki DirectX 11.0. Profil podstawowy biblioteki OpenGL 4.2 obsługuje także wcześniejsze wersje
GLSL, począwszy od wersji 1.40 (OpenGL 3.1).

Język GLSL jest opisany w odrębnej specyfikacji, która tworzy spójną całość ze specyfikacją

podstawowego API biblioteki OpenGL.

Powiązania z systemami okienkowymi

Biblioteka OpenGL wymaga wsparcia API systemu okienkowego do tworzenia i zarządzania

kontekstami graficznymi, renderingu w oknie oraz obsługi innych zasobów. Wszystkie liczące się
współczesne systemy operacyjne posiadają API obsługujące bibliotekę OpenGL. W systemie X
Window, dostępnym głównie pod Unix i Linux, jest to GLX. Warto także wspomnieć, że
implementacje GLX istnieją również w systemach Microsoft Windows, MacOS X i kilku innych
platformach, na których jest dostępny X Serwer. OpenGL w systemach Microsoft Windows
obsługiwany jest przez WGL. Quartz, warstwa graficzna systemu MacOS X, zawiera kilka interfejsów
API obsługujących OpenGL. Są to CGL, AGL i NSGLView. Natomiast na platformy mobilne
przeznaczona jest biblioteka EGL, która współpracuje z OpenGL ES 2.0, ale może także obsłużyć
standardowy OpenGL.

Oczywiście stosowanie rozwiązań specyficznych dla danego systemu operacyjnego powoduje,

że danego programu nie można skompilować i uruchomić w innym systemie operacyjnym bez
dokonania szeregu zmian w kodzie źródłowym. Rozwiązanie tego problemu stanowią biblioteki
oferujące jeden, niezależny od systemu operacyjnego, interfejs do obsługi okien i komunikatów.
Pierwszą biblioteką tego typu była biblioteka AUX (ang. Auxiliary Library), jednak bodaj największą
popularność zdobyła biblioteka GLUT (ang. OpenGL Utility Toolkit), opracowana i rozwijana w latach
1994-1998 przez Marka J. Kilgarda. Pomimo sędziwego wieku jest ona ciągle powszechnie stosowana.

Typy danych

Typy danych w obsługiwane przez bibliotekę OpenGL przedstawia Tabela 1. Specyfikacja nie

określa jakiego rodzaju typy danych są użyte w konkretnej implementacji, zawiera jedynie minimalne
wymagania związane z precyzją i wykonywaniem operacji na całkowitych i zmiennoprzecinkowych
typach danych. W szczególności implementacja może używać typów danych o większej ilości bitów
niż minimalne wskazane we wspomnianej wyżej tabeli. Typy GLintptr, GLsizeiptr i GLsync
mają ilość bitów opisaną jako prtbits, co oznacza minimalną ilość bitów niezbędną w danej
implementacji do umieszczenia wskaźnika. Typy te muszą umożliwić zapamiętanie dowolnego
adresu. Trzeba także wyraźnie podkreślić, że typy danych OpenGL nie są typami danych
występującymi w języku C (pomimo częściowej zgodności nazw). Zatem przykładowo, typ GLint
niekoniecznie jest równoznaczny z typem int w C. Ponadto, poza wymienionymi typami, funkcje
OpenGL korzystają z typu pustego GLvoid.

Typ GL

Minimalna ilość

bitów

Opis

GLboolean

1 lub więcej

typ logiczny

GLbyte

8

liczba całkowita ze znakiem w kodzie uzupełnień do 2

GLubyte

8

liczba całkowita bez znaku

GLchar

8

znaki tworzące ciągi

GLshort

16

liczba całkowita ze znakiem w kodzie uzupełnień do 2

GLushort

16

liczba całkowita bez znaku

GLint

32

liczba całkowita ze znakiem w kodzie uzupełnień do 2

GLuint

32

liczba całkowita bez znaku

GLfixed

32

dwie 16-bitowe liczby całkowite ze znakiem w kodzie uzupełnień

background image

do 2 (odpowiednik liczb zmiennoprzecinkowych)

GLint64

64

liczba całkowita ze znakiem w kodzie uzupełnień do 2

GLuint64

64

liczba całkowita bez znaku

GLsizei

32

nieujemna liczba całkowita

GLenum

32

typ wyliczeniowy

GLintptr

ptrbits

liczba całkowita ze znakiem w kodzie uzupełnień do 2

GLsizeiptr

ptrbits

nieujemna liczba całkowita

GLsync

ptrbits

uchwyt obiektu synchronizacji

GLbitfield

32

pole bitowe

GLhalf

16

liczba zmiennoprzecinkowa połówkowej precyzji zakodowana
jako skalar bez znaku

GLfloat

32

liczba zmiennoprzecinkowa

GLclampf

32

liczba zmiennoprzecinkowa ograniczona do przedziału

GLdouble

64

liczba zmiennoprzecinkowa podwójnej precyzji

GLclampd

64

liczba zmiennoprzecinkowa podwójnej precyzji ograniczona do
przedziału

Tabela 1 Typy danych w bibliotece OpenGL.

Arytmetyka zmiennoprzecinkowa

W przypadku liczb zmiennoprzecinkowych (GLfloat i GLdouble) specyfikacja OpenGL nie

określa sposobu ich reprezentacji, zawiera jedynie minimalne wymagania dla implementacji
biblioteki. W szczególności implementacja może stosować arytmetykę zmiennoprzecinkową zgodną
ze standardem IEEE 754. Wymagania nie obejmują obsługi dodatniej i ujemnej nieskończoności
( oraz , oznaczenia matematyczne odpowiednio i ) oraz symboli nieoznaczonych
(ang. not a number), np.


. Nieco inaczej sprawa wygląda w przypadku języka GLSL, gdzie

stosowne są liczby zmiennoprzecinkowe zgodne z wymienionym wyżej standardem IEEE 754.

Nieco inaczej sytuacja wygląda w przypadku liczb zmiennoprzecinkowych połówkowej

precyzji (GLhalf), gdzie specyfikacja OpenGL określa format liczb. 16-bitowe liczby
zmiennoprzecinkowe ze znakiem mają 1 bit znaku ( ), 5 bitów wykładnika ( ) oraz 10 bitów mantysy
( ). Wartość takiej liczby jest określana następująco:


Jeżeli liczba typu GLhalf jest interpretowana jako 16-bitowa liczba całkowita bez znaku ,

to wartość znaku, wykładnika i mantysy wynosi odpowiednio:

background image

Warto także zwrócić uwagę, że niektóre formaty pikseli korzystają z liczb

zmiennoprzecinkowych bez znaku korzystające z 11 lub 10 bitów. Formaty te opiszemy bliżej w
odcinku kursu OpenGL poświęconemu teksturom.

Składnia poleceń

Polecenia OpenGL są funkcjami lub procedurami. Różne grupy poleceń wykonują tę samą

operację, lecz różnią się sposobem w jaki dostarczane są do nich argumenty. Ogólnie rzecz biorąc,
deklaracja polecenia posiada formę:

rtype Name{1|2|3|4}{b|s|i|i64|f|d|ub|us|ui|ui64}{v}( [args ,] T arg1, … ,
T argN [, args] );


Polecenia OpenGL są zbudowane z nazwy, po której następuje, w zależności od konkretnego

przypadku, maksymalnie do 4 znaków. rtype jest typem zwracanym przez funkcję. Nawiasy {}
dołączają szereg znaków (lub zestawów znaków), z których tylko jeden jest używany. Pierwszy znak
{1|2|3|4} oznacza liczbę wartości wskazanego typu, które muszą być przedstawione do
polecenia. Drugi znak lub zestaw znaków {b|s|i|i64|f|d|ub|us|ui|ui64} wskazuje
konkretne rodzaje argumentów: 8-bitowe liczby całkowite, 16-bitowe liczby całkowite, 32-bitowe
liczby całkowite, 64-bitowe liczby całkowite, liczby zmiennoprzecinkowe pojedynczej precyzji lub
liczby zmiennoprzecinkowe podwójnej precyzji – patrz Tabela 2. Zauważmy, że wskazując na typ bez
znaku dodajemy literę u na początku tego nazwy typu (na przykład unsigned byte jest skracany
do ubyte). Ostatni znak {v}, jeśli jest obecny, wskazuje, że polecenie zawiera wskaźnik do tablicy
(wektora) wartości, a nie serię poszczególnych argumentów.

Litera

Odpowiadający typ OpenGL

b

GLbyte

s

GLshort

i

GLint

i64

GLint64

f

GLfloat

d

GLdouble

ub GLubyte
us GLushort
ui GLuint

ui64 GLuint64

Tabela 2 Powiązanie sufiksów poleceń OpenGL z typami argumentów. Patrz Tabela 1 z definicjami typów OpenGL.

Argumenty poleceń umieszczone w nawiasach [args ,] i [, args] mogą, ale nie muszą

być obecne. N argumentów od arg1 do argN ma typ T, który odpowiada jednemu ze znaków lub
parze znaków wskazanych w Tabela 2. Jeśli ostatnim znakiem nie jest v, to N jest podane przez znaki
{1|2|3|4}

(jeśli ich nie ma, to liczba argumentów jest ustalona). Jeśli ostatnim znakiem jest v, to

jest obecny tylko argument arg1 i jest nim tablica N wartości wskazanego typu. Argumenty, których
typ jest stały (tzn. nie wskazany przez sufiks polecenia) są jednym z typów danych OpenGL zawartych
w Tabela 1 lub wskaźnikami do jednego z tych typów.

Przykładowo grupa funkcji glUniform* w specyfikacji OpenGL jest opisywana następująco:


void Uniform{1234}{if}( int location, T value );

co wskazuje osiem deklaracji funkcji, przedstawionych poniżej w notacji ANSI C:

void Uniform1i( int location, int value );

background image

void Uniform1f( int location, float value );
void Uniform2i( int location, int v0, int v1 );
void Uniform2f( int location, float v0, float v1 );
void Uniform3i( int location, int v0, int v1, int v2 );
void Uniform3f( int location, float v0, float v2, float v2 );
void Uniform4i( int location, int v0, int v1, int v2, int v3 );
void Uniform4f( int location, float v0, float v1, float v2,
float v3 );

W dalszej części kursu będziemy używać pełnych deklaracji funkcji w języku ANSI C, jednak

prezentacja zasad budowy poleceń OpenGL ułatwi Czytelnikowi samodzielne zgłębianie specyfikacji
biblioteki.

Obsługa błędów

Biblioteka OpenGL zawiera mechanizmy obsługi błędów. Jednak z założenia wykrywanych

jest tylko część sytuacji spośród zbioru warunków, które mogłyby być uważane za błędy. Stanowi to
kompromis na rzecz efektywności, ponieważ w wielu przypadkach kontrola błędów miałby
niekorzystny wpływ na wydajność programu wolnego od błędów.

Informację o kodzie bieżącego błędu zwraca funkcja:


GLenum glGetError( void );

Zestawienie zwracanych kodów błędów zawiera Tabela 3. Działanie OpenGL jest

niezdefiniowane tylko w przypadku wystąpienia błędu o kodzie GL_OUT_OF_MEMORY. W
pozostałych przypadkach błąd nie powoduje przerwania wykonywania programu, nie jest
wykonywana jedynie funkcja odpowiedzialna za jego powstanie. Polecenie takie na ma wpływu na
stan OpenGL lub zawartość bufora ramki. Jeśli polecenie generujące błąd zwraca wartości, zwracana
jest wartość zero. Jeśli polecenie generujące błąd zmienia wartości poprzez wskaźnik do argumentu,
nie są dokonywane zmiany tych wartości.

Kod błędu

Opis

Czy błędne polecenie
jest ignorowane?

GL_INVALID_ENUM

Argument GLenum
poza zakresem

Tak

GL_INVALID_VALUE

Argument liczbowy poza
zakresem

Tak

GL_INVALID_OPERATION

Nielegalna operacja w
bieżącym stanie

Tak

GL_INVALID_FRAMEBUFFER_OPERATION Niekompletny obiekt

bufora ramki

Tak

GL_OUT_OF_MEMORY

Brak pamięci do
wykonania polecenia

Nieokreślone

GL_NO_ERROR

Brak błędu

-

Tabela 3 Zestawienie kodów błędów OpenGL.

Kiedy błąd zostanie wykryty, ustawiana jest flaga błędu i rejestrowany jest jego kod. Jeżeli

występują kolejne błędy nie wpływają one na ten zarejestrowany kod. Dopiero, gdy wywołana jest
funkcja glGetError, zwracany jest kod błędu i czyszczona jest flaga tak, aby kolejny błąd mógł być
zarejestrowany. Jeżeli glGetError zwraca GL_NO_ERROR oznacza to, że od ostatniego
wywołania glGetError (lub od kiedy GL został zainicjowany) nie był wykryty żaden błąd.
Implementacja może przechowywać więcej niż jedną informację o błędzie. W takim przypadku

background image

wywołanie glGetError zwraca kody kolejnych zarejestrowanych błędów, tak długo aż nie zostanie
zwrócona wartość GL_NO_ERROR.

Typowy kod odczytujący błędy zgłaszane przez bibliotekę OpenGL ma postać poniższej pętli:

GLenum error;
while( ( error = glGetError() ) != GL_NO_ERROR )
{
// obsługa błędu
}


Ogólne warunki generowania błędów przedstawione są poniżej:

Jeśli polecenie wymagające wartości wyliczeniowej określonej, jako stała symboliczna
otrzymuje wartość, która nie należy do dopuszczalnych dla tego polecenia, generowany jest
błąd GL_INVALID_ENUM.

Jeśli przekazywana jest ujemna liczba, a typ argumentu jest określony, jako GLsizei lub
GLsizeiptr, zostanie wygenerowany błąd GL_INVALID_VALUE.

Jeśli ubocznym efektem wykonania polecenia jest wyczerpanie pamięci, może być
wygenerowany błąd GL_OUT_OF_MEMORY.

W innych wypadkach, błędy generowane są tylko na warunkach, które są wyraźnie opisane w
specyfikacji biblioteki OpenGL.

Operacje OpenGL i potok renderingu

OpenGL koncentruje się jedynie na renderingu do bufora ramki oraz czytaniu wartości

zapisanych w tym buforze. Biblioteka nie ma wsparcia dla innych urządzeń, takich jak myszy i
klawiatury, stąd trzeba używać innych mechanizmów w celu przetworzenia informacji
wprowadzanych przez użytkownika.

Rysunek 1 przedstawia schemat działania OpenGL. Z lewej strony diagramu wprowadzane są

polecenia OpenGL. Niektóre z nich określają obiekty geometryczne, które mają zostać narysowane,
inne kontrolują obsługę obiektów przez różne etapy renderingu. Polecenia zawsze są przetwarzane w
kolejności, w jakiej zostały przekazane do OpenGL, a ewentualne opóźnienie realizacji skutków
danego polecenia nie mogą mieć wpływu na wyniki późniejszych poleceń. To samo dotyczy operacji
pobierania zmiennych stanu zapytania i odczytu pikseli, które zwracają stan odpowiadający
kompletnemu wykonaniu wszystkich wcześniej wywoływanych poleceń OpenGL (poza przypadkami
wyraźnie określonymi w specyfikacji). OpenGL wiąże dane w chwili wywoływania polecenia, a
wszelkie późniejsze zmiany danych nie mają wpływu na jego wynik.

Rysunek 1 Diagram blokowy potoku renderingu OpenGL (źródło: specyfikacja OpenGL).

background image

Pierwszy etap potoku renderingu operuje na prymitywach geometrycznych: punktach,

segmentach linii i wielokątach, które są opisane przez wierzchołki. W tym etapie wierzchołki są
przekształcane i oświetlane, a prymitywy są obcinane do bryły widoku w celu przygotowania do
następnego etapu, rasteryzacji. Możliwe jest także programowe lub sprzętowe generowanie nowych
prymitywów, a także usuwanie istniejących. Rasteryzator generuje szereg fragmentów - elementów
bufora ramki używanych do dwuwymiarowego opisu punktów, segmentów linii lub wielokątów.
Każdy tak wyprodukowany fragment jest przekazywany do następnego etapu, gdzie wykonywane są
operacje na poszczególnych fragmentach przed finalnym zapisem w buforze ramki. Operacje na
buforze ramki obejmują m.in. obsługę wartości bufora głębokości, mieszanie kolorów oraz
maskowanie i inne operacje logiczne na wartościach fragmentów. Dane bufora ramki mogą być
również odczytywane lub kopiowane pomiędzy różnymi częściami bufora ramki.

OpenGL wykorzystuje dwa rodzaje buforów ramek: domyślny bufor ramki obsługiwany i

kontrolowany wyłącznie przez system okienkowy (np. za pomocą jednej z opisanych wcześniej
bibliotek) oraz obiekty bufora ramki tworzone i kontrolowane przez aplikacje.

Biblioteka OpenGL wykorzystuje model działania klient-serwer. W typowym przypadku

klientem OpenGL jest aplikacja wykorzystująca polecenia OpenGL, a serwerem OpenGL aktualnie
używana implementacja biblioteki (np. w sterowniku karty graficznej), przetwarzająca polecenia
przesłane przez klienta. Serwer OpenGL może, ale nie musi działać na tym samym komputerze co
klient OpenGL.

Biblioteka OpenGL została zaprojektowana jako biblioteka wieloplatformowa, z założenia

przeznaczona do obsługi wielu rodzajów sprzętu graficznego. Stąd specyfikacja OpenGL zawiera opis
zalecanych rozwiązań dla określonych poleceń, z wyraźnie wskazanymi możliwymi od nich
odstępstwami. Wspomniane dopuszczalne wariacje implementacji OpenGL powodują, że
poszczególne implementacje nie muszą być zgodne "piksel do piksela", tzn. ten sam zestaw danych
źródłowych i poleceń nie musi w różnych implementacjach dać identycznego końcowego rezultatu w
buforze ramki.

Zmienne stanu i maszyna stanu

Zmienne stanu (ang. state) są ważnym wewnętrznym elementem OpenGL opisującym

ustawienia biblioteki. Wiele zmiennych stanu jest dwustanowych, inne mają wartości całkowite lub
zmiennoprzecinkowe. Wszystkie zmienne stanu tworzą tzw. maszynę stanów (ang. state machine),
która steruje zachowaniem OpenGL.

Wyróżniamy dwa rodzaje zmiennych stanu. Pierwszy typ, zwany zmiennymi serwera OpenGL,

znajduje się w serwerze OpenGL. Większość zmiennych stanu OpenGL wchodzi w skład tej kategorii.
Drugi typ, zwany zmiennymi klienta OpenGL, znajduje się w kliencie OpenGL. Każdy egzemplarz
kontekstu OpenGL zawiera jeden pełny zestaw zmiennych stanu serwera OpenGL. Natomiast każde
połączenie od klienta do serwera zawiera odrębny zbiór zmiennych stanu zarówno klienta jak i
serwera OpenGL.

Specyfikacja przewiduje możliwość współdzielenia niektórych zmiennych stanu w ramach

różnych kontekstów renderingu. Tą tematyką zajmiemy się na końcu kursu przy opisie obsługi wielu
kontekstów renderingu w ramach jednej aplikacji OpenGL.

Zmienne stanu modyfikowane są, w sposób jawny lub nie, przez polecenia biblioteki OpenGL,

które będziemy poznawać w dalszych odcinkach kursu. Natomiast do odczytu zmiennych stanu służy
bardzo liczna grupa funkcji glGet*, z których najbardziej podstawowe są następujące funkcje:

void glGetBooleanv( GLenum pname, GLboolean *params );
void glGetIntegerv( GLenum pname, GLint *params );
void glGetInteger64v( GLenum pname, GLint64 *params );
void glGetFloatv( GLenum pname, GLfloat *params );
void glGetDoublev( GLenum pname, GLdouble *params );

background image

Parametr pname określa, którą wartość maszyny stanów OpenGL chcemy pobrać (wszystkie

zmienne stanu przedstawiamy w odrębnym odcinku kursu), a params wskaźnik na zwracaną
wartość. W zależności od rodzaju pobieranej zmiennej tablica params może zawierać pojedynczą
zmienną lub tablicę zmiennych. Program musi zapewnić odpowiednią ilość miejsca w pamięci.
Zauważmy, że rodzaj zwracanej lub zwracanych wartości jednoznacznie określa końcowa część nazwy
funkcji.

Podobna grupa funkcji obsługuje tzw. indeksowane zmienne stanu, których wartości mogą

różnić się dla poszczególnych indeksów zawartych w parametrze index:

void glGetBooleani_v( GLenum target, GLuint index,
GLboolean *data );
void glGetIntegeri_v( GLenum target, GLuint index, GLint *data );
void glGetInteger64i_v( GLenum target, GLuint index,
GLint64 *data );
void glGetFloati_v( GLenum target, GLuint index, GLfloat *data );
void glGetDoublei_v( GLenum target, GLuint index, GLdouble *data );

Dostępne są jeszcze dwie funkcje pobierające informacje o włączeniu lub wyłączeniu danej

właściwości maszyny stanu OpenGL (dwuwartościowe zmienne stanu). Pierwsza z nich obsługuje
pojedyncze właściwość wskazaną w parametrze cap, druga właściwość indeksowaną, której indeks
zawiera parametr index (właściwość może mieć różną wartość dla różnych indeksów).

GLboolean glIsEnabled( GLenum cap );
GLboolean glIsEnabledi( GLenum target, GLuint index );

Włączanie i wyłącznie zmiennych stanu określających dwustanowe właściwości maszyny

stanu umożliwiają poniższe funkcje:

void glDisable( GLenum cap );
void glEnable( GLenum cap );
void glEnablei( GLenum target, GLuint index );
void glDisablei( GLenum target, GLuint index );

które w wersji indeksowej działają na zmiennej o indeksie wskazanym w parametrze index.

Podczas odczytu i zapisu zmiennych stanu OpenGL może zachodzić konieczność dokonania

konwersji (np. zmienna stanu jest typu całkowitego a odczytujemy dane typu
zmiennoprzecinkowego). W takich przypadkach OpenGL dokonuje konwersji stosując następujące
zasady:

dla zmiennej stanu typu GLboolean całkowita lub zmiennoprzecinkowa liczba o wartości 0
jest konwertowana na wartość GL_FALSE, a każda niezerowa liczba na wartość GL_TRUE,

dla zmiennej stanu typu całkowitego lub typu wyliczeniowego GLenum, wartości logiczne
GL_FALSE i GL_TRUE konwertowane są odpowiednio do wartości 0 i 1; liczby
zmiennoprzecinkowe są zaokrąglane do najbliższej liczby całkowitej,

dla zmiennej stanu typu zmiennoprzecinkowego, wartości logiczne GL_FALSE i GL_TRUE
konwertowane są odpowiednio do wartości 0,0 i 1,0; liczby całkowite są bezpośrednio
konwertowane do liczby zmiennoprzecinkowej.

Wykorzystane w powyższym opisie stałe GL_TRUE i GL_FALSE to odpowiedniki słów

zarezerwowanych true i false z języka C++. Stałe te mają odpowiednio wartość 1 i 0.

background image

Przyjęte w kursie konwencje i zasady

Programy w kursie zasadniczo wymagają dostępności biblioteki OpenGL w wersji 4.2, ale w

wielu przypadkach wystarczy dostępność wersji 4.1 lub 4.0. Przy ich pisaniu wykorzystano nowe
funkcje zarządzające kontekstem OpenGL, co powoduje, że programy nie mogą być bezpośrednio
uruchamiane na starszych niż 3.0 wersjach biblioteki. Jednakże część z nich, po odpowiednich
modyfikacjach, może działać z OpenGL 2.0/2.1. Programy przykładowe przy tworzeniu okien
korzystają bezpośrednio z Motif (systemu UNIX i Linux) oraz API WIN32 (systemy Microsoft
Windows). Fragmenty programu specyficzne dla danego systemu operacyjnego są oddzielone od
elementów korzystających z biblioteki OpenGL, co ułatwi Czytelnikowi ewentualną migrację
programu na wybraną platformę systemową lub programową.

Wszystkie programy napisane są przy użyciu języka C++ i kompilowane za pomocą

kompilatorów: Microsoft Visual C++ 2008 EE i GCC 4.4 w systemie Linux Mandriva 2010. Programy
testowano na karcie graficznej NVidia GeForce GTX 460. W przypadku programów napisanych w
OpenGL ES 2.0 używana jest biblioteka EGL i emulator pochodzący z pakietu PowerVR Insider SDK.

Do kompilacji programów nie są wymagane żadne dodatkowe zewnętrzne biblioteki. Jednak

najczęściej używane funkcje zostały pogrupowane w odrębne pliki, które mogą stanowić przydatną
podręczną bibliotekę.


Document Outline


Wyszukiwarka

Podobne podstrony:
01 opengl wprowadzenie 2011id 2873 pptx
01 opengl 3 2 wprowadzenie
01 Gramatyka wprowadzenie
01 CalkaNieozn Wprowadzenieid 3058
01 NoZ wprowadzenie
01 html wprowadzenie
01 Algorytmy wprowadzenieid 2595 ppt
01 xml wprowadzenie
01-CalkaNieozn-Wprowadzenie
MEBS 01 Metody wprowadzenie
01 OT wprowadzenie
01 PKON wprowadzenie
01 Gramatyka wprowadzenie
01 CalkaNieozn Wprowadzenieid 3058
01 Algorytmy wprowadzenieid 2595 ppt

więcej podobnych podstron