Gérer les couleurs dans OpenGL SE sous Android

 

Jusqu'à présent, la définition de la couleur était faîte directement dans le fragment shader via la variable gl_FragColor qui est un vecteur vec4.

La couleur est donc composée de 4 éléments qui sont: le rouge, vert, bleu et alpha pour la transparence. C'est le classique RGBA.

 

Couleur uniforme

Il serait intéressant de fournir cette couleur en paramètre à ce fragment shader pour permettre de définir une couleur sur l'objet que l'on veut afficher.

Une solution consiste à utiliser une variable uniforme.

Le code du shader se présente alors ainsi:

Il reste ainsi plus qu'à déterminer la position de cette variable uniforme après compilation du code des shaders en un programme:

uColor = GLES20.glGetUniformLocation(p1GLSL.getNumProgram(), UCOLOR);

avec UCOLOR: private static final String UCOLOR = "uColor";

et enfin passer une couleur au shader: GLES20.glUniform4fv (uColor, 1, maCouleur, 0);

avec maCouleur: private final float[] maCouleur = { 255, 255, 0, 0 }; // Composantes rouge, vert, bleu et alpha

 

D'où les codes modifiés de la classe Triangle...

... pour mettre la multi instances de triangles avec des couleurs et des coordonnées que l'on peut passer en paramètre...

et du Renderer...

Pour créer deux instances de triangles et au passage mettre le fond en noir, car le rouge devient insupportable !

Image non trouvée !Au passage, on ressent de plus en plus l'utilité d'une classe abstraite pour gérer les triangles et autres figures ...

 

 

Définir la couleur au niveau des vertice

Il est possible d'indiquer une couleur à un vertex. Dans ce cas, le fragment shader va traiter la couleur entre deux vertice des pixels de la façon suivante: affichage de la couleur du premier vertex puis, pour chaque pixel entre les vertices, la couleur va varier de la couleur du premier vertex vers la couleur du second vertex. Il en sera de même pour tous les pixels d'une surface...

Pour y parvenir, la couleur sera donc passée au vertex shader via une variable de type attribut, puisque c'est lui qui traite les vertices. Ce sera ensuite à lui d'indiquer au fragment shader cette couleur, mais en passant par une variable de type "variable" (à lire dans la langue de Shakespeare). C'est en effet dans ce type de variable que la couleur pourra évoluer de pixel en pixel entre deux vertice correspondant.

Exemple de deux triangles ayant une couleur définie au niveau des vertice:

Image non trouvée !

Le vertex shader:

Et le fragment shader:

Reste à passer ces informations au vertex shader dans la classe Triangle. Deux solutions existent:

 

Utilisation de 2 buffers

Le principe est simple, réserver 2 buffers pour transmettre les données à OpenGL. Une pour les vertice, la seconde pour les couleurs.

private FloatBuffer colorBuffer;

private static final int NB_COORDS_PAR_COULEUR = 4; // 4 couleurs par vertex: r,v,b et alpha

private float triangleColors[] = {
0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f
};

Ensuite un ajout dans le constructeur la réservation de la mémoire utilisée entre DALVIK et le système natif après celle utilisée pour les coordonnées des vertice:

// réutilisation de l'instance byte buffer pour les couleurs
bb = ByteBuffer.allocateDirect(triangleColors.length * BYTES_PER_FLOAT);

// Utiliser l'ordre correspondant à la plateforme
bb.order(ByteOrder.nativeOrder());

// creer un buffer de type flottant depuis le ByteBuffer
colorBuffer = bb.asFloatBuffer();

// Et transmission des couleurs
colorBuffer.put(triangleColors);
// enfin initialiser le pointeur à 0
colorBuffer.position(0);

Lors de l'appel à la méthode draw:

GLES20.glBindAttribLocation(mProgram, 1, "aColor");

indexAColor = GLES20.glGetAttribLocation(mProgram, "aColor");

GLES20.glEnableVertexAttribArray(indexAColor);

GLES20.glVertexAttribPointer(indexAColor, // Index de l'attribut
NB_COORDS_PAR_COULEUR, // 4 informations par couleur (correspondant à r, v, b et alpha)
GLES20.GL_FLOAT, // De type float
false,
NB_COORDS_PAR_COULEUR * BYTES_PER_FLOAT, // La taille occupée en byte par un vertex est donc 4 * taille d'un float
colorBuffer); // Et un pointeur sur le buffer

pour enfin dessiner l'objet

// Ok, on peut dessiner maintenant !
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, NB_VERTEX);

et

// Disable des attributs du vertex array
GLES20.glDisableVertexAttribArray(indexVPosition);
GLES20.glDisableVertexAttribArray(indexAColor);

 

Utilisation d'un seul buffer

Bonne nouvelle, les shaders ne changent pas. Nous avons donc toujours les deux attributs comme point d'entrée pour la position et la couleur dans le vertex shader...

Par contre, il faudra être bien ordonnées dans ce que vous faîtes. Dans ce buffer sera transmis à la fois les vertice et les couleurs.

float triangle[] = {
0.0f, 1.5f, 1.0f, 3.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -0.5f, 1.0f, 1.0f, 1.0f,1.0f,1.0f, 1.0f,
1.0f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f
};

Déjà le tableau est plus difficile à lire non ?

Les 4 premières informations sont les coordonnées homogènes d'un vertex, puis les 4 suivantes sont les composantes rouge, vert, bleu et alpha de la couleur du vertex...

La définition du buffer reste classique:

// Création d'un byte buffer pour les vertice
ByteBuffer bb = ByteBuffer.allocateDirect(triangle.length * BYTES_PER_FLOAT);

// Utiliser l'ordre correspondant à la plateforme
bb.order(ByteOrder.nativeOrder());

// creer un buffer de type flottant depuis le ByteBuffer
vertexBuffer = bb.asFloatBuffer();

// Et transmission des coordonnées
vertexBuffer.put(triangle);
// enfin initialiser le pointeur à 0
vertexBuffer.position(0);

 

C'est dans la phase affichage qu'il faudra faire attention:

Un tableau, mais deux pointeurs...

// Création d'un attribut aPosition à l'index 0
GLES20.glBindAttribLocation(mProgram, 0, "aPosition");
GLES20.glBindAttribLocation(mProgram, 1, "aColor");

// Récupération de l'index, c'est juste pour l'exemple, puisqu'ici, il est déjà connu !
indexVPosition = GLES20.glGetAttribLocation(mProgram, "aPosition");
indexAColor = GLES20.glGetAttribLocation(mProgram, "aColor");

// Indiquer que l'attribut va pointer sur un buffer
GLES20.glEnableVertexAttribArray(indexVPosition);

GLES20.glEnableVertexAttribArray(indexAColor);

 

C'est là que se trouve le plus important:

// Dans le tableau, les coordonnées commencent à la position 0

vertexBuffer.position (0);
GLES20.glVertexAttribPointer(indexVPosition, // Index de l'attribut

// Pas de changement ici
NB_COORDS_PAR_VERTEX, // 4 informations par vertex (correspondant à x, y, z et w)
GLES20.GL_FLOAT, // De type float
false,

// Par contre, le pas lui change, il faut en effet "sauter les couleurs", car on ne traite que les positions ici. Le pas devra donc comprendre la taille occupé par les positions + celle des couleurs,

// Mais il s'agit d'un pas dont l'unité et l'octet: il faut donc multiplier le résultat précédent par la taille d'un float en octet
(NB_COORDS_PAR_VERTEX + NB_COORDS_PAR_COULEUR) * BYTES_PER_FLOAT, // La taille occupée en byte par un vertex est donc 3 * taille d'un float
vertexBuffer); // Et un pointeur sur le buffer

// Pour les couleurs, la position n'est pas en 0, mais commence à 4, les 4 premières informations sont toujours les coordonnées ! C'est extrémement important, sinon, vous obtiendrez n'importe quoi comme rendu !

vertexBuffer.position (NB_COORDS_PAR_VERTEX);

GLES20.glVertexAttribPointer(indexAColor, // Index de l'attribut

// Rien de particulier ici

NB_COORDS_PAR_COULEUR, // 4 informations par couleur (correspondant à r, v, b et alpha)
GLES20.GL_FLOAT, // De type float
false,

// Là aussi, calculé le pas en octet en n'oubliant pas qu'il faut se d"placer de la taille qu'occupe les coordonnées et la couleur
(NB_COORDS_PAR_VERTEX + NB_COORDS_PAR_COULEUR) * BYTES_PER_FLOAT, // La taille occupée en byte par un vertex est donc 4 * taille d'un float
vertexBuffer); // Et un pointeur sur le buffer

Restera à dessiner le triangle

GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, NB_VERTEX);

et

// Disable des attributs du vertex array
GLES20.glDisableVertexAttribArray(indexVPosition);
GLES20.glDisableVertexAttribArray(indexAColor);

Annexes

Une classe intéressante: Color sous Android (Déjà abordé dans Dessiner sous Android dans le développement sous Android).