Gerade bin ich dabei, ein relativ kleines Spiel zu programmieren. Allerdings bin ich noch Anfänger in OpenGL und habe jetzt ein Problem mit der Kamera: In dem Spiel habe ich ein Labyrinth in dem man sich in der First-Person-Perspektive bewegen soll. Ich habe Variablen für die Verschiebung in X-, Y- und Z- Richtung (Y wird später ja nicht mehr gebraucht, sollte aber zunächst trotzdem möglich sein) und für die Rotation um alle Achsen. Anfangs hat es ganz gut funktioniert, alle Objekte in der Szene mit glRotate zu drehen, aber wenn ich mich dann in der Szene bewegen wollte, habe ich mich entweder um den Mittelpunkt gedreht oder ich musste eine andere Richtungstaste drücken, als für die Richtung, in die ich eigentlich wollte...
Wie mache ich sowas am besten? Mit gluLookAt komme ich auch nicht ganz zurecht. Entweder kippt dann die Sicht oder es tritt das gleiche Problem auf wie mit glRotate und glTranslate.
Hier mal C++-Code aus meiner Diplomarbeit. CombinedCamera ist eine Camera-Klasse die eine Ego-Shooter-Camera ("Free-Mode") und eine Orbit-Camera ("Orbit-Mode") in einem vereint. Mein lookAt habe ich selbst implementiert, es funktioniert aber genauso. Der Unterschied liegt darin, dass man direkt Vektoren übergeben kann und nicht jeden Wert einzeln. Außerdem wird natürlich die resultierende Matrix zurückgegeben und nicht mit dem Stack multipliziert, aber das ist hier unwichtig. Der %-Operator ist bei mir überladen und berechnet das Kreuzprodukt zwischen zwei Vektoren. Ansonsten sollte überall klar sein was gemeint ist. Hier noch die wichtigsten Klassen-Attribute:
Code:
CML::Vector3f m_position; // Position der Kamera (Zentrum der Rotation) CML::Vector3f m_direction; // Blickrichtung CML::Vector3f m_up; // Vektor zeigt nach oben...nicht immer (0,1,0) float m_angleHorizontal; // entspricht deinem roty float m_angleVertical; // entspricht deinem rotx float m_speed; // aktuelle geschwindigkeit float m_zoom; // Zoomfaktor...wird für Orbit-Modus gebraucht float m_distance; // = 2 hoch 'm_zoom'
Code:
/** called by ViewerWidget::mainloop(). * @param cursor cursor transformed into space -1...1 * @param keys set of keycodes from Qt::Key. * @param wheel delta of the wheel in degree. * @param timeElapsed time in seconds since last mainloop iteration. Note that large values * may trigger a special reset action. * @return true if the camera does require an update and repaint of the scene, false otherwise. */ bool CombinedCamera::update(CML::Vector2f cursor, const std::set<int>& keys, float wheel, float timeElapsed) { bool updateRequired = false; if (timeElapsed > m_options->getResetTime()) { m_speed = m_options->getSpeedMin(); } else { CML::Vector3f front, side, up; switch (m_cameraMode) { case FREE_MODE: { front = m_direction; side = m_up % m_direction; up = m_up; break; } case ORBIT_MODE: { front = CML::Vector3f(m_direction.x, 0.0f, m_direction.z).normalized(); side = CML::Vector3f(front.z, 0.0f, -front.x); up = CML::Vector3f(0.0f, 1.0f, 0.0f); break; } default: qCritical("CombinedCamera: Unknown camera mode!"); return false; } CML::Vector3f dir(0,0,0); std::set<int>::const_iterator notfound = keys.end(); if ( keys.find(Qt::Key_W ) != notfound || keys.find(Qt::Key_Up ) != notfound ) { dir += front; } if ( keys.find(Qt::Key_S ) != notfound || keys.find(Qt::Key_Down ) != notfound ) { dir += -front; } if ( keys.find(Qt::Key_A ) != notfound || keys.find(Qt::Key_Left ) != notfound ) { dir += side; } if ( keys.find(Qt::Key_D ) != notfound || keys.find(Qt::Key_Right) != notfound ) { dir += -side; } if ( keys.find(Qt::Key_Q ) != notfound ) { dir += up; } if ( keys.find(Qt::Key_E ) != notfound ) { dir += -up; }
Tut mir Leid, aber so ganz verstehe ich den Quellcode nicht (was bedeutet z.B. += oder *=?). Gäbe es evtl. noch eine einfachere Möglichkeit. Im Grunde habe ich ja vor, dass vom aktuellen Standpunkt aus die Kamera verschoben wird und dann gedreht. Danach sollte aber die neue Drehausrichtung der Ausganspunkt dafür sein, wo vorne ist, damit man sich nicht plötzlich seitwärts bewegt.
In C++/Java und ähnlichen Sprachen ist "a += b + c;" eine Abkürzung für "a := a + (b + c);". Analog ist "a *= b + c;" eine Abkürzung für "a := a * (b + c);".
Der Vollständigkeit halber: Da ich für Vektoren eine Klasse verwende habe ich natürlich die entsprechenden Operatoren überladen. Also z.B. für zwei Vektoren A und B bedeutet C = A + B das ich komponentenweise addiere: C.x = A.x + B.x; C.y = A.y + B.y; C.z = A.z + B.z;
Zitat:
Gäbe es evtl. noch eine einfachere Möglichkeit.
Also ich mache abgesehen von dem Cursor/Mauswheel-Kram nichts anderes als du. Der wesentliche Unterschied liegt darin das ich mit m_position, m_direction und m_up die Vektoren für gluLookAt direkt berechne. Ich verwende kein glRotate. Das wesentliche liegt also in diesen Zeilen:
Des weiteren ist meine Kamerabewegung (im Free-Modus) immer in Blickrichtung, nicht entlang der Achsen. Das ist der Grund warum da zuerst diese Vektoren front, side und up berechnet werden. Der Vektor front ist die Blickrichtung, side zeigt nach links und up nach oben. Alles von der Kamera aus gesehen wohlgemerkt.
Den Kram mit dem Cursor/Mauswheel habe ich dazu getan, weil ich mir denke das dies wahrscheinlich das nächste sein wird nachdem du fragen wirst
Ich habe mal versucht, die Stelle zu übersetzen, aber das Problem ist immer noch das gleiche: Wenn ich mich gedreht habe und nach vorne will, bewege ich mich seitwärts...
Wenn ich mich gedreht habe und nach vorne will, bewege ich mich seitwärts...
Das liegt daran das du dein transx/y/z entsprechend den Koordinatenachsen änderst. Ich nehme mal an mit den Tasten A bzw. D willst du dich seitwärts bewegen:
Code:
if getasynckeystate(ord('D')) < 0 then transx := transx + 0.25*(TimeFactor); if getasynckeystate(ord('A')) < 0 then transx := transx - 0.25*(TimeFactor);
Das Problem: Wenn du dich gedreht hast, ist die X-Achse nicht mehr "seitwärts", sondern irgendwas anderes.
Du musst dir erst ein lokales Koordinatensystem berechnen um rauszufinden wo z.B. "links" ist. Bei mir sieht das so aus:
Code:
CML::Vector3f front, side, up;
// ...
front = m_direction; side = m_up % m_direction; up = m_up;
// ...
CML::Vector3f dir(0,0,0); std::set<int>::const_iterator notfound = keys.end(); if ( keys.find(Qt::Key_W ) != notfound || keys.find(Qt::Key_Up ) != notfound ) { dir += front; } if ( keys.find(Qt::Key_S ) != notfound || keys.find(Qt::Key_Down ) != notfound ) { dir += -front; } if ( keys.find(Qt::Key_A ) != notfound || keys.find(Qt::Key_Left ) != notfound ) { dir += side; } if ( keys.find(Qt::Key_D ) != notfound || keys.find(Qt::Key_Right) != notfound ) { dir += -side; } if ( keys.find(Qt::Key_Q ) != notfound ) { dir += up; } if ( keys.find(Qt::Key_E ) != notfound ) { dir += -up; }
CML::Vector3f move = dir * (m_speed * timeElapsed);
Was ist V (nochmal Velocity?) und warum rotierst du zweimal? 'tschuldigung für die vielen Fragen, aber wie gesagt, bin ich in OpenGL noch Anfänger (und mit Vektoren hatte ich so direkt wahrscheinlich auch noch nicht zu tun)
Ich habe mir jetzt einen neuen Ansatz überlegt, der bewirken soll, dass auch in die Richtung verschoben wird, in die man will. Für eine Verschiebung nach rechts sieht das bis jetzt so aus (funktioniert natürlich noch nicht, weiß aber auch noch nicht genau, warum; vielleicht finde ich es selbst noch irgendwann raus...):
Ich schätze, das (neue) Problem hat jetzt mit der 90 zu tun. Wahrscheinlich würde es aber auch nicht funktionieren, wenn ich für jeden 90°-Schritt eine andere Zahl (180, 270...; aber was ist dann mit dem negativen Bereich) nehmen würde, oder? Bin wohl gerade etwas verwirrt
[edit] So hatte ich mir das ungefähr aufgezeichnet:
Code:
transx(new)
^_ _ | ''- _ ----------|-----------_ ------- |+transx(new)I | / I | / I | / I | / I a | / I |-_ /a I +transz(new) | ''/ I |rotx | I | / | I | /90-rotx I transx(new)/ | I transz(new) ------------------------- I I I I I I I I Itransz Itransz I I I I I I
a=0.25*TimeFactor (wäre wahrscheinlich doch einfacher gewesen, das zu malen Ich hoffe, man kann erkennen, was ich meine.) [/edit]
Ich schätze, das (neue) Problem hat jetzt mit der 90 zu tun.
Ähm...bist du sicher das deine sin/cos-Funktionen mit Grad arbeiten? Üblicherweise wird da das Bogenmaß verwendet. Ich kenne mich aber mit Delphi nicht aus...
Also das was du haben willst hast du an anderer Stelle schon berechnet (nämlich für das LookAt):
Der Vektor dir ist die Richtung wo bei der Kamera "vorne" ist, also in die Richtung musst du dich bewegen wenn der User auf W drückt. Um einen Vektor nach links zu erhalten muss du das Kreuzprodukt aus dir und up bilden. Zur Erinnerung: Das Ergebnis des Kreuzproduktes ist ein Vektor der im rechten Winkel zu beiden Vektoren steht. In diesem Fall wäre das entweder ein Vektor der nach links oder nach rechts zeigt. Kreuzprodukt geht so:
Ich finde das ziemlich entmutigend. (Btw., wie alt wart ihr, als ihr das verstanden habt? )
Naja, für mich ist das alles ganz einfach....ich studiere aber auch Informatik mit Vertiefung in Computergrafik im 12. Semester Mach dir keine Sorgen, das wird schon...
Danke für den Tipp mit dem Bogenmaß (ich wusste vorher gar nicht, was das ist )! Jetzt funktioniert aucht meine Rechnung!
Code:
function DegSin(X: Extended): Extended; begin result := sin(X*PI/180); end;
function DegCos(X: Extended): Extended; begin result := cos(X*PI/180); end;
//...
if getasynckeystate(ord('S')) < 0 then //move backward begin transx := transx+0.25*TimeFactor*degcos(90-roty); transz := transz-0.25*TimeFactor*degsin(90+roty); end; if getasynckeystate(ord('W')) < 0 then //move forward begin transx := transx-0.25*TimeFactor*degcos(90-roty); transz := transz+0.25*TimeFactor*degsin(90+roty); end; //... die restlichen Tasten muss ich noch fertig machen
Danke nochmal für die vielen Antworten! Bin jetzt aber froh, dass ich meinen eigenen Code verwenden kann und keinen fremden, den ich nicht ganz verstehe
Mitglieder in diesem Forum: Bing [Bot], Majestic-12 [Bot] und 8 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.