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;
}
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;
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;
}
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
};
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.
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
z
rozszerzeo
GLX_ARB_create_context
i
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 );
// 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
};
W
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.
//////////////////////////////////////////////////////////////////////
// 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)
$(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
#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
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:
//////////////////////////////////////////////////////////////////////
// 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.
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 );
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
//////////////////////////////////////////////////////////////////////
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)