2011-07-14 11 views
6

Estoy en Android OpenGL-ES 2.0 y después de todas las limitaciones que vienen con él, no puedo imaginar cómo tomar toques de pantalla en 2D a los puntos 3D que tengo. No puedo obtener los resultados correctos.Android OpenGL selección 3D

Estoy tratando de implementar el tiro de un rayo en la nube de puntos, que luego puedo comparar las distancias de mis puntos al rayo, encontrando el punto más cercano.

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

Aquí está mi función de selección. He establecido la ubicación para el centro de la pantalla sólo para propósitos de depuración:

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

Cambio de la persepctive zNear y Lejano no tiene los resultados esperados, ¿cómo podría el punto de una perspectiva 1,0-1000,0 nunca tal 11 unidades de distancia?

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 

Buscando cualquier sugerencia o bugs con suerte que vea en mi código. Muy apreciado. Estoy haciendo un Bountying tanto como puedo (esto ha sido un problema por un tiempo).

Respuesta

9

Estoy trabajando en esto también, es un problema irritante muy irritante. Tengo dos pistas potenciales: 1. De alguna manera, la z resultante depende de dónde está la cámara, y no de cómo esperas. Cuando la cámara z está en 0, la z resultante es -1, sin importar qué winZ sea. Hasta ahora he estado mirando principalmente la z resultante, por lo que no tengo ninguna cifra exacta en las otras coordenadas, pero me equivoqué con mi código y su código, justo ahora, y descubrí que el informe la longitud de los rayos aumenta cuanto más lejos se encuentra la cámara (0,0,0). En (0,0,0), se informa que la longitud del rayo es 0. Hace una hora más o menos, reuní un montón de puntos (cameraZ, winZ, resultZ) y los conecté a Mathematica. El resultado parece indicar un tipo hiperbólico de cosas; con una de las variables fijadas, la otra hace que la z resultante varíe linealmente, con la tasa de cambio dependiendo de la variable fija.

Mi segunda ventaja es de http://www.gamedev.net/topic/420427-gluunproject-question/; el pez espada cita una fórmula:

winz = (1.0f/fNear-1.0f/fDistance)/(1.0f/fNear-1.0f/FFAR)

Ahora bien, esto no parece para que coincida con el datos que recopilé, pero probablemente valga la pena echarle un vistazo. Creo que voy a ver si puedo descubrir cómo funcionan las matemáticas de esta cosa y descubrir qué está mal. Déjame saber si resuelves algo. Ah, también, aquí está la fórmula ajustado a los datos que recogí:

-0,11072114015496763 - 10.000231721597817 x - ,0003149873867479971 x^2 - 0.8633277851535017 y + 9.990256062051143 x y + 8.767260632968973 *^- 9 y^2

Wolfram Alpha traza que de este modo: 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! ¡Éxito! Por lo que puedo decir, gluUnProject simplemente está roto. O, nadie entiende cómo usarlo en absoluto. De todos modos, hice una función que deshace correctamente la función gluProject, que parece ser realmente lo que usan para dibujar en la pantalla de alguna manera.Código es el siguiente:

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

Actualmente asume las siguientes variables globales: mg - un MatrixGrabber con matrices actuales de ventana gráfica - un flotador [4] con el visor ({x, y, anchura, altura})

Las variables que toma son equivalentes a las que se suponía que debía tomar gluUnProject. Por ejemplo:

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

Esto devolverá el punto debajo del mouse, en el plano lejano. También agregué una función para convertir entre una distancia específica de la cámara y su 0-1 ... representación ... cosa. De esta manera:

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

Esto devolverá el punto debajo del mouse a 5 unidades de la cámara. Probé esto con el método dado en la pregunta - Comprobé la distancia entre el plano cercano y el plano lejano. Con fNear de 0.1 yfFar de 100, la distancia debe ser 99.9. Constantemente he obtenido aproximadamente 99.8977, independientemente de la posición o la orientación de la cámara, por lo que puedo ver. Jaja, es bueno tener eso resuelto. Avíseme si tiene/no tiene ningún problema o si desea que lo reescriba para tomar entradas en lugar de usar variables globales. Espero que esto ayude a algunas personas; Me había estado preguntando sobre esto durante unos días antes de intentar seriamente solucionarlo.


Hey, por lo que, después de haber descubierto la manera que se supone que es, he descubierto lo que se perdieron en la implementación de gluUnProject. Olvidaron (¿no se lo dijo a nadie y no se lo dijo a nadie?) Dividirse por el cuarto elemento del vector resultante, que normaliza un poco el vector o algo así. gluProject lo establece en 1 antes de aplicar matrices, por lo que necesita ser 1 cuando haya terminado de deshacerlos. Para resumir, en realidad se puede utilizar gluUnProject, pero hay que pasar un flotador [4], y luego dividir todas las coordenadas resultantes por el cuarto uno, así:

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 debe contener ahora el relevante coordenadas espaciales Esto parece funcionar exactamente igual que el que improvisé. Puede ser un poco más rápido; Creo que combinaron uno de los pasos.

+0

He progresado un poco. Puedo tomar el resultado ** nearCoords ** e iterar desde allí en la ** dirección ** de ** farCoords ** (simplemente voy por la magnitud de Z_FAR), y el rayo es algo cercano a lo que realmente selecciono. Vea cómo esto funciona para usted. – RedLeader

+0

No he intentado iterar en la dirección de los nearCoords, sin embargo, en parte porque me pareció que ni los nearCoords ni los farCoords tenían mucha relación con el lugar donde hice clic en primer lugar. Me he dado cuenta de que gluUnProject no parece funcionar (de una manera nueva) - Corrí algunas coordenadas a través de gluProject, luego tomé el resultado y lo ejecuté a través de gluUnProject inmediatamente después, y obtuve algo totalmente diferente. Actualmente estoy tratando de hacer una transformación inversa. Noté que usas lookAt para elegir, pero una serie de transformaciones para dibujar. ¿Porqué es eso? – Erhannis

+0

Era solo mi diseño, y funciona de alguna manera, no es exactamente el punto (está cerca si tomo a la izquierda, no tan cerca a la derecha), pero es decentemente preciso ... – RedLeader