Forum: PC-Programmierung Objekt im 3D Raum rotieren/transformieren - Umwandlungsmatrix gesucht


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Kaj G. (Firma: RUB) (bloody)



Lesenswert?

Hallo Forum,

Für ein Spiel möchte ich ein spherisches Objekt erstellen, ähnlich zu 
einem Conway/Goldberg Polyhedron. Dazu habe ich 5 und 6 eckige Kacheln 
erstellt und versuche diese jetzt zu platzieren. Die Sphere soll einen 
Radius von 25 Meter haben.
Dazu habe ich die Kacheln mit einem Offset von 25 Meter auf der X Achse 
zum Koordinatenursprung versehen (die Kacheln liegen also auf der 
Außenseite einer Kugel mit Radius=25m). Also so, als wenn man einen 
Stift an eine Schnur bindet, und das andere Ende der Schnur festnagelt. 
Mein Gedanke dabei war, das ich dann ja "nur" die Winkel x/y/z 
einstellen brauche, und dann ist die Kachel schon am richtigen Ort.

Für die Spielentwicklung nutze ich die Godot Engine.
https://godotengine.org/

Da ich keine Lust habe x solcher Kacheln von Hand zu platzieren, habe 
ich versucht mir die Objekte automatisiert zu erstellen.
Der Editor speichert das Objekt in folgendem Format:
1
transform = Transform3D(x1, x2, x3, y1, y2, y3, z1, z2, z3, w1, w2, w3)
x/y/z sind die Skalierungs und Rotationsvektoren (M=R.S)

w1,w2,w3 sind die x/y/z Entfernung in Meter zum Koordinatenursprung (in 
diesem Falle 0, wegen dem Offset auf dem Objekt selbst).

Jetzt kommt das "Problem":
Im Editor der Engine kann ich ein Objekt um jede Achse einzeln drehen, 
ohne das die anderen Achsen beeinflusst werden. Um bei dem Beispiel mit 
dem Stift an der Schnur zu bleiben: Stift um X Grad nach rechts ziehen, 
und Y Grad nach oben.

Das scheint aber keine Rotation im mathematischen Sinne zu sein.

Als Beispiel habe ich mal die Kachel im Editor um x=17° y=29° und z=35° 
"rotiert". Wenn ich das Objekt speicher, dann ergeben sich folgende 
Werte:
1
transform = Transform3D(0.716447, -0.501661, 0.48481, 0.664624, 0.702058, -0.255714, -0.212082, 0.505422, 0.836403, 0, 0, 0)

Wenn ich aber versuche das zu berechnen, also eine Matrix [[1,0,0] 
[0,1,0] [0,0,1]] nehme, und nacheinander an den Achsen um die jeweiligen 
Winkel rotiere, dann erhalte ich aus meinem Programm diese Werte:
1
transform = Transform3D(0.716447, 0.664624, -0.212082, -0.501661, 0.702058, 0.505422, 0.484810, -0.255714, 0.836403, 0, 0, 0)
Wenn ich das dann im Editor öffne, zeigt der Editor diese Winkel für das 
Objekt an:

x: -31,1°
y: -12,2°
z: -42,9°

Ehrlich gesagt bin ich nicht so fit in Mathe, dass ich da jetzt erkennen 
könnte, was ich genau machen muss, um die richtigen Werte zu erhalten.
Ich bin für jede Hilfe dankbar und freue mich, wenn ihr mir hier etwas 
helfen könntet.

Grüße

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Man muss bedenken, dass Matrix-Multiplikationen nicht kommutativ sind. 
Das heißt, die Reihenfolge ist wichtig. Wenn du z.B. erst um x, dann um 
y drehst, kann das Ergebnis anders sein, als wenn du erst um y und dann 
um x drehst. Vielleicht passiert hier so etwas.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Rolf M. schrieb:
> Man muss bedenken, dass Matrix-Multiplikationen nicht kommutativ sind.
> Das heißt, die Reihenfolge ist wichtig.
Richtig, das habe ich auch schon rausgefunden. Das ist aber nicht, was 
ich möchte. Im Editor ist die Reihenfolge anscheinend nicht wichtig, 
bzw. scheint dort etwas anderes zu passieren.

Vllt ist es eher transformation/translation statt rotation, oder es 
fehlt noch ein korrektur schritt?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Die in der zweiten Transformation enthaltene Rotationsmatrix (die ersten
9 Elemente) ist die Transponierte der Rotationsmatrix der ersten
Transformation. Da hast du bei deiner Berechnung wohl irgendetwas
durcheinandergeschmissen.

Edit:

Da bei Rotationsmatrizen die Transponierte gleich der Inversen ist, hast
du evtl. statt der Rotation von der Start- zur Zielorientierung
versehentlich die rückwärts gerichtete Rotation, also von der Ziel- zur
Startorientierung berechnet.

: Bearbeitet durch Moderator
von Sebastian W. (wangnick)


Lesenswert?

Kaj G. schrieb:
> die Kacheln liegen also auf der Außenseite einer Kugel mit Radius=25m

Das Goldberg-Polyhedron ist ziemlich tricky. Ich hatte mit denen das 
Problem, dass meine Kacheln nicht planar wurden. Mir hat dann ein 
hawaiianischer Mathematiker geholfen. Das Resultat findest du unter 
https://github.com/eddyverl/FreeCad-Pyramids-and-Polyhedrons.

HTH.

LG, Sebastian

von Rüdiger B. (rbruns)


Lesenswert?


von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Yalu X. schrieb:
> Da hast du bei deiner Berechnung wohl irgendetwas
> durcheinandergeschmissen.
Das steht außer Frage :D

Hier mein Code (Rust) zur Berechnung (hät ich auch gleich posten können 
-.-* ):
1
use na::{Rotation3, SMatrix, Vector3};
2
use std::{io::Write, process::exit};
3
4
type Matrix3x3 = SMatrix<f32, 3, 3>;
5
6
fn main() {
7
    let base = Matrix3x3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
8
    let result = rotate(base, 17.0, 29.0, 35.0);
9
    // result[0.716447, 0.664624, -0.212082, -0.501661, 0.702058, 0.505422, 0.484810, -0.255714, 0.836403]
10
}
11
12
fn deg_to_rad(deg: f32) -> f32 {
13
    deg * std::f32::consts::PI / 180f32
14
}
15
16
fn rotate(m: Matrix3x3, x: f32, y: f32, z: f32) -> Matrix3x3 {
17
    let x_axis = Vector3::x_axis();
18
    let y_axis = Vector3::y_axis();
19
    let z_axis = Vector3::z_axis();
20
21
    let x_angle = deg_to_rad(x);
22
    let y_angle = deg_to_rad(y);
23
    let z_angle = deg_to_rad(z);
24
25
    let x_rotation = Rotation3::from_axis_angle(&x_axis, x_angle);
26
    let y_rotation = Rotation3::from_axis_angle(&y_axis, y_angle);
27
    let z_rotation = Rotation3::from_axis_angle(&z_axis, z_angle);
28
29
    let mut m = m;
30
    m *= x_rotation;
31
    m *= y_rotation;
32
    m *= z_rotation;
33
34
    m
35
}

von Rolf M. (rmagnus)


Lesenswert?

Kaj G. schrieb:
> m *= x_rotation;
> m *= y_rotation;
> m *= z_rotation;

Die Multiplikation ist falsch herum. Du willst nicht m mit x_rotation 
multiplizieren, sondern x_rotation mit m. Wie schon anfangs gesagt: 
Matrix-Multiplikation ist nicht kommutativ.

: Bearbeitet durch User
von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Rolf M. schrieb:
> Die Multiplikation ist falsch herum. Du willst nicht m mit x_rotation
> multiplizieren, sondern x_rotation mit m.
Das scheint es gewesen zu sein. Danke :)

von Yalu X. (yalu) (Moderator)


Angehängte Dateien:

Lesenswert?

Wenn ich deinem Code noch ein
1
    println!("{}", result);

hinzufüge, erhalte ich folgende Ausgabe:
1
  ┌                                     ┐
2
  │   0.7164465  -0.5016613   0.4848096 │
3
  │  0.66462433  0.70205754 -0.25571406 │
4
  │ -0.21208239   0.5054217  0.83640295 │
5
  └                                     ┘

Die Rotationsmatrix stimmt mit der von Godot angezeigten überein (s.
Anhang).

Wenn ich das richtig sehe, wird die Transformation im .tscn-File wie
folgt abgespeichert:
1
Transform3D(r₁₁, r₁₂, r₁₃, r₂₁, r₂₂, r₂₃, r₃₁, r₃₂, r₃₃, t₁, t₂, t₃)

Die Rotationsmatrix wird also zeilenweise abgelegt, gefolgt vom
Translationsvektor. Du bist irrtümlicherweise davon ausgegangen, dass
die Matrix spaltenweise abgelegt wird, so dass die drei Elemente der
Basisvektoren jeweils direkt hintereinander in der Argumentliste
auftauchen.

Rolf M. schrieb:
> Die Multiplikation ist falsch herum.

Nein, das stimmt so. Godot bezieht die Winkel auf intrinsische
Rotationen (mitdrehendes Koordinatensystem), während Kaj in seinem
Rust-Programm mit extrinsischen Rotationen (feststehendes
Koordinatensystem) rechnet. Diesen Unterschied hat Kaj (bewusst oder
unbewusst) dadurch kompensiert, dass er die Reihenfolge der drei
Einzelrotationen umgedreht hat.

Kaj G. schrieb:
> Das scheint es gewesen zu sein. Danke :)

Ich glaube nicht, denn durch Umdrehen der Rotationsreihenfolge erhältst
du ganz andere Zahlenwerte.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Yalu X. schrieb:
> Die Rotationsmatrix stimmt mit der von Godot angezeigten überein (s.
> Anhang).
Ja, das stimmt schon. Aber es wird (anscheinend) anders abgespeichert.
1
fn get_rotation(m: Matrix3x3, x: f32, y: f32, z: f32) -> Matrix3x3 {
2
    let x_axis = Vector3::x_axis();
3
    let y_axis = Vector3::y_axis();
4
    let z_axis = Vector3::z_axis();
5
6
    let x_angle = deg_to_rad(x);
7
    let y_angle = deg_to_rad(y);
8
    let z_angle = deg_to_rad(z);
9
10
    let x_rotation = Rotation3::from_axis_angle(&x_axis, x_angle);
11
    let y_rotation = Rotation3::from_axis_angle(&y_axis, y_angle);
12
    let z_rotation = Rotation3::from_axis_angle(&z_axis, z_angle);
13
14
    z_rotation * (y_rotation * (x_rotation * m))
15
}
liefert mir genau das, was ich haben möchte (abgesehen vom vorzeichen).

von Yalu X. (yalu) (Moderator)


Lesenswert?

Kaj G. schrieb:
> Yalu X. schrieb:
>> Die Rotationsmatrix stimmt mit der von Godot angezeigten überein (s.
>> Anhang).
> Ja, das stimmt schon. Aber es wird (anscheinend) anders abgespeichert.

Eben. Und genau das (und nur das) ist der Grund für die ganze
Verwirrung.

Kaj G. schrieb:
> z_rotation * (y_rotation * (x_rotation * m))
> }
> liefert mir genau das, was ich haben möchte (abgesehen vom vorzeichen).

Das liefert
1
  ┌                                     ┐
2
  │   0.7164465 -0.43240356   0.5474775 │
3
  │   0.5016613   0.8646603  0.02642797 │
4
  │  -0.4848096  0.25571406  0.83640295 │
5
  └                                     ┘

Du wolltest aber doch diese Werte hier sehen:

Kaj G. schrieb:
> Als Beispiel habe ich mal die Kachel im Editor um x=17° y=29° und z=35°
> "rotiert". Wenn ich das Objekt speicher, dann ergeben sich folgende
> Werte:
> transform = Transform3D(0.716447, -0.501661, 0.48481, 0.664624,
> 0.702058, -0.255714, -0.212082, 0.505422, 0.836403, 0, 0, 0)

Selbst wenn man die Werte geeignet anordnet, stimmen nur zwei davon mit
denen der obigen Matrix überein. Drei weitere haben das falsche
Vorzeichen, und die restlichen vier sind völlig verschieden.

Die mit deinem modifizierten Rust-Programm berechneten Werte erhältst du
auch in Godot, wenn du dort die "Rotation Order" von "XYZ" in "ZYX"
änderst.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.