La bande graphique est une chaîne d'opérations exécutées par les processeurs GPU. Certaines de ces opérations sont décrites dans les programmes appelés shaders (eng. Shaders ), écrit par le programmeur et transmis au GPU pour l' exécution de ses processeurs. Pour les distinguer des autres opérations effectuées sur la bande graphique, que le programmeur ne peut pas modifier, les shader sont appelés “étapes programmables”. Ils offrent une grande flexibilité pour la création d’images statiques ou dynamiques avec des effets complexes rendus en temps réel (par exemple, la génération d’eau, de nuages, de feu, etc. au moyen de fonctions mathématiques).
En utilisant OpenGL sont transmises au GPU : les coordonnées des pics, les matrices de transformation des pics (M: modélisation, V: visualisation, P: projection, MV: modélisation-visualisation, MVP: modélisation-visualisation-projection), la topologie des primitives, textures et données .
1. Dans l’étape programmable, VERTEX SHADER transforme les coordonnées d’un pic à l’aide de la matrice MVP, en passant de coordonnées d’objet à coordonnées de coupe (coordonnées angulaires du clip ). En outre, des calculs d'éclairage au niveau de pointe peuvent être effectués. Le programme VERTEX SHADER est exécuté en parallèle pour un très grand nombre de pics.
2. Une étape fixe suit dans laquelle sont effectuées les opérations suivantes:
3. La prochaine étape est la rastérisation . Cela comprend:
Le résultat de l’étape de pixellisation est une image stockée dans un tableau de pixels qui sera affichée à l’écran, appelée ^^ frame buffer ^^ .
GLSL (GL Shading Language) est utilisé pour l'implémentation des programmes SHADER dans OpenGL.
Lier un shader au programme qui utilise OpenGL est une opération compliquée, c'est pourquoi vous avez reçu le code pour charger un shader.
Un VERTEX SHADER est un programme qui s'exécute pour TOUS les sommets envoyés à la bande graphique. Le résultat des transformations, qui représente la coordonnée post-projection du sommet traité, doit être écrit dans la variable standard gl_Position qui est ensuite utilisée par la bande graphique. Un vertex shader a toujours une fonction appelée main. Un exemple 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 est un programme qui est exécuté pour CHAQUE fragment généré à partir des opérations pour la rastérisation ( quoi? ). Fragment shader a une fonction appelée main. Un exemple de fragment de shader:
#version 330 layout(location = 0) out vec4 out_color; void main() { out_color = vec4(1, 0, 0, 0); }
La connexion entre les objets (maillage, lignes, etc.) et les shaders se fait via des attributs. En raison des nombreuses versions d’OpenGL, il existe de nombreuses méthodes permettant d’établir cette connexion. En laboratoire, nous apprendrons la méthode spécifique OpenGL 3.3 et OpenGL 4.1. Les méthodes les plus anciennes ne sont plus utilisées que lorsque le matériel utilisé impose des restrictions d' API .
L'API OpenGL moderne (3.3+) utilise la méthode de liaison basée sur la présentation . Dans cette méthode, les canaux qui lient un attribut OpenGL à un nom d'attribut dans le shader sont utilisés.
glEnableVertexAttribArray(2); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)0);
La première commande définit le canal avec le nombre 2 utilisé. La deuxième commande décrit la structure de données dans le VBO comme suit:
Dans Vertex Shader, nous obtiendrons l'attribut sur le canal avec l'index spécifié à la liaison, comme suit:
layout(location = 2) in vec3 vertex_attribute_name;
Plus d'informations sont disponibles sur la page de documentation de l' index d'attribut Vertex Shader Vertex Shader attribute index.
À un shader, nous pouvons envoyer des données de la CPU via des variables uniformes. Ils sont appelés uniformes car ils ne varient pas pendant l'exécution du shader . Pour envoyer des données à une variable du shader, vous devez obtenir l'emplacement de la variable dans le programme du shader à l'aide de la fonction glGetUniformLocation:
int location = glGetUniformLocation(int shader_program, "uniform_variable_name_in_shader");
Ensuite, après avoir l'emplacement (qui représente un décalage / un pointeur), nous pouvons envoyer à ce pointeur des informations avec des fonctions de type 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));
Les fonctions glUniform ont la forme glUniform[Matrix?]NT[v?] (regex) où:
n général, le pipeline planifié se compose de plusieurs programmes de shader. Dans le cours EGC, nous utiliserons uniquement Vertex Shader et Fragment Shader . OpenGL offre la possibilité de communiquer des données entre des programmes de shader consécutifs via les attributs in et 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;
Touche F5 - recharge le shader lors de l'exécution de l'application. Il n'est pas nécessaire d'arrêter l'application car les shader sont compilés et exécutés par la carte vidéo et ne sont pas liés au code source C ++ lui-même.
RenderSimpleMesh
pour que les valeurs soient correctement envoyées à ShaderglUniformMatrix4fv
des tableaux appropriés à envoyer au shaderlayout(location = 0) in vec3 v_position; // same for the rest of the attributes ( check Lab6.cpp CreateMesh() );
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);