31 votes

Le didacticiel Android OpenGL de Google enseigne-t-il une algèbre linéaire incorrecte?

Après avoir aider un autre utilisateur avec une question concernant la Réponse aux Événements Tactiles Android tutoriel, j'ai téléchargé le code source, et a été assez dérouté par ce que j'ai vu. Le tutoriel semble pas être en mesure de décider s'il souhaite utiliser la ligne de vecteurs ou de vecteurs colonnes, et il semble tout mélangé jusqu'à moi.

Sur l'Android page de la Matrice, ils prétendent que leur convention de la colonne vecteur/de la colonne principale, ce qui est typique de l'OpenGL.

Ai-je raison, ou est-il quelque chose que je suis absent? Voici les bouts de:

Commençons par créer un MVPMatrix en multipliant mProjMatrix * mVMatrix. So far So good.

    // Set the camera position (View matrix)
    Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

    // Calculate the projection and view transformation
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0)

Ensuite, ils sont en ajoutant une rotation vers la gauche de la MVPMatrix? Cela semble un peu bizarre.

    // Create a rotation for the triangle
    Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);

    // Combine the rotation matrix with the projection and camera view
    Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0)

Le téléchargement non transposé l'ordre.

    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

Enfin dans leur shader, un vecteur*multiplication de matrice?

    // the matrix must be included as a modifier of gl_Position
    "  gl_Position = vPosition * uMVPMatrix;" 

Ajouter tout cela ensemble, nous obtenons:

gl_Position = vPosition * mRotation * mProjection * mView;

Qui n'est pas correcte par un tronçon de mon imagination. Est il une explication que je ne suis pas voyant que de ce qui se passe ici?

26voto

Joe Fernandez Points 2058

Comme le gars qui a écrit que OpenGL tutoriel, je peux confirmer que l'exemple de code est incorrect. Plus précisément, l'ordre des facteurs dans le code du shader doit être inversée:

"  gl_Position = uMVPMatrix * vPosition;"

Quant à l'application de la matrice de rotation, l'ordre des facteurs doivent également être inversées, de sorte que la rotation est le dernier facteur. La règle de base est que les matrices sont appliquées dans de droite à gauche, et la rotation est appliquée en premier (c'est le "M" de "MVP"), de sorte qu'il doit être le plus à droite opérande. En outre, vous devez utiliser un scratch de la matrice pour ce calcul, tel que recommandé par Ian Ni-Lewis (voir sa réponse plus complète, ci-dessous):

float[] scratch = new float[16];
// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);

Merci d'appeler l'attention sur ce problème. Je vais chercher les cours de formation et des exemples de code fixe dès que je peux.

Edit: Ce problème a été corrigé dans la version téléchargeable des exemples de code et l'OpenGL ES de cours de formation, y compris des commentaires sur le bon ordre des facteurs. Merci pour les commentaires, les gens!

10voto

Ian Ni-Lewis Points 1219

Le tutoriel est incorrect, mais beaucoup d'erreurs s'annulent ou ne sont pas évidentes dans ce contexte très limité (caméra fixe centré en (0,0), la rotation autour de Z uniquement). La rotation est à l'envers, mais sinon, il sorte de ressemble à droite. (Pour voir pourquoi c'est mal, essayez de moins trivial appareil: définissez les yeux et de regarder pour y=1, par exemple).

L'une des choses qui fait cela très difficile à déboguer est que la Matrice méthodes ne fais pas n'importe quel alias de détection sur leurs intrants. Le tutoriel code permet de croire que vous pouvez appeler de la Matrice.multiplyMM avec la même matrice utilisée comme une entrée et le résultat. Ce n'est pas vrai. Mais parce que la mise en œuvre multiplie une colonne à la fois, c'est beaucoup moins évident que quelque chose est incorrect si le côté droit est réutilisé (comme dans le code actuel, où mMVPMatrix est l'ers et le résultat) que si la gauche est réutilisé. Chaque colonne sur la gauche est à lire avant de la colonne correspondante dans le résultat est écrit, afin que la sortie sera correct même si la LHS est écrasé. Mais si le côté droit est le même que le résultat, puis sa première colonne sera remplacé avant qu'il est fini d'être lu.

Donc le tutoriel de code est une sorte de maximum local: il semble que cela fonctionne, et si vous modifiez une chose, il se brise de façon spectaculaire. Ce qui nous amène à croire que le mal, comme il semble, il est peut-être juste correct. ;-)

De toute façon, voici un code de remplacement qui obtient ce que je pense est le résultat prévu.

Le code Java:

@Override
public void onDrawFrame(GL10 unused) {
    float[] scratch = new float[16];

    // Draw background color
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    // Set the camera position (View matrix)
    Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

    // Calculate the projection and view transformation
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);

    // Draw square
    mSquare.draw(mMVPMatrix);

    // Create a rotation for the triangle
    Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, 1.0f);

    // Combine the rotation matrix with the projection and camera view
    Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);

    // Draw triangle
    mTriangle.draw(scratch);
}

Code du Shader:

gl_Position =  uMVPMatrix * vPosition;

NB: ces correctifs faire la projection de corriger, mais ils ont également inverser le sens de rotation. C'est parce que le code original a appliqué des transformations dans le mauvais ordre. Pensez-y de cette façon: au lieu de la rotation de l'objet dans le sens horaire, c'était de la rotation de la caméra dans le sens antihoraire. Lorsque vous corrigez l'ordre des opérations, de sorte que la rotation est appliquée à l'objet à la place de la caméra, l'objet commence à aller dans le sens antihoraire. Ce n'est pas la matrice, c'est faux; c'est l'angle qui a été utilisé pour créer la matrice.

Donc, pour obtenir la "bonne" raison, vous avez également besoin pour inverser le signe de la mutilation.

2voto

talukm Points 21

J'ai résolu ce problème comme suit:

 @Override
public void onDrawFrame(GL10 unused) {      
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -1f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);        

    Matrix.setRotateM(mModelMatrix, 0, mAngle, 0, 0, 1.0f);
    Matrix.translateM(mModelMatrix, 0, 0.4f, 0.0f, 0);

    mSquare.draw(mProjMatrix,mViewMatrix,mModelMatrix);
}

@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
    ...  
    Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 99);

}

class Square {

    private final String vertexShaderCode =
        "uniform mat4 uPMatrix; \n" +
        "uniform mat4 uVMatrix; \n" +
        "uniform mat4 uMMatrix; \n" +

        "attribute vec4 vPosition; \n" +
        "void main() { \n" +
        "  gl_Position = uPMatrix * uVMatrix * uMMatrix * vPosition; \n" +
        "} \n";

    ...

    public void draw(float[] mpMatrix,float[] mvMatrix,float[]mmMatrix) {

        ...

        mPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uPMatrix");
        mVMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uVMatrix");
        mMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix");

        GLES20.glUniformMatrix4fv(mPMatrixHandle, 1, false, mpMatrix, 0);
        GLES20.glUniformMatrix4fv(mVMatrixHandle, 1, false, mvMatrix, 0);
        GLES20.glUniformMatrix4fv(mMMatrixHandle, 1, false, mmMatrix, 0);

        ...
    }
}
 

1voto

user1716083 Points 19

Je suis en train de travailler sur le même sujet, et c'est ce que j'ai trouvé:

Je crois que Joe sample est CORRECT,
y compris l'ordre des facteurs dans le code du shader:

gl_Position = vPosition * uMVPMatrix;

Pour le vérifier, il essaie juste de faire pivoter le triangle inversé les facteurs de l'ordre, il permet d'étirer le triangle de point de fuite à 90 degrés.

Le vrai problème semble être dans setLookAtM fonction.
Dans Joe paramètres de l'échantillon sont les suivants:

Matrix.setLookAtM(mVMatrix, 0,
     0f, 0f,-3f,   0f, 0f, 0f,   0f, 1f, 0f );

ce qui est parfaitement logique ainsi.
Cependant, le résultat de l'affichage de la matrice semble bizarre pour moi:

-1  0  0  0
 0  1  0  0
 0  0 -1  0
 0  0 -3  1

Comme nous pouvons le voir, cette matrice inverse coordonnée X, depuis le premier membre est -1,
ce qui conduira à gauche/droite flip sur l'écran.
Il sera également inverser l'ordre-Z, mais concentrons-nous sur la coordonnée X ici.

Je pense que setLookAtM fonction est aussi de travailler correctement.
Cependant, depuis la Matrice de classe n'est PAS une partie de l'OpenGL, il peut utiliser un autre système de coordonnées,
par exemple régulièrement des coordonnées de l'écran avec l'axe Y vers le bas.
C'est juste une supposition, je n'ai pas vraiment vérifier que.

Solutions possibles:
Nous pouvons construire souhaitable matrice de vue manuellement,
le code est:

Matrix.setIdentityM(mVMatrix,0);
mVMatrix[14] = -3f;

OU
nous pouvons essayer de tromper setLookAtM fonction en lui donnant inversé caméra coordonnées: 0, 0, +3 (au lieu de -3).

Matrix.setLookAtM(mVMatrix, 0,
     0f, 0f, 3f,   0f, 0f, 0f,   0f, 1f, 0f );

Le résultat de l'affichage de la matrice seront:

1  0  0  0
0  1  0  0
0  0  1  0
0  0 -3  1

C'est exactement ce dont nous avons besoin.
Maintenant, la caméra se comporte comme prévu,
et l'exemple fonctionne correctement.

0voto

user2934090 Points 36

Aucune autre suggestion n'a fonctionné pour moi en utilisant l'exemple de code Android mis à jour, à l'exception des suivantes lorsque vous essayez de déplacer le triangle.

Le lien suivant contient la réponse. A pris plus d'une journée pour le localiser. Publier ici pour aider les autres comme je l'ai vu à plusieurs reprises. Transformations de matrice Android OpenGL ES

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X