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转换