Google的Android OpenGL教程是不正确的线性代数吗?

在帮助其他用户提出有关“ 响应触摸事件” Android教程的问题之后,我下载了源代码,对于我所看到的内容感到十分困惑。 该教程似乎无法决定是否要使用行向量或列向量,它看起来都混淆了我。

在Androidmatrix页面上,他们声称他们的惯例是列向量/列主要,这是OpenGL的典型。

我是对的,还是有我失踪的东西? 这里是它的相关位:

通过乘以mProjMatrix * mVMatrix创build一个MVPMatrix来开始。 到现在为止还挺好。

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

接下来,他们在MVPMatrix的左侧添加一个旋转? 这似乎有点奇怪。

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

以非转置顺序上传。

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

最后在他们的着色器中,一个向量*matrix乘法?

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

把这一切加在一起,我们得到:

 gl_Position = vPosition * mRotation * mProjection * mView; 

我想象中的任何一点都不正确。 有什么解释,我没有看到这是怎么回事?

作为编写OpenGL教程的人,我可以确认示例代码是不正确的。 具体来说,着色器代码中的因素顺序应该颠倒过来:

 " gl_Position = uMVPMatrix * vPosition;" 

对于旋转matrix的应用,也应该颠倒这些因素的顺序,使得旋转是最后一个因素。 经验法则是,matrix是按照从右到左的顺序来应用的,首先应用旋转(这是“MVP”的“M”部分),所以它需要是最右边的操作数。 此外,您应该使用一个临时matrix进行计算,正如Ian Ni-Lewis所推荐的(请参阅下面的更完整的答案):

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

感谢注意这个问题。 我会尽快修复培训课程和示例代码。

编辑:此问题现已在下载的示例代码和OpenGL ES培训课程中得到纠正,包括正确的因素顺序的评论。 感谢您的反馈,伙计!

教程是不正确的,但许多错误要么相互抵消,要么在这个非常有限的背景下不明显(固定相机居中在(0,0),只围绕Z旋转)。 旋转是向后的,否则它看起来是正确的。 (要明白为什么它是错误的,请尝试一个不太重要的相机:例如设置眼睛,然后看看y = 1。)

其中一个非常难以debugging的事情是Matrix方法不会对其input做任何别名检测。 教程代码使得您可以使用与input和结果相同的matrix调用Matrix.multiplyMM。 这是不正确的。 但是因为实现一次乘以一列,所以如果右边被重用(如在当前代码中,其中mMVPMatrix是rhs和结果),那么就不太明显,如果左边被重新使用。 在写入结果中的相应列之前读取左边的每列,所以即使LHS被覆盖,输出也是正确的。 但是,如果右侧与结果相同,那么在读取完第一列之前,它将被覆盖。

所以教程代码是在某种地方最大的:它看起来像是有效的,如果你改变任何一件事情,它会突破。 这导致人们认为错误,因为它看起来,这可能是正确的。 😉

无论如何,这是一些替代的代码,得到我认为是预期的结果。

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

着色器代码:

 gl_Position = uMVPMatrix * vPosition; 

注意:这些修正使投影正确,但它们也反转了旋转的方向。 这是因为原始代码以错误的顺序应用了转换。 这样想:不是顺时针旋转物体,而是逆时针旋转相机。 当您修复操作的顺序,以便将旋转应用于对象而不是相机时,对象将逆时针旋转。 这不是matrix是错的。 这是用来创buildmatrix的angular度。

所以要得到“正确”的结果,你还需要翻转mAngle的符号。

我解决了这个问题如下:

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

我正在研究同一个问题,这就是我发现的:

我相信Joe的样本是正确的,
包括着色器代码中的因素顺序:

 gl_Position = vPosition * uMVPMatrix; 

要validation它只是尝试旋转三angular形以反向因子顺序,它将拉伸三angular形消失点在90度。

真正的问题似乎在setLookAtM函数中。
Joe的示例参数是:

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

这也是合乎逻辑的。
但是,由此产生的视图matrix看起来很奇怪:

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

我们可以看到,这个matrix将倒置X坐标,因为第一个成员是-1,
这将导致屏幕上的左/右翻转。
它也会颠倒Z顺序,但是我们来关注X坐标。

我认为setLookAtM函数也正常工作。
但是,由于Matrix类不是OpenGL的一部分,因此可以使用其他坐标系,
例如 – Y轴指向下的常规屏幕坐标。
这只是一个猜测,我没有真正确认。

可能的解决scheme:
我们可以手动build立理想的视图matrix,
代码是:

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

要么
我们可以通过给它反转的摄像机坐标:0,0,+3(而不是-3)来欺骗setLookAtM函数。

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

由此产生的视图matrix将是:

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

这正是我们需要的。
现在相机的行为如预期,
和示例正常工作。

在尝试移动三angular形时,没有其他build议为我使用当前更新的Android示例代码,除了以下内容。

以下链接包含答案。 花了一天的时间来find它。 张贴在这里帮助别人,因为我多次看到这个post。 OpenGL ES Androidmatrix转换