[opengl] triangle animé

Description

Ce code, prévu pour les Android dont l'api vaut au moins 3, permet d'afficher un triangle rouge sur fond blanc qui avance progressivement vers l'avant de l'écran, avant de disparaître.

Pour cela, on utilise l'api OpenGL ES 1.0 d'Android (la version 1.5 d'Android ne dispose pas d'Open GL ES 2.0 :=( ).

Je vous poste aussi les liens sur des tutos qui m'ont été très utiles (le livre rouge disponible sur le site de khronos aussi m'a semblé indispensable pour le débutant que je suis, ainsi que les spécifications OpenGL ES 1.0 => on y apprend les restrictions de la version ES 1.0)

http://www.khronos.org/opengles/sdk/1.1/docs/man/
http://obviam.net/index.php/opengl-es-android-displaying-graphical-elements-primitives/
http://blog.jayway.com/2009/12/03/opengl-es-tutorial-for-android-part-i/
http://www.droidnova.com/android-3d-game-tutorial-part-ii,328.html

L'EDI utilisé est Eclipse Helios.

Source / Exemple :


import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLU;
import android.opengl.GLSurfaceView.Renderer;

// Classe qui définit et rend la scène 3D
public class OpenGLRenderer_TriangleAvancant implements Renderer {
	
	// Lorsque la scène doit être rendue
	/* 
		Appelée en permanence
		Tient alors compte des transformations précédentes
		de la scène (translate, rotate, scale, color, ...)

  • /
@Override public void onDrawFrame(GL10 gl) { clearGlScreen(gl); // Effacer la zone : cf plus bas moveCamera_ThatIs_MoveObject(gl); // Deplacer la caméra : cf plus bas drawTriangle(gl); // Seulement là, dessiner le triangle : cf plus bas } //Effacer la zone en blanc private void clearGlScreen(GL10 gl) { /* Le mode modèle-vue est le mode qui sera utilisé pour le
  • rendu des objets. Donc ne pas oublier de revenir dans ce mode
  • pour que les prochaines fonctions puissent directement rendre la scène
  • /
gl.glMatrixMode(GL10.GL_MODELVIEW); // Rappel : réinitialiser l'état du mode courant (modèle-vue) gl.glLoadIdentity(); // Réinitialise la caméra /*
  • Ici on positionne la camera (soit en x=0, y=0 et z=6),
  • on définit sa visée (x=0, y=0, z=0) et on définit ce qu'est
  • pour elle le sens de dessus (x=0, y=1, z=0)
  • Attention, cette commande en elle-même ne suffit pas
  • à garantir une position constante de la caméra !!!
  • Appeller glLoadIdentity() auparavant=> là on sera sûr de repartir de zéro
  • à chaque fois. (Pourquoi ? ces deux fonctions sont des opérations sur la matrice
  • appellée modèle-vue : si l'opération de "nettoyage de matrice" manque, les effets
  • des différents gluLookAt() se cumulent !!! (Avec les erreurs dues à un trop grand nombre
  • de cumuls que cela peut entrainer !!!)
  • /
GLU.gluLookAt(gl, 0, 0, 6, 0, 0, 0, 0, 1, 0); // intègre des appels à glTranslate et glRotate !!! // Couleur effacement : en (rouge, vert, bleu, alpha) : 1.0 = intensité maximale gl.glClearColor(FULL_INTENSITY, FULL_INTENSITY, FULL_INTENSITY, EMPTY_INTENSITY); // Effacer le tampon de couleur (ie les données du dessin) et le tampon de profondeur // (pour gérer les recouvrements d'objets dus à l'axe z) gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); } // Deplacer la caméra private void moveCamera_ThatIs_MoveObject(GL10 gl) { /* Effectue une translation (x, y, z)
  • (depuis l'origine grâce à glLoadIdentity() de la ligne précédente
  • /
gl.glTranslatex(ORIGIN, ORIGIN, currentZTranslationValue); currentZTranslationValue+=1000; } /*
  • La grande différence avec OpenGL classique (ici on est en ES) !!!
  • Il faut utiliser un tampon regroupant tous les points de la figure à dessiner
  • Ce tampon sera ensuite utiliser par la méthode gl.glVertexPointer pour faire OpenGL
  • pointer sur notre structure "en points", le moment adéquat. Un appel à gl.DrawArrays permettra alors
  • de "flusher" le dessin de la figure (le valider).
  • /
private void buildVerticesBuffer() { // On alloue la taille de l'ensemble des points en nombre d'octets ByteBuffer baseBuffer = ByteBuffer.allocateDirect(triangleVertices.length * SHORTS_SIZE_BYTES); // Important !!! L'ordre du tampon doit être natif baseBuffer.order(ByteOrder.nativeOrder()); // Récupération de la version short du tampon (on aurait pu utiliser des Float :) ) verticesBuffer = baseBuffer.asShortBuffer(); //Stockage des valeurs à partir du tableau de short verticesBuffer.put(triangleVertices); // On dit que la 1ere valeur du tampon est la 1ere valeur utilisable (indice 0, logique :) ) verticesBuffer.position(START_AT_FIRST_POSITION); } private void drawTriangle(GL10 gl) { // Autoriser OpenGL à utiliser les tableaux de vertex pour dessiner gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); /*
  • Ne pas confondre avec glClearColor !!!
  • Ici on définit la couleur de dessin et non d'effacement
  • Couleur : (rouge, vert, bleu, alpha)
  • /
gl.glColor4f(FULL_INTENSITY, EMPTY_INTENSITY, EMPTY_INTENSITY, HALF_INTENSITY); /*
  • OpenGL devra maintenant chercher les données de notre tampon définissant la figure,
  • pour son prochain dessin
  • TRIANGLE_COORDS_PER_VERTEX : nombte de coordonnées par vertex (3 ont été choisis pour notre triangle)
  • GL10.GL_SHORT : on a codé les coordonnées à l'aide de short
  • START_AT_FIRST_POSITION : index 0 comme valeur de départ dans le tampon ( logique :) )
  • /
gl.glVertexPointer(TRIANGLE_COORDS_PER_VERTEX, GL10.GL_SHORT, START_AT_FIRST_POSITION, verticesBuffer); /*
  • OpenGL dessine une figure à partir des données chargées précédemment
  • GL10.GL_TRIANGLE_STRIP : dessin de triangles concaténés (ici, il y en a qu'un car on
  • n'a défini que 3 points, mais avec 4, on aurait défini un carré => doc OpenGL officielle
  • pour plus de précision :) )
  • START_AT_FIRST_POSITION : index de départ (0 ici, logique :) )
  • VERTICES_NUMBER : nombre de vertices à utiliser
  • /
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, START_AT_FIRST_POSITION, VERTICES_NUMBER); // Interdire OpenGL à utiliser les tableaux de vertex pour dessiner // ( Performances++ :) ) gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); } // Lorsque la surface doit être repainte // Seulement susceptible d'être appelée au besoin @Override public void onSurfaceChanged(GL10 gl, int width, int height) { // Définit la zone de la SurfaceView, qui sera utilisée pour le rendu // (ici, la zone entière) gl.glViewport(0, 0, width, height); /*
  • On bascule en mode projection :
  • les prochaines transformations affecteront, non pas les objets
  • ou la caméra, mais le mode de vue de la scène (je pense avoir compris le
  • but du mode projection, mais sans en être sûr => à vérifier tout de même :) )
  • /
gl.glMatrixMode(GL10.GL_PROJECTION); // Efface les dernières transformations gl.glLoadIdentity(); float aspectRatio = (float) width / (float) height; /*
  • Définit le cône qui servira à donner l'impression de perspective
  • Il englobe les plans avant et arrière que l'on défini, le plan arrière
  • étant plus grand que le plan avant (Cela vous permettra de déterminer/définir
  • le sens du cône) (Ne pas hésiter à consulter le livre rouge
  • officiel d'OpenGL, cela y sera mieux expliqué)
  • 60.0f => Moitié de l'angle du cône (à partir de l'axe z)
  • Une valeur de 60/70 garantie un champ de vision humaine.
  • aspectRatio => Rapport largeur/hauteur du cône
  • 0.1f et 10000000 => 0.1f : plan avant et 100.0 (car dernier paramètre) : plan arrière
  • /
/*
  • Attention !!! Dans ce cas, seuls les objets ayant z compris entre -0.1f et -10000000
  • seront rendus. (Il faut prendre l'opposé des valeurs)
  • /
/*
  • Si l'on souhaite une cône plus précis (notamment sur un autre axe que l'axe z)
  • on peut toujours utiliser le moins recommandé gl.glFrustum
  • (Vous me connaissez :) Doc officielle :) )
  • /
GLU.gluPerspective(gl,60.0f, aspectRatio, 0.1f, 10000000); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { /* Les collections gérant les définitions des points n'auront pas à être recrées
  • /
buildVerticesBuffer(); } private int currentZTranslationValue = 0; // Stockage des définitions des vertices private ShortBuffer verticesBuffer; // Position des vertices dans le système /* Ici z vaut -1 pour être en adéquation avec la perspective d'observation, définie par l'appel gl.gluPerspective
  • /
private short triangleVertices [] = { 0, 1, 0,// point d'index 0 (x,y,z) 1, -1, 0, // point d'index 1 (x,y,z) -1, -1, 0,// point d'index 2 (x,y,z) }; // Taille des shorts en octets private final static int SHORTS_SIZE_BYTES = 2; // L'origine en terme de position private final short ORIGIN = 0; // Intensité des couleurs private final float FULL_INTENSITY = 1.0f; private final float HALF_INTENSITY = 0.5f; private final float EMPTY_INTENSITY = 0.0f; // Index de depart des "collections" private final int START_AT_FIRST_POSITION = 0; //Nombre de vertices à afficher private final int VERTICES_NUMBER = 3; // Nombre de coordonnées définies par vertex (1,2 ou 3) private final int TRIANGLE_COORDS_PER_VERTEX = triangleVertices.length / VERTICES_NUMBER; }

Conclusion :


N'hésitez pas à visiter les tutoriels dont je vous ai mis les liens !!!
Mon but est juste de proposer un exemple, pas d'en expliquer les moindres détails.

Enfin, dernière information pour la source présentée ci-dessus : il s'agit d'une instance de GLRenderer, à combiner avec une GLSurfaceView. Cette GLSurfaceView sera la vue à utiliser dans l'Activity. Ne pas oublier également d'adapter les méthodes onPause() et onResume() de l'Activity. En fait ces deux méthodes doivent appeler les méthodes respectives onPause() et onResume() de la classe héritée de GLSurfaceView.

Enfin, notez que l'ordre d’enchaînement des opérations pourrait être imparfait (ne connaissant pas encore bien OpenGL à la base et ses matrices) : je m'en suis aperçu avec des expérimentations ultérieures. Si tel est le cas, merci aux experts et initiés OpenGL de me le signaler. Des modifications pourraient alors être apportées.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.