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

时间:2015-03-12 16:41:21

标签: java android opengl-es-2.0 raycasting glu

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

    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是翻译和缩放参数(由ScaleGestureDetector中的GLSurfaceView提供)

根据我读过的所有内容应该正在运行,但它始终是错误的,我不知道还有什么可以尝试。任何帮助/反馈将不胜感激!

1 个答案:

答案 0 :(得分:0)

我没有查看你的所有数学,但是你的一些矩阵操作和规范看起来不对。这很可能至少是你问题的一部分。

首先看一下你所说的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()的结果,即View矩阵。其余的看起来更像是一个模型矩阵。但由于渲染通常只使用View和Model矩阵的产品,因此这只是术语。可以公平地说,你在mViewMatrix中存储的是你的ModelView矩阵。

然后命名在这里变得陌生:

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和Projection矩阵。基于方法定义,您应该传递ModelView和Projection矩阵。

由于我们确定你所谓的mViewMatrix对应于一个ModelView矩阵,如果只改变那个参数,这应该会更好地开始工作:

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

然后你可以完全摆脱不是模型矩阵的mModelMatrix