OpenGL Kamera Problem



  • Das hier ist deine Matrix, wenn ich das richtig sehe:
    http://upload.wikimedia.org/math/5/d/5/5d5a3e63e4a88919fa13d1b3a79ccba7.png

    Damit hast Du doch schon alles zusammen, was für eine Vektorkamera notwendig ist. Eigentlich solltest Du meinen Code jetzt direkt übernehmen können.

    (Anmerkung: Bei Roll, Pitch und Yaw sollte man den dritten Vektor des Kamerasystems vielleicht noch aus dem Kreuzprodukt der beiden anderen bilden. Als Vorsichtsmaßnahme gegen Rundungsfehler, damit die Vektoren stets orthogonal sind.)



  • µ schrieb:

    Das hier ist deine Matrix, wenn ich das richtig sehe:
    http://upload.wikimedia.org/math/5/d/5/5d5a3e63e4a88919fa13d1b3a79ccba7.png

    Bei genauem Hinsehen fällt auf, dass ich genau diese Matrix nutze. :p

    µ schrieb:

    Damit hast Du doch schon alles zusammen, was für eine Vektorkamera notwendig ist. Eigentlich solltest Du meinen Code jetzt direkt übernehmen können.

    Ich würde eigentlich gerne bei der Matrix bleiben. Oder zumindest verstehen, warum das nicht funktioniert.

    µ schrieb:

    (Anmerkung: Bei Roll, Pitch und Yaw sollte man den dritten Vektor des Kamerasystems vielleicht noch aus dem Kreuzprodukt der beiden anderen bilden. Als Vorsichtsmaßnahme gegen Rundungsfehler, damit die Vektoren stets orthogonal sind.)

    Ja, könnte helfen gegen Rundungsfehler. Werde ich vielleicht noch einbauen. Aber leider hilft es nicht gegen mein Problem. 😞

    Hier möchte ich noch einmal drauf eingehen:

    rapso schrieb:

    sieht irgendwie aus als ob du die transformation immer weiter auf die vorherigen transformationen draufrechnest. dann wuerde das verhalten durchaus normal sein, esseiden ich missverstehe was das problem sein soll.

    Genau das ist der Fall. Wie könnte ich es anders machen? Ich muss das lokale Koordinatesystem ja irgendwie speichern? (Hier halt in der Matrix.)



  • Ich glaube Rapso meinte, dass Du die aktuelle Transformation (fälschlicherweise) immer zur Modelview-Matrix multiplizierst. Die muss aber in jedem Frame zurückgesetzt werden. Das lokale Koordinatensystem der Kamera wird aber natürlich bei jeder Bewegung verändert und gespeichert.

    cooky451 schrieb:

    Ich würde eigentlich gerne bei der Matrix bleiben. Oder zumindest verstehen, warum das nicht funktioniert.

    Ob Du forward, right, up einzeln als Vektoren oder zusammen in einer Matrix speicherst ist wohl unerheblich, wenn am Ende das gleiche rauskommt. Versuchs mit den Vektoren, Code steht oben. Wenn das funktioniert, press alles in eine Matrix.

    Eigentlich ist die Lösung hier im Thread. Du musst es nur umsetzen 😉



  • µ schrieb:

    Eigentlich ist die Lösung hier im Thread. Du musst es nur umsetzen 😉

    Ich mache ja eigentlich alles genau so, nur halt, dass ich um die globalen Achsen rotiere. Wenn ich wie beschrieben die lokalen nehme, sieht das so aus.
    Interessant wäre halt noch wie CreateFromAxisAngle() und Transform() implementiert sind, denn wenn sie das so sind wie ich mir das vorstelle, dürfte sich nichts ändern. 😉

    Allerdings bin ich so verzweifelt, dass ich es jetzt trotzdem mit "direkten" Vektoren versuche.



  • Ok, heute habe ich wieder etwas Zeit gefunden um zu basteln. Ich habe mal alles mit Vektoren aufgezogen, allerdings verhalten die sich (warum wusste ich das? :D) genau so wie die Matritzen.
    Eine Sache ist mir allerdings noch aufgefallen. Wenn ich um die globalen Achsen rotiere, muss ich, damit es funktioniert, die Matrix am Ende so erstellen:

    mat4 rotation
          (vec4(right_[0], right_[1], right_[2], 0.f), 
           vec4(up_[0], up_[1], up_[2], 0.f), 
           vec4(forward_[0], forward_[1], forward_[2], 0.f), 
           vec4(0.f, 0.f, 0.f, 1.f));
    

    Wenn ich dagegen um die lokalen Achsen rotiere bzw. mich auf diesen fortbewege, muss ich die Matrix so erstellen:

    mat4 rotation
          (vec4(right_[0], up_[0], forward_[0], 0.f), 
           vec4(right_[1], up_[1], forward_[1], 0.f), 
           vec4(right_[2], up_[2], forward_[2], 0.f), 
           vec4(0.f, 0.f, 0.f, 1.f));
    

    Vertausche ich das Ganze, bewege (und rotiere) ich mich unabhängig von der Rotation. Kann da vielleicht schon jemand sehen, was ich falsch mache?

    (PS: Die Position wird im Shader dazugerechnet, da muss ich dann am Ende gucken, ob ich das noch vereinen kann.)

    (PPS: Es geht immernoch um den anfangs beschriebenen Effekt. Der bleibt auch mit Vektoren genau so erhalten.)



  • Generierst Du am Ende auch eine Matrix mit Hilfe einer LookAt-Funktion, oder verwendest Du direkt diese als Modelview-Matrix?

    Vielleicht solltest Du mal den ganzen Code der Kamera bis hin zum setzen der MV-Matrix hier posten.



  • µ schrieb:

    Vielleicht solltest Du mal den ganzen Code der Kamera bis hin zum setzen der MV-Matrix hier posten.

    Wollte ich vermeiden, da etwas unübersichtlich. Aber ich werde wohl nicht drum rum kommen.
    Unwichtige Teile (Shader Aktivierung etc.) wurden entfernt.

    class Object
    {
    protected:
      vec3 forward_, right_, up_;
      vec3 position_;
    public:
      Object(ShaderProgram *shader, vec3 const& pos)
        : right_(1.f, 0.f, 0.f), 
          up_(0.f, 1.f, 0.f), 
          forward_(0.f, 0.f, 1.f), 
          position_(pos)
      {}
      virtual ~Object() = 0
      {}
      void moveX(f32 amount)
      {
        position_ -= vectorX() * amount;
      }
      void moveY(f32 amount)
      {
        position_ -= vectorY() * amount;
      }
      void moveZ(f32 amount)
      {
        position_ -= vectorZ() * amount;
      }
      void rotateX(f32 angle)
      {
        mat3 m = lin::rotate(vectorX(), angle);
        //mat3 m = lin::rotateX(angle);
        up_ = m * up_;
        up_.normalize();
        forward_ = m * forward_;
        forward_.normalize();
      }
      void rotateY(f32 angle)
      {
        mat3 m = lin::rotate(vectorY(), angle);
        //mat3 m = lin::rotateY(angle);
        right_ = m * right_;
        right_.normalize();
        forward_ = m * forward_;
        forward_.normalize();
      }
      void rotateZ(f32 angle)
      {
        mat3 m = lin::rotate(vectorZ(), angle);
        //mat3 m = lin::rotateZ(angle);
        right_ = m * right_;
        right_.normalize();
        up_ = m * up_;
        up_.normalize();
      }
      vec3 vectorX()
      {
        return right_;
      }
      vec3 vectorY()
      {
        return up_;
      }
      vec3 vectorZ()
      {
        return forward_;
      }
      virtual void render() = 0;
    };
    

    Kamera:

    class Camera : public Object
    {
      mat4 frustum_;
    public:
      Camera(ShaderProgram *shader, vec3 const& pos)
        : Object(shader, pos)
      {
        setPerspective(45.f, 16.f / 9.f, 1.f, 10000.f);
      }
      void setPerspective(f32 angle, f32 aspect, f32 min, f32 max)
      {
        using namespace std;
        f32 rad = lin::make_rad(angle);
        frustum_ = mat4
          (vec4(1.f / tan(rad), 0.f, 0.f, 0.f), 
           vec4(0.f, aspect / tan(rad), 0.f, 0.f), 
           vec4(0.f, 0.f, (min + max) / (min - max), 1.f), 
           vec4(0.f, 0.f, -2.f * min * max / (min - max), 0.f));
      }
      void render()
      {
        glUniformMatrix4fv(shader_frustum_, 1, GL_FALSE, frustum_[0]);
        glUniform3fv(shader_cam_translation_, 1, &position_[0]);
        //mat4 rotation
        //  (vec4(right_[0], right_[1], right_[2], 0.f), 
        //   vec4(up_[0], up_[1], up_[2], 0.f), 
        //   vec4(forward_[0], forward_[1], forward_[2], 0.f), 
        //   vec4(0.f, 0.f, 0.f, 1.f));
        mat4 rotation
          (vec4(right_[0], up_[0], forward_[0], 0.f), 
           vec4(right_[1], up_[1], forward_[1], 0.f), 
           vec4(right_[2], up_[2], forward_[2], 0.f), 
           vec4(0.f, 0.f, 0.f, 1.f));
        glUniformMatrix4fv(shader_cam_rotation_, 1, GL_FALSE, rotation[0]);
      }
    };
    

    GLSL-Code:

    #version 110
    
    uniform mat4 frustum;
    uniform vec3 cam_translation;
    uniform mat4 cam_rotation;
    
    uniform vec3 translation;
    uniform mat4 rotation;
    
    attribute vec4 position;
    
    mat4 translate(vec3 v)
    {
        return mat4(vec4(1.0, 0.0, 0.0, 0.0), 
                    vec4(0.0, 1.0, 0.0, 0.0), 
                    vec4(0.0, 0.0, 1.0, 0.0), 
                    vec4(v.x, v.y, v.z, 1.0));
    }
    
    void main()
    {
      gl_Position = frustum * cam_rotation * translate(cam_translation) * 
        translate(translation) * rotation * position;
    }
    

    Falls die Vermutung besteht, dass die Mathe-Funktionen (rotate, operator * etc.) falsch sind, werde ich die auch noch posten.



  • Nur kurz überflogen weil ich jetzt in die Falle muss 😉

    cooky451 schrieb:

    mat4 rotation
          (vec4(right_[0], up_[0], forward_[0], 0.f), 
           vec4(right_[1], up_[1], forward_[1], 0.f), 
           vec4(right_[2], up_[2], forward_[2], 0.f), 
           vec4(0.f, 0.f, 0.f, 1.f));
        glUniformMatrix4fv(shader_cam_rotation_, 1, GL_FALSE, rotation[0]);
    

    Versuche mal aus dieser Umrechnung deine MV-Matrix zu erstellen, statt right, up und forward direkt zu verwenden:
    http://wiki.delphigl.com/index.php/gluLookAt



  • Hm.. also damit bekomme ich sehr eigenartige Effekte. 😃

    template<typename T>
      inline BlockMatrix4<T> lookAt(GeometricVector3<T> const& eye, 
        GeometricVector3<T> const& lookat, GeometricVector3<T> up)
      {
        vec3 forward = lookat - eye;
        forward.normalize();
        up.normalize();
        vec3 side = cross(forward, up);
        side.normalize();
        up = cross(side, forward);
        // up.normalize(); ?
        BlockMatrix4<T> mat
          (GeometricVector4<T>(side[0], side[1], side[2], 0.0), 
           GeometricVector4<T>(up[0], up[1], up[2], 0.0),
           GeometricVector4<T>(forward[0], forward[1], forward[2], 0.0),
           GeometricVector4<T>(0.f, 0.f, 0.f, 1.0));
        return mat;
      }
    


  • Das:

    mat4 translate(vec3 v)
    {
        return mat4(vec4(1.0, 0.0, 0.0, 0.0),
                    vec4(0.0, 1.0, 0.0, 0.0),
                    vec4(0.0, 0.0, 1.0, 0.0),
                    vec4(v.x, v.y, v.z, 1.0));
    }
    

    ist keine Translation wenn du von Rechts multiplizierst, sondern eine verzerrung entlang der w-koordinate. transponier das.

    Ich war also mit meinem ersten Kommatar ganz nah ran:

    So einen Effekt kannst du aber kriegen, wenn du row major/column major in der Projektionsmatrix vertauschst.



  • otze schrieb:

    ist keine Translation wenn du von Rechts multiplizierst, sondern eine verzerrung entlang der w-koordinate. transponier das.

    OpenGL erwartet diese Anordnung. Ist leider alles etwas nervig, weil man bei jeder Matrix aufpassen muss, welcher Ordnung die jetzt folgt. Wie gesagt, ist diese Matrix aber korrekt, falls du allerdings im restlichen Code eine nicht dazu passende Matrix findest, zeig sie gerne auf.

    (Zeile 29 - 33 passen natürlich nicht dazu, deshalb hatte es mich anfänglich ja auch so gewundert, dass das so auf einmal "funktioniert". Da muss also irgendwie vorher ein Haken sein, ich komme nur nicht drauf. Muss jetzt leider kurz ~2 Stunden weg.)



  • Den Effekt hatte ich auch mal. Der ist auch logisch, wenn man sich überlegt, dass man beim Rotieren um die x-Achse die lokalte Y-Koordinate der Kamera nach vorne kippt. Sobald man dann um die y-Achse rotiert, wird die Kamera dann schief und dreht sich relativ gesehen zur globalen z-Achse.
    Was man machen muss ist, dass man nicht um die lokale y-Achse der Kamera rotiert, sondern um die globale. D.h. man muss erst die globale y-Achse (0, 1, 0) in das lokalte Koordinatensystem der Kamera transformieren (Kamera-Matrix * (0, 1, 0)), dann aus dieser Matrix die die "Position" herausfiltern (also die ersten 3 Elemente der letzten Spalte), welche dann die Achse darstellen, um die man dann die Kamera rotieren muss.



  • Powerpaule schrieb:

    D.h. man muss erst die globale y-Achse (0, 1, 0) in das lokalte Koordinatensystem der Kamera transformieren (Kamera-Matrix * (0, 1, 0)), dann aus dieser Matrix die die "Position" herausfiltern (also die ersten 3 Elemente der letzten Spalte), welche dann die Achse darstellen, um die man dann die Kamera rotieren muss.

    Würde ich ja gerne testen, aber Matrix * Vektor gibt bei mir am Ende wieder einen Vektor. Was soll ich da rausfiltern? (Ich habe es natürlich auch mal mit dem Vektor als Rotationachse getestet, aber das funktioniert leider auch nicht so richtig.)

    Edit:
    Zumal logischerweise auch nichts an der Position geändert wird. So funktioniert es einigermaßen:

    void rotateY(f32 angle)
      {
        mat4 mat = vec4(0.f, 1.f, 0.f, 0.f) * rotation_;
        vec3 rot(mat[1][0], mat[1][1], mat[1][2]);
        rot.normalize();
        rotation_ *= lin::rotate(rot, angle);
      }
    

    Das alte Problem wird dadurch aber nicht gelöst, immer noch dasselbe Verhalten. Das mit dem "reinrotieren" war übrigens auch meine Ursprungsidee, aber irgendwas hat da nicht gestimmt..



  • cooky451 schrieb:

    Powerpaule schrieb:

    D.h. man muss erst die globale y-Achse (0, 1, 0) in das lokalte Koordinatensystem der Kamera transformieren (Kamera-Matrix * (0, 1, 0)), dann aus dieser Matrix die die "Position" herausfiltern (also die ersten 3 Elemente der letzten Spalte), welche dann die Achse darstellen, um die man dann die Kamera rotieren muss.

    Würde ich ja gerne testen, aber Matrix * Vektor gibt bei mir am Ende wieder einen Vektor. Was soll ich da rausfiltern? (Ich habe es natürlich auch mal mit dem Vektor als Rotationachse getestet, aber das funktioniert leider auch nicht so richtig.)

    Wenns bei dir schon einen Vektor ergibt, müsste das dann eigentlich der Vektor sein um den du rotieren musst... bei mir kommt da halt nur eine Matrix raus, da muss man halt den zusätzlichen schritt noch einbauen^^ Ansonsten müsste das eigentlich genau die Lösung sein, denn bei mir war das Problem genau das gleiche, und wenn man es sich wie beschrieben durchspielt (also gekippte y-Achse bei Rotation um x-Achse), dann sieht man ja doch ganz gut dass man da dann um die falsche Achse rotiert.



  • Powerpaule schrieb:

    Wenns bei dir schon einen Vektor ergibt,

    Also soweit ich informiert bin sollte das immer einen Vektor ergeben. Oder gibt es da irgendwie mathematische Ausnahmefälle?

    Powerpaule schrieb:

    müsste das dann eigentlich der Vektor sein um den du rotieren musst...

    Habe ich wie gesagt schon getestet, das "funktioniert" dann genau so, als würde ich um die normalen (lokalen) Achsen rotieren.

    Powerpaule schrieb:

    bei mir kommt da halt nur eine Matrix raus,

    Macht mich etwas stutzig.. hast du das selbst geschrieben oder nutzt du eine Mathe-lib?

    Powerpaule schrieb:

    (also gekippte y-Achse bei Rotation um x-Achse), dann sieht man ja doch ganz gut dass man da dann um die falsche Achse rotiert.

    Hast du dir die Videos aus dem Einangspost angesehen? Ich habe irgendwie das Gefühl, dass du von einem anderen Problem sprichst. Es geht darum, dass bei den Rotationen links, oben, rechts, unten, eine Rotation um die Z-Achse stattfindet, die gar nicht da sein sollte. (Bzw. sich genau aufheben sollte.)



  • Folgender Vorschlag:

    1. Breche das Projekt mal auf ein Minimum herunter. Alles was nicht benötigt wird, sollte rausfliegen.
    2. Eliminiere jeden Shader-Code und programmiere rein klassisch
    3. Implementiere die Kamera 1:1 wie in dem Code von mir
    4. Wenn es dann immer noch nicht funktioniert lade das Projekt (kompilierbar in Visual Studio 2008, ohne Shader, ohne sonstigen problemfremden Code, nur das isolierte Problem mit minimaler Szene!) irgendwo hoch, ich würde es mir dann ansehen und den Fehler suchen. Mit etwas Gedult, bestimmt auch finden. Bin sehr hartnäckig was Rätsel betrifft 😉


  • Vielen Dank für das Angebot, ich hätte es auch fast angenommen. 😉
    Die Lösung ist eigentlich ganz einfach, und irgendwie auch erschreckend logisch. 😞
    Ein Vektor (hier der up-Vektor) muss konstant bleiben. Diese Drehen muss immer gleich bleiben. Die anderen Beiden Vektoren müssen sich aber mitdrehen. Manchmal hilft nachdenken halt doch mehr als stumpfes Testen, ich hatte natürlich immer nur alle Vektoren global oder alle lokal gehalten.
    (Bei einer Rotation um die Z-Achse muss man den "up"-Vektor natürlich ändern.)
    Edit:
    Na, das mit dem up-Vektor ändern klappt leider noch nicht so. Aber da bin ich auf dem richtigen Weg, leider muss ich jetzt erst mal eine Woche weg. 🙂

    Eine kleine Frage hätte ich aber noch: Ich nutze jetzt Quaternionen, welche ja angeblich recht performant sein sollen. Aber wenn ich die jetzt um eine eigene Achsen rotiere, muss ich ja erst eine Matrix aus dem Quaternion erstellen, dann die globale Achse über diese Matrix in das lokale Koordinatensystem drehen, nur um nachher wieder ein Quaternion um diese Achse zu erstellen. Das muss doch schneller möglich sein?


Anmelden zum Antworten