Gérer les différents modes écran
Mode portrait et paysage (ou landscape)
Sur Android, les portables peuvent être tenus en mode portrait ou paysage.
Cependant, le rendu est des plus effrayant !
En portrait:
Et en paysage:
Bref pas le même rendu, et c'est normal, la résolution de l'écran n'est pas la même dans un sens ou dans l'autre. Donc les proportions ne sont pas gardées:
de [-1 à 1] sur les axes x et y mais des résolutions autres sur l'écran physique (Ex. 1280x720 puis 720x1080)
La solution consiste à calculer un ratio qui sera appliqué ensuite lors du rendu de l'image.
Matrice de projection, caméra et matrice view
Dans OpenGL, il existe une matrice nommée matrice de projection. Elle permet en fait de gérer ce que vous pouvez voir à travers une caméra. Mais cette caméra pourra être positionnée n'importe où dans l'espace et regarder ce qu'elle veut, c'est la matrice view.
Matrice de projection
- Projection en perspective
La matrice de projection sera initialisée lors de changement de dimension de la surface. Ceci afin de faire en sorte que les proportions de votre objet soit toujours les mêmes.
Ce ratio sera donc calculé dans votre classe GLSurfaceView.Renderer dans la méthode onSurfaceChanged qui reçoit la hauteur et la largeur de l'écran pour initialiser une matrice de projection.
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);Suivant l'orientation de l'appareil, calcul du ratio
float ratio = (float) width > height ?
(float) width / (float) height : // Landscape
(float) height / (float) width; // PortraitIl reste à définir une matrice de projection qui sera appliquée ensuite à la vue caméra
private final float[] mProjectionMatrix = new float[16];
Il s'agit ici de Matrix provenant de android.opengl. Ne pas prendre la classe Matrix de android.graphics !
import android.opengl.Matrix;
Il reste à ajuster la matrice de projection selon vos besoins, en terme de plan:
void android.opengl.Matrix.frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far)
qui attend:
m : Un tableau de 16 float qui recevra le résultat de la matrice de projection
offset : offset de départ dans le tableau: à 0 ici !
left : limite à gauche
right : limite à droite
bottom : limite en bas
top : limite en haut
near : limite proche en profondeur (> 0)
far : limite au loin.if (width > height)
{
// Landscape, appliquer le ratio sur x
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 1, 7);}
else
{
// Portrait, appliquer le ratio sur y
Matrix.frustumM(mProjectionMatrix, 0, -1, 1, -ratio, ratio, 1, 7);
}
frustnum a été buggé (correction fin juillet 2012, cette méthode multipliait les x par 2, pas y, d'où des affichages pouvant être bizarres (http://code.google.com/p/android/issues/detail?id=35646) sur de vieux appareils.
Vous pouvez aussi utiliser public static void perspectiveM (float[] m, int offset, float fovy, float aspect, float zNear, float zFar) à la place, mais cette méthode n'est disponible qu'à partir de l'API 14.
- Projection orthogonale
Une autre solution serait de passer par une matrice de projection orthogonale:
static void orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)
Remplacez les frustumM par orthoM de la façon suivante:
Matrix.orthoM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, -1, 1);
et
Matrix.orthoM(mProjectionMatrix, 0, -1, 1, -ratio, ratio, -1, 1);
Vous pouvez aussi inverser l'axe z s'il ne vous convenez pas:Matrix.orthoM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 1, -1);
Cf. annexe pour connaitre la différence entre une projection orthogonale et perspective
Caméra et matrice view
Reste à positionner cette caméra dont je parlais précédemment:
void android.opengl.Matrix.setLookAtM(float[] rm, int rmOffset, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)
Avec:
Parameters:
rm returns the result
rmOffset index into rm where the result matrix starts
eyeX
eyeY
eyeZ
centerX
centerY
centerZ
upX
upY
upZ
Les trois premières valeurs sont les coordonnées du point de vue (où se trouve la caméra), les trois suivantes, l'endroit où on regarde avec la caméra. Enfin les trois dernières correspondent à la verticale de la caméra (où encore comment vous tenez votre caméra dans les mains: 0, 1, 0 correspondant à une caméra tenu normalement, mais rien ne vous empêche de tenir votre caméra couchée, la partie basse vers le haut, voire l'objectif vers vous, mais dans ce cas, vous n'allez pas voir grand chose !).
private final float[] mViewMatrix = new float[16];
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -1, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Qui permet d'obtenir une matrice View
Appliquer les tranformations des deux matrices sur les coordonnées des vertice
Reste à calculer la matrice résultante de mProjectionMatrix x mViewMatrix et à l'appliquer sur les coordonnées des vertice afin de positionner correctement les objets à l'affichage:
private final float[] mPVMatrix = new float[16];
Matrix.multiplyMM(mPVMatrix, 0, mProjectionMatrix,
0, mViewMatrix, 0);
Conservez la bien, car maintenant il faut appliquer ce résultat à
tous vos vertice !
Cela se passe donc au niveau du vertex shader...
gl_position contenant la position d'un vertex en cours, il suffit de le multiplier avec la matrice uPVMatrix (de type uniform, car cette variable ne va pas beaucoup évoluer dans le temps en principe !):
private final String vertexShaderCode =
"uniform mat4 uPVMatrix;" +
"attribute vec4 aPosition;\n" +
"void main() {" +
" gl_Position = uPVMatrix * aPosition;" +
"} ";
De plus, lors de la création du renderer, dans la méthode nSurfaceCreated après génération du shader, recherche de la variable uPVMatrix...
Exemple
D'où les modifications dans la classe GLES20Renderer vu dans Dessiner des objets géométriques.
Dans la classe triangle (vu dans Dessiner des objets géométriques), il faudra passer cette matrice résultante de la multiplication des matrices Projection et View.
Annexe
Différence entre une projection orthogonale et perspective:
La projection orthogonale est une représentation d'une vue 3d dans un cube.
La projection perspective est une représentation d'une vue dans une pyramide dont le sommet est tronqué, c'est depuis ce sommet que vous regardez.