Video Laborator 6: https://youtu.be/f7q2TGCRly0
Autor: Anca Băluțoiu
Banda Grafica este un lant de operatii executate de procesoarele GPU. Unele dintre aceste operatii sunt descrise in programe numite shadere (eng. shaders), care sunt scrise de programator si transmise la GPU pentru a fi executate de procesoarele acestuia. Pentru a le deosebi de alte operatii executate in banda grafica, pe care programatorul nu le poate modifica, shaderele sunt numite „etape programabile”. Ele dau o mare flexibilitate in crearea de imagini statice sau dinamice cu efecte complexe redate in timp real (de ex. generarea de apa, nori, foc etc prin functii matematice).
Folosind OpenGL sunt transmise la GPU: coordonatele varfurilor, matricile de transformare a varfurilor (M: modelare, V: vizualizare, P: proiectie, MV: modelare-vizualizare, MVP: modelare-vizualizare-proiectie), topologia primitivelor, texturi si ale date.
1. In etapa programabila VERTEX SHADER se transforma coordonatele unui varf, folosind matricea MVP, din coordonate obiect in coordonate de decupare (eng. clip coordinates). De asemenea, pot fi efectuate si calcule de iluminare la nivel de varf. Programul VERTEX SHADER este executat in paralel pentru un numar foarte mare de varfuri.
2. Urmeaza o etapa fixa, in care sunt efectuate urmatoarele operatii:
3. Urmatoarea etapa este Rasterizarea. Aceasta include:
Rezultatul etapei de rasterizare este o imagine memorata intr-un tablou de pixeli ce va fi afisat pe ecran, numit ^^frame buffer^^.
Pentru implementarea de programe SHADER in OpenGL se foloseste limbajul dedicat GLSL (GL Shading Language).
Legarea unui shader la programul care foloseste OpenGL este o operatie complicata, de aceea va este oferit codul prin care se incarca un shader.
Un VERTEX SHADER e un program care se executa pentru FIECARE vertex trimis catre banda grafica. Rezultatul transformarilor, care reprezinta coordonata post-proiectie a vertexului procesat, trebuie scris in variabila standard gl_Position care e folosita apoi de banda grafica. Un vertex shader are tot timpul o functie numita main. Un exemplu de vertex shader:
#version 330 layout(location = 0) in vec3 v_position; // Uniform properties uniform mat4 Model; uniform mat4 View; uniform mat4 Projection; void main() { gl_Position = Projection * View * Model * vec4(v_position, 1.0); }
Un FRAGMENT SHADER e un program ce este executat pentru FIECARE fragment generat in urma operatiei de rasterizare (ce inseamna?). Fragment shader are in mod obligatoriu o functie numita main. Un exemplu de fragment shader:
#version 330 layout(location = 0) out vec4 out_color; void main() { out_color = vec4(1, 0, 0, 0); }
Legarea intre obiecte (mesh, linii etc.) si shadere se face prin atribute. Datorita multelor versiuni de OpenGL exista multe metode prin care se poate face aceasta legare. In laborator vom invata metoda specifica OpenGL 3.3 si OpenGL 4.1. Metodele mai vechi nu mai sunt utilizate decat in atunci cand hardware-ul utilizat impune restrictii de API.
API-ul OpenGL modern (3.3+) utilizeaza metoda de legare bazata pe layout-uri. In aceasta metoda se folosesc pipe-uri ce leaga un atribut din OpenGL de un nume de atribut in shader.
glEnableVertexAttribArray(2); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)0);
Prima comanda seteaza pipe-ul cu numarul 2 ca fiind utilizat. A doua comanda descrie structura datelor in cadrul VBO-ului astfel:
In Vertex Shader vom primi atributul respectiv pe pipe-ul cu indexul specificat la legare, astfel:
layout(location = 2) in vec3 vertex_attribute_name;
Mai multe informatii se pot gasi pe pagina de documentatie Vertex Shader attribute index.
La un shader putem trimite date de la CPU prin variabile uniforme. Se numesc uniforme pentru ca nu variaza pe durata executiei shader-ului. Ca sa putem trimite date la o variabila din shader trebuie sa obtinem locatia variabilei in programul shader cu functia glGetUniformLocation:
int location = glGetUniformLocation(int shader_program, "uniform_variable_name_in_shader");
shader→GetProgramID()
sau direct accesand variabila membru shader→program
Apoi, dupa ce avem locatia (care reprezinta un offset/pointer) putem trimite la acest pointer informatie cu functii de tipul glUniform:
//void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) glm::mat4 matrix(1.0f); glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); // void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) glUniform4f(location, 1, 0.5f, 0.3f, 0); //void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2) glUniform3i(location, 1, 2, 3); //void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) glm::vec3 color = glm::vec3(1.0f, 0.5f, 0.8f); glUniform3fv(location, 1, glm::value_ptr(color));
Functiile glUniform sunt de forma glUniform[Matrix?]NT[v?] (regex) unde:
In general pipeline-ul programat este alcatuit din mai multe programe shader. In cadrul cursului de EGC vom utiliza doar Vertex Shader si Fragment Shader. OpenGL ofera posibilitatea de a comunica date intre programele shader consecutive prin intermendiul atributelor in si out
Vertex Shader:
#version 330 // GLSL version of shader (GLSL 330 means OpenGL 3.3 API) out vec3 attribute_name;
Fragment Shader:
in vec3 attribute_name;
Vertex Shader:
#version 410 // GLSL 410 (OpenGL 4.1 API) layout(location = 0) out vec4 vertex_out_attribute_name;
Fragment Shader:
#version 410 layout(location = 0) in vec4 fragment_in_attribute_name;
tasta F5 - reincarca shaderele in timpul rularii aplicatiei. Nu este nevoie sa opriti aplicatia intrucat shaderele sunt compilate si rulate de catre placa video si nu au legatura cu codul sursa C++ propriu zis.
RenderSimpleMesh
astfel inca sa trimiteti corect valorile uniform catre ShaderglUniformMatrix4fv
sa se trimita matricile corespunzatoare catre shaderVertex Shader
folosind layout location layout(location = 0) in vec3 v_position; // same for the rest of the attributes ( check Lab6.cpp CreateMesh() );
Fragment Shader
out vec3 frag_color; // same for other attributes
main()
frag_color = vertex_color; // same for other attributes
gl_Position = Projection * View * Model * vec4(v_position, 1.0);
Vertex Shader
in vec3 frag_color;
out_color = vec4(frag_color, 1);
VertexFormat
pentru a intelege ceea ce se trimite pe fiecare pipe