Android OpenGL 3D采摘

时间:2011-07-14 20:13:55

标签: android opengl-es matrix depth-buffer picking

我使用Android OpenGL-ES 2.0并且考虑到它带来的所有限制,我无法弄清楚如何将2D屏幕触摸带到我拥有的3D点上。我无法得到正确的结果。

我正在尝试将射线射入点云,然后我可以比较我的点与光线的距离,找到最近的点。

public class OpenGLRenderer extends Activity implements GLSurfaceView.Renderer {
    public PointCloud ptCloud;
    MatrixGrabber mg = new MatrixGrabber();
...
    public void onDrawFrame(GL10 gl) {

        gl.glDisable(GL10.GL_COLOR_MATERIAL);
        gl.glDisable(GL10.GL_BLEND);
        gl.glDisable(GL10.GL_LIGHTING);

        //Background drawing
        if(customBackground)
            gl.glClearColor(backgroundRed, backgroundGreen, backgroundBlue, 1.0f);
        else
            gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        if (PointCloud.doneParsing == true) {
            if (envDone == false)
                setupEnvironment();

            // Clears the screen and depth buffer.
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

            gl.glMatrixMode(GL10.GL_PROJECTION);
            gl.glLoadIdentity();
            GLU.gluPerspective(gl, 55.0f, (float) screenWidth / (float) screenHeight, 10.0f ,10000.0f);

            gl.glMatrixMode(GL10.GL_MODELVIEW);

            gl.glLoadIdentity();
            GLU.gluLookAt(gl, eyeX, eyeY, eyeZ, 
                              centerX, centerY, centerZ, 
                              upX, upY, upZ);

            if(pickPointTrigger)
                pickPoint(gl);


            gl.glPushMatrix();

            gl.glTranslatef(_xTranslate, _yTranslate, _zTranslate);
            gl.glTranslatef(centerX, centerY, centerZ);
            gl.glRotatef(_xAngle, 1f, 0f, 0f);
            gl.glRotatef(_yAngle, 0f, 1f, 0f);
            gl.glRotatef(_zAngle, 0f, 0f, 1f);
            gl.glTranslatef(-centerX, -centerY, -centerZ);

            ptCloud.draw(gl);

            gl.glPopMatrix();
        }
    }
}

这是我的拣货功能。我已将位置设置到屏幕中间,仅用于调试目的:

public void pickPoint(GL10 gl){

        mg.getCurrentState(gl);

        double mvmatrix[] = new double[16];
        double projmatrix[] = new double[16];
        int viewport[] = {0,0,screenWidth, screenHeight};

        for(int i=0 ; i<16; i++){
            mvmatrix[i] = mg.mModelView[i];
            projmatrix[i] = mg.mProjection[i];
        }

        mg.getCurrentState(gl);

        float realY = ((float) (screenHeight) - pickY);
        float nearCoords[] = { 0.0f, 0.0f, 0.0f, 0.0f };
        float farCoords[] = { 0.0f, 0.0f, 0.0f, 0.0f };


        GLU.gluUnProject(screenWidth/2, screenHeight/2, 0.0f, mg.mModelView, 0, mg.mProjection, 0,
                    viewport, 0, nearCoords, 0);
        GLU.gluUnProject(screenWidth/2, screenHeight/2, 1.0f, mg.mModelView, 0, mg.mProjection, 0,
                    viewport, 0, farCoords, 0);

        System.out.println("Near: " + nearCoords[0] + "," + nearCoords[1] + "," + nearCoords[2]);
        System.out.println("Far:  " + farCoords[0] + "," + farCoords[1] + "," + farCoords[2]);



      //Plot the points in the scene
        nearMarker.set(nearCoords);
        farMarker.set(farCoords);
        markerOn = true;

        double diffX = nearCoords[0] - farCoords[0];
        double diffY = nearCoords[1] - farCoords[1];
        double diffZ = nearCoords[2] - farCoords[2];

        double rayLength = Math.sqrt(Math.pow(diffX, 2) + Math.pow(diffY, 2) + Math.pow(diffZ, 2));
        System.out.println("rayLength: " + rayLength);

        pickPointTrigger = false;   
    }

更改persepctive zNear和Far没有预期的结果,1.0-1000.0视角的远点怎么可能是11个单位?

GLU.gluPerspective(gl, 55.0f, (float) screenWidth / (float) screenHeight, 1.0f ,100.0f);
.....
07-18 11:23:50.430: INFO/System.out(31795): Near: 57.574852,-88.60514,37.272636
07-18 11:23:50.430: INFO/System.out(31795): Far:  0.57574844,0.098602295,0.2700405
07-18 11:23:50.430: INFO/System.out(31795): rayLength: 111.74275719790872

GLU.gluPerspective(gl, 55.0f, (float) width / (float) height, 10.0f , 1000.0f);
...
07-18 11:25:12.420: INFO/System.out(31847): Near: 5.7575016,-7.965394,3.6339219
07-18 11:25:12.420: INFO/System.out(31847): Far:  0.057574987,0.90500546,-0.06634784
07-18 11:25:12.420: INFO/System.out(31847): rayLength: 11.174307289026638

寻找您在我的代码中看到的任何建议或希望错误。非常感激。我尽可能多地进行Bountying(这已经有一段时间了。)

1 个答案:

答案 0 :(得分:9)

我也正在研究这个问题 - 这是一个非常恼人的刺激性问题。我有两个潜在的潜在客户:1。不知何故,结果z取决于相机的位置,而不是你期望的方式。当摄像机z为0时,无论winZ是什么,结果z都是-1。到目前为止,我一直在查看生成的z,所以我在其他坐标上没有任何确切的数字,但是我刚才用我的代码和代码搞砸了,我发现报告了光线长度越大,摄像机距离越远(0,0,0)。在(0,0,0),光线长度报告为0.大约一个小时前,我收集了一堆点(cameraZ,winZ,resultZ)并将它们插入Mathematica。结果似乎表明了一种双曲线的东西;其中一个变量固定,另一个导致结果z线性变化,变化率取决于固定变量。

我的第二个领导来自http://www.gamedev.net/topic/420427-gluunproject-question/;箭鱼引用一个公式:

WinZ =(1.0f / fNear-1.0f / fDistance)/(1.0f / fNear-1.0f / fFar)

现在,这似乎与我收集的数据不符,但它可能值得一看。我想我会看看我是否可以弄清楚这个东西的数学运算方式并弄清楚出了什么问题。如果你搞清楚了,请告诉我。哦,这也是我收集的数据的公式:

-0.11072114015496763 - 10.000231721597817 x -  0.0003149873867479971 x^2 - 0.8633277851535017 y +  9.990256062051143 x y + 8.767260632968973 * ^ - 9 y ^ 2

Wolfram Alpha就是这样绘制的: http://www.wolframalpha.com/input/?i=Plot3D[-0.11072114015496763%60+-+10.000231721597817%60+x+-++++0.0003149873867479971%60+x^2+-+0.8633277851535017%60+y+%2B++++9.990256062051143%60+x+y+%2B+8.767260632968973%60*^-9+y^2+%2C+{x%2C+-15%2C++++15}%2C+{y%2C+0%2C+1}]


AHA!成功!就像我所知道的那样,gluUnProject只是简单的破碎。或者,没有人理解如何使用它。无论如何,我做了一个功能,正确地取消了gluProject功能,这看起来真的是他们用来以某种方式绘制到屏幕上的东西!代码如下:

public float[] unproject(float rx, float ry, float rz) {//TODO Factor in projection matrix
    float[] modelInv = new float[16];
    if (!android.opengl.Matrix.invertM(modelInv, 0, mg.mModelView, 0))
        throw new IllegalArgumentException("ModelView is not invertible.");
    float[] projInv = new float[16];
    if (!android.opengl.Matrix.invertM(projInv, 0, mg.mProjection, 0))
        throw new IllegalArgumentException("Projection is not invertible.");
    float[] combo = new float[16];
    android.opengl.Matrix.multiplyMM(combo, 0, modelInv, 0, projInv, 0);
    float[] result = new float[4];
    float vx = viewport[0];
    float vy = viewport[1];
    float vw = viewport[2];
    float vh = viewport[3];
    float[] rhsVec = {((2*(rx-vx))/vw)-1,((2*(ry-vy))/vh)-1,2*rz-1,1};
    android.opengl.Matrix.multiplyMV(result, 0, combo, 0, rhsVec, 0);
    float d = 1 / result[3];
    float[] endResult = {result[0] * d, result[1] * d, result[2] * d};
    return endResult;
}

public float distanceToDepth(float distance) {
    return ((1/fNear) - (1/distance))/((1/fNear) - (1/fFar));
}

它目前假定以下全局变量: mg - 具有当前矩阵的MatrixGrabber viewport - 带有视口({x,y,width,height})

的float [4]

它所采用的变量等同于gluUnProject应该采用的变量。例如:

float[] xyz = {0, 0, 0};
xyz = unproject(mouseX, viewport[3] - mouseY, 1);

这将返回远处平面上鼠标下方的点。我还添加了一个函数,用于在相机的指定距离和0-1 ...表示之间进行转换。像这样:

unproject(mouseX, viewport[3] - mouseY, distanceToDepth(5));

这将从相机返回鼠标下5个单位的点。 我用问题中给出的方法测试了这个 - 我检查了近平面和远平面之间的距离。当fNear为0.1且fFar为100时,距离应为99.9。据我所知,无论相机的位置或方向如何,我一直得到99.8977左右。哈哈,很高兴有这个想法。如果您对此没有任何问题,或者您希望我重写它以获取输入而不是使用全局变量,请告诉我。希望这有助于少数人;在认真尝试解决之前,我一直在想这个问题。


嘿,所以,在弄清楚它应该是怎么回事之后,我已经弄清楚他们在实施gluUnProject时错过了什么。他们忘记了(意图不是也没有告诉任何人?)除以结果向量的第四个元素,这有点规范了向量或类似的东西。 gluProject在应用矩阵之前将其设置为1,因此当你完成撤消它们时它需要为1。长话短说,您实际上可以使用gluUnProject,但是您需要将它传递给float [4],然后将所有得到的坐标除以第4个,如下所示:

float[] xyzw = {0, 0, 0, 0};
android.opengl.GLU.gluUnProject(rx, ry, rz, mg.mModelView, 0, mg.mProjection, 0, this.viewport, 0, xyzw, 0);
xyzw[0] /= xyzw[3];
xyzw[1] /= xyzw[3];
xyzw[2] /= xyzw[3];
//xyzw[3] /= xyzw[3];
xyzw[3] = 1;
return xyzw;

xyzw现在应该包含相关的空间坐标。这似乎与我拼凑的那个完全相同。它可能会快一点;我认为他们结合了其中一个步骤。