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:

  1. /*------------------------------------------------*/
  2. /* opengl/01glfw/main.c                           */
  3.  
  4. #include <stdio.h>
  5. #include <GLFW/glfw3.h>
  6.  
  7. static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
  8. {
  9.         if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
  10.                 printf("Byla stisknuta klavesa Escape.\n");
  11.                 glfwSetWindowShouldClose(window, GLFW_TRUE);
  12.         }
  13. }
  14.  
  15. void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
  16. {
  17.         double xpos, ypos;
  18.         glfwGetCursorPos(window, &xpos, &ypos);
  19.         printf("Click na pozici [%f, %f]\n", xpos, ypos);
  20.         if (button == GLFW_MOUSE_BUTTON_RIGHT) {
  21.                 printf("Stisk nebo uvolneni praveho tlacitka.\n");
  22.         }
  23.         if (action == GLFW_PRESS) {
  24.                 printf("Stisk tlacitka\n");
  25.         }
  26.         if (action == GLFW_RELEASE) {
  27.                 printf("Uvolneni tlacitka\n");
  28.         }
  29. }
  30.  
  31. int main(void)
  32. {
  33.         GLFWwindow* window;
  34.  
  35.         /* Initialize the library */
  36.         if (!glfwInit()) {
  37.                 return -1;
  38.         }
  39.         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  40.         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  41.         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  42.  
  43.         /* Create a windowed mode window and its OpenGL context */
  44.         window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
  45.         if (!window)
  46.         {
  47.                 printf("Nelze inicializovat kontext\n");
  48.                 glfwTerminate();
  49.                 return -1;
  50.         }
  51.  
  52.         /* Make the window's context current */
  53.         glfwMakeContextCurrent(window);
  54.         /* events */
  55.         glfwSetKeyCallback(window, key_callback);
  56.         glfwSetMouseButtonCallback(window, mouse_button_callback);
  57.  
  58.         printf("%s\n", glGetString(GL_VERSION));
  59.  
  60.         /* Loop until the user closes the window */
  61.         while (!glfwWindowShouldClose(window))
  62.         {
  63.                 /* Render here */
  64.                 glClear(GL_COLOR_BUFFER_BIT);
  65.                 glColor3f(1.0f, 0.0f, 0.0f);
  66.  
  67.                 glBegin(GL_POLYGON);
  68.                 glVertex2f(-0.5f, -0.5f);
  69.                 glVertex2f(0.5f, -0.5f);
  70.                 glVertex2f(0.5f, 0.5f);
  71.                 glVertex2f(-0.5f, 0.5f);
  72.                 glEnd();
  73.  
  74.                 /* Swap front and back buffers */
  75.                 glfwSwapBuffers(window);
  76.  
  77.                 /* Poll for and process events */
  78.                 glfwPollEvents();
  79.         }
  80.  
  81.         glfwDestroyWindow(window);
  82.         glfwTerminate();
  83.         return 0;
  84. }
  85. /*------------------------------------------------*/
GLFW okno

První okno

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:

  1. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  2. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  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).

Číslo za 4.5 označuje číslo update opengl verze, poslední číslo je číslo ovladače grafické karty. Tyto čísla nejsou důležitá.

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.

  1. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  2. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  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

  1. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  2. 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/

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/
  • Parametry:
    • Linker:
      • -lglfw3 -lopengl32

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 :(.

  1. /*------------------------------------------------*/
  2. /* 01glfw/freeglut/main.c                         */
  3.  
  4. #include <stdlib.h>
  5. #include <GL/freeglut.h>
  6. #include <stdio.h>
  7.  
  8. #define GLUT_KEY_ESCAPE '\x1B'
  9.  
  10. void display(void);
  11. void key_callback(unsigned char key, int x, int y);
  12. void mouse_button_callback(int button, int state, int x, int y);
  13.  
  14.  
  15. int main(int argc, char** argv)
  16. {
  17.         glutInit(&argc, argv);
  18.         glutInitContextVersion(3, 3);
  19.         glutInitContextProfile(GLUT_CORE_PROFILE);
  20.         glutInitWindowSize(600, 450);
  21.         glutCreateWindow("Hello World");
  22.         glutKeyboardFunc(&key_callback);
  23.         glutMouseFunc(&mouse_button_callback);
  24.         glutDisplayFunc(&display);
  25.         printf("%s\n", glGetString(GL_VERSION));
  26.         glutMainLoop();
  27.  
  28.         return EXIT_SUCCESS;
  29. }
  30.  
  31. void display()
  32. {
  33.         glClear(GL_COLOR_BUFFER_BIT);
  34.         glColor3f(1.0f, 0.0f, 0.0f);
  35.  
  36.         glBegin(GL_POLYGON);
  37.         glVertex2f(-0.5f, -0.5f);
  38.         glVertex2f(0.5f, -0.5f);
  39.         glVertex2f(0.5f, 0.5f);
  40.         glVertex2f(-0.5f, 0.5f);
  41.         glEnd();
  42.  
  43.         glFlush();
  44. }
  45.  
  46. void key_callback(unsigned char key, int x, int y)
  47. {
  48.         switch (key)
  49.         {
  50.         case GLUT_KEY_ESCAPE:
  51.                 printf("Byla stisknuta klavesa Escape.\n");
  52.                 exit(EXIT_SUCCESS);
  53.                 break;
  54.         }
  55. }
  56.  
  57. void mouse_button_callback(int button, int state, int x, int y)
  58. {
  59.         printf("Click na pozici [%i, %i]\n", x, y);
  60.         if (button == GLUT_RIGHT_BUTTON) {
  61.                 printf("Stisk nebo uvolneni praveho tlacitka.\n");
  62.         }
  63.         if (state == GLUT_DOWN) {
  64.                 printf("Stisk tlacitka\n");
  65.         }
  66.         if (state == GLUT_UP) {
  67.                 printf("Uvolneni tlacitka\n");
  68.         }
  69. }
  70. /*------------------------------------------------*/

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.

Komentář Hlášení chyby
Created: 24.12.2016
Last updated: 24.11.2017
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..