2009-07-02 7 views
5

The Screen-to-Weltproblem auf dem iPhon e
Bildschirm-zu-Welt-Koordinaten-Konvertierung in OpenGLES eine einfache Aufgabe?

Ich habe ein 3D-Modell (CUBE) in einem EAGLView gemacht und ich möchte in der Lage sein zu erkennen, wenn ich in der Mitte eines bestimmten Gesicht berühre (Von jedem Orientierungswinkel) des Würfels. Klingt ziemlich einfach, aber es ist nicht ...

Das Problem:
Wie kann ich genau Bildschirm-Koordinaten (Berührungspunkt) zu Weltkoordinaten (eine Position in OpenGL 3D-Raum) beziehen? Sicher, die Umwandlung eines bestimmten Punktes in einen "Prozentsatz" der Bildschirm-/Weltachse scheint die logische Lösung zu sein, aber es treten Probleme auf, wenn ich den 3D-Raum vergrößern oder verkleinern muss. Hinweis: Drehen & Zoomen in und aus dem 3D-Raum wird die Beziehung der 2D-Bildschirmkoordinaten mit den 3D-Welt Koordinaten ändern ... Außerdem müssen Sie "Abstand" zwischen dem Blickpunkt und Objekte im 3D-Raum berücksichtigen . Dies mag zunächst als eine "leichte Aufgabe" erscheinen, aber das ändert sich, wenn Sie die Anforderungen tatsächlich untersuchen. Und ich habe keine Beispiele dafür gefunden, dass Leute das auf dem iPhone machen. Wie wird das normalerweise gemacht?

Eine ‚einfache‘ Aufgabe ?:
Sicher, man könnte die Aufgabe des Schreibens eine API verpflichten sich als Mittler zwischen Bildschirm und Welt zu handeln, aber die Aufgabe würde einen solchen Rahmen zu schaffen, um einige ernsthafte erfordern Design und würde wahrscheinlich Zeit nehmen zu tun - nicht etwas, das one-bemannt in 4 Stunden sein kann ... Und 4 Stunden passiert, meine Frist sein.

Die Frage:

  • Was sind einige der einfachsten Möglichkeiten, um wissen, ob ich bestimmte Orte im 3D-Raum in der iPhone OpenGL ES Welt berührt?
+0

AFAIK Das ist kein triviales Problem, es sei denn, Sie haben es vorher gelöst oder es ist bereits Unterstützung in Ihre Umgebung integriert (was nicht der Fall zu sein scheint). Ich weiß, es sieht einfacher aus als es ist, wir hatten dieselbe Diskussion mit meinen Kollegen. – zoul

Antwort

1

Zwei Lösungen präsentieren sich. Beide sollten das Endziel erreichen, wenn auch mit anderen Mitteln: Anstatt zu beantworten, welche Weltkoordinate unter der Maus steht, beantworten sie die Frage "Welches Objekt wird unter der Maus dargestellt?".

Eine besteht darin, eine vereinfachte Version Ihres Modells in einen Offscreen-Puffer zu zeichnen, wobei die Mitte jeder Fläche mit einer bestimmten Farbe dargestellt wird (und die Beleuchtung so angepasst wird, dass die Farbe identisch beibehalten wird). Sie können dann diese Farben im Puffer (z. B. Pixmap) erkennen und ihnen Mauspositionen zuordnen.

Die andere ist OpenGL Picking zu verwenden. Es gibt ein ordentliches Tutorial here.Die Grundidee besteht darin, OpenGL in den Auswahlmodus zu setzen, das Ansichtsfenster auf ein kleines (etwa 3x3 oder 5x5) Fenster um den interessanten Punkt herum zu beschränken und dann die Szene (oder eine vereinfachte Version davon) unter Verwendung von OpenGL "Namen" (Ganzzahl) zu rendern Identifikatoren), um die Komponenten zu identifizieren, aus denen jedes Gesicht besteht. Am Ende dieses Prozesses kann OpenGL Ihnen eine Liste der Namen geben, die im Selektionsfenster gerendert wurden. Durch die Zuordnung dieser Bezeichner zu Originalobjekten können Sie bestimmen, welches Objekt sich unter dem Mauszeiger befindet.

+1

Nur eine Anmerkung für das iPhone: keine OpenGL-Kommissionierung in Open GL ES 1.1, also hast du kein Glück. – jv42

0

Google für opengl screen to world (zum Beispiel gibt es einen Thread, wo jemand will genau das tun, was man auf GameDev.net suchen). Es gibt eine gluUnProject Funktion, die genau dies tut, aber es ist nicht auf dem iPhone verfügbar, so dass Sie es portieren müssen (siehe this source aus dem Mesa-Projekt). Oder vielleicht gibt es schon irgendwo eine öffentlich zugängliche Quelle?

+0

Assoziiert Berührungen mit Bereichen auf 3D-Modellen in einem EAGLView dies kompliziert? Über dieses Thema scheint es nur wenig zu geben. Der Link, den Sie mir gegeben haben, hat mir keine Anhaltspunkte dafür gegeben, dies in Xcode zu erreichen. Danke für die Antwort. – RexOnRoids

+0

Ich denke, dass es wirklich so kompliziert ist und dass der Link deine Frage beantwortet, aber lass uns warten - vielleicht wird jemand eine bessere Antwort finden. – zoul

+0

EAGLView und OpenGL sind nur Werkzeuge zum Zeichnen Ihrer Modelle. Sie kümmern sich nicht um Treffererkennung oder ähnliches. Das musst du tun. –

2

Stellen Sie sich eine Linie vor, die vom Auge des Betrachters
durch den Bildschirm Berührungspunkt in Ihren 3D-Modellraum reicht.

Wenn diese Linie eine der Flächen des Würfels schneidet, hat der Benutzer den Würfel berührt.

+0

Das würde funktionieren, wenn es nur ein einfacher Würfel mit 8 Ecken wäre. Meine ist eigentlich ein 3D-Lego-Würfel mit mehr als 500 Scheitelpunkten (von den Zylindern oben) und die Daten werden automatisch aus einer Blender-Exportdatei geladen - dh: Ich habe keine Ahnung, welche der über 500 Scheitelpunkte ich als meine 8 Ecken verfolgen soll und kann somit meine Gesichter nicht verfolgen. – RexOnRoids

+0

Warum berechne/beginne nicht ein begrenzendes rechteckiges Prisma für deinen Legostein, dann überprüfe das auf Schnittpunkt. Komm schon, du weißt, dass du es willst. –

+0

Wie würde ich eine Begrenzungsbox für eine Kreuzung in Bezug auf eine Berührung, die ein 2D-Punkt auf dem Bildschirm ist, "überprüfen"? – RexOnRoids

3

Sie benötigen die OpenGL-Projektions- und Modelview-Matrizen. Multiplizieren Sie sie, um die Modelview-Projektionsmatrix zu erhalten. Invertieren Sie diese Matrix, um eine Matrix zu erhalten, die Clip-Space-Koordinaten in Weltkoordinaten umwandelt. Verwandeln Sie Ihren Berührungspunkt so, dass er Clip-Koordinaten entspricht: Die Bildschirmmitte sollte Null sein, während die Kanten für X bzw. Y + 1/-1 sein sollten.

konstruieren Sie zwei Punkte, einen bei (0,0,0) und einen bei (touch_x, touch_y, -1) und transformieren Sie beide durch die inverse Modellansicht Projektionsmatrix.

Machen Sie die Umkehrung einer perspektivischen Teilung.

Sie sollten zwei Punkte erhalten, die eine Linie von der Mitte der Kamera in "die ferne" (farplane) beschreiben.

Die Kommissionierung basiert auf vereinfachten Begrenzungsrahmen Ihrer Modelle. Sie sollten in der Lage sein, Ray/Box-Kreuzungsalgorithmen im Internet zu finden. Eine andere Lösung besteht darin, jedes der Modelle in einer etwas anderen Farbe in einen Offscreen-Puffer zu zeichnen und die Farbe am Berührungspunkt von dort zu lesen, um zu sagen, welcher Brich berührt wurde.

Hier Quelle für einen Cursor ich für ein kleines Projekt Physik mit Kugel schrieb:

float x=((float)mpos.x/screensize.x)*2.0f -1.0f; 
    float y=((float)mpos.y/screensize.y)*-2.0f +1.0f; 
    p2=renderer->camera.unProject(vec4(x,y,1.0f,1)); 
    p2/=p2.w; 
    vec4 pos=activecam.GetView().col_t; 
    p1=pos+(((vec3)p2 - (vec3)pos)/2048.0f * 0.1f); 
    p1.w=1.0f; 

    btCollisionWorld::ClosestRayResultCallback rayCallback(btVector3(p1.x,p1.y,p1.z),btVector3(p2.x,p2.y,p2.z)); 
    game.dynamicsWorld->rayTest(btVector3(p1.x,p1.y,p1.z),btVector3(p2.x,p2.y,p2.z), rayCallback); 
    if (rayCallback.hasHit()) 
    { 
     btRigidBody* body = btRigidBody::upcast(rayCallback.m_collisionObject); 
     if(body==game.worldBody) 
     { 
      renderer->setHighlight(0); 
     } 
     else if (body) 
     { 
      Entity* ent=(Entity*)body->getUserPointer(); 

      if(ent) 
      { 
       renderer->setHighlight(dynamic_cast<ModelEntity*>(ent)); 
       //cerr<<"hit "; 
       //cerr<<ent->getName()<<endl; 
      } 
     } 
    } 
7

Sie jetzt gluUnProject in http://code.google.com/p/iphone-glu/ finden. Ich habe keine Verbindung mit dem iPhone-Glu Projekt und habe es noch nicht selbst ausprobiert, wollte nur den Link teilen.

Wie würden Sie eine solche Funktion verwenden? This PDF erwähnt, dass:

Die Utility Library Routine gluUnProject() führt diese Umkehrung der Transformationen. Wenn die dreidimensionalen Fensterkoordinaten für eine Position und alle sie betreffenden Transformationen angegeben sind, gibt gluUnProject() die Weltkoordinaten von ihrem Ursprung zurück.

int gluUnProject(GLdouble winx, GLdouble winy, GLdouble winz, 
const GLdouble modelMatrix[16], const GLdouble projMatrix[16], 
const GLint viewport[4], GLdouble *objx, GLdouble *objy, GLdouble *objz); 

Karte der angegebenen Fensterkoordinaten Koordinaten (winx, weinig, winz) in dem Objekt, unter Verwendung von Transformationen, die durch eine Model-View-Matrix (modelMatrix), Projektionsmatrix (projMatrix) und Darstellungsfeld (Ansichtsfenster) . Die resultierenden Objektkoordinaten werden in objx, objy und objz zurückgegeben. Die Funktion gibt GL_TRUE zurück, die den Erfolg anzeigt, oder GL_FALSE, die einen Fehler anzeigt (z. B. eine nichtinvertierbare Matrix). Diese Operation versucht nicht, die Koordinaten in das Ansichtsfenster zu schneiden oder Tiefenwerte zu eliminieren, die außerhalb von glDepthRange() liegen.

Es gibt inhärente Schwierigkeiten beim Versuch, den Transformationsprozess umzukehren.Eine zweidimensionale Bildschirmposition könnte von irgendwo auf einer gesamten Linie im dreidimensionalen Raum stammen. Um das Ergebnis eindeutig zu machen, erfordert gluUnProject(), dass eine Fenstertiefenkoordinate (winz) bereitgestellt wird und dass winz in Bezug auf glDepthRange() angegeben wird. Für die Standardwerte von glDepthRange() fordert winz bei 0.0 die Weltkoordinaten des transformierten Punktes in der nahen Clipping-Ebene an, während winz bei 1.0 den Punkt in der fernen Clipping-Ebene anfordert.

Beispiel 3-8 (siehe wiederum the PDF) zeigt gluUnProject() mit der Mausposition zu lesen und die dreidimensionalen Punkte am nahen und fernen Bestimmung Abschneideebene, aus denen sie umgewandelt wurde. Die berechneten Weltkoordinaten werden auf Standardausgabe gedruckt, aber das gerenderte Fenster selbst ist nur schwarz.

In Bezug auf die Leistung, I found this quickly via Google als ein Beispiel, was Sie möglicherweise nicht mit GluUnProject tun möchten, mit einer Verknüpfung zu . Ich habe absolut keine Ahnung, wie anwendbar es auf das iPhone ist, da ich noch ein Newb mit OpenGL ES bin. Frag mich nochmal in einem Monat. ;-)