Android OpenGL三维拾取
我使用的是Android OpenGL ES 2.0,在经历了它带来的所有限制之后,我不知道如何对我拥有的3D点进行2D屏幕触摸。我不能得到正确的结果 我正在尝试实现将光线投射到点云中,然后我可以比较我的点到光线的距离,找到最近的点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
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.0003149873867479971x^2-0.8633277851535017
y+
9.990256062051143xy+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;