Android OpenGL三维拾取

Android OpenGL三维拾取,android,opengl-es,matrix,depth-buffer,picking,Android,Opengl Es,Matrix,Depth Buffer,Picking,我使用的是Android OpenGL ES 2.0,在经历了它带来的所有限制之后,我不知道如何对我拥有的3D点进行2D屏幕触摸。我不能得到正确的结果 我正在尝试实现将光线投射到点云中,然后我可以比较我的点到光线的距离,找到最近的点 public class OpenGLRenderer extends Activity implements GLSurfaceView.Renderer { public PointCloud ptCloud; MatrixGrabber mg

我使用的是Android OpenGL ES 2.0,在经历了它带来的所有限制之后,我不知道如何对我拥有的3D点进行2D屏幕触摸。我不能得到正确的结果

我正在尝试实现将光线投射到点云中,然后我可以比较我的点到光线的距离,找到最近的点

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;   
    }

寻找任何建议或希望在我的代码中看到的bug。非常感谢。我正在尽我所能地赏金(这已经是一个问题了一段时间)。

我也在研究这个问题——这是一个非常令人恼火的问题。我有两个潜在的线索:1。不知何故,产生的z取决于相机的位置,而不是你期望的方式。当摄影机z为0时,无论winZ是什么,结果z都为-1。到目前为止,我主要关注的是得到的z,所以我没有其他坐标的精确数字,但我刚才把我的代码和你的代码弄乱了,我发现报告的光线长度随着相机距离(0,0,0)越远而增加。在(0,0,0)处,光线长度报告为0。大约一个小时前,我收集了一大堆要点(cameraZ、winZ、resultZ),并将它们插入Mathematica。结果似乎表明这是一种双曲线类型的东西;其中一个变量固定后,另一个变量会导致结果z线性变化,变化率取决于固定变量

我的第二条线索来自;箭鱼引用了一个公式:

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
xy+8.767260632968973
*^-9 y^2

Wolfram Alpha这样绘制:


啊哈!成功!据我所知,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-具有当前矩阵的矩阵抓取器 视口-带有视口({x,y,width,height})的浮动[4]

它使用的变量与GlunProject应该使用的变量相等。例如:

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。哈哈,很高兴能弄明白。如果您有/没有任何问题,或者您希望我重写它以获取输入而不是使用全局变量,请告诉我。希望这能帮助一些人;在我认真地试图修复它之前,我已经对这个问题疑惑了几天


嘿,所以,在弄明白了它应该是怎样的之后,我已经弄明白了他们在实现GlunProject时遗漏了什么。他们忘记了(不打算也没有告诉任何人?)除以结果向量的第四个元素,这有点规范化向量或类似的东西。gluProject在应用矩阵之前将其设置为1,因此在撤消矩阵时需要将其设置为1。长话短说,实际上可以使用gluUnProject,但需要给它传递一个float[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现在应该包含相关的空间坐标。这似乎和我拼凑的一样。它可能会快一点;我认为他们结合了其中一个步骤。

我已经取得了一些进展。我可以得到结果nearCoords,然后从那里沿着farCoords的方向迭代(我只是按照Z_FAR的大小),光线有点接近我实际选择的光线。看看这对你是如何工作的。我还没有尝试沿着nearCoords的方向进行迭代,部分原因是我觉得nearCoords和farCoords都与我最初点击的位置没有太大关系。我注意到GlunProject似乎不起作用(以一种新的方式)-我通过GlunProject运行了一些坐标,然后获取结果并立即通过GlunProject运行,得到了完全不同的结果。我现在正在做一个逆变换。我注意到您使用lookAt进行拾取,但使用一系列变换进行绘制。这是为什么?这只是我的设计,它在某种程度上起作用,不完全是点(如果我在左侧拾取,它会很近,在右侧则不会很近),但它非常准确。我试图像你提到的那样使第四个坐标生效,但它甚至可能使点选择更加偏离目标。你做了其他的改变吗?嗯,你把其他的除以第四,对吗?您是否尝试过在ptCloud.draw函数旁边设置pickPoint,以便它们使用相同的矩阵?此外,对近平面/远平面距离是否有任何影响?
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;