L’objectif de ce chapitre est la réalisation d’un premier exemple mais visuellement intéressant.
Comme toute application Android, il est nécessaire de définir une activity et d’y inclure la méthode onCreate pour y définir les layouts et les vues. Dans le cas présent, on s’intéresse à la partie OpenGL et l’on n’utilisera pas de layout. Il suffit donc d’écrire dans notre classe dérivée d’activity:
//! OpenGL SurfaceView
public GLSurfaceView mGLSurfaceView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!isOGLES20Compatible()) {
// C++ Reflex sorry
mGLSurfaceView = null;
showOGLES20ErrorDialogBox();
return;
}
// We don't use Layout. But you can.
// create an OpenGLView
mGLSurfaceView = new GLSurfaceView(this);
mGLSurfaceView.setEGLContextClientVersion(2);
mGLSurfaceView.setRenderer(new GLES20Renderer(this));
setContentView(mGLSurfaceView);
}
Description détaillée:
La nomemclature [internal] indique cette fonction n’est pas fournie telle quelle par Android. Le code est dans le fichier source.
Comme toute activité, il faut définir également définir ce que l’on fait en cas de Pause et de reprise. Il faut donc définir le code des méthodes onResume et onPause. La classe GLSurfaceView contient les méthodes onResume et onPause qui suffisent dans un premier temps. On se contente donc de les appeler.
Enfin, après avoir défini l’Activity, il faut indiquer le code du renderer. Le renderer est une instance de la classe GLES20Renderer qui est exécuté par un thread définit par la GLSurfaceView. Il faut définir trois méthodes:
Le code:
public class GLES20Renderer implements GLSurfaceView.Renderer {
private Activity mActivity;
GLES20Renderer(Activity activity) {
mActivity = activity;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
gl.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
}
}
On découvre nos premières commandes OGLES 2.x:
Remarque: un benchmark qui fonctionne à 56fps sur un Samsung GS est le témoin que le GPU peut faire mieux! Bref ne pas tirer de conclusions hâtives quand on voit des benchmarks au dessus de 50fps pour comparer les devices.
Code de la première partie: Episode 1 part 1
Pour dessiner en OpenGL, on dispose de très peu d’outils:
Il existe des versions optimisées FAN et STRIP pour les lignes et les triangles mais nous étudierons cela en détail prochainement.
La définition d’un pointsprite, d’une ligne ou d’un triangle est réalisée au travers de sommets (vertex, vertices au pluriel). Chaque sommet dispose de propriétés que vous définissez vous même. On trouve généralement:
Généralement, pour éviter un nombre trop important de transferts, on regroupe les vertices dans un tableau. Et dans un second, on place les indices définissant les formes. Bien évidemment, un point est constitué d’un indice, une ligne deux, le triangle trois.
Ainsi, dans notre exemple, on définit une classe Vertices, qui contient un tableau de Vertices et un tableau d’indices, et la classe P3FT2FR4FVertex qui permet de définir un Vertex avec un attribut position (3 float), une texture (2 float) et une couleur (4 float).
Comme il a été indiqué, OGLES 2.x utilise un pipeline programmable. Il est donc nécessaire de fournir le programme chargé du dessin.
Nous ne détaillerons pas toutes les étapes nécessaires à l’utilisation du programme. En quelques lignes:
Dans l’API OGLES 2.x, il existe deux types de shaders:
Comme il a été vu dans la section Dessiner en OpenGL, quand on dessine un triangle, on fournit trois Vertices. Chaque Vertex de ce triangle est alors soumis à une fonction “le vertex shader”.
Cette fonction dans notre exemple réalise la transformation du point en 3D en 2D.
Notre code de vertex shader:
uniform mat4 uMvp;
attribute vec3 aPosition;
attribute vec2 aTexCoord;
attribute vec4 aColor;
varying vec4 vColor;
void main() {
vec4 position = uMvp * vec4(aPosition.xyz, 1.);
vColor = aColor;
gl_PointSize = 1.;
gl_Position = position;
}
Si vous êtes curieux, vous pouvez tenter de changer la valeur de gl_pointsize.
Dans un chapitre futur, nous étudierons en détail le GLSL.
Lorsque l’on dessine un triangle en 3D, on obtient un triangle en 2D. Ce triangle est un ensemble de points à l’écran. Pour chaque de ses points, OGLES 2.x exécute une fonction pour définir la couleur de ce point, le fragment shader.
Notre code de fragment shader:
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D tex0;
varying vec4 vColor;
void main()
{
gl_FragColor = vColor;
}
La gestion du programme OGLES 2.x est confiée à la classe GLSLProgram. Elle contient les méthodes suivantes:
Afin de faire fonctionner le tout, il suffit d’assembler le puzzle:
Pour rendre le programme plus intéressant, j’ai ajouté une rotation dans le GLSLProgramme:
counter += 1.f;
Matrix.setRotateEulerM(mRotation,0, 0.f, 0.f, counter);
Matrix.multiplyMM(mMvp, 0, mProjection, 0, mRotation, 0);
Performance: Sur mon nexus One, je suis à 40fps pour 30 000 points.
Code de la seconde partie: Episode 1 part 2
J’utilise ant pour compiler les programmes sous Android. Si vous utilisez Eclipse, il suffit de créer un nouveau projet Android à partir de code source existant. Enfin, il faut définir une configuration pour pouvoir le lancer et voilà c’est tout.