Előadás Home Introduction Environment setup Best homeworks

Az első window

Hozzunk létre egy új Xcode projektet és adjuk hozzá a projekthez a glad.c fájt.

In [ ]:
#include <glad/glad.h> // always include glad before glfw
#include <GLFW/glfw3.h>

#include <iostream>

Hozzuk létre a main függvényt, amiben inicializálni fogjuk a GLFW window-t.

In [ ]:
int main() {
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
    
    #ifdef __APPLE__
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // only with OS X
    #endif
}
Először inicializáljuk a window-t a glfwInit függvénnyel, majd a glfwWindowHint függvény segítségével beállítjuk a window tulajdonságait. Az első paraméter egy enum, ami meghatározza a beállítandó tuajdonságot, a második paraméter pedig a kívánt érték. Window dokumentáció.

Beállítottuk, hogy az OpenGL 4.0-ás verzióját és a Core profile-t szeretnénk használni. Ha a videó kártyánk nem támogatja a 4.0-ás verziót, akkor a window nem fog elindulni. Az OpenGL Extension Viewer segítségével meg lehet nézni a támogatott OpenGL verziót.

A következő lépésben létre hozzuk a window objektumot, mely tárolja és kezeli a window-hoz kötődő adatokat.

In [ ]:
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);
if (window == nullptr) {
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
}
    
glfwMakeContextCurrent(window);
A glfwCreateWindow függvényben meghatározzuk a window szélességét és magasságát, majd harmadik paraméterként egy nevet adunk a window-nak. A függvény egy window objektummal tér vissza, amire más GLFW műveleteknél is szükség lesz. Majd végül beállítjuk, hogy az adott window legyen a fő context.
OpenGL context: Az OpenGL állapota, ami meghatározza a renderelés működését (pl.: pontokat, vagy vonalat rajzoljon).

GLAD

Mielőtt bármilyen GL függvényt hívnánk, először inicializálnunk kell a GLAD-et. A GLAD operációs rendszer specifikusan meghatározza, az egyes GL függvény pointerek helyét.

In [ ]:
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
}

Viewport

Mielőtt elkezdhetnénk rajzolni, meg kell mondanunk az OpenGL-nek a window méretét, hogy fel tudja építeni a koordinátarendszert. Az első két paraméter a bal alső sarok koordinátái, még az utolső két paraméter a szélesség és a magasság. Ez lehet kisebb, mint a tényleges window méret.

In [ ]:
glViewport(0, 0, 800, 600);

Ahhoz hogy a window átméretezhető legyen, frissítenünk kell a viewport-ot. Ehhez egy callback függvényt regisztrálunk a window-hoz és ha a user szeretné átméretezni a window-t, akkor a callback függvényen keresztül a viewport átméretezi az ablakot.

In [ ]:
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    glViewport(0, 0, width, height);
}

Ha a window mérete változik, akkor a framebuffer_size_callback függvény meghívódik és a paraméterben kapott window-t a megfelelő méretre változtatja. Ahhoz, hogy ez működjön, a GLFW-n keresztül a window-hoz kell regisztrálnunk a callback függvényt.

In [ ]:
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

Render loop

A render loop funkciója, hogy az applikáció ne záródjon be egy rajzolás parancs után, hanem folyamatosan fusson, rajzoljon és fogadja a felhasználó parancsait, amíg a felhasználó be nem zárja az alkalmazást. Ezt egy while loop segítségével érjük el és az alkalmazás akkor fog leállni, amikor maga a GLFW leáll.
In [ ]:
while(!glfwWindowShouldClose(window)) {
    processInput(window); // user inputs
        
    // rendering commands here ...
        
    glfwSwapBuffers(window);
    glfwPollEvents();
}

A glfwWindowShouldClose függvény folyamatosan figyeli, hogy a GLFW window szeretne e bezáródni és ha igen, akkor true értékkel tér vissza és megszakad a while loop. A glfwPollEvents függvény folyamatosan figyeli, hogy történt e valamilyen esemény (keyboard, mouse...) és ennek megfelelően frissíti a window állapotát, a megfelelő függvényt meghívva, melyet callback függvényként regisztrálhatunk. A glfwSwapBuffers függvény minden iteráció alatt üríti a buffert, mely a szín értékeket tartalmazza a window egyes pixeleihez, így megjelenik a kívánt rajz a képernyőn.

Double buffering

Egy buffer esetén, a rajzolás során az ablakozó rendszerek pixelenként, általában balről jobbra és fentről lefelé rajzolják ki az adott képet, tehát az nem instant jelenik meg a képernyőn, hiszen az egyes OpenGL primitíveket (modelleket) folyamatosan rendereli a pipeline a bufferbe. Ezért, hogy ne legyenek hibák, az ablakoző rendszerek double buffer-elés technikát használnak. A front buffer tárolja a végleges képet, melyet a képernyőre rajzol a rendszer, míg egy back buffer szolgál a primitívek folyamatos renderelésére. Ahogy a renderelés parancs véget ér, a back buffer tartalmát áttölti a rendszer a front bufferbe és az instant megjelenik.

Miután kiléptünk a render loop-ból, fel kell szabadítanunk az összes window-hoz tartozó erőforrást. Ezt a glfwTerminate függvény meghívásával tehetjük meg.

Input

A GLFW számos függvényt biztosít az input esemény kezelésére. A glfwGetKey függvény paraméterként az adott window-t várja, ahol figyeli a billentyű eseményt, továbbá egy enum formályában a figyelni kívánt billenyű kódját. Ha az adott billentyű lenyomódik, akkor aktiválódik a függvény.
In [ ]:
void processInput(GLFWwindow *window) {
    if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}
A processInput függvény egy segédfüggvény, ami csak a kód szervezéséhez szükséges. A glfwGetKey függvény segítségével arra figyeleünk, hogy a user lenyomta e az escape billentyűt és ha igen, akkor bezárjuk a windowt. A processInput függvényt a while loop-ban minden egyes iteráció során hívjuk.

title

Mostmár készen állunk futtatni a kódot, mely megjeleníti az első ablakunkat. Jelenleg még nem rendereltünk rá semmit, ezért az alap fekete színű window-t látjuk.

A teljes forráskód: Code