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 

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

GLubyte 

liczba całkowita bez znaku 

GLchar 

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 

GLbyte 

GLshort 

GLint 

i64 

GLint64 

GLfloat 

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