[OpenGL] Kamera um Objekt herum drehen



  • Hallo zusammen,

    ich möchte mit OpenGL um einen Würfel, der im Ursprung liegt mit der Kamera drum herumdrehen.
    Zuerst habe ich das mit gluLookAt + Kugelkoordinaten versucht, das hat aber leider nicht gut geklappt (z.B. in X-Richtung wars so, dass es eigentlich ganz ok aussah, aber bei 180 grad hat sich auf einmal alles verdreht).

    Ich wollte das jetzt einfach mit glRotatef machen. Ich weiß, dass das deprecated ist, aber im Moment interessiert mich das nicht wirklich.

    Ich habe das jetzt nach dem Schema gemacht:
    Ich habe 2 variablen, die jeweils für einen Winkel stehen und beim zeichnen mach ich dann

    glLoadIdentity();
    glRotatef(xAngle, 0, 1, 0);
    glRotatef(zAngle, 1, 0, 0);
    

    Bei Mausbewegungen oder Tastatureingaben verändere ich die beiden Winkel (pfeiltaste nach rechts -> xAngle wird höher).
    Das ganze funktioniert halbwegs, hat aber ein paar komische Effekte.
    Wenn ich z.B. erst um 180 Grad nach rechts drehe und dann an der y-achse drehe, drehe ich dir y-achse in die entgegengesetzte richtung zu der die ich eigentlich erwarten würde.
    Ich kann mir auch denken woran das liegt - weil ich die aktuelle drehung mitberücksichtigen muss, wenn ich die Winkel ändere.
    Allerdings fällt mir dazu keine gute Formel ein. Ich vermute, dass ich da etwas mit sin/cos machen müsste, oder mit Matrizen.
    Außerdem macht das glaube ich auch noch Probleme, dass ich erst an der x-Achse rotiere und dann an der z-Achse und nicht beide gleichzeitig, so dass die zweite drehung von der ersten Beeinflusst wird.

    Hat wer Ideen, wie ich das beheben kann? Oder vielleicht ein Tutorial für drehbare kamera oder so?
    Ich würde auch wieder das mit den Kugelkoordinaten einführen, wenn jemand eine Idee hat, wie ich das verbessern kann.

    Vielen Dank schonmal für eure Hilfe!



  • Kugelkoordinaten wären eigentlich die imo beste Lösung. Was genau war da das Problem?



  • Ich find das schwer zu beschreiben, ich lade gleich mal eine .exe datei hoch (hoffe mal, dass du windows oder sonst whine hast).
    Kennst du gluLookAt?
    Das Problem könnte damit zusammenhängen, dass phi zwischen -180 grad und 180 liegen muss. Ich hatte das eben einfach so gemacht, dass er wenn er über 180 grad drüber geht er weider bei -180 grad anfängt. Aber sowohl mit als auch ohne dieser Änderung gab es einen Sprung bei einem bestimmten Winkel.
    Und die ganzen bewegungen sind irgendwie einfach "falsch" ^^
    Irgendwas habe ich da noch nciht so ganz richtig gemacht mit den kugelkoordinaten.
    Ich poste gleich noch etwas code.



  • Ja der Sprung passiert wohl genau wenn deine Kamera im Zenith steht. Das Problem ist, dass dein Up-Vektor nicht stimmt. Überleg mal in welche Richtung der zeigt...



  • Das sieht so aus:

    void CubeWidget::AdjustCamera()
    {
    	glLoadIdentity();
    	//fix aspect ratio
    	glScalef(1.f, float(width()) / height(), 1.f);
    	gluPerspective(45, 1, 0.01, maxRadius);
    
    	float x = radius * sin(theta) * cos(phi);
    	float y = radius * sin(theta) * sin(phi);
    	float z = radius * cos(theta);
    	gluLookAt(x,y,z,0,0,0, cos(theta), sin(theta),1);
    	//gluLookAt(x,y,z,0,0,0,0,1,0);
    	//da bin ich mir nicht sicher, hab beide varianten versucht gehabt.
    }
    
    void CubeWidget::keyPressEvent(QKeyEvent * event)
    {
    	switch(event->key())
    	{
    	case Qt::Key_Right:
    		ChangePhi(radPerPress);
    		break;
    	case Qt::Key_Left:
    		ChangePhi(-radPerPress);
    		break;
    	case Qt::Key_Up:
    		ChangeTheta(-radPerPress);
    		break;
    	case Qt::Key_Down:
    		ChangeTheta(radPerPress);
    		break;
    	...
    	}
    
    //hier hatte ich auch mal beschränkungen für die winkel drin, die aber nichts gebracht hatten
    void CubeWidget::ChangePhi(float delta)
    {
    	phi += delta;
    	update();
    }
    
    void CubeWidget::ChangeTheta(float delta)
    {
    	theta += delta;
    	update();
    }
    

    Im moment merk ich aber gar nichts mehr von dem Sprung, die ganze Bewegung ist irgendwie "falsch".
    Soll ich die exe mal hochladen?



  • Also dein up Vektor schaut mir seltsam aus...



  • Sieht der rest denn gut aus? Und wie ist es mit der Beschränkung der Winkel, muss ich da nochwas für reintun?

    Ich überleg mir das nochmal mit dem up vektor - im prinzip brauche ich eine tangente an der kugel im punkt (x,y,z) den ich ja schon ausgerechnet habe.



  • Die eye-Koordinaten schaun gut aus. Du brauchst nicht einfach irgendeine Tangente...



  • Irgendwie komm ich nicht drauf...

    Ich habe es mal so versucht, dass ich den Punkt nochmal für ein kleines bischen größeres Theta berechnet habe und den differenzvektor als up-vektor genommen habe, aber es kommt immer noch schwachsinn raus 😞

    void CubeWidget::AdjustCamera()
    {
    	glLoadIdentity();
    	//fix aspect ratio
    	glScalef(1.f, float(width()) / height(), 1.f);
    	gluPerspective(45, 1, 0.01, maxRadius);
    
    	float x = radius * sin(theta) * cos(phi);
    	float y = radius * sin(theta) * sin(phi);
    	float z = radius * cos(theta);
    
    	float dtheta = 0.05f;
    	float x2 = radius * sin(theta+dtheta) * cos(phi);
    	float y2 = radius * cos(theta+dtheta) * sin(phi);
    	float z2 = radius * sin(theta+dtheta);
    	float upX = x2 - x;
    	float upY = y2 - y;
    	float upZ = z2 - z;
    
    	gluLookAt(x,y,z,0,0,0,upX, upY, upZ);
    }
    


  • Fangen wir mal ganz wo anders an: Ist dir eigentlich klar, wofür der Up-Vektor genau steht?



  • Er zeigt von der mitte der "Kamera" aus nach oben?



  • Genau, und damit legt er die Drehung der Kamera um die Blickrichtung fest. Damit sollte eigentlich klar sein, in welche Richtung der Vektor genau zeigen muss.



  • Bildlich kann ichs mir vorstellen:
    http://upload.wikimedia.org/wikipedia/commons/8/82/Sphericalcoordinates.svg
    Auf diesem Bild müsste er vom roten punkt senkrecht zu r in Richtung des Winkelbogens von theta gehen.
    Ich komme nur gerade nicht darauf, wie ich diese Richtung ausrechnen kann.



  • Q schrieb:

    Auf diesem Bild müsste er vom roten punkt senkrecht zu r in Richtung des Winkelbogens von theta gehen.

    exakt

    Q schrieb:

    Ich komme nur gerade nicht darauf, wie ich diese Richtung ausrechnen kann.

    Kleiner Tipp: Überleg mal was passiert, wenn du Theta um 90° weiterdrehst 😉



  • Blöd das ich da nicht drauf gekommen bin, hab heute irgendwie nen Brett vorm Kopf 😃

    Habe jetzt folgendes gemacht:

    void CubeWidget::AdjustCamera()
    {
    	glLoadIdentity();
    	//fix aspect ratio
    	glScalef(1.f, float(width()) / height(), 1.f);
    	gluPerspective(45, 1, 0.01, maxRadius);
    
    	float x = radius * sin(theta) * cos(phi);
    	float y = radius * sin(theta) * sin(phi);
    	float z = radius * cos(theta);
    
    	float dtheta = pi / 2.f;
    	float upX = radius * sin(theta + dtheta) * cos(phi);
    	float upY = radius * cos(theta + dtheta) * sin(phi);
    	float upZ = radius * sin(theta + dtheta);
    
    	gluLookAt(x,y,z,0,0,0,upX, upY, upZ);
    	//gluLookAt(x,y,z,0,0,0,0,1,0);
    	//gluLookAt(x,y,z,0,0,0, cos(theta), sin(theta),1);
    }
    

    Aber leider funktionierts immer noch nicht.
    Hab ich das mit den 90 grad jetzt falsch eingebaut, oder ist das was anderes?



  • Schaut auf den ersten Blick so falsch nicht aus. Was genau funktioniert denn nicht? Kanns sein, dass du dein theta irgendwo künstlich begrenzt, was dann zu merkwürdigen Sprüngen führt?


  • Mod

    wenn man mit opengl arbeiten, vergisst man manchmal, dass die math lib mit rad arbeitet, also 0 bis 2*PI und nicht 0 bis 360, dann kann es kaputt aussehen weil sich alles 60mal zu schnell dreht.



  • rapso schrieb:

    wenn man mit opengl arbeiten, vergisst man manchmal, dass die math lib mit rad arbeitet, also 0 bis 2*PI und nicht 0 bis 360, dann kann es kaputt aussehen weil sich alles 60mal zu schnell dreht.

    Danke für den Tipp, aber das habe ich schon beachtet.

    Die entstehenden Drehungen sind einfach vollkommen falsch! Drehe ich nach links, dann dreht es sich erst in einem komischen Bogen etwas nach links und springt dann wieder zurück (beim zurückspringen wird alles um 180 grad oder so gedreht).
    In der anderen achse passiert etwas ähnliches.

    Ich habe im Moment keinerlei Beschränkungen an die Winkel drin.

    Ich überlege gerade, ob es nicht vielleicht doch mit glRotatef einfacher wäre.



  • Ich grab den thread jetzt nochmal aus, weil ichs immer noch nicht richtig hinbekommen hab.

    Ich habe mit gluLookAt einiges probiert, aber es hat nur probleme gemacht, deswegen habe ich es wieder auf glRotatef umgestellt, was einigermaßen funktioniert, aber noch ein paar Probleme bereitet.

    Folgendes Problem:
    Drehe ich nach starten des Programms um 180 grad nach rechts, klappt das super.

    Drehe ich nach starten des Programms aber erst um 180 "in den Bildschirm rein" und dann 180 grad nach rechts, dann dreht sich der würfel nach links statt nach rechts.

    Ich sehe dafür 2 Mögliche Ursachen:
    1.

    void CubeWidget::AdjustCamera() 
    { 
    	glLoadIdentity(); 
    	float scaleFac = 1.f/radius;
    	glScalef(scaleFac, scaleFac * float(width()) / height(), scaleFac); 
    	glRotatef(theta, 1, 0, 0);	
    	glRotatef(phi, 0, 1, 0);
    }
    

    Es wird erst um die eine Achse gedreht und dann um die andere Achse, wodurch die 2. Drehung durch die 1. beeinflusst wird.

    void CubeWidget::ChangePhi(float delta)
    {
    	phi += delta;
    	update();
    }
    
    void CubeWidget::ChangeTheta(float delta)
    {
    	theta += delta;
    	update();
    }
    

    Bei mausbewegungen/tastendrücken addiere ich einfach auf die Winkel drauf, ohne die aktuelle Kameraposition zu beachten. Wenn die Kamera "auf dem Kopf" steht müsste der Winkel sich aber anders ändern, als wenn sie normal ist (siehe Problem ganz oben).

    Ich bin mir nicht ganz sicher, ob beide Möglichkeiten relevant sind, oder ob es nur eins von beidem ist.

    Zur Lösung von 1. müsste ich irgendwie beide Drehungen zusammenfassen, so dass ich sie durch eine Drehung ersetzen kann.
    Zur Lösung von 2. müsste ich die Winkel basierend auf der aktuellen Kameralage anpassen.

    Bei beiden Problemen kann man sicher irgendwie mit den trigonometrischen Funktionen arbeiten, aber wie genau habe ich noch nicht rausgefunden. Alternativ könnten Drehmatrizen helfen, ich könnte mir z.B. vorstellen, dass ich die erste Drehung durchführe und die Drehmatrix für die zweite Drehung vor Anwendung mit der inversen der aktuellen Matrix multipliziere.
    Allerdings kompliziert die Verwendung solcher Matrizen alles etwas.

    Habt ihr ideen, wie ich das lösen kann?


Anmelden zum Antworten