3. diel - 3D grafika v OpenGL a C ++ - Základy vykresľovanie
V minulej lekcii, 3D grafika v OpenGL a C ++ - Vytvorenie okna , sme si otvorili naše prvé okno. Poďme si do neho niečo vykresliť.
Vykresľovanie trojuholníka
Konečne sa dostávame k samotnému vykresľovanie. OpenGL vidí všetky modely ako trojuholníky, preto náš prvý model bude práve trojuholník. OpenGL obsahuje mnoho pokročilých štruktúr (VAO, VBO, shadery atď ...), preto je zo začiatku aj obyčajné vykreslenie trojuholníka zložitá práca. Nakoniec zistíte, že je to naopak celkom jednoduchá záležitosť.
Shadery a dodatočný kód
Ešte pred písaním kódu si stiahnite zdrojové kódy a pridajte si k projektu nasledujúce súbory:
NacitacShaderu.h
NacitacShaderu.cpp
bodovy_shader.vert
pixelovy_shader.frag
Prvé dva súbory si rovno pridáme do projektu skrz Visual Studio a
NacitacShaderu.h
naincludujeme do nášho súboru z minulej
lekcie:
#include <GL/glew.h> #include <GLFW/glfw3.h> #include "NacitacShaderu.h" int main(void) { // kód z předchozí lekce }
Zvyšné súbory .vert
a .frag
si pridajme niekam k
nášmu projektu, najlepšie ku knižnici glew32.dll
.
pozn .: Shadery nemajú špecifickú príponu súboru, preto si môžete
zvoliť príponu ľubovoľnú, pokojne aj .txt
. Silne ale
odporúčam shader súbory segregovat podľa typu, aby s nimi bolo možné
rozumne pracovať.
OpenGL využíva pri vykresľovaní tzv. Shadery, čo sú
programy ovplyvňujúce celý proces vykresľovanie. O shader si povieme viac v
nasledujúcej lekcii. Zatiaľ nám ale stačí vedieť, že shadery sú nutné
pre vykresľovanie a že budeme používať 2 hlavné typy
shaderov, vrcholový (vertex) a pixelový
(fragment) shader. Implementácia shaderov je v OpenGL individuálne, preto v
seriáli budeme používať funkcie z NacitacShaderu
.
Body trojuholníka
Presuňme sa teraz do funkcie main()
pod inicializáciu GLFW a
Glewe. Aby sme vykreslili trojuholník, potrebujeme k tomu súradnice jeho troch
vrcholov. Prvý bod bude vľavo dole, druhý bude hore
uprostred a tretí bude vpravo dole:
// inicializace GLEW a GLFW /* GLfloat je pouze standartní float, má ale speciální deklaraci, aby se OpenGL vyhnulo kompilačním problémům, takto fungují i jiné typy */ GLfloat pole_bodu_data[] = { -1.0f, -1.0f, 0.0f, // první bod 0.0f, 1.0f, 0.0f, // druhý bod 1.0f, -1.0f, 0.0f // třetí bod };
Body nám zodpovedajú súradniciam v našom okne podľa tvaru
X, Y, Z
. Súradnicu Z
si necháme na hodnote
0.0f
, pretože vykresľujú 2D. Teoreticky by sme si ju mohli
vypustiť, ale museli by sme upraviť bodový shader.
Načítanie shaderov
Na použitie shaderov si najskôr musíme vytvoriť shader program. To sú už skompilované shadery v jednom kuse, ktoré sa nachádza na grafickej karte. Pri vykresľovanie nám stačí zvoliť daný shader program a všetky shadery k nemu pripojené sa automaticky použijú.
Najskôr si teda vytvoríme náš shader program:
GLuint shader_program = glCreateProgram(); // vytvoříme si na GPU nový shader program
OpenGL nám vráti (rovnako ako u samotných shaderov a ostatných
konštrukcií) miesto na GPU, kde sa program nachádza (nemôže nám vrátiť
priamy ukazovateľ, pretože GPU je iné zariadenie). Ďalej k
programu pripojíme naše shadery, bodovy_shader
a
pixelovy_shader
, ktoré rovno skompilujeme:
GLuint bodovy_shader = pripojShaderKProgramu("bodovy_shader.vert", GL_VERTEX_SHADER, shader_program); // přidáme k němu bodový shader GLuint pixelovy_shader = pripojShaderKProgramu("pixelovy_shader.frag", GL_FRAGMENT_SHADER, shader_program); // přidáme k němu pixelový shader
Ako posledný prepojíme náš program s príslušným shaderovým procesorom a skontrolujeme prípadné chyby:
bool uspech_linkovani = napojProgram(shader_program); // připojíme program k GPU // pokud kompilace nebo linkování shaderů selže, terminujeme program if (bodovy_shader == 0 || pixelovy_shader == 0 || !uspech_linkovani) { glfwTerminate(); return -1; }
Vertex Array Object a Vertex Buffer Object
Ako už bolo spomenuté, GPU (grafický procesor) je úplne iné zariadenie, preto s ním nemožno pracovať rovnako ako sa CPU (procesorom) a RAM. Naše dáta musíme najskôr odovzdať nášmu GPU do video-pamäte.
Dáta sa neskladajú iba z bodov modelu, ale aj z farieb alebo súradníc textúr. Určite by bolo vhodné, keby boli všetky dáta pohromade a všetko sa vykreslilo jedným príkazom.
VBO čiže Vertex Buffer Object je štruktúra, ktorá skladuje dáta nejakého typu. Môžu to byť napríklad body modelov (vrcholy), farby alebo súradnice textúr.
VAO čiže Vertex Array Object je štruktúra, ktorá skladuje VBO a ich nastavenie pohromade. Môžeme k nemu pripojiť treba vrcholy nášho modelu a súradnice textúr (napr. Model kríky aj s jeho textúrou), nastaviť spôsob čítania bufferov a napojiť indexy shaderov. Potom stačí pri vykresľovaní len načítať náš VAO a všetko sa vykreslí v jednom príkaze.
Ako prvý si vygenerujeme nové VAO a nastavíme ho ako aktívny:
GLuint VAO_trojuhelnik; // založíme si náš VAO pro trojúhelník glGenVertexArrays(1, &VAO_trojuhelnik); // řekneme OpenGL ať ho vygeneruje (chceme pouze jeden) glBindVertexArray(VAO_trojuhelnik); // nastavíme ho jako aktivní VAO
Teraz si môžeme vytvoriť VBO, do ktorého budeme kopírovať vrcholy trojuholníka:
GLuint VBO_vrcholy; // založíme si náš VBO pro vrcholy trojúhelníku glGenBuffers(1, &VBO_vrcholy); // řekneme OpenGL ať ho vygeneruje (chceme pouze jeden) glBindBuffer(GL_ARRAY_BUFFER, VBO_vrcholy); // připojíme ho k aktuálnímu VAO (tedy našemu trojúhelníku) glBufferData(GL_ARRAY_BUFFER, sizeof(pole_bodu_data), pole_bodu_data, GL_STATIC_DRAW); // překopírování dat vrcholů do našeho VBO
Pred kopírovaním dát nesmieme zabudnúť na napojenie VBO k aktuálnemu VAO. Po napojení VBO môžeme dáta úspešne kopírovať. Nakoniec už len potrebujeme nastaviť formát dát:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // nastavíme správný formát dat, aby OpenGL vědělo, jak tento buffer číst glEnableVertexAttribArray(0); // umožníme našemu shaderu používat data na indexu 0
Ako prvý hodnotu nastavíme index, na ktorom sa nachádza
buffer s vrcholmi. Pre nás to bude index 0 (nemusí byť nutne 0, ale musí sa
zhodovať s indexom v bodovy_shader.vert). Dáta sú typu float
,
normalizovať nechceme a offset / skok je 0. Následne už musíme náš index
iba zapnúť.
Pre bezpečnostné účely je dobrý postup všetky aktívne štruktúry odbindovat, aby sme sa vyhli chybám:
// pro bezpečnost musíme přepnout aktivní VBO a VAO na nulovou hodnotu glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0);
Všetky operácie, ktoré sme doteraz vykonali (založenie VAO / VBO, načítanie dát, kompilácie shaderov, nastavenie formátu dát ...) patrí do inicializačnú časti programu a stačí ich vykonať len raz (ak je v priebehu nebudeme upravovať). Pri ich ďalšom použití je už stačí zvoliť ako aktívny štruktúry a všetko sa vykreslí pomocou jedného príkazu. Bez použitia VAO by sa všetky nastavenia musela aplikovať pri každom vykreslovacím cyklu, čo by bolo výpočtovo neefektívne.
Vykreslenie bodov
Presuňme sa teraz do našej slučky, kam pridáme trochu vykreslovacího kódu:
while (!glfwWindowShouldClose(okno)) { glClearColor(0.0, 0.1, 0.0, 1.0); // nastavení barvy pozadí glClear(GL_COLOR_BUFFER_BIT); // vyčištění okna glUseProgram(shader_program); // nastavení našeho shader programu jako aktivního glBindVertexArray(VAO_trojuhelnik); // nastavení našeho VAO jako aktivního glDrawArrays(GL_TRIANGLES, 0, sizeof(pole_bodu_data) / (sizeof(float) * 3)); // vykreslení našich bodů // bezpečnostní "odbindování" našich shaderů a našeho trojúhelníku glBindVertexArray(0); glUseProgram(0); glfwSwapBuffers(okno); // OpenGL vymění buffery glfwPollEvents(); // získání všech eventů (stisk klávesy atd.) }
Najskôr nastavíme náš shader program a VAO ako aktívny a spustíme vykresľovací funkciu. Tá má nastavené, že vykresľuje trojuholníky, začína na offsetu 0 a vykresľuje 3 vrcholy (vypočítali sme z veľkosti dát). Tu môžete vidieť, že nám len stačí nabindovať náš VAO a všetky jeho dáta / nastavenia sa pri vykresľovanie použijú. Nakoniec už len odbindujeme aktívnej štruktúry.
Pri ukončení nesmieme zabudnúť na vyčistenie aktuálne používaných zdrojov na GPU:
// vyčištění zdrojů a terminování GLFW glDeleteShader(bodovy_shader); glDeleteShader(pixelovy_shader); glDeleteProgram(shader_program); glDeleteBuffers(1, &VBO_vrcholy); glDeleteVertexArrays(1, &VAO_trojuhelnik); glfwTerminate(); return 0;
V našom prípade by sme to mohli vynechať, pretože pri ukončení programu sa všetko automaticky vyčistí. Pri väčších projektoch by to však predstavovalo veľký problém.
Konečný výstup programu by mohol vyzerať nejako takto:
Ak vám niečo nefungovalo, zdrojové kódy sú nižšie k stiahnutiu.
V ďalšej lekcii, 3D grafika v OpenGL a C ++ - Shadery , sa pozrieme bližšie na shadery a pridáme nášmu trojuholníka farby za behu programu.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 33x (4.77 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C++