GLFW vs Freeglut
Dnes si konečně poprvé vytvoříte okno. Ukáži vám hned dvě knihovny. GLFW je ta
novější, kterou budu používat po zbytek tohoto tutoriálu. Freeglut je starší
knihovna, která je použita na mnoha online tutoriálech. Můžete si porovnat, jak
se která používá.
Kromě těchto dvou existují ještě další, například SDL, což je v určitých
ohledech lepší knihovna než GLFW. Popisovat jí zde nebudu, ale určitě se na ni
někdy podívejte.
Pokud jste tak ještě neučinili, je na čase stáhnout si zdrojové kódy.
GLFW
Knihovna GLFW se postará o vytvoření okna a OpenGL kontextu. Funguje jak ve Windows, tak v Linuxu. Dokonce i v OSX. Budu používat verzi GLFW 3.2 (verze GLFW 3.1 např. ještě nemá definovanou konstantu GLFW_TRUE (která je definována jako 1)).
Jak už víte, GLFW se také stará o interakci s klávesnicí a myší. Následující příklad obsahuje ukázku toho všechno:
- /*------------------------------------------------*/
- /* opengl/01glfw/main.c */
- #include <stdio.h>
- #include <GLFW/glfw3.h>
- {
- glfwSetWindowShouldClose(window, GLFW_TRUE);
- }
- }
- {
- glfwGetCursorPos(window, &xpos, &ypos);
- }
- }
- }
- }
- {
- GLFWwindow* window;
- /* Initialize the library */
- }
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
- /* Create a windowed mode window and its OpenGL context */
- window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
- {
- glfwTerminate();
- }
- /* Make the window's context current */
- glfwMakeContextCurrent(window);
- /* events */
- glfwSetKeyCallback(window, key_callback);
- glfwSetMouseButtonCallback(window, mouse_button_callback);
- /* Loop until the user closes the window */
- {
- /* Render here */
- glClear(GL_COLOR_BUFFER_BIT);
- glColor3f(1.0f, 0.0f, 0.0f);
- glBegin(GL_POLYGON);
- glVertex2f(-0.5f, -0.5f);
- glVertex2f(0.5f, -0.5f);
- glVertex2f(0.5f, 0.5f);
- glVertex2f(-0.5f, 0.5f);
- glEnd();
- /* Swap front and back buffers */
- glfwSwapBuffers(window);
- /* Poll for and process events */
- glfwPollEvents();
- }
- glfwDestroyWindow(window);
- glfwTerminate();
- }
- /*------------------------------------------------*/
První, čím začínám, je definice funkce key_callback()
.
Ta se volá při stisku a uvolnění tlačítka. Registrovat se musí pomocí funkce
glfwSetKeyCallback()
, viz řádek 55.
Argument key určuje, jaká klávesa byla
stisknuta.
Argument action, jestli byla klávesa stisknuta
(GLFW_PRESS
) nebo uvlolněna (GLFW_RELEASE
).
V příkladu zjišťuji, jestli byla stisknuta klávesa escape (bez podmínky
action == GLFW_PRESS
by se blok v if
volal dvakrát
— i pro GLFW_RELEASE
).
Obdobně funguje další definovaná funkce, mouse_button_callback()
.
Co dělá je asi jasné. Jen nezapomeňte, že se musí zaregistrovat pomocí
glfwSetMouseButtonCallback()
,
viz řádek 56.
Funkce main()
začíná voláním funkce glfwInit()
.
Tato funkce se musí volat jako první glfw funkce.
Na řádcích 39 až 41 se nastavuje, pro jakou verzi OpenGL se má vytvořit kontext
(toto se musí volat před voláním funkce glfwCreateWindow()
).
Funkce glfwCreateWindow()
konečně vytvoří okno a OpenGL kontext.
Pokud byste požádali o nějakou nepodporovanou funkci, nepodaří se okno s
kontextem vytvořit a funkce vrátí NULL.
Oken/kontextů si můžete vytvořit více, ale funkce OpenGL pracují jen s jedním
kontextem. S tím, který je nastaven jako aktuální. O to se postará volání
funkce glfwMakeContextCurrent()
, viz řádek 53. Protože v tomto
tutoriálu budu používat jen jedno okno, více se o nastavování aktuálního
kontextu nebudete muset starat.
Na řádku 58 je volání první OpenGL funkce,
glGetString(GL_VERSION)
. Tato funkce je deklarována v
hlavičkových souborech pro OpenGL. Ty mohou být na různých systémech různé, ale
o jejich includování se už postarala knihovna GLFW/glfw3.h
.
Pokud byste chtěli používat na Windows knihovnu windows.h, includujte jí před GLFW/glfw3.h.
Na řádku 61 začíná blok cyklu, který se provádí, dokud vrací funkce
glfwWindowShouldClose(window)
false. Tato funkce vrátí true, pokud
stisknete ALT+F4, kliknete na křížek u okna, nebo zavoláte funkci
glfwSetWindowShouldClose(window, GLFW_TRUE)
, viz řádek 11.
Následuje volání několika OpenGL funkcí, které smažou okno, nastaví kreslící
barvu na červenou a vykreslí červený obdélník. Kromě funkce
glClear
se jedná o zastaralé funkce z OpenGL 1.1, které byste už
neměli používat. Kdykoliv uvidíte nějaký tutoriál, kde bude glBegin(),
utečte!
Více se k tomuto tématu může dočíst na stránce www.opengl.org/wiki/Legacy_OpenGL.
Na řádku 75 se volá funkce glfwSwapBuffers(window);
. Okno má dva
buffery. Jeden, do kterého OpenGL funkce kreslí a druhý, který se zobrazuje.
Když do back bufferu zakreslíte co potřebujete, prohodíte jej s front bufferem
a tím velmi rychle zobrazíte nový obrázek. Důvodem těchto dvou bufferů je
právě optimalizace rychlosti zápisu a zobrazování.
Poslední funkce z cyklu, glfwPollEvents();
se stará o zpracování
vstupů klávesnice a myši. Bez volání této funkce by se nikdy nezavolali funkce
key_callback()
a mouse_button_callback()
.
Na konci funkce main()
je ještě úklid okna, opengl kontextu a
toho, co se vytvořilo funkcí glfwInit()
.
Jedna z velkých výhod GLFW je jeho dokumentace. Určitě se do ní podívejte, ikdyž třeba vaše angličtina není nejdokonalejší. Díky příkladům z ní lecos pochopíte i tak. Třeba se můžete podívat na to, jak spustit okno v fullscreen režimu.
Jen si dovolím doplnit, že ukázkový příklad z dokumentace používá pro matemetiku funkce z knihovny https://raw.githubusercontent.com/glfw/glfw/master/deps/linmath.h..
Verze OpenGL
Vraťme se teď na chvíli k funkcím, kterými se žádá o verzi OpenGL kontextu:
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
Podívejte se na to, co všechno se může stát, když požádáte o různé verze:
1) Žádnou z těchto funkcí nezavolám
Pak dostanu nejvyšší možnou verzi kontextu. Volání
printf("%s\n", glGetString(GL_VERSION));
vrátí toto:
4.5.13399 Compatibility Profile Context 15.200.1062.1004 // Ve Windows 3.0 Mesa 17.2.5 // V Linuxu
Protože se jedná o compatibility profile, červený čtverec se vykreslí (funkce z OpenGL 1.1 budou fungovat).
2) Zavolám jen glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
Tohle moje ovladače nezvládnou a kontext se nepodaří inicializovat.
Funkce glfwCreateWindow()
vrátí NULL.
3) Verze 3.3 core profile
Toto budu používat v tomto tutoriálu.
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
3.3.13399 Core Profile Context 15.200.1062.1004 // Ve Windows 4.1 (Core Profile) Mesa 17.2.5 // V Linuxu
Čtverec se nevykreslí. V linuxu mi poskytne sice jinou, vyšší verzi než o kterou jsem požádal, ale tato verze nezavrhla žádné funkce z 3.3 core, takže bude vše fungovat jako s verzí 3.3 (jen jsou dostupné navíc funkce z 4.1).
4) Verze 3.1
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
3.1.13399 Compatibility Profile Context 15.200.1062.1004 // Ve Windows 4.1 (Core Profile) Mesa 17.2.5 // V Linuxu
Ve Windows dostanu co požaduji a čtverec se vykreslí. V Linuxu bohužel dostanu vyšší verzi, která už OpenGL 1.1 funkce nepodporuje, takže se čtverec nevykreslí. Tady se tedy Linux a Windows v chování liší (i na jednom a tom samém počítači).
Core a compatibility profily byly představeny až v OpenGL 3.2. Pokud tedy žádáte o starší verzi OpenGL, natavení core/compatibility profilu nehraje roli, resp. způsobí, že se nepodaří kontext inicializovat.
Někde jsem se dočetl, že v OSX musíte ještě zavolat
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
, aby vám vše
fungovalo jak má. Možná budete muset nastavit ještě nějaké jiné hinty pro
kontext, nevím, OSX nepoužívám. Mac uživatelům doporučuji nastudovat OpenGL Programming Guide for Mac.
Překlad v Linuxu
Nejdříve si nainstalujte balíčky libglfw-devel a libglfw3. Takto se alespoň jmenují v mé OpenSuSE distribuci. Určitě budete potřebovat verzi 3.2 nebo novější.
Po instalaci můžete použít program pkg-config pro nalezení potřebných cest a knihoven. Po čerstvé instalaci předchozích balíčků se možná budete muset nejdříve odhlásit a znovu přihlásit (alespoň ke konzoli), aby byly nastaveny potřebné proměnné prostředí, které pkg-config používá (možná budete muset restartovat PC).
Zdroják pak jednoduše přeložíte takto:
$ clang `pkg-config --cflags glfw3` -o main main.c `pkg-config --libs glfw3`
Připomínám, jak spustit OpenGL 3.3 program pomocí softwarové emulace, pokud máte obstarožní systém:
$ LIBGL_ALWAYS_SOFTWARE=1 ./main
Překlad ve Visual Studiu
Pro začátek si musíte stáhnout GLFW ze stránky www.glfw.org/download.html. Stáhněte si verzi 32-bit Windows binaries. V tomto balíčku najdete v adresáři include/ hlavičkové soubory (GLFW/glfw3.h). Dále přeložené knihovny pro různé verze Visual Studia i MinGW.
Pokud jste si stáhli mé zdrojové kódy pro tento tutoriál o OpenGL, najdete v nich už všechno potřebné staženo.
Stáhněte si 32-bitovou verzi. S tou 64-bytovou prý bývají problémy. (Pokud máte Visual Studio Community/Express Edition, tak stejně můžete překládat jen 32-bitové programy.)
Při zakládání projektu ve Visual Studiu postupujte stejně, jako je pospáno v kapitole o Visual Studiu.
Dále musíte Visual Studiu napovědět, kde najde hlavičkové soubory. Musíte ho nasměrovat do adresáře include/.
Otevřete PROJECT → Properties
, vlevo klikněte na
C/C++
a do Additional Include Directories
přidejte
cestu k adresáři include/.
Od této chvíle Visual Studio zná všechny glfw a (open)gl funkce a dokáže vám je napovídat.
Funkce z OpenGL jsou definované v knihovně opengl32.lib.
Funkce z GLFW jsou v glfw3.lib. Obě knihovny přidáte v
PROJECT → Properties
v záložce
Linker → Input
k Additional Dependencies
.
(Nějaké knihovny už tam jsou, tak je tam nechte.)
Číslo 32 v opengl32.lib nic neznamená. Ikdyž nedbáte mých rad a překládáte pro 64-bitový procesor, stejně použijte opengl32.lib (a k tomu opengl32.dll).
Knihovna opengl32.lib přišla už s Visual Studiem, takže o jejím umístění Visual Studio ví. Musíte ale říct, kde najít knihovnu glfw3.lib.
V PROJECT → Properties
v záložce
Linker → General
najděte Additional
Library Directories
a přidejte tam cestu k adresáři
s glfw3.lib. Vyberte adresář podle verze Visual
Studia které používáte. Pro Visual Studio 2017 to bude adresář
glfw-3.2.1.bin.WIN32\lib-vc2015
(2017 je označení
verze vývojového prostředí, ale 2015 je označení verze překladače).
Nezapomeňte nastavení provést jak pro Debug, tak pro Release
profil (což můžete udělat
najednou skrze Configuration: All configuration
,
viz obrázky).
A to je vše. Teď už byste měli být schopni program přeložit a spustit.
Je tu ale ještě jeden háček. Program bude pro běh potřebovat knihovnu opengl32.dll. Tuto už budete mít někde v systému. Uživatelé, co mají nainstalované nějaké OpenGL aplikace, ji budou mít také. Ale je možné, že nebudou. V takovém případě si jí musí přidat do stejného adresáře, ve kterém program budou spouštět, nebo do systémového adresáře (např. C:\Windows\System32). Schválně se podívejte, jestli tam tu knihovnu máte.
Na stránkách opengl.org je napsáno toto:
The important thing to know is that opengl32.dll belongs to Microsoft. No one can modify it. You must not replace it. You must not ship your application with this file. You must not ship nvoglv32.dll or any other system file either.
Nikde jinde jsem tuto informaci neověřil. Prodávám, jak jsem koupil. Ale když nic jiného, vždycky můžete uživatelům říct, ať si nainstalují Visual Studio :D.
Překlad v Code::blocks (ve Windows)
V Code::Blocks musíte nastavit projekt obdobně jako ve Visual Studiu. Pokud jste tu část o Visual Studiu přeskočili, tak si jí přečtěte, ať víte, co stáhnout a tak. Takže nastavení v Code::Blocks proberu jen telepaticky:
1) Nastavení knihoven a cest:
V Projekt → Properties → Project settings → Project's builds options...
(Ne v Projekt → Build options... !)
-
Linker settings:
-
Link libraries:
- glfw3
- gdi32
- opengl32
-
Other linker options:
- -Wl,--subsystem,windows
-
Search directories:
- Compiler:
- Cesta k adresáři include/
- Linker:
- Cesta k adresáři lib-mingw/
- Compiler:
-
Link libraries:
Pozor, v Code::Block se zapisují knihovny bez koncovky .lib. Také si dejte pozor na pořadí. Knihovna gdi32 musi byt až za glfw3.
Nastavení v Other linker options:
způsobí, že se nebude zobrazovat při spuštění programu konzole. To ale nepoužívejte,
protože v tomto tutoriálu budu používat konzoli pro informativní výstup (vypisuje se tam výstup z funkce printf()
).
opengl32 knihovna je součástí MinGW, takže byste ji neměli potřebovat nikde schánět.
Dev-C++
Dev-C++ také proberu jen telegraficky, jako Code::Blocks.
V Projekt → Vlastnosti projektu
:
- Adresáře:
- Knihovny:
- Cesta k adresáři lib-mingw/
-
Vložené soubory Include:
- Cesta k adresáři include/
- Knihovny:
-
Parametry:
- Linker:
- -lglfw3 -lopengl32
- Linker:
Ještě můžete v Projekt → Vlastnosti projektu → Kompilátor → Linker
vybrat u "nevytvářet konzoli" YES (přidá volbu -mwindows). Tím zabráníte vytváření konzole.
Freeglut
Jak už jsem psal, freeglut se používá na to samé, co GLFW. Na netu najdete pár tutoriálů, které jej používají, tak proč ho tu neukázat.
Následující ukázka dělá to samé, co ukázka v GLFW. Jediný rozdíl je, že
funkci key_callback()
volá freeglut při použití klávesy jen
jednou. Takže nerozlišíte stisk od uvolnění tlačítka :(.
- /*------------------------------------------------*/
- /* 01glfw/freeglut/main.c */
- #include <stdlib.h>
- #include <GL/freeglut.h>
- #include <stdio.h>
- #define GLUT_KEY_ESCAPE '\x1B'
- {
- glutInit(&argc, argv);
- glutInitContextVersion(3, 3);
- glutInitContextProfile(GLUT_CORE_PROFILE);
- glutInitWindowSize(600, 450);
- glutCreateWindow("Hello World");
- glutKeyboardFunc(&key_callback);
- glutMouseFunc(&mouse_button_callback);
- glutDisplayFunc(&display);
- glutMainLoop();
- return EXIT_SUCCESS;
- }
- {
- glClear(GL_COLOR_BUFFER_BIT);
- glColor3f(1.0f, 0.0f, 0.0f);
- glBegin(GL_POLYGON);
- glVertex2f(-0.5f, -0.5f);
- glVertex2f(0.5f, -0.5f);
- glVertex2f(0.5f, 0.5f);
- glVertex2f(-0.5f, 0.5f);
- glEnd();
- glFlush();
- }
- {
- {
- case GLUT_KEY_ESCAPE:
- }
- }
- {
- }
- }
- }
- }
- /*------------------------------------------------*/
Oproti předchozímu příkladu je tu pár nedůležitých rozdílů. Např. místo
while
cyklu je použita funkce display()
, která se
registruje pomocí funkce glutDisplayFunc()
, viz řádek 24.
Dále ve funkci key_callback()
nezobrazuji jestli uživatel
tlačítko stiknul nebo pustil, protože to nejde zjistit.
Alespoň ne ve funkci registrované pomocí glutKeyboardFunc()
.
Freeglut ale poskytuje funkce glutKeyboardUpFunc()
a glutSpecialUpFunc()
,
viz dokumentace
…
Freeglut definuje konstanty pro různá tlačítka, ale Escape mezi nimi zrovna není. Proto jsem si tuto konstantu dovolil na řádku 8 definovat sám.
Také jsem tentokrát nejdříve callback funkce deklaroval před funkcí
main()
a definoval až po ní. Žádný jiný důvod než svou rozmařilost
k tomu nemám.
Pokud vás nějaká GLUT funkce zajímá podrobněji, najdete ji v dokumentaci starého GLUT. Můžete se podívat i na dokumentaci přímo k freeglut.
Ať vás ale ani nenapadne používat starý GLUT. Už je opravdu out of date.
Překlad v Linuxu
Nejdříve si nainstalujte balíček freeglut-devel. S ním se mi automaticky vybral ještě balíček Mesa-libGL-devel. Takto se alespoň menují v mé OpenSuSE distribuci.
Zdroják pak jednoduše přeložíte takto:
$ gcc -o main main.c -lglut -lGL
Knihovna GL je v Linuxu něco jako knihovna opengl32.lib ve Windows.
Překlad ve Windows
Postup je podobný jako s GLFW. Stáhnete si binární distribuci, pak nastavíte Visual Studio, Code::blocks nebo Dev-C++ cesty k include/ a knihovnám.
Binární distribuci nenajdete na oficiální stránce freeglutu, ale zde. Můžete si stáhnout verzi pro Visual Studio i MinGW. Pro Visual Studio je tam aktuálně verze pro Visual Studio 2013, ale funguje i s Visual Studio 2017.
Freeglut, narozdíl od GLEW, načítá funkce dynamicky z knihovny — freelgut.dll. Tu najdete v adresáři freeglut/bin/ (z binární distribuce). Zkopírujte ji do systémového adresáře (C:\Windows\System32), nebo na místo, odkud budete program spouštět (do rootu vašeho projektu).
Kdyby vám to nějak nešlo, přečtěte si freeglut/Readme.txt, kde je popsaný použití freeglut krok za krokem.
Popis použití freeglut s MinGW najdete zde: www.transmissionzero.co.uk/computing/using-glut-with-mingw/.
Závěr
V této kapitole jste se naučili vytvářet okna, inicializovat OpenGL kontext
v požadované verzi a jak detekovat stisk klávesy nebo tlačítka myši.
Viděli jste také zastaralé funkce OpenGL (glBegin()
atp.),
které byste už neměli používat. Proto jsem vám o nich ani nic neřekl.