是谷歌';教授错误线性代数的Android OpenGL教程?
在帮助另一个用户回答了一个关于Android教程的问题后,我下载了源代码,对我看到的内容感到非常困惑。教程似乎无法决定是要使用行向量还是列向量,在我看来,这一切都混淆了 在Android Matrix页面上,他们声称他们的约定是列向量/列主,这是OpenGL的典型 我是对的,还是我遗漏了什么?以下是它的相关部分: 首先,将mProjMatrix*mVMatrix相乘,创建一个MVP矩阵。到目前为止还不错是谷歌';教授错误线性代数的Android OpenGL教程?,android,opengl-es,linear-algebra,Android,Opengl Es,Linear Algebra,在帮助另一个用户回答了一个关于Android教程的问题后,我下载了源代码,对我看到的内容感到非常困惑。教程似乎无法决定是要使用行向量还是列向量,在我看来,这一切都混淆了 在Android Matrix页面上,他们声称他们的约定是列向量/列主,这是OpenGL的典型 我是对的,还是我遗漏了什么?以下是它的相关部分: 首先,将mProjMatrix*mVMatrix相乘,创建一个MVP矩阵。到目前为止还不错 // Set the camera position (View matrix)
// 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)
接下来,他们将在MVP矩阵的左侧附加旋转?这似乎有点奇怪
// 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);
最后在他们的着色器中,向量*矩阵乘法
// 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;"
至于旋转矩阵的应用,因子的顺序也应该颠倒,以便旋转是最后一个因子。经验法则是,矩阵按从右到左的顺序应用,并且首先应用旋转(它是“MVP”的“M”部分),因此它需要是最右边的操作数。此外,您应该按照Ian Ni Lewis的建议,使用划痕矩阵进行此计算(参见下面更完整的答案):
谢谢你提醒大家注意这个问题。我会尽快修复培训课程和示例代码
编辑:这个问题现在已经在可下载的示例代码和OpenGL ES培训课程中得到纠正,包括对因子正确顺序的注释。谢谢大家的反馈 本教程不正确,但许多错误要么相互抵消,要么在这个非常有限的上下文中不明显(固定摄影机以(0,0)为中心,仅围绕Z旋转)。旋转是向后的,但在其他方面看起来是正确的。(若要了解其错误原因,请尝试一种不那么简单的相机:例如,将“眼睛和注视”设置为y=1。) 其中一个让调试变得非常困难的原因是矩阵方法不会对其输入进行任何别名检测。教程代码使您看起来可以使用作为输入和结果的相同矩阵调用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;
gl_Position = vPosition * uMVPMatrix;
注意:这些固定会使投影正确,但也会反转旋转方向。这是因为原始代码以错误的顺序应用了转换。可以这样想:它不是顺时针旋转对象,而是逆时针旋转相机。固定操作顺序以便将旋转应用于对象而不是摄影机时,对象将开始逆时针旋转。不是矩阵错了;这是用来创建矩阵的角度
因此,要获得“正确”的结果,您还需要翻转mAngle的符号。我正在处理同一问题,这就是我发现的: 我相信乔的样本是正确的,
包括…在内 着色器代码中因子的顺序:
gl_Position = uMVPMatrix * vPosition;
gl_Position = vPosition * uMVPMatrix;
要验证它,请尝试按相反的顺序旋转三角形,
它会将三角形拉伸到90度的消失点
真正的问题似乎在setLookAtM函数中。在Joe的示例中,参数包括:
Matrix.setLookAtM(mVMatrix, 0,
0f, 0f,-3f, 0f, 0f, 0f, 0f, 1f, 0f );
这也是完全合乎逻辑的。但是,结果视图矩阵在我看来很奇怪:
-1 0 0 0
0 1 0 0
0 0 -1 0
0 0 -3 1
如我们所见,这个矩阵将反转X坐标,
由于第一个成员是–1,这将导致屏幕左/右翻转。
它也将反转Z顺序,但让我们关注X坐标 我认为setLookAtM功能也正常工作。
但是,由于Matrix类不是OpenGL的一部分, 它可以使用其他坐标系,
例如-Y轴向下的常规屏幕坐标。
这只是一个猜测,我并没有真正证实这一点 可能的解决方案:
我们可以手动构建理想的视图矩阵,
代码是:
Matrix.setIdentityM(mVMatrix,0);
mVMatrix[14] = -3f;
或我们可以尝试通过赋予函数来欺骗setLookAtM 反转摄影机坐标: 0,0,+3(而不是–3) 生成的视图矩阵将是:
1 0 0 0
0 1 0 0
0 0 1 0
0 0 -3 1
这正是我们需要的。现在,相机的行为与预期一致,
示例工作正常。我解决了这个问题,如下所示:
@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);
...
}
}
使用当前更新的Android示例代码,除了以下尝试移动三角形的建议外,没有其他建议对我有效
下面的链接包含答案。花了一天的时间找到它。在这里发布帮助他人,因为我多次看到这篇文章 对我来说有两种可能。这两个例子中的任何一个都是