Simulation des Sonnensystems: Ungenauigkeitsproblem



  • Seid gegrüßt 😉

    Momentan arbeite ich an einer Simulation des Sonnensystems in OpenGL/wxWidgets. In der Übersicht sieht alles gut aus und Bewegungen sind schön weich.

    Wenn man jetzt jedoch auf einen Planeten (fast) bildschirmfüllend zoomt, fängt der Planet und dessen Beschriftung an, wild hin- und herzuspringen.
    Ich habe davon einfach mal ein Video gemacht: Demo Video

    Ich weiß einfach nicht genau, worauf ich den Fehler beziehen soll.
    Anfangs habe ich gedacht, dass es an meinen Einheiten liegt. Momentan ist eine OpenGL Einheit gleich 1000 000 km, aber es hat sich nichts verändert, selbst bei 1km.
    Ich gehe jetzt ja mal davon aus, dass es einfach an der Fließkommaungenauigkeit liegt (ich benutze ausschließlich doubles), aber ich weiß nicht, was ich dagegen machen kann.

    Die Planetenpositionen werden jeden Frame neu berechnet nach diesen Formeln. Bei diesen ganzen Operationen ist es ja eigentlich klar, dass hohe Genauigkeit flöten geht.

    Die Kamera platziere ich mit ein paar glTranslated/glRotated und die Schrift wird mit glRasterPos3d und Bitmapfonts gerendert.

    Hat jemand eine Idee, wie ich das Problem in den Griff bekommen kann?

    Vielen vielen Danke schonmal.. 🙂



  • keine ahnung aber
    man kann doch noch eine Variable dranhängen.

    long nehmen oder so, diese zahlen wären dann die weiteren kommazahlen. kann man damit unendlich lang machen... um performance einzudämmen, sollten diese variablen nur bei nahzoom aktiviert sein und alles unsichtbare wird nicht berechnet.



  • Das Problem ist die Genauigkeit.
    Du kannst dem mit Näherungsverfahren entgegenwirken.
    Bsp: http://de.wikipedia.org/wiki/Runge-Kutta-Verfahren

    Viel Erfolg 🙂



  • Ich denke das Problem liegt nicht bei der Berechnung der Koordinaten für die Planeten, sondern im OpenGL Teil. Wenn du das Ganze mal selbst (mit doubles) transformierst und rotierst bevor du die Koordinaten in OpenGL reinsteckst, und dann nurmehr die Perspektivische Projektion über OpenGL machst, sollte der ausgewählte Planet eigentlich immer in der Mitte stehen (weil halt "x minus x ist null", egal wie ungenau x ursprünglich berechnet wurde).

    BTW: *du* rechnest zwar vielleicht mit doubles, aber deine Grafikkarte wird wohl nicht mit doubles, sondern einfach mit 32 Bit floats rechnen. Und da die Umrechnung vom World Space in den Screen Space normalerweise die Grafikkarte erledigt helfen dir die ganzen doubles nix, solange eben die Graka dazwischenpfuscht.

    Eine kleine Rechnung dazu:
    Pluto liegt bei etwa 38 AU, das sind 5.684.724.000.000 Meter.
    Ein double hat eine Mantisse von 52 Bit.
    2^52 ist 4.503.599.627.370.496, und 2^52 / 5.684.724.000.000 ist 729,... .

    D.h. ein double welches einen Wert in der Grössenordnung von 38 AU speicher (egal ob in Meter, Kilometer oder was auch immer) hat immernoch eine Genauigkeit von ~~1/729 Meter, also etwa 1-2mm.

    Natürlich leidet die Genauigkeit durch Summenbildung oder dergleichen, aber ich denke sie sollte in diesem Fall allemal noch ausreichen. (Um auf einen Fehler von 10km zu kommen müsste man schon etwa 22 Bit Genauigkeit vertrödeln)

    Was ich leider nicht weiss ist wie genau die ganzen Winkelfunktionen mit doubles arbeiten, also wieviel Bits der Mantisse vom Ergebnis "Schmarrn" sind.


  • Mod

    hustbaer schrieb:

    Was ich leider nicht weiss ist wie genau die ganzen Winkelfunktionen mit doubles arbeiten, also wieviel Bits der Mantisse vom Ergebnis "Schmarrn" sind.

    soweit ich weiss darf laut IEEE 0.5bit ungenauigkeit sein. und da x87 fpus sich schon immer IEEE compilant nannten, ist dem wohl auch so (bzw intern rechnen die selbst 80bit noch mit 0.5bit genauigkeit soweit ich weiss.

    also alles in eine zeile zu schreiben und dem compiler auf "fast" zu stellen sollte die besten resultate liefern 😃



  • hustbaer schrieb:

    Wenn du das Ganze mal selbst (mit doubles) transformierst und rotierst bevor du die Koordinaten in OpenGL reinsteckst, und dann nurmehr die Perspektivische Projektion über OpenGL machst, sollte der ausgewählte Planet eigentlich immer in der Mitte stehen (weil halt "x minus x ist null", egal wie ungenau x ursprünglich berechnet wurde).

    Diesen Ansatz verfolge ich gerade, doch jetzt habe ich anscheinend ein kleines Problem:

    Die Kugeln für die Planeten habe ich mit glu Quadric erstellt und in Displaylisten gepackt. Beim Rendern transformiere ich die Szene eben mittels OpenGL an die Positionen der Planeten und rufe die Displaylisten auf.

    Mein Problem ist jetzt, dass ich nicht weiß, wie ich die Szene selbst transformieren soll, da gluSphere im Grunde nur den Radius erwartet und die Kugel um 0|0|0 erstellt.

    Zusammengefasst sieht meine Rendermethode noch so aus:

    glLoadIdentity();
    
    	// Set camera
    	glTranslated(0.0, 0.0, -this->camera.distance);
    	glRotated(-this->camera.verticalAngle, 1.0, 0.0, 0.0);
    	glRotated(-this->camera.horizontalAngle, 0.0, 0.0, 1.0);
    	glTranslated(-selectedPlanet->position.x, -selectedPlanet->position.y, -selectedPlanet->position.z);
    
    	// Draw sun
    	glCallList(this->sun.displayList);
    
    	// and the other planets
    	for (unsigned int i = 0; i < this->sun.children.size(); i++) {
    		Planet *planet = &this->sun.children.at(i);
    
    		glTranslated(planet->position.x, planet->position.y, planet->position.z);
    		glCallList(planet->displayList);
    		glTranslated(-planet->position.x, -planet->position.y, -planet->position.z);
    	}
    


  • Na ja, du hast zwei Moeglichkeiten:

    a) du berechnest nur die Rotationsmatrizen und gibst die dann ueber glLoadMatrix() ein, bevor du die Displayliste darstellst. (du kannst dir vorher die aktuelle Modelview-Matrix ueber glGetFloatv(GL_MODELVIEW_MATRIX...) abrufen und die Modelview-Matrix mit deiner eigenen Matrix zusammenmultiplizieren.

    b) du errechnest dir auch die Kugeln selber. Code dazu findest du entweder, indem du dir die Implementation von gluSphere in FreeGlut anschaust, oder z. B. im Red Book am Ende von http://fly.cc.fer.hr/~unreal/theredbook/chapter02.html



  • Muharhar 🙂 Ich habe mir einen netten Workaround ausgedacht, der auch ohne viel Arbeit zu implementieren war.

    Und zwar kann man ausnutzen, dass der ausgewählte Planet eh immer zentriert ist.
    Da kann man einfach der Entfernung entsprechend nach hinten verschieben, drehen und zeichnen. Und dann die Szene neu transformieren und die restlichen Planeten rendern.

    Im Code ist es besser verständlich:

    // [...aufs wesentliche konzentriert]
    
    // Ausgewählter Planet
    glLoadIdentity();
    
    glTranslated(0.0, 0.0, -this->camera.distance);	
    glRotated(-this->camera.verticalAngle, 1.0, 0.0, 0.0);
    glRotated(-this->camera.horizontalAngle, 0.0, 0.0, 1.0); 
    
    glCallList(this->selectedPlanet->displayList);
    
    // Die anderen Planeten
    glLoadIdentity();
    
    glTranslated(0.0, 0.0, -this->camera.distance);
    glRotated(-this->camera.verticalAngle, 1.0, 0.0, 0.0);
    glRotated(-this->camera.horizontalAngle, 0.0, 0.0, 1.0);
    
    glTranslated(-selectedPlanet->position.x, -selectedPlanet->position.y, -selectedPlanet->position.z);
    
    for (unsigned int i = 0; i < this->sun.children.size(); i++) {
       Planet *planet = &this->sun.children.at(i);
    
       if (planet == this->selectedPlanet)
          continue;
    
       glPushMatrix();
    
       glTranslated(planet->position.x, planet->position.y, planet->position.z);
       glCallList(planet->displayList);
    
       glPopMatrix();
    }
    

    Update:
    Ok, das oben konnte man ja noch wunderbar vereinfachen:

    // Draw planets
    	glLoadIdentity();
    	glTranslated(0.0, 0.0, -this->camera.distance);
    	glRotated(-this->camera.verticalAngle, 1.0, 0.0, 0.0);
    	glRotated(-this->camera.horizontalAngle, 0.0, 0.0, 1.0);
    
    	for (unsigned int i = 0; i < this->sun.children.size(); i++) {
    		Planet *planet = &this->sun.children.at(i);
    
    		glPushMatrix();
    
    		if (planet != this->selectedPlanet)
    			glTranslated(planet->position.x - selectedPlanet->position.x, planet->position.y - selectedPlanet->position.y, planet->position.z - selectedPlanet->position.z);
    
    		glBindTexture(GL_TEXTURE_2D, planet->textureID);
    		glCallList(planet->displayList);
    
    		glPopMatrix();
    	}
    
    	// Required for correct planet labels
    	glTranslated(-selectedPlanet->position.x, -selectedPlanet->position.y, -selectedPlanet->position.z);
    
    	// Draw planets' names
    	if (this->planetsNames) {
    
    		glDisable(GL_DEPTH_TEST);
    		glDisable(GL_LIGHTING);
    		glDisable(GL_TEXTURE_2D);
    
           // [...]
    


  • Das wird solange gut gehen bis du mal bei "ganz weit reingezoomt" einen anderen Planeten z.B. im Hintergrund hast.
    Der wird dann vermutlich auch sehr "herumtorkeln".

    Wenn dir das egal ist ist dein "Workaround" natürlich gut.



  • hustbaer schrieb:

    Das wird solange gut gehen bis du mal bei "ganz weit reingezoomt" einen anderen Planeten z.B. im Hintergrund hast.
    Der wird dann vermutlich auch sehr "herumtorkeln".

    Wenn dir das egal ist ist dein "Workaround" natürlich gut.

    Nope das ist kein Problem, weil die Abstände zwischen den Planeten so groß sind. Wenn man sehr weit weg ist, sieht man diese minimalen Schwankungen nicht.

    Wen's interessiert, hier mal einen Screenshot, wie's momentan aussieht.

    Edit: ja der Begriff Workaround ist hier nicht korrekt 😉


Anmelden zum Antworten