2012-08-12 48 views
30

Después de ayudar a otro usuario con una pregunta sobre el tutorial de Android Responding to Touch Events, descargué el código fuente y quedé bastante desconcertado por lo que vi. El tutorial parece no ser capaz de decidir si quiere usar vectores de fila o columnas, y todo se ve confundido para mí.¿Es el tutorial Android OpenGL de Google que enseña el álgebra lineal incorrecta?

En la página de Android Matrix, afirman que su convención es column-vector/column-major, que es típica de OpenGL.

¿Tengo razón, o hay algo que me falta? Aquí están los bits relevantes:

Comience creando una MVPMatrix multiplicando mProjMatrix * mVMatrix. Hasta aquí todo bien.

// 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) 

siguiente están añadiendo un giro a la izquierda de la MVPMatrix? Esto parece un poco raro.

// 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) 

Carga con el fin de no transpuesta.

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

último en su shader, un vector * multiplicación de matrices?

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

La adición de esta todos juntos, obtenemos:

gl_Position = vPosition * mRotation * mProjection * mView; 

cual no es correcto por cualquier tramo de la imaginación. ¿Hay alguna explicación de que no estoy viendo qué está pasando aquí?

+2

Dos posibilidades para mí. O el ejemplo es incorrecto o implementaron las operaciones de la matriz de manera diferente. [ver] (http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/opengl/Matrix.java) –

+0

¿Podría aclarar el problema, por favor? – user1071136

Respuesta

25

Como el tipo que escribió ese tutorial de OpenGL, puedo confirmar que el código de ejemplo es incorrecto. Específicamente, el orden de los factores en el código de shader debería invertirse:

" gl_Position = uMVPMatrix * vPosition;" 

En cuanto a la aplicación de la matriz de rotación, el orden de los factores también deben ser invertido, de manera que la rotación es el último factor. La regla general es que las matrices se aplican en orden de derecha a izquierda, y la rotación se aplica primero (es la parte "M" de "MVP"), por lo que debe ser el operando más a la derecha. Por otra parte, se debe utilizar una matriz cero para este cálculo, según lo recomendado por Ian Ni-Lewis (véase su respuesta más completa, más adelante):

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

Gracias por llamar la atención sobre este problema. Obtendré la clase de entrenamiento y el código de muestra corregidos tan pronto como pueda.

Editar: Este problema ha sido corregido en el código de muestra descargable y en la clase de entrenamiento de OpenGL ES, incluidos los comentarios sobre el orden correcto de los factores. Gracias por los comentarios, amigos!

+2

Hay un problema adicional: Matrix.multiplyMM no comprueba el aliasing. Si reutiliza uno de los parámetros de entrada como parámetro de salida, se sobrescribirá. –

+0

Gracias. Estaba buscando esta respuesta. Y 'uniform mat4 uMVPMatrix' debe agregarse antes de' void main' o dará una pantalla en blanco con clearColor. –

+0

También encontré 'Matrix.multiplyMM (mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);' para dar la transformación de rotación correcta que la ordenada nuevamente. 'T (x) = A.x' y' multiplicalyMM (float [] result, int resultOffset, float [] lhs, int lhsOffset, float [] rhs, int rhsOffset) '. Solo arreglar el Vertex Shader funcionó para mí. –

10

El tutorial es incorrecto, pero muchos de los errores se cancelan mutuamente o no son obvios en este contexto limitado (cámara fija centrada en (0,0), rotación solo alrededor de Z). La rotación es hacia atrás, pero por lo demás se ve bien.(Para ver por qué está mal, pruebe con una cámara menos trivial: configure el ojo y mire Hasta y = 1, por ejemplo).

Una de las cosas que hizo que esto sea muy difícil de depurar es que los métodos Matrix no hacer cualquier detección de alias en sus entradas. El código del tutorial hace que parezca que puede llamar a Matrix.multiplyMM con la misma matriz utilizada como entrada y como resultado. Esto no es verdad Pero debido a que la implementación multiplica una columna a la vez, es mucho menos obvio que algo esté mal si se reutiliza el lado derecho (como en el código actual, donde mMVPMatrix es el rhs y el resultado) que si se reutiliza el lado izquierdo . Cada columna de la izquierda se lee antes de escribir la columna correspondiente en el resultado, por lo que la salida será correcta incluso si se sobrescribe el LHS. Pero si el lado derecho es el mismo que el resultado, entonces se sobrescribirá su primera columna antes de que termine de leerse.

Así que el código del tutorial está en una especie de máximo local: parece que funciona, y si cambias una cosa, se rompe espectacularmente. Lo que lleva a uno a creer que lo que está mal parece, podría ser correcto. ;-)

De todos modos, aquí hay un código de reemplazo que obtiene lo que creo que es el resultado esperado.

código 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); 
} 

código de Shader:

gl_Position = uMVPMatrix * vPosition; 

NB: estas soluciones hacen que la proyección correcta, sino que también invierten el sentido de giro. Esto se debe a que el código original aplicaba las transformaciones en el orden incorrecto. Piénselo de esta manera: en lugar de girar el objeto en el sentido de las agujas del reloj, estaba girando la cámara en sentido antihorario. Cuando arregla el orden de las operaciones para que la rotación se aplique al objeto en lugar de a la cámara, el objeto comienza a girar en sentido antihorario. No es la matriz lo que está mal; es el ángulo que se usó para crear la matriz.

Para obtener el resultado 'correcto', también necesita voltear el signo de mAngle.

1

estoy trabajando en el mismo tema y eso es lo que encontré:

Creo que la muestra de Joe es CORRECTO,
incluyendo el orden de los factores en el código de sombreado:

gl_Position = vPosition * uMVPMatrix; 

Para verificarlo, simplemente intente girar el triángulo con orden de factores invertidos, , estirará el triángulo hasta el punto de fuga a 90 grados.

El problema real parece estar en la función setLookAtM.
En parámetros de la muestra de Joe son:

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

que es perfectamente lógico también.
Sin embargo, la matriz resultante vista parece extraño para mí:

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

Como podemos ver, esta matriz se invertirá la coordenada X, desde el primer miembro es -1,
lo que conducirá a la izquierda/derecha voltear en la pantalla.
También invertirá el orden Z, pero vamos a centrarnos en la coordenada X aquí.

Creo que la función setLookAtM también funciona correctamente.
Sin embargo, dado que la clase Matrix NO es parte de OpenGL, puede usar algún otro sistema de coordenadas,
por ejemplo - coordenadas de pantalla regulares con el eje Y apuntando hacia abajo.
Esto es solo una suposición, realmente no lo verifiqué.

soluciones posibles:
podemos construir la matriz vista deseable manualmente,
el código es:

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

O
podemos tratar de engañar a setLookAtM función dándole coordina invertida de la cámara: 0, 0, +3 (en lugar de -3).

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

La matriz vista resultante será:

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

eso es exactamente lo que necesitamos.
Ahora la cámara se comporta como se esperaba,
y la muestra funciona correctamente.

+1

Tienes razón en que simplemente arreglar el orden de los operandos en el sombreador producirá resultados peores, no mejores. Pero eso se debe a que hay más de un problema con la muestra. –

+0

Debo mencionar que su solución fue, de hecho, lo primero que probé, porque estaba convencido de que estaba viendo un problema de uso de las manos. Pero a pesar de que se ve bien, no lo es; intente mover y girar la cámara ligeramente y verá rápidamente por qué. El código que publiqué a continuación es correcto. –

2

He resuelto este problema de la siguiente manera:

@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); 

     ... 
    } 
} 
0

No hay otras sugerencias trabajaron para mí utilizando el código de ejemplo Android actualizado corriente con las siguientes excepciones al intentar mover el triángulo.

El siguiente enlace contiene la respuesta. Tomó más de un día para localizarlo. Publicando aquí para ayudar a otros ya que he visto esta publicación muchas veces. OpenGL ES Android Matrix Transformations

Cuestiones relacionadas