如何在OpenGL ES 2.0(Android)中取消投影屏幕坐标



在过去的几天里,我一直在努力将应用程序的触摸事件"取消投影"到渲染器的坐标空间中。为了实现这一目标,我尝试了各种自定义的取消投影方法和替代技术(包括尝试使用比例因子和转换值转换坐标)无休止。我已经接近了(我的触摸稍微偏离了),但是我使用GLU.gluUnProject的尝试已经偏离了通常将坐标放在视图的中心周围。"最接近"的结果是由Xandy的方法产生的,但即使是这些结果通常也是关闭的。我的主要问题是如何设置我的视口矩阵GLU.gluUnProject我是否传递了正确的参数?我的数学是基于这个问题的答案。以下是我的代码的相关摘录(显示我如何设置矩阵和当前尝试):

    public void onSurfaceChanged(GL10 gl, int width, int height) {
            // Set the OpenGL viewport to fill the entire surface.
            glViewport(0, 0, width, height);
            ...
            float ratio = (float) width / height;
            Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    }
    public void onDrawFrame(GL10 gl) {
            glClear(GL_COLOR_BUFFER_BIT);
            Matrix.setLookAtM(mViewMatrix, 0, 0f, 0f, -4.5f, 0f, 0f, 0f, 0f, 1f, 0f);
            Matrix.scaleM(mViewMatrix, 0, mScaleFactor, mScaleFactor, 1.0f);
            Matrix.translateM(mViewMatrix, 0, -mOffset.x, mOffset.y, 0.0f);
            Matrix.multiplyMM(mModelMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
            ...
    }
    public PointF convertScreenCoords(float x, float y) {
            int[] viewPortMatrix = new int[]{0, 0, (int)mViewportWidth, (int)mViewportHeight};
            float[] outputNear = new float[4];
            float[] outputFar = new float[4];
            y = mViewportHeight - y;
            int successNear = GLU.gluUnProject(x, y, 0, mModelMatrix, 0, mProjectionMatrix, 0, viewPortMatrix, 0, outputNear, 0);
            int successFar = GLU.gluUnProject(x, y, 1, mModelMatrix, 0, mProjectionMatrix, 0, viewPortMatrix, 0, outputFar, 0);
            if (successNear == GL_FALSE || successFar == GL_FALSE) {
                throw new RuntimeException("Cannot invert matrices!");
            }
            convert4DCoords(outputNear);
            convert4DCoords(outputFar);
            float distance = outputNear[2] / (outputFar[2] - outputNear[2]);
            float normalizedX = (outputNear[0] + (outputFar[0] - outputNear[0]) * distance);
            float normalizedY = (outputNear[1] + (outputFar[1] - outputNear[1]) * distance);
            return new PointF(normalizedX, normalizedY);
    }

convert4DCoords只是一个辅助函数,它将数组的每个坐标(x,y,z)除以w。 mOffsetmScaleFactor是平移和缩放参数(由我们GLSurfaceView中的ScaleGestureDetector给出)

根据我阅读的所有内容,这应该可以工作,但它始终是错误的,我不确定还能尝试什么。任何帮助/反馈将不胜感激!

我没有看过你所有的数学,但你的一些矩阵运算和规范对我来说是错误的。这很可能至少是您问题的一部分。

先看看你所说的mViewMatrix

Matrix.setLookAtM(mViewMatrix, 0, 0f, 0f, -4.5f, 0f, 0f, 0f, 0f, 1f, 0f);
Matrix.scaleM(mViewMatrix, 0, mScaleFactor, mScaleFactor, 1.0f);
Matrix.translateM(mViewMatrix, 0, -mOffset.x, mOffset.y, 0.0f);

这并不一定有问题。但大多数人可能只会将第一部分称为视图矩阵,这是setLookAtM()的结果。其余的看起来更像是一个模型矩阵。但由于渲染通常只使用视图和模型矩阵的乘积,所以这只是术语。可以公平地说,您存储在mViewMatrix中的是您的模型视图矩阵。

然后这里的命名变得更加奇怪:

Matrix.multiplyMM(mModelMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

这将计算投影和视图矩阵的乘积。所以这将是一个ViewProjection 矩阵。将其存储在名为 mModelMatrix 的变量中是令人困惑的,我认为您可能已经用这个命名为自己设置了一个陷阱。

这让我们想到了glUnproject()电话:

GLU.gluUnProject(x, y, 0, mModelMatrix, 0, mProjectionMatrix, 0, viewPortMatrix, 0, outputNear, 0);

基于上述内容,您为两个矩阵参数(视口不是真正的矩阵)传递的是 ViewProjection 和投影矩阵。根据方法定义,应传递模型视图和投影矩阵。

由于我们确定您所谓的mViewMatrix对应于 ModelView 矩阵,因此如果您仅更改该参数,这将开始更好地工作:

GLU.gluUnProject(x, y, 0, mViewMatrix, 0, mProjectionMatrix, 0, viewPortMatrix, 0, outputNear, 0);

然后,您可以摆脱根本不是模型矩阵的mModelMatrix

最新更新