Open GL - Pierwszy program
Parametry:
Dodany: 2006-04-01 przez
!Reg
Autor: Wojciech Dudek
Opis: Jak powinien wyglądać szablon applikacji wykożystującej biblioteke OpenGL
Poziom:
Ocena: 22,8
Autor: Wojciech Dudek
Wstęp, czyli po co komu ten artykuł Opisy Open GL można znaleźć w wielu miejscach (książki, pliki z sieci itp.). W większości jednak są
one albo bardzo nieprecyzyjne, albo bardzo szerokie, co skutecznie do nich zniechęca osoby chcące zacząć zabawę z grafiką 3D. W tym
artykule chcę przedstawić podstawowy program w standardzie GL, od jakiego powinno się zaczynać. Konkretnie, bez zbytniego wgłębiania się
opiszę, jak zbudować pierwszą aplikację korzystającą z Open GL. Uwierzcie mi 90% rozbudowanych programów w GL opiera się na niej. W
opisie tym nie ma podanych nagłówków funkcji wraz z opisem, zakładam, że każdy coś na ten temat ma. Są za to dość dokładnie opisane
kolejne kroki przykładowego programu. Mam nadzieję, że artykuł będzie pomocą w rozpoczęciu programowania w Open GL.
1. Co jest potrzebne do pisania programów w Open GL
Kompilator języka C/C++ generujący kod 32 bitowy, czyli po ludzku taki, na którym można tworzyć programy pod Win 95 (lub
nowszym),
Biblioteki statyczne Open GL: opengl32.lib i glu32.lib,
Pliki nagłówkowe Open GL: gl.h i glu.h,
W systemie MUSI znajdować się sterownik Open GL o nazwie opengl32.dll i biblioteka pomocnicza glu32.dll. Mogą być w katalogu
programu, albo w podkatalogu Windows'a /System,
Bardzo zalecany byłby akcelerator 3D, chociaż nie jest konieczny.
Wszystko to (oprócz bibliotek .dll) jest w pliku do tego artykułu gl.rar. [Niestety plik zaginął - przyp. redakcja]
Uwaga 1: Biblioteki statyczne (lib) są różne dla Borland C++ i Visual C++. Są w podkatalogach: /libBOR i /libVC.
Uwaga 2: Sterownik opengl32.dll dla niektórych kart może mieć inną nazwę. No cóż, trzeba poeksperymentować (np. skopiować go do
katalogu programu i zmienić nazwę).
2. Szkielet programu w Open GL
Głównym plikiem programu jest gl.cpp.
Jest to standardowy program pod Win 95, w API.
Jest funkcja WinMain:
int
WINAPI WinMain(HINSTANCE Zad,HINSTANCE, LPSTR,
int
To)
która inicjuje okno i rozpoczyna cykl obsługi komunikatów.
W programie Open GL we właściwościach okna należy wpisać: WS_CLIPCHILDREN i WS_CLIPSIBLINGS, zalecam też WS_POPUP,
czyli pracę na pełnym ekranie.
Dalej znajduje się funkcja która obsługuje komunikaty:
LRESULT CALLBACK ProcOkna(HWND Okno,UINT Kom,WPARAM WP,LPARAM LP)
Interesują nas zdarzenia: . Rozpoczęcie programu: WM_CREATE, . Naciśnięcie klawisza: WM_KEYDOWN, . Zmiany rozmiaru okna:
WM_SIZE, . Zakończenia programu: WM_DESTROY, . Odrysowywania zawartości okna: WM_PAINT.
Program Open GL podczas rozpoczęcia programu inicjuje pewne rzeczy (o tym dalej). Odrysowanie zawartości okna to najlepsza część
programu (GL rendering). Musimy wychwycić naciśnięcie klawiszy: kursorów, Entera i Esc. Na końcu programu pewne rzeczy należy
doprowadzić do stanu przed jego uruchomieniem. Zmiana rozmiaru okna nam raczej nie grozi-rysujemy po całym ekranie. Z pewnych jednak
względów ten komunikat będzie jednak potrzebny.
3. Inicjalizacja Open GL
Jest realizowana w ramach komunikatu WM_CREATE, i jest to najdłuższa (i niezbędna) część programu.
Po kolei działa to tak:
hDC=GetDC(Okno); //Pobranie tzw. kontekstu okna
SetDCPixelFormat(hDC); //Ustawienie formatu pixeli (o tym poniżej)
hRC=wglCreateContext(hDC); //Stworzenie tzw. kontekstu Open GL
Open GL - Pierwszy program
http://www.gamedev.pl/articles.php?x=view&id=156
1 z 4
2010-04-30 10:33
wglMakeCurrent(hDC, hRC); //I ustawienie go
glViewport(
0
,
0
,GetSystemMetrics( SM_CXSCREEN ),GetSystemMetrics( SM_CYSCREEN ));
// Ustawienie widoku, czyli tej części ekranu, na której będziemy renderować.
// W tym przypadku cały ekran
gluPerspective(
90.0f
,
1.0f
,
1.0f
,
1000.0f
);
//Ustawienie tzw. ostrosłupa widzenia.
// Kamera będzie mieć pole widzenia 90 stopni, będzie widzieć obiekty
//położone względem niej od 1 pixla do 1000 pixli,
// i będzie widzieć obiekty nie zniekształcone
glEnable(GL_DEPTH_TEST);
//Włączenie usuwania powierzchni zasłoniętych
glDepthFunc(GL_LEQUAL);
//Taki nieistotny szczegół związany z powyższym, można usunąć
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
//tzw. korekcja, tekstura będzie "ładnie" nałożona
glEnable(GL_CULL_FACE);
//usuwanie wielokątów odwróconych tyłem do obserwatora. Stosowane w grach,
//na razie można wyłączyć.
I to tyle niezbędnych ustawień. Teraz tylko trzeba załadować tekstury, których będziemy używać. W przykładzie są dwie: t0.bmp i t1.bmp.
Tu małe wyjaśnienie po co jest plik pomoc.cpp. W tym pliku znajdują się procedury ładujące bitmapę oraz ustawiające format pixeli. Na razie
można je traktować jako coś co jest i nie ważne skąd się wzięło. Nie jest to potrzebne do zrozumienia programu.
Za załadowanie tekstur odpowiada fragment kodu:
bits = LoadDIBitmap(
"t1.bmp"
,&info);
rgb = ConvertRGB(info, bits);
glBindTexture(GL_TEXTURE_2D,
1
); // od tego momentu tekstura "t1.bmp" jest pod uchwytem 1
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D,
0
,
3
, info->bmiHeader.biWidth, info->Header.biHeight,
0
,
GL_RGB, GL_UNSIGNED_BYTE, rgb);
free
(info);
free
(bits);
free
(rgb);
Pewne fragmenty kodu powtarzają się w komunikacie WM_SIZE. Jest to niestety konieczne, ze względu na właściwość API, która "traci"
pewne ustawienia przy wyświetlaniu okna (które traktuje jako zmianę rozmiarów).
Od tego momentu można zająć się już ciekawszymi sprawami.
4. Klawiatura
Podczas obsługi komunikatu WM_KEYDOWN rejestrujemy naciśnięcia kursorów (konkretnie zmieniamy pewne kąty, o których za chwilę),
Entera (zmienia sposób renderowania) i Esc (wyjście z programu).
5. Wyjście z programu
I usuwamy ten którego używaliśmy:
wglDeleteContext(hRC);
6. Rendering
Czyli najważniejsza część programu.
Renderowanie jest realizowane podczas obsługi komunikatu WM_PAINT. Niektóre źródła podają, że można je włączyć do pętli obsługi
komunikatów. Ma to zaletę w postaci zwiększenia szybkości (minimalnie). Wadą natomiast jest możliwość utraty niektórych zdarzeń np.
naciśnięcia klawisza. Ja proponuję pozostać przy WM_PAINT.
W naszym programie za renderowanie odpowiada funkcja void RenderujX(void); gdzie X to nr od 0 do 3. Cztery funkcje renderujące na
cztery różne sposoby.
Obsługa WM_PAINT wygląda tak:
Renderuj0(); //Renderuje funkcja 0
SwapBuffers(hDC); //tzw. przerzucenie buforów, mające zapobiec migotaniu obrazu.
Zajmijmy się teraz funkcją renderującą. Najprostsza jest Renderuj0, rysująca ładnie pokolorowany trójkąt:
Open GL - Pierwszy program
http://www.gamedev.pl/articles.php?x=view&id=156
2 z 4
2010-04-30 10:33
void
Renderuj0(
void
)
{
Najpierw wyłączamy texturowanie:
glDisable(GL_TEXTURE_2D);
Teraz czyścimy bufory koloru i tzw Bufor Z:
glClearColor(
0.50f
,
0.50f
,
0.50f
,
1.0f
);
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
Zerujemy macierz transformacji:
glLoadIdentity();
Sprawdzamy, czy kąty o które obrócimy trójkąt są w granicach <0, 360) stopni:
if
(KatX<
0.0f
) KatX+=
360.0f
;
if
(KatX>=
360.0f
) KatX-=
360.0f
;
if
(KatY<
0.0f
) KatY+=
360.0f
;
if
(KatY>=
360.0f
) KatY-=
360.0f
;
Teraz ważna część. Chcemy obrócić trójkąt mniej więcej względem jego środka ciężkości o odpowiednie kąty KatX i KatY. W tym celu
przesuwamy obserwatora do środka (plus/minus) trójkąta, obracamy i oddalamy obserwatora z powrotem. Tu należy zwrócić uwagę na
ciekawą właściwość macierzy transformacji: transformacje Open GL należy przeprowadzać "od końca". Dlaczego ? Jak ktoś chce niech sobie
pomnoży kilka macierzy i będzie wiedział. Dla naszych potrzeb wystarczy wiedzieć, że jeśli chcemy obiekt najpierw przybliżyć (glTranslate), a
potem przekręcić (glRotate), to należy użyć: glRotate, glTranslate:
glTranslatef(
0.0f
,
0.0f
,-
200.0f
);
glRotatef(KatY,
1.0f
,
0.0f
,
0.0f
);
glRotatef(KatX,
0.0f
,
1.0f
,
0.0f
);
glTranslatef(
0.0f
,
0.0f
,
200.0f
);
Teraz już tylko narysowanie trójkąta. W tym celu używamy "klamer" glBegin...glEnd, wewnątrz których umieszczamy wierzchołki trójkąta.
Dodatkowo każdy wierzchołek kolorujemy innym kolorem:
glBegin(GL_TRIANGLES);
glColor3f(
1.0f
,
0.0f
,
0.0f
);
glVertex3f(
150.0f
,-
150.0f
,-
200.0f
);
glColor3f(
0.0f
,
1.0f
,
0.0f
);
glVertex3f(
0.0f
,
150.0f
,-
200.0f
);
glColor3f(
0.0f
,
0.0f
,
1.0f
);
glVertex3f(-
150.0f
,-
150.0f
,-
200.0f
);
glEnd();
Na koniec informujemy Open GL o skończonym renderingu. Teraz niech się martwi akcelerator (albo ostatecznie procesor).
glFinish();
}
Funkcja Renderuj1 jest znacznie ciekawsza, przedstawia teksturowany trójkąt. Nowe elementy to: Włączenie texturownia:
glEnable(GL_TEXTURE_2D);
Włączenie textury pierwszej:
glBindTexture(GL_TEXTURE_2D,
1
);
I podanie współrzędnych nabitmapie dla każdego wierzchołka:
glTexCoord2f(
0.0f
,
0.0f
);
Funkcja Renderuj2 to połączenie obu poprzednich.
Funkcja Renderuj3 pokazuje efekt Light Map'owania, czyli nakładania na teksturę światła obliczonego wcześniej (efekt stosowany w grach
3D). Realizuje się to przez dwukrotne nałożenie tekstury (zwykłej + światło). W nowych kartach 3D (od VooDoo 2) robi się to w jednym
przebiegu na raz, ale efekt jest ten sam.
Open GL - Pierwszy program
http://www.gamedev.pl/articles.php?x=view&id=156
3 z 4
2010-04-30 10:33
Z nowych rzeczy występują funkcje mieszające nałożone tekstury:
glEnable(GL_BLEND);
glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
7. Co dalej?
Dalej niech każdy poeksperymentuje sam... Proponuję rozbudować program tak, żeby wyświetlał np. więcej wielokątów, jakiś sześcian, może
prosty engine 3D w stylu Wolfa. Na podstawie tego kodu dużo da się zrobić.
8. Jak stąd daleko do Quake 3?
No cóż, to pytanie jest raczej bardziej śmieszne niż poważne, ale nie do końca... Funkcje renderujące w enginach 3D nie różnią się bardzo.
Należy zauważyć, że obecnie każdy może bawić się w programowanie silniczka 3D korzystającego z akceleratora. W Quake 1 w renderingu
programowym też jest funkcja rysująca trójkąt. Ile autorowi zajęło jej napisanie ? Miesiąc, dwa, albo więcej. Do tego konieczność stosowania
asemblera... Straszne ! Open GL rozwiązuje to w kilku linijkach kodu.
9. Open GL kontra Direct 3D
Zalety GL:
Bardzo łatwy w porównaniu w D3D,
Nieobiektowy, GL to raczej konsola do grafiki,
Ma więcej bajerów graficznych niż D3D,
Lepiej działa na VooDoo,
Dużo opisów dostępnych w sieci.
Wady GL:
Brak funkcji więżących program ze sterownikiem,
Zaawansowane właściwości GL (np. multiteksturowanie) są zawarte w tzw. GL Extensions, co zmusza do dokładnego badania
używanego sterownika.
D3D nie ma dwóch ostatnich wad, jest za to strasznie skomplikowany. Przeważnie jednak przydaje się znajomość obu, cóż takie czasy.
Powodzenia !
Komentarze (8)
Open GL - Pierwszy program
http://www.gamedev.pl/articles.php?x=view&id=156
4 z 4
2010-04-30 10:33