background image

OpenGL 3.2 

Szablon aplikacji OpenGL 

Janusz Ganczarski 

www.januszg.hg.pl

 

 

 

background image

Wstęp 

 

W  tym  odcinku  kursu  przygotujemy  oraz  omówimy  szablon  aplikacji  OpenGL.  Będą  to 

właściwie dwa szablony. Jeden przeznaczony dla systemów z Microsoft Windows, drugi dla systemów 
posiadających X Window Serwer, czyli głównie UNIX/Linux. 
 

Nasze  przykładowe  programy  będą  składały  się  z  trzech  zasadniczych  części.  Pierwszym 

będzie  zależny  od  platformy  systemowej  kod  tworzący  okno  wraz  z  kontekstem  OpenGL  oraz 
obsługujący  interfejs  użytkownika.  Druga  częśd  obejmuje  kod  wykorzystujący  bibliotekę  OpenGL, 
który napisany jest w sposób niezależny od systemu operacyjnego. Trzecią częśd stanowią biblioteki 
pomocnicze najczęściej korzystające z OpenGL, które również napisane są w sposób przenośny. 

Szablon aplikacji OpenGL dla Microsoft Windows 

Tworzenie okna 

 

Na  początek  kod  źródłowy  funkcji  WinMain,  której  zadaniem  jest  utworzenie  okna 

programu. Nie opisujemy bliżej tego kodu, bowiem opis API WIN32 przekracza tematykę niniejszego 
kursu, ale nie odbiega on od typowego kodu prezentowanego w dokumentacji MSDN. 
 

#include

 

"resource.h" 

 
… 
 

////////////////////////////////////////////////////////////////////// 
// funkcja main 
////////////////////////////////////////////////////////////////////// 

int

 WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, 

int

 

nCmdShow ) 

    

// utworzenie i rejestracja okna 

    WNDCLASSEX winClass;  
    winClass.lpszClassName = 

"MY_WINDOWS_CLASS"

    winClass.cbSize = 

sizeof

( WNDCLASSEX ); 

    winClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 
    winClass.lpfnWndProc = WindowProc; 
    winClass.hInstance = hInstance; 
    winClass.hIcon = LoadIcon( hInstance, (LPCTSTR)IDI_OPENGL_ICON ); 
    winClass.hIconSm = LoadIcon( hInstance, (LPCTSTR)IDI_OPENGL_ICON ); 
    winClass.hCursor = LoadCursor( NULL, IDC_ARROW ); 
    winClass.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH ); 
    winClass.lpszMenuName = NULL; 
    winClass.cbClsExtra = 0; 
    winClass.cbWndExtra = 0; 
    

if

( !RegisterClassEx( &winClass ) ) 

        

return

 FALSE; 

    HWND hWnd = NULL; 
    hWnd = CreateWindowEx( NULL, 

"MY_WINDOWS_CLASS"

"OpenGL 3.2 - szablon OpenGL"

,  

                WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 500, 500, NULL, NULL, hInstance, NULL 
); 
    

if

( hWnd == NULL ) 

        

return

 FALSE; 

 
    

// przetwarzanie pętli komunikatów 

    MSG msg = { 0 }; 
    

while

( WM_QUIT != msg.message ) 

    { 
        

if

( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 

        { 
            TranslateMessage( &msg ); 
            DispatchMessage( &msg ); 
        } 
        

else 

            SendMessage( hWnd, WM_PAINT, 0, 0 ); 
    } 
 
    

// wyrejestrowania okna i zakończenie działania programu 

    UnregisterClass( 

"MY_WINDOWS_CLASS"

, winClass.hInstance ); 

    

return

 msg.wParam; 

background image

 

Zasoby 

 

Okno zawiera zasoby z ikoną z logiem biblioteki OpenGL. Plik nagłówkowy zasobów wygląda 

następująco: 
 

#ifndef

 __RESOURCE_H__ 

#define

 __RESOURCE_H__ 

 

#define

 IDI_OPENGL_ICON         4001 

 

#endif

 

// __RESOURCE_H__

 

 
A tak wygląda sam plik zasobów: 
 

#include

 

"resource.h" 

 

////////////////////////////////////////////////////////////////////// 
// ikona OpenGL 
////////////////////////////////////////////////////////////////////// 

IDI_OPENGL_ICON         ICON    DISCARDABLE     

"opengl.ico" 

 

Funkcja przetwarzająca komunikaty 

 

Poniżej  znajduje  się  kod  źródłowy  funkcji  okna  w  API  WIN32,  czyli  funkcji  przetwarzającej 

komunikaty.  Funkcja  ta  wykorzystuje  dwie  zmienne  globalne  hRC  i  hDC,  deklaracje  funkcji 
korzystających  z  OpenGL  oraz  plik  nagłówkowy  „extension3.h”  będący  elementem  biblioteki 
pomocniczej extensions3, którą poznamy w dalszej części tego odcinka kursu. 
 

#include

 

"extensions3.h" 

 

////////////////////////////////////////////////////////////////////// 
// deklaracje funkcji obsługujących renderning w OpenGL 
////////////////////////////////////////////////////////////////////// 

void

 DisplayScene(); 

void

 Reshape( 

int

 width, 

int

 height ); 

void

 InitScene(); 

void

 DeleteScene(); 

 

////////////////////////////////////////////////////////////////////// 
// uchwyt kontekstu renderingu OpenGL 
////////////////////////////////////////////////////////////////////// 

HGLRC hRC3 = NULL; 
 

////////////////////////////////////////////////////////////////////// 
// uchwyt urządzenia 
////////////////////////////////////////////////////////////////////// 

HDC hDC = NULL; 
 

////////////////////////////////////////////////////////////////////// 
// funkcja okna 
////////////////////////////////////////////////////////////////////// 

LRESULT CALLBACK WindowProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) 

    

// standardowy kontekst renderingu OpenGL 

    HGLRC hRC; 
 
    

// przetwarzanie komunikatów 

    

switch

 (msg) 

    { 
    

// utworzenie okna 

    

case

 WM_CREATE:  

        

// utworzenie deskryptora pikseli 

        GLuint PixelFormat; 
        PIXELFORMATDESCRIPTOR pfd; 
        memset( &pfd, 0, 

sizeof

( PIXELFORMATDESCRIPTOR ) ); 

        pfd.nSize = 

sizeof

( PIXELFORMATDESCRIPTOR ); 

        pfd.nVersion = 1; 

background image

        pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 
        pfd.iPixelType = PFD_TYPE_RGBA; 
        pfd.cColorBits = 32; 
        pfd.cDepthBits = 24; 
 
        

// utworzenie uchwytu urządzenia 

        hDC = GetDC( hWnd ); 
        PixelFormat = ChoosePixelFormat( hDC, &pfd ); 
        SetPixelFormat( hDC, PixelFormat, &pfd ); 
 
        

// utworzenie kontekstu renderingu OpenGL 

        hRC = wglCreateContext( hDC ); 
        wglMakeCurrent( hDC, hRC ); 
 
        

// utworzenie kontekstu renderingu OpenGL 3.2 

        wglCreateContextAttribsARB = 

reinterpret_cast

 < PFNWGLCREATECONTEXTATTRIBSARBPROC > ( 

wglGetProcAddress( 

"wglCreateContextAttribsARB"

 ) ); 

        

if

( wglCreateContextAttribsARB ) 

        { 
            

int

 attribs[] =  

            {  
                WGL_CONTEXT_MAJOR_VERSION_ARB, 3, 
                WGL_CONTEXT_MINOR_VERSION_ARB, 2, 
                WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 
                0  
            }; 
            HGLRC hRC3 = wglCreateContextAttribsARB( hDC, hRC, attribs); 
            wglMakeCurrent( hDC, hRC3 ); 
            wglDeleteContext( hRC ); 
            hRC = hRC3; 
        } 
        

else 

        { 
            MessageBox( NULL, 

"Nie można utworzyć kontekstu OpenGL 3.2"

"Błąd"

, MB_OK | 

MB_ICONEXCLAMATION ); 
            PostQuitMessage( 0 ); 
        } 
 
        

// konfiguracja niezbędnych rozszerzeń 

        OpenGL30(); 
 
        

// inicjacja maszyny stanów i elementów sceny 

        InitScene(); 
        InvalidateRect( hWnd, NULL, TRUE ); 
        

break

 
    

// usuwanie okna 

    

case

 WM_DESTROY: 

        wglMakeCurrent( hDC, NULL ); 
        DeleteScene(); 
        wglDeleteContext( hRC3 ); 
        ReleaseDC( hWnd, hDC ); 
        PostQuitMessage( 0 ); 
        

break

 
    

// zmiana rozmiaru okna 

    

case

 WM_SIZE:  

        Reshape( LOWORD( lParam ), HIWORD( lParam ) ); 
        InvalidateRect( hWnd, NULL, TRUE ); 
        

break

 
    

// odrysowanie okna 

    

case

 WM_PAINT:  

        DisplayScene(); 
        SwapBuffers( hDC ); 
        ValidateRect( hWnd, NULL ); 
        

break

 
    

// automatyczne przetworzenie pozostałych komunikatów 

    

default

:  

        

return

 DefWindowProc( hWnd, msg, wParam, lParam ); 

    } 
    

return

 0L; 

 

background image

Tworzenie okna 

 

Strukturą  opisującą  zawartośd  okna  renderingu  i  bufora  ramki  OpenGL  jest 

PIXELFORMATDESCRIPTOR. Jej pełny opis znajduje się w MSDN, ale na nasze potrzeby wystarczy 
wypełnienie  tylko  kilku  jej  pól.  Pierwsze  z  nich  to  dwFlags  zawierające  rodzaj  okna,  a  także 
wskazujące na obsługę OpenGL i włączenie podwójnego buforowania: 
 

pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 

 
Trzy  pozostałe  pola  używane  w  naszym  szablonie  określają  rodzaj  danych  przechowywanych  w 
buforze ramki OpenGL. Przyjęte przez nas dane wskazuą typowy dla wspólczesnych kart graficznych 
format bufora ramki z 32-bitowym opisem koloru i 24-bitowym opisem głębi: 
 

pfd.iPixelType = PFD_TYPE_RGBA; 
pfd.cColorBits = 32; 
pfd.cDepthBits = 24; 

 
 

W niektórych programach zawartośd bufora ramki będzie rozszerzana o inne informacje (np. 

bufor szablonu). Operacje te będą odrębnie opisywane. 
 

Następnymi  etapami  tworzenia  okna  jest  wygenerowanie  uchwyt  urządzenia  i  kontekstu 

renderingu  OpenGL.  Funkcje  wglCreateContekst  i  wglMakeCurrent,  jak  wskazuje  ich 
prefiks, pochodzą z biblioteki WGL. Pierwsza funkcja tworzy kontekst, druga dokonuje jego aktywacji. 
 

// utworzenie uchwytu urządzenia 

hDC = GetDC( hWnd ); 
PixelFormat = ChoosePixelFormat( hDC, &pfd ); 
SetPixelFormat( hDC, PixelFormat, &pfd ); 
 

// utworzenie kontekstu renderingu OpenGL 

hRC = wglCreateContext( hDC ); 
wglMakeCurrent( hDC, hRC ); 

 
 

Razem z wersją 3.0 biblioteki OpenGL został wprowadzony nowy rozbudowany kontekst. W 

przypadku  biblioteki  WGL  jego  utworzenie  wymaga  użycia  rozszerzenia  WGL_ARB_create_context 
lub  WGL_ARB_create_context_profile.  Specyfika  budowy  bibliotek  dynamicznych  obsługujących 
OpenGL w systemach Microsoft Windows powoduje, że musimy pobierad wskaźniki funkcji obecnych 
w  rozszerzeniach.  Dotyczy  to  niestety  także  funkcji  z  biblioteki  OpenGL  od  wersji  1.2  wzwyż.  Sam 
wskaźnik  wglCreateContextAttribsARB  znajduje  się  w  obsługującej  ten  mechanizm  i 
wspomnianej już wcześniej bibliotece extensions3
 

// utworzenie kontekstu renderingu OpenGL 3.2 

wglCreateContextAttribsARB = 

reinterpret_cast

 < PFNWGLCREATECONTEXTATTRIBSARBPROC > ( 

wglGetProcAddress( 

"wglCreateContextAttribsARB"

 ) ); 

 
 

Po  sprawdzeniu  poprawności  pobranego  wskaźnika  tworzymy  nowy  kontekst  hRC3, 

używając  do  tego  celu  „starego”  kontekstu  hRC.  Koniecznośd  tworzenia  dwóch  kontekstów  jest 
bezpośrednią  konsekwencją  pobierania  wskaźników  funkcji,  która  to  czynnośd  wymaga  aktywnego 
podstawowego  kontekstu  OpenGL.  Tablica  attribs  opisuje  parametry  kontekstu  i  zawiera  pary 
liczb:  parametr  i  jego  wartośd.  W  naszym  przypadku tworzony  jest  kontekst  obsługujący  bibliotekę 
OpenGL 3.2 z wyłączoną obsługą przestarzałej funkcjonalności. 
 

if

( wglCreateContextAttribsARB ) 


    

int

 attribs[] =  

    { 
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3, 
        WGL_CONTEXT_MINOR_VERSION_ARB, 2, 
        WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 
        0 
    }; 

background image

    HGLRC hRC3 = wglCreateContextAttribsARB( hDC, hRC, attribs ); 
    wglMakeCurrent( hDC, hRC3 ); 
    wglDeleteContext( hRC ); 
    hRC = hRC3; 

else 


    MessageBox( NULL, 

"Nie można utworzyć kontekstu OpenGL 3.2"

"Błąd"

, MB_OK | 

MB_ICONEXCLAMATION ); 
    PostQuitMessage( 0 ); 

 
 

Alternatywnie od wersji 3.2 biblioteki OpenGL możemy wykorzystad profile. Interesujący nas 

profil  podstawowy,  który  nie  zawiera  funkcjonalności  określonych  jako  przestarzałe,  wymagałby 
następującego zestawu atrybutów: 
 

int

 attribs[] =  


    WGL_CONTEXT_MAJOR_VERSION_ARB, 3, 
    WGL_CONTEXT_MINOR_VERSION_ARB, 2, 
    WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 
    0  
}; 

 
 

W przypadku wyboru profilu kompatybilnego (tj. z dostępem do przestarzałej funkcjonalności 

OpenGL)  zamiast  stałej  WGL_CONTEXT_CORE_PROFILE_BIT_ARB  trzeba  zastosowad 
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB.  Biblioteka  WGL  musi  w  takim 
przypadku obsługiwad rozszerzenie WGL_ARB_create_context_profile. 
 

Po utworzeniu kontekstu pozostaje jedynie pobranie wskaźników funkcji OpenGL 3.0 (szablon 

nie używa funkcji z nowszych wersji), inicjacja naszej sceny oraz wymuszenie odrysowania okna. 
 

// konfiguracja niezbędnych rozszerzeń 

OpenGL30(); 
 

// inicjacja maszyny stanów i elementów sceny 

InitScene(); 
InvalidateRect( hWnd, NULL, TRUE ); 

 

Usuwanie okna 

 

Przy  usuwaniu  okna  w  pierwszej  kolejności  następuje  usunięcie  zasobów  używanych  przez 

OpenGL, a następnie usuwamy sam kontekst OpenGL. 
 

DeleteScene(); 
wglMakeCurrent( hDC, NULL ); 
wglDeleteContext( hRC3 ); 
ReleaseDC( hWnd, hDC ); 
PostQuitMessage( 0 ); 

 

Zmiana rozmiaru okna 

 

Przy zmianie rozmiaru okna przekazujemy do funkcji Reshape rozmiary nowego okna, które 

są dostępne jako parametry funkcji obsługującej komunikaty. 
 

Reshape( LOWORD( lParam ), HIWORD( lParam ) ); 
InvalidateRect( hWnd, NULL, TRUE ); 

 

Odrysowanie okna 

 

Odrysowanie  okna  sprowadza  się  do  wywołania  funkcji  rysującej  scenę  OpenGL  i  zamiany 

buforów koloru zawartych w buforze ramki. 
 

background image

DisplayScene(); 
SwapBuffers( hDC ); 
ValidateRect( hWnd, NULL ); 

 

Szablon aplikacji OpenGL dla X Window Serwer 

 

Przy  tworzeniu  szablonu  aplikacji  OpenGL  dla  X  Window  Serwer  użyjemy  najbardziej 

podstawowej  biblioteki  Xlib  oraz  biblioteki  pomocniczej  Xpmlib  (do  obsługi  ikony  okna  w  formacie 
XPM). Schemat budowy programu nie odbiega znacząco od szablonu aplikacji dla WIN32. W funkcji 
main tworzymy okno, a w odrębnej funkcji okna przetwarzamy komunikaty skierowane do aplikacji 
przez X Serwer. 

Tworzenie okna 

 

Tworzenie okna aplikacji podzielone jest na kilka etapów. W pierwszym tworzymy urządzenie 

wyświetlające  o  odpowiednich  parametrach  (w  przyszłych  programach  będziemy  niekiedy  te 
parametry  zmieniad).  Później  tworzymy  kontekst  renderingu  OpenGL  przy  użyciu  funkcji 
glXCreateContextAttribsARB 

rozszerzeo 

GLX_ARB_create_context 

GLX_ARB_create_context_profile,  przy  użyciu  tablicy  atrybutów  attribs.  Tablica  attribs 
opisuje  parametry  kontekstu  i  zawiera  pary  liczb:  parametr  i  jego  wartośd.  W  naszym  przypadku 
tworzony  jest  kontekst  obsługujący  bibliotekę  OpenGL  3.2  z  wyłączoną  obsługą  przestarzałej 
funkcjonalności. Następnie tworzone jest okno i dodawana jego ikona zawarta w pliku „opengl.xpm”. 
Cały kod tworzący okno z kontekstem renderingu OpenGL znajduje się poniżej. 
 

#include

 

"extensions3.h" 

#include

 

"opengl.xpm" 

 

#include

 

<X11/Xlib.h> 

#include

 

<X11/Xutil.h> 

#include

 

<X11/keysym.h> 

#include

 

<X11/xpm.h> 

 

////////////////////////////////////////////////////////////////////// 
// deklaracje funkcji obsługujących renderning w OpenGL 
////////////////////////////////////////////////////////////////////// 

void

 DisplayScene(); 

void

 Reshape( 

int

 width, 

int

 height ); 

void

 InitScene(); 

void

 DeleteScene(); 

 
… 
 

// utworzenie urządzenia wyświetlającego 

Display *display = XOpenDisplay( 0 ); 

int

 attrList[] = 


    GLX_RGBA, 
    GLX_DOUBLEBUFFER, 
    GLX_RED_SIZE, 8, 
    GLX_GREEN_SIZE, 8, 
    GLX_BLUE_SIZE, 8, 
    GLX_DEPTH_SIZE, 24, 
    None 
}; 
XVisualInfo *visual = glXChooseVisual( display, 0, attrList ); 
 

// utworzenie kontekstu renderingu OpenGL 

int

 nelements; 

GLXFBConfig *fbc = glXChooseFBConfig( display, DefaultScreen( display ), 0, &nelements ); 

int

 attribs[] = 


    GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 
    GLX_CONTEXT_MINOR_VERSION_ARB, 2, 
    GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 
    0 
}; 
GLXContext ctx = glXCreateContextAttribsARB( display, *fbc, 0, 

true

, attribs ); 

background image

 

// utworzenie okna X Window 

XSetWindowAttributes attr; 
attr.colormap = XCreateColormap( display, RootWindow( display, visual -> screen ), visual -> 
visual, AllocNone ); 
attr.border_pixel = 0; 
attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | 
PointerMotionMask | StructureNotifyMask; 
Window window = XCreateWindow( display, RootWindow( display, visual -> screen ), 
    0, 0, 512, 512, 0, visual -> depth, InputOutput, visual -> visual, CWBorderPixel | 
CWColormap | CWEventMask, &attr ); 
Atom wmDelete = XInternAtom( display, 

"WM_DELETE_WINDOW"

true

 ); 

XSetWMProtocols( display, window, &wmDelete, 1 ); 
XSetStandardProperties( display, window, 

"OpenGL 3.2 - szablon OpenGL"

"OpenGL 3.2 - szablon 

OpenGL"

, None, NULL, 0, NULL ); 

XMapRaised( display, window ); 
glXMakeCurrent( display, window, ctx ); 
 

// dodanie ikony okna 

Pixmap icon, mask; 
XpmCreatePixmapFromData( display, window, GLicon, &icon, &mask, NULL ); 
XWMHints winHints; 
winHints.flags = IconPixmapHint; 
winHints.icon_pixmap = icon; 
winHints.icon_mask = mask; 
XSetWMHints( display, window, &winHints ); 

 
 

Podobnie  jak  w  przypadku  aplikacji  w  systemie  Windows  od  wersji  3.2  biblioteki  OpenGL 

możemy wykorzystad profile. Profil podstawowy wymagałby następującego zestawu atrybutów: 
 

int

 attribs[] = 


    GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 
    GLX_CONTEXT_MINOR_VERSION_ARB, 2, 
    GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, 
    0 
}; 

 
 

przypadku 

wyboru 

profilu 

kompatybilnego, 

zamiast 

stałej 

GLX_CONTEXT_CORE_PROFILE_BIT_ARB 

trzeba 

zastosowad 

GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB.  Biblioteka  GLX  musi  w  tym  przypadku 
obsługiwad rozszerzenie GLX_ARB_create_context_profile. 
 

Po utworzeniu okna pozostaje jeszcze  inicjacja maszyny stanów  OpenGL. Zauważmy, że  nie 

musimy wcześniej pobierad wskaźników funkcji OpenGL: 
 

// inicjacja maszyny stanów OpenGL 

InitScene(); 

 

Przetwarzanie komunikatów X Serwera 

 

Wywołanie funkcji przetwarzającej komunikaty X Serwera wykonywane jest bezpośrednio w 

funkcji main programu: 
 

// pętla przetwarzania komunikatów 

WindowProc( display, window ); 

 
 

Popatrzmy  zatem  na  samą  funkcję  okna.  Zawiera  ona  nieskooczoną  pętlę,  którą  przerywa 

wystąpienie specjalnego komunikatu oznaczającego zamknięcie okna programu. Pozostałe elementy 
wnętrza pętli są odpowiednikami przedstawionej wcześniej funkcji okna w WIN32. Jedyna zasadnicza 
różnica polega na braku obsługi tworzenia i usuwania elementów sceny OpenGL, które wykonywane 
są  odpowiednio  przed  i  po  wywołaniem  funkcji  okna.  W  przyszłych  programach  poniższa  funkcja 
będzie w razie potrzeby rozbudowywana o kolejne elementy. 
 

////////////////////////////////////////////////////////////////////// 

background image

// funkcja okna 
////////////////////////////////////////////////////////////////////// 

void

 WindowProc( Display *display, Window window ) 


    

// przetwarzanie komunikatów 

    

bool

 running = 

true

    

while

( running ) 

    { 
        

while

( XPending( display ) > 0 ) 

        { 
            XEvent 

event

            XNextEvent( display, &

event

 ); 

            

switch

event

.type ) 

            { 
                

// zmiana rozmiaru okna 

                

case

 ConfigureNotify: 

                    Reshape( 

event

.xconfigure.width, 

event

.xconfigure.height ); 

                    

break

 
                

// zamknięcie okna 

                

case

 ClientMessage: 

                    

if

( *XGetAtomName( display, 

event

.xclient.message_type ) == 

*

"WM_PROTOCOLS"

 ) 

                    { 
                        running = 

false

                    } 
                    

break

 
                

// pominęcie pozostałych komunikatów 

                

default

                    

break

            } 
        } 
 
        

// odrysowanie okna 

        DisplayScene(); 
        glXSwapBuffers( display, window ); 
    } 

 

Zakmnięcie okna i zakończenie programu 

 

Kooczenie pracy programu wykonywane jest już po powrocie z funkcji okna i sprowadza się 

do  usunięcia  elementów  sceny  OpenGL  i  kontekstu  renderingu  OpenGL.  Na  koocu  zamykane  jest 
okno programu. 
 

// zamknięcie okna 

DeleteScene(); 
glXMakeCurrent( display, None, NULL ); 
glXDestroyContext( display, ctx ); 
XCloseDisplay( display ); 
 

// koniec programu 

return

 0; 

 

Plik makefike 

 

Przedstawimy jeszcze plik makefile służący do kompilacji szablonu programu. Zauważmy, że 

wszystkie pliki bibliotek pomocniczych znajdują się w katalogu common
 
PROJECT= szablon 
SOURCES= szablon.cpp glx_main.cpp ../common/extensions3.cpp 
OBJECTS= szablon.o glx_main.o extensions3.o 
CC= g++ 
 
$(PROJECT): $(OBJECTS) 

background image

 

$(CC)  -I../common  -O2  -g  $(OBJECTS)  -L/usr/X11R6/lib  -lm  -lGL  -lXpm  -lXxf86vm  -o 

$(PROJECT) 
 

@echo Compilation Complete 

 
$(OBJECTS): $(SOURCES) 
 

@echo Compiling Sources 

 

$(CC) -I../common -O2 -Wall -ansi -pedantic -g -c $(SOURCES) 

 
clean: 
 

@echo Deleting up $(OBJECTS) $(PROJECT) 

 

rm -f *.o;rm $(PROJECT) 

 

Biblioteka pomocnicza extensions3 

 

Biblioteka  pomocnicza  extensions3  składa  się  z  dwóch  plików  (extensions3.h  i 

extensions3.cpp) i przede wszystkim zawiera definicje wskaźników funkcji biblioteki OpenGL w wersji 
3.0, 3.1 i 3.2. Wskaźniki te są niezbędne do działania programów w systemach Microsoft Windows. W 
systemach  z  rodziny  UNIX/Linux  operacje  te  nie  są  niezbędne,  ale  biblioteka  także  jest 
wykorzystywana. 
 

Biblioteka  korzysta  z  trzech  podstawowych  plików  nagłówkowych:  gl3.h,  wglext.h  i  glxext.h 

dostępnych na stronie domowej OpenGL pod adresem 

http://www.opengl.org/registry/

. 

Obsługa WGL i wskaźników funkcji w systemach Microsoft Windows 

 

Początek pliku nagłówkowego biblioteki extensions3 w części obsługującej OpenGL i WGL w 

systemach Microsoft Windows zawiera włączenie standardowych plików nagłówkowych: 
 

#include

 

"gl3.h" 

#include

 

"wglext.h" 

 
 

Potem  musimy  ponownie  włączyd  plik  gl3.h,  ale  w  taki  sposób  aby  uzyskad  deklaracje  tych 

funkcji OpenGL z wersji 1.0 i 1.1, używanych także w wersji 3.0 - 3.2, do których nie musimy pobierad 
wskaźników  (o  pobieraniu  wskaźników  funkcji  wspominaliśmy  już  podczas  opisu  szablonu  aplikacji 
przy  tworzeniu  kontekstu  OpenGL).  Wprawdzie  deklaracje  te  zawiera  stary  plik  nagłówkowy 
biblioteki  OpenGL  (gl.h)  ale  jego  użycie  nie  jest  zalecane,  gdy  nie  korzystamy  z  przestarzałej 
funkcjonalności OpenGL. 
 

////////////////////////////////////////////////////////////////////// 
// prototypy funkcji z OpenGL 1.0 i OpenGL 1.1 
// (do tych funkcji nie są potrzebne wskaźniki) 
////////////////////////////////////////////////////////////////////// 
 

#ifndef

 __gl_h_ 

#ifndef

 __GL_H__ 

 

#ifdef

 __cplusplus 

extern

 

"C"

 { 

#endif 
 
#undef

 __gl3_h_ 

#define

 GL3_PROTOTYPES 

#undef

 GL_VERSION_1_0 

#undef

 GL_VERSION_1_1 

#include

 

"gl3.h" 

#undef

 GL3_PROTOTYPES 

 

#ifdef

 __cplusplus 

#endif 
 

background image

#endif 
#endif 

 
 

Potem następują kolejne definicje zmiennych globalnych - wskaźników funkcji pochodzących 

z  różnych  wersji  biblioteki  OpenGL  i  jej  rozszerzeo.  Na  początku  znajduje  się  wskaźnik  do  funkcji 
wglCreateContextAttribsARB którą używaliśmy przy tworzeniu kontekstu OpenGL. Z uwagi 
na  dużą  objętośd  tej  części  pliku  prezentujemy  początek  definicji  wskaźników  i  koocówkę  pliku  z 
deklaracjami funkcji pobierającymi wskaźniki. 
 

////////////////////////////////////////////////////////////////////// 
// funkcja z rozszerzenia WGL_ARB_create_context 
////////////////////////////////////////////////////////////////////// 

extern

 PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; 

 

////////////////////////////////////////////////////////////////////// 
// funkcje z OpenGL 1.2 
////////////////////////////////////////////////////////////////////// 

extern

 PFNGLBLENDCOLORPROC glBlendColor; 

extern

 PFNGLBLENDEQUATIONPROC glBlendEquation; 

extern

 PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements; 

extern

 PFNGLTEXIMAGE3DPROC glTexImage3D; 

extern

 PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D; 

extern

 PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D; 

 
… 
 

////////////////////////////////////////////////////////////////////// 
// pobranie wskaźników do funkcji z OpenGL 3.0 
////////////////////////////////////////////////////////////////////// 

bool

 OpenGL30(); 

 

////////////////////////////////////////////////////////////////////// 
// pobranie wskaźników do funkcji z OpenGL 3.1 
////////////////////////////////////////////////////////////////////// 

bool

 OpenGL31(); 

 

////////////////////////////////////////////////////////////////////// 
// pobranie wskaźników do funkcji z OpenGL 3.2 
////////////////////////////////////////////////////////////////////// 

bool

 OpenGL32(); 

 
 

Plik  źródłowy  extensions3.cpp  zawiera  już  właściwe  definicje  wskaźników  funkcji  oraz 

implementacje funkcji (ponownie przedstawiamy jedynie wybrane fragmenty pliku): 
 

////////////////////////////////////////////////////////////////////// 
// wskaźnik funkcji pochodzącej z rozszerzenia WGL_ARB_create_context 
////////////////////////////////////////////////////////////////////// 

PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; 
 

////////////////////////////////////////////////////////////////////// 
// wskaźniki funkcji pochodzących z OpenGL 1.2 
////////////////////////////////////////////////////////////////////// 

PFNGLBLENDCOLORPROC glBlendColor = NULL; 
PFNGLBLENDEQUATIONPROC glBlendEquation = NULL; 
PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements = NULL; 
PFNGLTEXIMAGE3DPROC glTexImage3D = NULL; 
PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D = NULL; 
PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D = NULL; 
 
… 
 

////////////////////////////////////////////////////////////////////// 
// pobranie wskaźników do funkcji z OpenGL 3.0 
////////////////////////////////////////////////////////////////////// 

bool

 OpenGL31() 


    

// sprawdzenie numeru wersji 

    

if

( OpenGLVersion() < 31 ) 

        

return

 

false

 
    

// pobranie wskaźników 

background image

    glDrawArraysInstanced = 

reinterpret_cast

 < PFNGLDRAWARRAYSINSTANCEDPROC > ( 

wglGetProcAddress( 

"glDrawArraysInstanced"

 ) ); 

    glDrawElementsInstanced = 

reinterpret_cast

 < PFNGLDRAWELEMENTSINSTANCEDPROC > ( 

wglGetProcAddress( 

"glDrawElementsInstanced"

 ) ); 

    glTexBuffer = 

reinterpret_cast

 < PFNGLTEXBUFFERPROC > ( wglGetProcAddress( 

"glTexBuffer"

 ) 

); 
    glPrimitiveRestartIndex = 

reinterpret_cast

 < PFNGLPRIMITIVERESTARTINDEXPROC > ( 

wglGetProcAddress( 

"glPrimitiveRestartIndex"

 ) ); 

 
    

// pobranie wskaźników - rozszerzenie ARB_uniform_buffer_object 

    glGetUniformIndices = 

reinterpret_cast

 < PFNGLGETUNIFORMINDICESPROC > ( wglGetProcAddress( 

"glGetUniformIndices"

 ) ); 

    glGetActiveUniformsiv = 

reinterpret_cast

 < PFNGLGETACTIVEUNIFORMSIVPROC > ( 

wglGetProcAddress( 

"glGetActiveUniformsiv"

 ) ); 

    glGetActiveUniformName = 

reinterpret_cast

 < PFNGLGETACTIVEUNIFORMNAMEPROC > ( 

wglGetProcAddress( 

"glGetActiveUniformName"

 ) ); 

    glGetUniformBlockIndex = 

reinterpret_cast

 < PFNGLGETUNIFORMBLOCKINDEXPROC > ( 

wglGetProcAddress( 

"glGetUniformBlockIndex"

 ) ); 

    glGetActiveUniformBlockiv = 

reinterpret_cast

 < PFNGLGETACTIVEUNIFORMBLOCKIVPROC > ( 

wglGetProcAddress( 

"glGetActiveUniformBlockiv"

 ) ); 

    glGetActiveUniformBlockName = 

reinterpret_cast

 < PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC > ( 

wglGetProcAddress( 

"glGetActiveUniformBlockName"

 ) ); 

    glUniformBlockBinding = 

reinterpret_cast

 < PFNGLUNIFORMBLOCKBINDINGPROC > ( 

wglGetProcAddress( 

"glUniformBlockBinding"

 ) ); 

 
    

// pobranie wskaźników - rozszerzenie ARB_copy_buffer 

    glCopyBufferSubData = 

reinterpret_cast

 < PFNGLCOPYBUFFERSUBDATAPROC > ( wglGetProcAddress( 

"glCopyBufferSubData"

 ) ); 

 
    

// sukces 

    

return

 

true


 
… 

 
 

Funkcje OpenGLVersion i OpenGLExtension opisane są na koocu niniejszego punktu. 

Obsługa OpenGL i GLX w systemach UNIX/Linux 

 

Obsługa bibliotek  OpenGL i  GLX  w  systemach UNIX/Linux  sprowadza się do  odpowiedniego 

włączenia  trzech  plików  nagłówkowych.  Standardowy  plik  gl3.h  jest  włączany  z  definicją 
preprocesora  GL3_PROTOTYPES  gwarantującą  bezpośredni  dostęp  do  deklaracji  funkcji  OpenGL. 
Później włączamy plik nagłówkowy biblioteki GLX (przyjmujemy, że znajduje się on w standardowym 
miejscu).  Na  koniec  włączamy  plik  glxext.h  zawierających  definicje  rozszerzeo  biblioteki  GLX. 
Zaprezentowane poniżej polecenia preprocesora zapewniają dostęp do deklaracji funkcji rozszerzeo, 
niezależnie od tego jaka wersja tego pliku znajduje się w systemie (plik glxext.h jest włączany przez 
plik glx.h). 
 

#define

 GL3_PROTOTYPES 

#include

 

"gl3.h" 

#undef

 GL3_PROTOTYPES 

 

#define

 GL_GLEXT_LEGACY 

#include

 

<GL/glx.h> 

 

#undef

 __glxext_h_ 

#undef

 GLX_GLXEXT_VERSION 

#undef

 GLX_ARB_create_context 

#define

 GLX_GLXEXT_PROTOTYPES 

#include

 

"glxext.h" 

#undef

 GLX_GLXEXT_PROTOTYPES 

 

Pozostałe elementy biblioteki 

 

Biblioteka  extensions3  zawiera  także  trzy  funkcje  pomocnicze:  pobierającą  numer  wersji 

OpenGL  i  GLSL  oraz  sprawdzającą  dostępnośd  wybranego  rozszerzenia.  Poniżej  prezentujemy  ich 
pełny kod źródłowy: 
 

background image

////////////////////////////////////////////////////////////////////// 
// pobranie numeru wersji biblioteki OpenGL 
// numer wersji pomnożony przez 10 
////////////////////////////////////////////////////////////////////// 

int

 OpenGLVersion() 


    

int

 major = 0, minor = 0; 

    glGetIntegerv( GL_MAJOR_VERSION, &major ); 
    glGetIntegerv( GL_MINOR_VERSION, &minor ); 
    

return

 10*major + minor; 


 

////////////////////////////////////////////////////////////////////// 
// sprawdzenie obsługi rozszerzenia biblioteki OpenGL 
// extName - nazwa sprawdzanego rozszerzenia OpenGL 
////////////////////////////////////////////////////////////////////// 

bool

 OpenGLExtension( 

const

 

char

 *extName ) 


    GLint numExt; 
    glGetIntegerv( GL_NUM_EXTENSIONS, &numExt ); 
    

for

int

 i = 0; i < numExt; i++ ) 

        

if

( std::string( 

reinterpret_cast

const

 

char

* >( glGetStringi( GL_EXTENSIONS, i ) ) ) 

== std::string ( extName ) ) 
            

return

 

true

    

return

 

false


 

////////////////////////////////////////////////////////////////////// 
// sprawdzenie wersji języka GLSL; numer wersji pomnożony przez 100 
////////////////////////////////////////////////////////////////////// 

int

 GLSLVersion() 


    

// pobranie numery wersji GLSL 

    std::stringstream version( std::stringstream::

in

 | std::stringstream::

out

 ); 

    version << 

reinterpret_cast

const

 

char

* >( glGetString( GL_SHADING_LANGUAGE_VERSION ) ); 

    

if

( glGetError() != GL_NO_ERROR ) 

return

 0; 

 
    

// numer wersji pomnożony przez 100 

    

int

 major = 0, minor = 0; 

    

char

 

dot

    version >> major; 
    

if

( version.fail() )

return

 0; 

    version >> 

dot

    

if

( version.fail() || 

dot

 != 

'.'

 ) 

return

 0; 

    version >> minor; 
    

if

( version.fail() ) 

return

 0; 

    

return

 100*major + minor; 

 
 

Teraz opiszemy krótko użyte w powyższych funkcjach elementy biblioteki OpenGL. Zmienne 

stanu  GL_MAJOR_VERSION  i  GL_MINOR_VERSION  zawierają  numer  wersji  i  podwersji 
implementacji OpenGL. Dwie użyte funkcje OpenGL: 
 
const GLubyte *glGetString( GLenum name ); 
const GLubyte *glGetStringi( GLenum name, GLuint index ); 
 
zwracają  bardziej  rozbudowane  informacje  o  bieżącej  implementacji  biblioteki  OpenGL.  Dane 
zwracane przez funkcję glGetString zależą od wartości parametru name: 

  GL_VENDOR

 - autor implementacji OpenGL, 

  GL_RENDERER

 - nazwa urządzenia renderującego, np. karty graficznej, 

  GL_VERSION

 - numer wersji implementacji OpenGL w formacie: <wersja>.<podwersja> lub 

<wersja>.<podwersja>.<wydanie>  oraz  opcjonalnie  po  pojedynczej  spacji  informacja  o 
producencie biblioteki, 

  GL_SHADING_LANGUAGE_VERSION

  –  numer  wersji  GLSL  -  języka  cieniowania  OpenGL, 

zwracany w formacie takim samym jak dla parametru GL_VERSION. 

Zauważmy,  że  OpenGL  zawiera  dwa  mechanizmy  pozwalające  na  sprawdzenie  numeru  wersji  i 
podwersji implementacji biblioteki. 

background image

 

Funkcja  glGetStringi  zwraca  informację  o  rozszerzeniach  obsługiwanych  przez 

implementację  OpenGL.  Nazwa  każdego  obsługiwanego  rozszerzenia  podawana  jest  odrębnie 
poprzez wskazanie numeru indeksu w parametrze index, a łączną ilośd rozszerzeo zawiera zmienna 
stanu  GL_NUM_EXTENSIONS.  Jedyną  dopuszczalną  wartością  parametru  name  funkcji 
glGetStringi jest stała GL_EXTENSIONS. 
 

Obie prezentowane funkcje zwracają ciągi znaków w kodowaniu UTF-8 zakooczone znakiem 

NULL (zerem). 

Podstawowe elementy programu w OpenGL 

 

Przedstawione  poniżej  cztery  funkcje  będą  stałym  elementem  każdego  programu  z  kursu 

biblioteki  OpenGL,  chod  oczywiście  różna  będzie  ich  zawartośd.  Poznamy  także  funkcje  biblioteki 
OpenGL wykorzystane w szablonie. 

Inicjacja maszyny stanu OpenGL 

 

To pierwszy element programu korzystającego z biblioteki OpenGL. Z założenia wykonywany 

jest jednokrotnie  i obejmuje  tylko te  elementy ustawieo OpenGL, które nie będą się zmieniały (lub 
będą  zmieniane  sporadycznie)  w  trakcie  działania  programu.  W  szablonie  umieściliśmy  wywołanie 
tylko jednej funkcji OpenGL 
 
void glClearColor( GLclampf red, GLclampf green, GLclampf blue, 
                   GLclampf alpha ); 
 
która  ustala  składowe  RGBA  koloru  czyszczącego  bieżącego  bufora  koloru,  co  w  programie 
przedkłada się na kolor tła okna. 
 

////////////////////////////////////////////////////////////////////// 
// inicjalizacja stałych elementów maszyny stanu OpenGL 
////////////////////////////////////////////////////////////////////// 

void

 InitScene() 


    

// kolor tła - zawartość bufora koloru 

    glClearColor( 1.0f, 1.0f, 1.0f, 1.0f ); 

 

Generowanie sceny 3D 

 

W szablonie programu OpenGL nie są generowane żadne elementy sceny. Wykonywane jest 

tylko  czyszczenie  bieżącego  bufora  koloru  i  wymuszane  jest  wykonanie  wszystkich  wywołanych 
poleceo OpenGL. Efektem będzie puste okno o kolorze tła określonym w poprzednio opisanej funkcji 
szablonu. 
 

////////////////////////////////////////////////////////////////////// 
// funkcja generująca scenę 3D 
////////////////////////////////////////////////////////////////////// 

void

 DisplayScene() 


    

// czyszczenie bufora koloru 

    glClear( GL_COLOR_BUFFER_BIT ); 
 
    

// skierowanie poleceń do wykonania 

    glFlush(); 

 
 

Pierwsza z użytych funkcji: 

 
void glClear( GLbitfield mask ); 
 

background image

zawiera maskę bitową z wartościami wskazującymi, które bufory są czyszczone. Wartości te wynoszą: 
GL_COLOR_BUFFER_BIT,  GL_DEPTH_BUFFER_BIT,  GL_STENCIL_BUFFER_BIT  i  wskazują 
odpowiednio aktualnie aktywne do zapisu bufory koloru, bufor głębokości i bufor szablonu. Stałe te 
mogą byd łączone za pomocą operatora |. 
 

Druga funkcja: 

 
void glFlush( void ); 
 
określa, że wszystkie polecenia OpenGL, które zostały wcześniej wywołane muszą zostad wykonane w 
skooczonym  czasie.  Działanie  to  dobrze  określa  słowo  wymiatanie  będące  bezpośrednim 
tłumaczeniem angielskiej nazwy funkcji. Podobną rolę wykonuje polecenie 
 
void glFinish( void ); 
 
które  wymusza  wykonanie  wszystkich  poprzednich  poleceo  OpenGL.  W  odróżnieniu  od  funkcji 
glFlush,  glFinish  nie  zakooczy  się  dopóki  wszystkie  efekty  wcześniej  wydanych  poleceo  dla 
zmiennych stanu klienta i serwera OpenGL oraz bufora ramki nie zostaną w pełni zrealizowane. 

Zmiana wielkości okna 

 

Typowym  zdarzeniem,  które  występuje  w  programach  okienkowych  jest  zmiana  rozmiaru 

okna.  W  przypadku  biblioteki  OpenGL  obsługa  takiego  zdarzenia  wymaga  nie  tylko  odrysowania 
okna. Niezbędne jest także podjęcie decyzji o modyfikacji (lub nie) wielkości obszaru renderingu, czyli 
przestrzeni okna wykorzystywanej przez OpenGL.  
 

Obszar renderingu określa zastosowana w szablonie funkcja: 

 
void glViewport( GLint x, GLint y, GLsizei width, GLsizei height ); 
 
której parametry oznaczają: 

  x

, y - współrzędne lewego dolnego narożnika obszaru renderingu względem lewego dolnego 

narożnika okna, 

  width

 - szerokośd okna renderingu, 

  height

 - wysokośd okna renderingu. 

Początkowo  obszar  renderingu  zajmuje  całe  okno  udostępnione  dla  aplikacji  OpenGL.  W  szablonie 
programu  glViewport  określa  za  każdym  razem  obszar  renderingu  tak,  aby  obejmował  on  całe 
okno. 
 

////////////////////////////////////////////////////////////////////// 
// zmiana wielkości okna 
////////////////////////////////////////////////////////////////////// 

void

 Reshape( 

int

 width, 

int

 height ) 


    

// obszar renderingu - całe okno 

    glViewport( 0, 0, width, height ); 

 
 

Wspomnijmy jeszcze, że parametry funkcji Reshape określają odpowiednio nową szerokośd 

i wysokośd okna i są określane na podstawie danych przekazywanych przez system okienkowy. 

Usunięcie elementów sceny 3D 

 

Ostatnia  funkcja  szablnu  jest  odpowiedzialna  za  usunięcie  elementów  sceny  OpenGL,  które 

nie  zostały  wcześniej  zniszczone.  W  szczególności  mogą  to  byd  elementy  tworzone  w  funkcji 
InitScene. Ponieważ w szabolnie nie mamy takich elementów, wnętrze funkcji pozostaje puste. 
 

////////////////////////////////////////////////////////////////////// 
// usunięcie obiektów OpenGL 

background image

////////////////////////////////////////////////////////////////////// 

void

 DeleteScene() 


 

Wygląd okna programu 

 

Poniżej znajdują się zrzuty ekranu przedstawiające okna programu szablonu aplikacji OpenGL 

w obu systemach operacyjnych wykorzystywanych w kursie. 
 

 

Rysunek 1 Okno szablonu OpenGL (WIN32) 

 

Rysunek 2 Okno szablonu OpenGL (Linux) 

 


Document Outline