gKit2 light
afficher plusieurs triangles, modifier les paramètres uniform d'un shader program

cf tuto3GL.cpp

pour pouvoir dessiner quelque chose, il faut commencer par configurer le pipeline openGL, repassez dans introduction api 3d, openGL et pipeline graphique si nécessaire.

la configuration minimale est :

glClearColor() et glClearDepthf() définissent les valeurs par défaut utilisées pour "éffacer" l'image et le zbuffer, avec glClear( ). la taille de l'image est fournie par glViewport( ). pour une application simple, il suffit de fixer les valeurs une seule fois dans init( ) :

glClearColor(0.2f, 0.2f, 0.2f);
glClearDepthf(1.0f);
glViewport(0, 0, window_width(), window_height());

remarque : si la fenetre de l'application change de dimension, il ne faut pas oublier de modifier glViewport(). si vous utilisez run( ) de window.h, c'est fait automatiquement.

puis effacer l'image et le zbuffer au début de draw( ):

glClear(GL_COLOR_BUFFER_BIT); // effacer l'image
glClear(GL_DEPTH_BUFFER_BIT); // effacer le zbuffer, si nécessaire

on peut combiner les deux, avec un OU binaire :

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // effacer l'image et le zbuffer

ztest et zbuffer

pour obtenir une visibilité correcte lorsque plusieurs triangles se dessinent sur le même pixel, il faut indiquer lequel garder, celui dont la profondeur est la plus petite, ou la plus grande, ou égale, etc.

glDepthFunc(GL_LESS); // conserver le triangle le plus proche,
glDepthFunc(GL_GREATER); // conserver le triangle le plus loin,
glDepthFunc(GL_ALWAYS); // conserver le dernier triangle dessine

remarque : pour conserver le triangle le plus loin, avec glDepthFunc(GL_GREATER), il faut initialiser le zbuffer correctement : avec la profondeur la plus petite, c'est à dire 0, dans le repère image, cf introduction api 3d, openGL et pipeline graphique.

glDepthFunc() permet de choisir le test, mais il faut aussi l'activer (ou le désactiver) :

glEnable(GL_DEPTH_TEST); // activer le ztest
glDisable(GL_DEPTH_TEST); // desactiver le ztest

élimination des faces arrières / back face culling

lorsque les triangles décrivent la surface d'un objet opaque, les triangles à l'arrière de l'objet ne sont pas visibles, il est assez simple de les détecter, cf introduction api 3d, openGL et pipeline graphique, par contre, il faut donner l'orientation normale des faces, sens trigo ou horaire, et choisir lesquelles on veut supprimer (avant ou arrière) et enfin, activer le test :

glFrontFace(GL_CCW); // les faces visibles / avant sont dans le sens trigo / counter clockwise
glFrontFace(GL_CW); // les faces visibles / avant sont dans le sens horaire / clockwise
glCullFace(GL_BACK); // eliminer les faces arrieres
glCullFace(GL_FRONT); // ou les faces avant ?
glEnable(GL_CULL_FACE); // activer le test
glDisable(GL_CULL_FACE); // desactiver le test

le test standard s'écrit :

glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);

attributs de sommets, vertex array object, vao

pour dessiner des triangles, il faut décrire les informations associées aux sommets, indiquer ou les trouver, leur organisation mémoire, et indiquer à quelles entrées du vertex shader elles sont associées.

le cas général est présenté dans configurer un format de sommet, vertex array object, pour l'instant, la solution la plus simple est d'utiliser un tableau uniform déclaré par le vertex shader, sans description de format de sommet. c'est un objet openGL, appelé vertex array object qui stocke la description du format de sommets. il suffit donc de créer un vertex array object vide / par défaut.

la création des objets openGL utilise des fonctions de la forme glGenXXX( int n, GLuint *names ). cette famille de fonctions permet de créer plusieurs objets en même temps et renvoye un tableau d'identifiants des nouveaux objets. pour en créer un seul, on peut utiliser :

GLuint vao;
glGenVertexArrays(1, &vao);

il ne reste plus qu'à le sélectionner pour configurer le pipeline :

glBindVertexArray(vao);

uniforms et shader program

après avoir compilé et linké un shader program, cf compiler et linker un shader program, il faut le sélectionner pour configurer le pipeline :

GLuint program= ... ;
glUseProgram(program);

avant de pouvoir dessiner, il faut affecter une valeur à tous les uniforms utilisés par le shader program. l'affectation se fait en 2 étapes :

attention : ne pas oublier que glUniform() affecte une valeur à un uniform du program actuellement sélectionné par glUseProgram()...

tuto3GL.glsl déclare 3 uniforms :

la démarche est identique dans les 3 cas, même pour le tableau. par contre, il faut utiliser la bonne version de glUniform() à chaque fois.

pour time, 1 float, il faut utiliser glUniform1f() pour l'affectation (cf interface C openGL pour les conventions de nommage) :

GLint location= glGetUniformLocation(program, "time");
glUniform1f(location, 12);

pour color, 4 float, il faut utiliser... glUniform4f() :

GLint location= glGetUniformLocation(program, "color");
glUniform4f(location, 1, 1, 1, 1);

pour le tableau positions, les éléments du tableau sont des vec3. pour un seul vec3, on utiliserait glUniform3f( ), pour un tableau, les valeurs sont passées par pointeur, et c'est la variante glUniform3fv(location, size, data) qu'il faut utiliser. size indique le nombre d'élements vec3 à transférer.

#include "vec.h"
vec3 data[36]= { ... };
GLint location= glGetUniformLocation(program, "positions");
glUniform3fv(location, 36, data);

remarque : on peut utiliser les variantes pointeurs pour une valeur unique, il suffit de donner 1 comme nombre d'éléments à affecter. par exemple :

float data= 12;
GLint location= glGetUniformLocation(program, "time");
glUniform1fv(location, 1, &data);

ou encore

#include "color.h"
Color data= Color(1, 1, 1);
GLint location= glGetUniformLocation(program, "color");
glUniform4fv(location, 1, &data);

uniforms.h définit plusieurs surcharges de la famille glUniform() pour les types les plus courants.

#include "uniforms.h"
program_uniform(program, "time", 12);
program_uniform(program, "color", Color(1, 1, 1));

glDraw( )

on peut enfin dessiner les 12 triangles, c'est à dire les indices de 0 à 36.

glDrawArrays(GL_TRIANGLES, 0, 36);

l'exemple complet est dans tuto3GL.cpp

résumé : configuration du pipeline

il faut fixer les paramètres, pour les applications simples, une seule fois dans init( ) :

// definir la taille de l'image a dessiner,
// glViewport(0, 0, width, height);
// fait par run( ) de window.h
glClearColor(0.2, 0.2, 0.2, 1); // definir la couleur par defaut
glClearDepth(1.f); // profondeur par defaut
glDepthFunc(GL_LESS); // ztest, conserver l'intersection la plus proche de la camera
glEnable(GL_DEPTH_TEST); // activer le ztest
glFrontFace(GL_CCW); // description des faces dans le sens trigo
glCullFace(GL_BACK); // eliminer les faces arrieres
glEnable(GL_CULL_FACE); // activer le test

et pour dessiner, dans draw( ), il faut au minimum :

glBindVertexArray(vao); // un vertex array object
glUseProgram(program); // un shader program
glUniform(...); // donner une valeur a tous les uniforms du program
glDrawArrays(...);

éventuellement, on peut vérifier que tous les uniforms utilisés par le program ont bien une valeur ou vérifier que les attributs déclarés par le vertex shader sont bien paramétrés, cf récupérer les uniforms et les attributs utilisés par un shader program.