Hallo zusammen, ich weis nich genau ob mein Problem besser hier oder eher im Matheforum aufgehoben ist, aber ich denk mal es wird schon passen.
Also um was geht es? In meinem aktuellen Projekt lese ich 2D-Layoutdaten ein, extrudiere diese und führe ein paar boolsche Operationen durch wodurch ich schließlich ein 3D-Modell erhalte, dieses schön einfärbe und dem Nutzer präsentiere. Nun soll der Nutzer sich dass aber nicht nur angucken können sondern auch ein bischen interaktiv werden. Dazu gehören zbsp. Querschnitte und auch das Messen von Abständen im Modell.Zur Zeit versuche ich mich wie aus dem Titel zu erahnen am Messen von Abständen. Der Nutzer soll hierbei die Möglichkeit haben, von Kante zu Kante zu messen. Der mathematische Teil an der Sache ist dabei ja relativ simpel (Pythagoras und Co.). Jedoch fehlt mir eine überzeugende Idee wie ich es dem Nutzer ermögliche auch wirklich von Kante zu Kante messen zu können. Dh. wenn er die Maus an eine Kante annährt soll diese "andocken".
Ich hab mir jetzt schon ein paar Gedanken gemacht, aber nichts schien mir bis jetzt wirklich "richtig", aber ich werde sie trotzdem mal erwähnen vll war ja doch etwas brauchbares dabei.
Grundlegendes: Mein Modell besteht aus zig Dreiecken, über einen Algorithmus kann ich jedoch z.Z. fast alle doppelten Kanten eliminieren wordurch ich theoretisch alle Außenkanten erhalte.
1. Idee: Wenn ich die Maus über meine Zeichenfläche bewege kann ich sicherlich auf die OpenGL-Koordinaten zurückrechnen (also wo sich meine Maus im bezug auf das Modell befindet). Wenn ich mir um diesen Punkt eine Kugel denke, könnte ich theoretisch ja überprüfen welche aktuellen Kanten sich mit dieser Kugel schneiden, dann wähle ich die, die am nächsten liegt und positioniere meine Maus auf diesem Punkt. Problem: * Ich müsste für meine Kugel gegen jede Kante rechnen und das bei jeder Bewegung bzw. Frame - scheint recht rechenlastig * Wie schaut es mit der Z-Koordinate aus, woher weiß ich wie tief ich bin? Sonst positioniert sich die maus auf einer Kante die von mir aus unter dem Model liegt :<
2. Überlegung Die zweite Idee war eigentlich der Grundstein für die erste. Hier verzichte ich auf die Kugel und berechne immer den geringsten Abstand zu allen Kanten und wenn ich einen mind. Abstand unterschreite docke ich an. Problem: * Ich denke mal nicht ganz so aufwändig, aber dennoch zu viel Rechenarbeit um es flüssig laufen zu lassen * Auch hier das Problem mit Z oder wenn das Modell rotiert wurde...
Generelles Problem: * Zur Zeit habe ich keine richtige Idee wie ich auf die OpenGL-Koordinaten komme, vor allem nicht wie ich Z bestimmen soll.. das könnte ja theoretisch überall sein :S
Nun, vielleicht hat sich der ein oder andere schon mit diesem Thema beschäftigt, es ist ja auch nichts völlig Neues und kann mir ein paar Tips geben ob ich mich überhaupt in die richtige Richtung bewege. Vll bietet OpenGL sogar von Haus aus solch eine Funktion (was wohl eher Wunschdenken sein wird). Bin für Kritik und Anregungen offen :]
Grüße
PS: Ich hab mal ein Bild drangehangen was mein Ziel verdeutlichen soll Links: Weiße Punkte = Eckpunkt der Kanten Rechts: Die roten Punkte liegen irgendwo auf der Kante
Zuletzt geändert von TheQaa am Di Jun 22, 2010 08:54, insgesamt 1-mal geändert.
Bei meinem O3DC-Projekt (Diplomarbeit) gehe ich wie folgt vor:
Dein Cursor soll von Kanten im Radius von N Pixeln "angezogen" werden. Erzeuge daher einen FBO der Größe (2*N+1) mal (2*N+1).
Erzeuge dir eine Projektionsmatrix die genau den Bereich um den Cursor herum abbildet. Vom Prinzip wird ausgehend von der normalen Projektion einfach nur der entsprechende Bereich in die Bildmitte verschoben und rangezoomt:
Code:
Vector2f viewport = m_widget->getViewportSize(); Vector2f cursor = m_widget->getCursorPosition(); // Cursor transformiert in den Bereich -1...1 float radius = getCursorRadius(); // das oben erwähnte N Vector2f scale = viewport * (0.5f/radius); CML::Matrix44f matWindow(1); // 'verschiebe und ranzoom'-Matrix matWindow._11 = scale.x; matWindow._22 = scale.y; matWindow._41 = -scale.x * cursor.x; matWindow._42 = -scale.y * cursor.y; Camera* camera = m_widget->getCamera(); // aktuelle Kamera nehmen m_matView = camera->getView(); m_matProj = camera->getProjection() * matWindow;
rendere die Szene ganz normal, schreibe aber nur in den Z-Buffer. Dadurch werden gleich Kanten auf der Rückseite von Objekten nicht gerendert. Damit du kein Z-Fighting bekommst solltest du glPolygonOffset oder glDepthRange benutzen.
rendere nun alle Kanten, wobei die RGBA-Farbe jeder Kante eine ID für die jeweilige Kante ist. Natürlich aufpassen das (0,0,0,0) bereits für den Hintergrund vergeben ist.
hole dir den Inhalt des FBO in den Hauptspeicher
suche dir den Pixel der nicht schwarz ist und am nächsten zum Mittelpunkt des Buffers liegt. Interpretiere die Farbe des Pixels als ID => Kante gefunden.
Das ganze ist auch unter der Bezeichnung Color-Picking bekannt.
Edit: Der Vorteil das ganze im Screenspace zu machen ist das der Cursor-Radius korrekt beachtet wird. Würde man das im 3D-Raum machen müsste der Radius mit zunehmender Entfernung größer werden. Auch kann man natürlich die ganze Infrastruktur zum rendern der Szene (=> Frustum-Culling!!) einfach benutzen und muss nicht das Rad neu erfinden.
Das löst wirklich mein Problem mit den Koordinaten, jedoch denke ich mal ist der Weg den Coolcat beschreibt wohl reichlich effektiver als meine Kugelmethode, weshalb ich mich da auch gleich dran versucht habe. Nunja und da hab ich auch gleich ein paar Probleme.
1. Ich habe mir das Tutorial zu den FBOs mal zu Gemüte geführt und es versucht umzusetzen. Gleich am Anfang hat ich das Problem dass das Programm mit glGenFrameBufferExt abgebrochen ist da die Extensions nicht vorlagen, im Tutorial wird gesagt dass man Version 1.1 benötigt ich hatte 1.4. (Fehler?). Nun mit neuen Treibern und Version 2.0 liefs dann auch und das ist dabei rumgekommen. Da ich noch relativ neu in OpenGL bin und noch nichts in diese Richtung gemacht habe, wollte ich gerne wissen ob das so passt :>
Aufrufen tue ich die Funktion zusammen mit der Initialisierung am Programmanfang.
2. Danach habe ich mich daran versucht den Code von dir nach Delphi zu bringen, wobei ich mir an einigen Stellen nicht sicher bin. Hier erstmal meine Übersetzung:
Code:
var Scale, Cursor : TVector3f; Viewport : TGLVectori4; MatWindow : TMatrix4f; Begin glGetIntegerv(GL_VIEWPORT,@Viewport); <-- Frage ich die richtigen Werte ab?
Scale[0] := Viewport[2] * (0.5 / N); <-- Stimmen 2/3 (Width/Height) ? (0/1 sind immer der Ursprung (Null/Null)) Scale[1] := Viewport[3] * (0.5 / N);
MatWindow[0,0] := Scale[0]; <-- Wird hier eine Einheitsmatrix als Grundlage genommen? MatWindow[1,1] := Scale[1]; MatWindow[3,0] := -Scale[0] * Cursor[0]; <-- Auch ist mir nicht wirklich klar warum man die W-Zeile nutzt MatWindow[3,1] := -Scale[1] * Cursor[1]; Den Part mit der Kamera habe ich weggelassen, weil ich mir über die Umsetzung nicht im klaren war
3. Gerendert wird dann wie folgt aus meiner OGL-Klasse herraus:
Code:
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity; { Licht } ... unwichtig, denke ich mal { Kamera } Camera.SetView; <-- Über ein glMultMatrix setze ich mein Model-Koord-System wie der Nutzer es mag { User-Render-Funktion } FOnRender; <-- Übergebene Renderfunktion SwapBuffers(DC);
Und hier die entsprechende Renderfunktion:
Code:
procedure TForm1.Render; begin RenderFBO; <-- vertausch ich es, gibts nen Absturz RenderShape; <-- Ich zeichne einfach 4 verschieden bunte Striche end;
procedure TForm1.RenderFBO; begin glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); RenderShape; glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); end;
//Edit: Mittlerweile habe ich das Tutorial nochmal gefressen und zumindest das mit der Rendern verstanden. Somit dürften meine obigen Funktionen falsch sein, da man die Szene komplett (inkl. glClear/matrix/load/kamera) in das FBO rendern, muss sowie die Szene einmal komplett außerhalb.
* Nun zunächst meintest du ich sollte nur in den Z-Buffer rendern, leider hab ich keine Ahung wie ich das anstellen kann. * Im Tutorial wird in eine Textur gerendert, müsste ich das auch machen? Die Daten die ich dann möcht kann ich doch so aus dem Buffer auslesen oder? * Generell kann ich mir nicht wirklich zusammenreimen wie ich die "Zoom-Matrix" und die Projektionsmatrix vereine. ( Auch über glMultMatrix mit glMatrixMode GL_PROJECTION?) * Das Prinzip an der ganzen Sache habe ich verstanden nur leider hängts an einigen Wissenslücken
im Tutorial wird gesagt dass man Version 1.1 benötigt ich hatte 1.4. (Fehler?). Nun mit neuen Treibern und Version 2.0 liefs dann auch und das ist dabei rumgekommen.
Du brauchst mindestens OpenGL 1.1 und die Extension GL_EXT_FRAMEBUFFER_OBJECT.
Zitat:
<-- Auch ist mir nicht wirklich klar warum man die W-Zeile nutzt
Liegt daran ob deine Matrizen transponiert sind oder nicht. Bei mir ist die entsprechende Zeile die Translation. Ggf. einfach Zeilen/Spalten vertauschen. Meine Matrix ist wie folgt definiert und wird auch in dieser Reihenfolge an OpenGL übergeben:
Nun zunächst meintest du ich sollte nur in den Z-Buffer rendern, leider hab ich keine Ahung wie ich das anstellen kann
glColorMask und glDepthMask sind deine Freunde Also einfach glColorMask(false, false, false, false) und dann wieder glColorMask(true, true, true, true).
Zitat:
Im Tutorial wird in eine Textur gerendert, müsste ich das auch machen? Die Daten die ich dann möcht kann ich doch so aus dem Buffer auslesen oder?
Beim erstellen deines FBOs hast du eine Textur drangehängt. Nach dem aushängen des FBO sind da deine gerenderten Daten drin. Du musst also diese Textur in den Hauptspeicher kopieren. Z.B. so:
Generell kann ich mir nicht wirklich zusammenreimen wie ich die "Zoom-Matrix" und die Projektionsmatrix vereine. ( Auch über glMultMatrix mit glMatrixMode GL_PROJECTION?)
Exakt....mit glMultMatrix die Zoom-Matrix an die Projektion dran multiplizieren. Modelview bleibt unverändert. Du musst aber aufpassen, dass du das an der richtigen Seite dran multiplizierst.
Zitat:
{ Licht } ... unwichtig, denke ich mal
Nicht ganz, das Licht muss aus, die Farben dürfen nicht verfälscht werden.
Hallo, also das generelle Rendern in das FBO scheint zu funktionieren, nur kriege ich das Verschieben der "ZoomMatrix" auf die aktuelle Cursorpostion nicht richtig hin. Berechnen tue ich meine Matrix (zu Beginn ist es noch eine Einheitsmatrix) noch immer wie folgt:
Bewege ich meine Maus bleibt die Textur nahezu unverändert, die Werte für MatWindow[3,0/1] können ja max +/- Scale annehmen, aber erst ab Werte über 1000 kriege ich auch andere Ausschnitte in den "Zoom". Die 3. Spalte also W gibt meines erachtens ja die Position an, man müsste hier doch theoretisch also die X/Y Koordinaten angeben. Aber wie ich auf die komme ist mir gerade Schleierhaft :< Hier mal der Rendercode:
Code:
procedure TForm1.RenderFBO; begin glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); glClearColor(0.0,0.0,0,0); glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); MyGL.SetProjection; MyGL.Camera.SetView; glMultMatrixf(@MatWindow);
procedure TMyCamera.SetView; begin glMatrixMode(GL_PROJECTION); glTranslatef(mPosition[0],mPosition[1],mPosition[2]); <-- Werte(0,0,-1000) glMultMatrixf(@mRotation); <- Zur Zeit Einheitsmatrix end;
Die 3. Spalte also W gibt meines erachtens ja die Position an, man müsste hier doch theoretisch also die X/Y Koordinaten angeben. Aber wie ich auf die komme ist mir gerade Schleierhaft :<
Wenn bei dir die 3. Spalte die Position ("Translation") ist, dann musst du meine Matrix transponieren, also Zeilen und Spalten vertauschen:
So hatte ich es auch schon ausprobiert und das Ergebnis (Ich lasse mir die Textur auf eine Quad zeichnen) sah recht merkwürdig aus. Mit der Matrix wie ich sie verwende bekomme ich schonmal eine Vergrößerung auf die Stelle der Szene. Das Problem ist nur dass ich den Bildabschnitt nicht am Cursor positioniert bekomme. Mit dieser Rechnung "-Cursor[0] * Scale[0]" würde ich meinen Ausschnitt ja maximal auf Scale[0] bringen können. Kleines Beispiel:
Code:
Bild: 640x480 Radius: 5 Scale: 64 / 48
Wenn ich den Cursor jetzt ganz Rechts in der Mitte habe ist X=1 und Y = 0 und dann meine Matrix würde auf X=64 zoomen!? Müsste es nicht eigentlich der Bildabschnitt bei 320/0 abgebildet werden? Ich habe das ganze auch mit -Cursor[0] * (Viewport[2]/2) probiert, aber auch hier hat er sich nicht bis nach ganz außen bewegt. Es war zwar schon mehr Bewegung im Spiel aber hat die Bewegung mit steigendem X immer mehr abgenommen. Ich habe den Verdacht dass hier irgendwie die Perspektive was zu tun hat. Ich habe mir mal vor dem glMult die Projektionsmatrix ausgegeben und sie war nicht "einheitlich" (sie müsste es aber doch sein?), ich nehme an das könnte durch das Setzen von gluPerspective kommen!? Ich habe das Programm leider jetzt nicht mehr zur Hand, aber meine Matrix sieht vor dem Mult ungefähr so aus:
Zitat:
1,28 0 0 0 0 1,38 0 0 0 0 -1 0 0 0 -1 -1
Nach dem glMult dann ca. so (Das wäre dann für Cursor ganz rechts:
Zitat:
80 0 0 -81 0 63 0 32 0 0 -1 0 0 0 -1 -1
Eigentlich ist es von der Sache her ja logisch, Ich "vergrößere" die X/Y-Achsen und positioniere meine Projektion so, dass ich auf die Position des Cursors schaue (Wie eine Bildschirmlupe). Nur leider verstehe ich nicht wie ich an die Position komme, womöglich über glUnProject etc. das werde ich morgen probieren
Und ich hatte noch eine Frage die mir gekommen ist: Ich kann meine Textur ja trotzdem schon Auslesen. Als Werte der Pixel erhalte ich Zahlen wie 4291234124 (10 Stellen) als RGBA Werte. Ich habe leider nirgends wirklich finden können wie ich diese in die einzelnen Komponenten R/G/B/A zerlegen kann um dann auf die Farbe deuten zu können. Ich nehme an dass es was mit 2^8 Bit pro Farbe zu tun hat und diese verrechnet wurden (2^8)^4 = 4294967296 ist ja schon ziemlich nah an diesen Zahlen^^. Bietet OpenGL da Funktionen oder ist es noch simpler?
Also das was aus deiner Projektionsmatrix raus kommt ist ein Spaltenvektor v = (x,y,z,w). Die Werte von x,y und z liegen im Bereich -w....w. Wir wollen das nun so skalieren bzw. verschieben das der Bereich um den Cursor im Bereich -w...w liegt. Wir können annehmen das w = 1 ist, das kürzt sich später nämlich raus.
Als erstes verschieben wir so, dass der Cursor auf (0,0) abgebildet wird. Dazu müssen wir die Cursorposition c einfach abziehen: x' = x - c Nun muss noch so skaliert werden das eine Enfernung von [Radius] Pixeln dem Rand des FBOs entspricht: x'' = x' * scale wobei scale = ( width / (2*radius) )
Insgesamt also: x'' = x * scale - c * scale
Zitat:
Wenn ich den Cursor jetzt ganz Rechts in der Mitte habe ist X=1 und Y = 0 und dann meine Matrix würde auf X=64 zoomen!?
Ja, du musst 64 fach ranzoomen. Der Bereich um den Cursor herum wird ja quasi auf den ganzen Bildschirm aufgezogen. Also bei mir läuft der Code, das funktioniert so
Hallo, tut mir Leid wenn ich mittlerweile ein bissl nerve, aber ich hab jetzt das ganze nochmal ein bischen beobachtet und ich glaube ich habe das Problem eingrenzen können.
Ich glaube das Problem liegt bei mir an der Kamera, und dass ich die Verschiebung durch diese nicht richtig verrechne (eigentlich fliest die garnicht in die Berechnung ein, nur im Rendern). Ich hab es eben noch einmal ohne die Kamera probiert und die Szene direkt auf Z = -1 (sonst bei Z=-750) ausgeben lassen, und es hat funktioniert. Je höher ich das Z dann gesetzt habe, desto verzerrter wurde das Ergebnis.
Im Prinzip läuft das Rendern bei mir so ab (Beispiel mit Z=-750): Ohne FBO
Wie gesagt je mehr meine Kamera aus der Szene raus geht, desto ungenauer wird es, somit denke ich muss es die Kamera sein. Ich nehme an da du ja auch scheinbar die Kamera-Matrix ausliest die noch in die Berechnung der Zoom-Matrix einfließen müsste:/ Leider kann ich mich in dieses ganze System noch nicht richtig reindenken.
Grüße
//Edit: So wie ich mir das Vorstelle, wird zunächst gezoomt und danach die Szene nach hinten gesetzt. Wenn der Cursor am Null-Punkt liegt, kann ich alle Linien perfekt "ranzoomen" die auch auf X/Null,Null/Y liegen, dabei spielt Z keine Rolle. Liegt der Cursor aber nicht auf einer Achse läuft es doch aber so: Er zoomt ran, danach wird das Modell durch die Kamera gesetzt (Z > 1). Der Zoom blickt zwar auf die Koordinate wo die Linie in der Normalen Szene ist, aber durch die Perspektive wir die Linie leicht versetzt gezeichnet (um halt räumlich zu wirken) und landet irgendwo neben dem Zoom!? Theoretisch könnte ich die Szene doch auch orthogonal zeichnen, womit die Perspektive keinen Einfluss mehr hätte. Aber da du es ja auch ohne gemacht hast, muss es ja auch so funktionieren :S
Ich kann mir nie merken wie rum das muss...weil ich halt mein eigens System verwende
Zitat:
Ich nehme an da du ja auch scheinbar die Kamera-Matrix ausliest die noch in die Berechnung der Zoom-Matrix einfließen müsste:/ Leider kann ich mich in dieses ganze System noch nicht richtig reindenken.
Nein, die ModelView-Matrix wird einfach übernommen. Das ganze ist bei mir eine Kamera-Klasse und der Konstruktor der Selektions-Kamera nimmt halt ein anderes Kamera-Objekt als Quelle und liest da die ModelView+Projektions-Matrix.
Eigentlich auch logisch wenn man sich den Wiki-Artikel zu gluPerspective mal komplett durchliest
Jetzt muss ich nur noch die Konvertierung der GL_RGBA Werte in die einzelnen Komponenten hinkriegen und ich kann meine Linien identifizieren. Nur stellt sich mir immer noch die Frage wie ich von diesen Werten auf meine R/G/B/A - Anteile komme. Oder brauch man die vll garnicht. Ich belese mich gerade zu Color-Indizes, ist das schon die richtige Richtung oder muss man da einen ganz anderen Weg gehen?
Ahh wollte es gerade rauseditieren weil ich selber drauf gestoßen bin^^
Trotzdem ein großes Dankeschön an dich, du hast mir echt super geholfen Ich werd mich jetzt mal dran probieren meine Linien zu identifizieren. Sollte jetzt ja kein großes Problem mehr darstellen.
Hallo, also die Implementierung hat soweit relativ problemlos geklappt. Nun stellen sich mir jedoch wieder zwei weitere Probleme. Ich denke mal da es ja alles zum Thema hier gehört lohnen sich da extra Threads nicht, weshalb ich einfach hier weiter frage
1. Hier habe ich noch ein Problem mit dem Rendern der Szene in den Z-Buffer. Ich habe mir glColorMask & glDepthMask angeschaut und auch nach deren Verwendung gesucht aber bin noch nicht wirklich schlauer. Ich bin mir zwar über deren Wirkung bewusst, aber nicht wie sie mir behilflich sein könnten den gwünschten Effekt zu erzielen.
1.1. Das Selektieren der Kanten klappt ja einwandfrei, nur habe ich halt hier ähnlich wie in 1. das Problem dass Kanten die eigentlich nicht sichtbar sind, auch gezeichnet werden und somit selektierbar werden.
2. Wenn mein Nutzer seine Kante gefunden hat, soll er auch einen Messpunkt auf dieser platzieren können. Ich habe mir bereits eine Funktion gebaut welche mir anhand des Cursors und dem Kantenvektor diesen Messpunkt berechnet. Jedoch komme ich einfach nich an die Koordinaten des Cursors. Ich habe bereits das Beispiel aus gluUnProject genutzt, da kriege ich jedoch keine brauchbaren Werte, da für Z meist willkürliche Werte kommen (Maus muss sich ja nicht über etwas befinden). Ich habe mir hier überlegt ob man nicht ähnlich wie bei der Textur auch die Z-Werte auslesen könnte? Jedoch tappe ich hier noch ein bischen im Dunkeln.
//Gerade noch gesehen, mein FBO hat ja einen Tiefenbuffer, leider weis ich nicht wie ich auf diese zugreifen kann da es nur ein GluInt ist :<
Mitglieder in diesem Forum: 0 Mitglieder und 0 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.