Android OpenGL 3D 拾取 [英] Android OpenGL 3D picking

查看:30
本文介绍了Android OpenGL 3D 拾取的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是 Android OpenGL-ES 2.0,并且在它附带的所有限制之后,我无法弄清楚如何将 2D 屏幕触摸带到我拥有的 3D 点.我无法得到正确的结果.

I'm on Android OpenGL-ES 2.0 and after all the limitations that come with it, I can't figure out how to take 2D screen touches to the 3D points I have. I can't get the right results.

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

I'm trying to implement shooting a ray into the point cloud, which I can then compare distances of my points to the ray, finding the closest point.

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

这是我的拣货功能.出于调试目的,我将位置设置为屏幕中间:

Here is my picking function. I've set the location to the middle of the screen just for debugging purposes:

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

改变透视zNear和Far都没有预期的效果,1.0-1000.0透视的远点怎么可能是11个单位呢?

Changing the persepctive zNear and Far doesn't have the expected results, how could the far point of a 1.0-1000.0 perspective be 11 units away?

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

寻找您在我的代码中看到的任何建议或错误.非常感激.我正在尽我所能(这已经是一个问题).

Looking for any suggestions or hopefully bugs you see in my code. Much appreciated. I'm Bountying as much as I can (this has been a problem for a while).

推荐答案

我也在努力解决这个问题 - 这是一个非常恼人的恼人问题.我有两个潜在的线索: 1. 不知何故,生成的 z 取决于相机的位置,而不是你所期望的.当相机 z 为 0 时,无论 winZ 是什么,结果 z 都是 -1.到目前为止,我主要关注的是生成的 z,所以我在其他坐标上没有任何确切的数字,但是我刚刚弄乱了我的代码和你的代码,我发现报告的相机距离 (0,0,0) 越远,光线长度就会增加.在 (0,0,0) 处,光线长度报告为 0.大约一个小时前,我收集了一堆点(cameraZ、winZ、resultZ)并将它们插入 Mathematica.结果似乎表明了一种双曲线的事情.在其中一个变量固定的情况下,另一个导致结果 z 线性变化,变化率取决于固定变量.

I'm working on this, too - it's a very irritating irritating problem. I have two potential leads: 1. Somehow, the resulting z depend on where the camera is, and not how you'd expect. When the camera z is at 0, the resulting z is -1, no matter what winZ is. Up until now I've mainly been looking at the resulting z, so I don't have any exact figures on the other coordinates, but I messed around with my code and your code, just now, and I've discovered that the reported ray-length increases the farther the camera gets from (0,0,0). At (0,0,0), the ray-length is reported to be 0. An hour or so ago, I gathered a bunch of points (cameraZ, winZ, resultZ) and plugged them into Mathematica. The result seems to indicate a hyperbolic sort of thing; with one of the variables fixed, the other causes the resulting z to vary linearly, with the rate of change depending on the fixed variable.

我的第二条线索来自 http://www.gamedev.net/topic/420427-gluunproject-question/;剑鱼引用了一个公式:

My second lead is from http://www.gamedev.net/topic/420427-gluunproject-question/; swordfish quotes a formula:

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

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

现在,这似乎与我收集的数据不匹配,但可能值得一看.我想我会看看我是否能弄清楚这件事的数学原理并找出问题所在.让我知道你是否想出任何办法.哦,还有,这是适合我收集的数据的公式:

Now, this doesn't seem to match up with the data I collected, but it's probably worth a look. I think I'm going to see if I can figure out how the math of this thing works and figure out what's wrong. Let me know if you figure anything out. Oh, also, here's the formula fitted to the data I collected:

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

-0.11072114015496763- 10.000231721597817 x - 0.0003149873867479971x^2 - 0.8633277851535017 y + 9.990256062051143x 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+60+6+2+5+9.93%0+60+2B+9.93%%2B+8.767260632968973%60*^-9+y^2+%2C+{x%2C+-15%2C++++15}%2C+{y%2C+0%2C+1}]

Wolfram Alpha plots it like so: 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}]

啊哈!成功!据我所知,gluUnProject 完全坏掉了.或者,根本没有人知道如何使用它.无论如何,我做了一个函数来正确地撤销 gluProject 函数,这似乎真的是他们用来以某种方式绘制到屏幕上的东西!代码如下:

AHA! Success! As near as I can tell, gluUnProject is just plain broken. Or, nobody understands how to use it at all. Anyway, I made a function that properly undoes the gluProject function, which appears to really be what they use to draw to the screen in some fashion! Code is as follows:

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 - 具有当前矩阵的 MatrixGrabberviewport - 带有视口 ({x, y, width, height}) 的 float[4]

It currently assumes the following global variables: mg - a MatrixGrabber with current matrices viewport - a float[4] with the viewport ({x, y, width, height})

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

The variables it takes are equivalent to the ones that gluUnProject was supposed to take. For example:

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

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

This will return the point under the mouse, on the far plane. I also added a function to convert between a specified distance from the camera and its 0-1...representation...thing. Like so:

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

这将返回距离相机 5 个单位的鼠标下方的点.我用问题中给出的方法对此进行了测试——我检查了近平面和远平面之间的距离.fNear 为 0.1,fFar 为 100,距离应为 99.9.据我所知,无论相机的位置或方向如何,我一直得到大约 99.8977.哈哈,很高兴知道这一点.如果您有/没有任何问题,或者您希望我重写它以获取输入而不是使用全局变量,请告诉我.希望这可以帮助一些人;在认真尝试修复它之前,我一直在想这个问题.

This will return the point under the mouse 5 units from the camera. I tested this with the method given in the question - I checked the distance between the near plane and the far plane. With fNear of 0.1 and fFar of 100, the distance should be 99.9. I have consistently gotten about 99.8977, regardless of position or orientation of the camera, as far as I can tell. Haha, good to have that figured out. Let me know if you do/don't have any problems with it, or if you want me to rewrite it to take inputs instead of using global variables. Hopefully this helps a few people; I had been wondering about this for a few days before seriously trying to fix it.

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

Hey, so, having figured out how it's supposed to be, I've figured out what they missed in implementing gluUnProject. They forgot (intended not to and didn't tell anyone?) to divide by the fourth element of the resulting vector, which kinda normalizes the vector or something like that. gluProject sets it to 1 before applying matrices, so it needs to be 1 when you're done undoing them. Long story short, you can actually use gluUnProject, but you need to pass it a float[4], and then divide all the resulting coordinates by the 4th one, like so:

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 现在应该包含相关的空间坐标.这似乎与我拼凑起来的完全一样.它可能会快一点;我认为他们结合了其中一个步骤.

xyzw should now contain the relevant space coordinates. This seems to work exactly the same as the one I cobbled together. It might be a little bit faster; I think they combined one of the steps.

这篇关于Android OpenGL 3D 拾取的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆