Simple 3D zu 2D Projektion

  • VB.NET

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von Niko Ortner.

    Simple 3D zu 2D Projektion

    Mein Ziel ist es, Punkte in einem 3-dimensionalen Raum auf den Bildschirm zu zeichnen.
    Es ist hier einfacher, das Ganze anhand eines Modells zu erklären. Als Modell verwende ich zum Erklären einen Würfel. Der hat 8 Punkte, die berechnet werden müssen.
    Jetzt soll berechnet werden, wo sich diese Punkte, projeziert, auf dem Bildschirm befinden werden (offset in die Mitte des Bildschirms lassen wir jetzt mal weg, damit es einfacher bleibt). Dabei sollen zwei Rotationswinkel beachtet werden: Die Drehung des Punktes um die Z-Achse des Modells und die Drehung der Szene um die X-Achse der Projektionsfläche.
    Dazu hab ich ein Bild gezeichnet:

    Die Achsen des Modells sind X, Y und Z (mit jeweils einem angestellten M, was für "Modell" steht). Die Drehachsen wären A (um die X-Achse), B (um die Y-Achse) und C (um die Z-Achse). Hier soll aber nur um die Z-Achse gedreht werden. Damit's einheitlich bleibt und man erkennen kann, dass es ein Winkel ist, habe ich die Drehachse αC (grichischer Buchstabe "Alpha") genannt.
    Die Projektionsfläche hat die Achsen X und Y (mit angestelltem P für "Projektion"). Gedreht soll hier um die X-Achse werden, deshalb αX.
    Aufgrund der Drehmöglichkeiten kann die ZM-Achse immer nur eine vertikale Linie auf der Projektionsfläche darstellen (oder einen theoretischen Punkt, wenn das Modell von oben/unten projeziert wird).
    Damit etwas besser ersichtlich ist, wie die drehungen funktionieren, habe ich mal ein paar Möglichkeiten aufgezeichnet:
    Im ersten Bild sind 4 Eckpunkte grün umrandet. Punkt 0 ist der Nullpunkt. Diese Punkte habe ich in diesem Bild auch eingezeichnet. Die ZM-Achse ist durch einen Pfeil links von der entsprechenden Kante eingezeichnet.

    Mein Problem ist, dass ich nicht weiß, wie ich die Punkte berechnen muss.
    Die X-Koordinate eines Punktes müsste eigentlich XP = Sin(αC) * XM sein.
    Aber die Y-Koordinate wird schwieriger. Wenn αX 0° ist, dann wird das Modell von einer Seite projeziert. Die ZM-Achse ist also parallel zur YP-Achse. Dann ist YP = ZM.
    Und wenn αX 90° ist, dann ist YP = YM, wobei das aber noch von αC abhängt. Und da komme ich nicht weiter.
    Wie berechnet sich da der YP-Wert?

    PS: Ich würde das schon lieber selber machen, als mir z.B. DirectX zuzulegen. Denn erstens finde ich, dass das ziemlicher Overkill wäre, um nur ein paar Punkte zumzurechnen. Und zweitens möchte ich wissen, wie das funktioniert.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Hallo Niko Ortner,
    willst du parallele Projektion oder Zentralprojektion? Erstere ist einfacher.
    Die allgemeinen Formeln dafür findest du in Netz. Wenn du bestimmte
    Teile davon nicht nutzt, setzt du sie einfach auf einen festen Wert und
    vereinfachst die Formeln. Wenn du z.B. den Scalierungsfaktor nicht nutzt,
    setzt du ihn auf 1.

    Ich würde das schon lieber selber machen, als mir z.B. DirectX zuzulegen.

    DirectX ist meistens schon installiert.
    Gruss,

    Neptun
    Hi
    schau' dir mal folgende Artikel durch:
    de.wikipedia.org/wiki/Homogene_Koordinaten
    de.wikipedia.org/wiki/Roll-Nick-Gier-Winkel
    Bei den homogenen Koordinaten verwendet man statt einem 3-dimensionalen Vektor (x, y, z) einen 4-dimensionalen (x, y, z, 1). So funktioniert auch eine Translation, da die Komponenten m14, m24, m34 dann einfach eine Verschiebung erzeugen. Wenn du nicht weißt, wie man mit einer Matrix rechnet, kannst du dir auch den Wikipediaartikel Matrix noch ansehen. Wenn's dir dann noch immer unklar ist, frag nochmal nach ;).

    Gruß
    ~blaze~
    Schau mal ob du in einem Antiquariatversand dieses Buch findest:

    Mikrocomputer Grafik
    Roy E. Myers
    ISBN 3-89058-000-9
    Preis jetzt ca 6€

    Da wird zwar alles in Applesoft Basic erklärt, aber die Formeln
    haben sich in den 30 Jahren nicht geändert.
    Ich finde in so alten Bücher versteht man noch leichter was
    gemeint ist.
    Weiß nicht, ob das wirklich nötig ist. Alles, was man machen muss, ist, eine 4x4-Matrix mit Single (float) oder Double zu implementieren (Double ist ineffizienter). Zusätzlich halt dann Addition und Subtraktion, die sind komponentenweise und Multiplikation

    VB.NET-Quellcode

    1. For i As Integer = 0 To 3
    2. For j As Integer = 0 To 3
    3. Dim v As Single = 0
    4. For k As Integer = 0 To 3
    5. v += left(i, k) * right(k, j)
    6. Next
    7. result(i, j) = v
    8. Next
    9. Next

    oder so ähnlich (lässt sich übrigens in C# mit dem StructLayoutAttribute und unsafe schön lösen, indem man einfach ein Feld als [ic=c]private fixed float _buffer[16];//... {/ic] deklariert, das ist dann quasi ein Zeiger auf 16 Floats innerhalb des Structs, die Floats liegen direkt auf dem Stapel).
    Außerdem halt noch die komponentenweise Multiplikation mit einem Skalar.
    Außerdem sollte man eine Vector4D-Struktur definieren, die eben 4 Floats umfasst und eine Funktion, die eine Vector4D-Instanz mit einer Matrix multipliziert. Vector4D kann hierbei als Zeilen (1x4-Matrix) oder Spaltenvektor (4x1-Matrix) angesehen werden, Transformationen werden aber normalerweise so durchgeführt, dass man die i-te Zeile mit den Komponenten multipliziert und aufaddiert, somit ergibt sich die Multiplikation mit einem Vektor halt zu vI = Dot(spalteI, vektor), das Punktprodukt ist halt einfach left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W. Für einen 3dimensionalen Vektor dividiert davor nochmal durch die 4. Komponente.

    Gruß
    ~blaze~

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „~blaze~“ ()

    Hallo Niko,

    hier hast du ab Kapitel 14 mal ein bisschen mathematisches Rüstzeug zum Thema. Hier noch ein weiterer Link (leider Englisch) - im Prinzip geht es generell um die Koordinatentransformation von einem System (3D) in ein anderes (2D). Das ist Mathematik 1. & 2. Semester; Foren wie z.B. Matheplanet erklären diesen Zusammenhang oft sehr ausführlich...

    Lass dich nicht abschrecken, dass sieht schwieriger aus als es ist - mit ein wenig ausprobieren und den Infos der anderen User hier im Thread sollte sich damit in absehbarer Zeit ein grundsätzliches Verständnis einstellen.
    Wow... Danke für die vielen vielen Antworten.
    Als ich die Wikipedia-Seite über homogene Koordinaten geöffnet habe, war meine erste Reaktion


    Dann hab ich mir das mal durchgelesen und bin an diesem Teil hängen geblieben.
    Das mit der zusätzlichen W-Koordinate hab ich schon mal irgendwo gesehen. Deshalb hab ich mir gedacht, ich probier's einfach mal aus.
    Ich wusste erst garnicht, was mit der homogenen Matrix bei
    Die Abbildung eines Punktes Px,y,z von einem Koordinatensystem in ein anderes geschieht durch die Multiplikation mit der homogenen Matrix M:
    gemeint war. Ich hab dann einfach mal die darunter beschriebenen Matrizen verwendet.
    Die Multiplikationen haben mir Kopfzerbrechen bereitet, aber das hier hat mir da weitergeholfen. Und die Multiplikation mit einem Vektor hab ich hier gefunden.
    Das ist eigentlich wie eine Matrix, nur dass eine Dimension fehlt.

    Und tatsächlich. Es funktioniert. Ich hätte mir nicht gedacht, dass da so wenig Code nötig ist. Also ein dickes Dankeschön nochmal.

    Nebenbei: Kennt jemand eine möglichst nicht rechenlastige Methode, den größten Abstand zwischen mehreren Punkten im 3-dimensionalen Raum herauszufinden?
    Eine Möglichkeit wäre, per Pythagoras den Abstand von jedem Punkt zu jedem anderen Punkt zu berechnen und einfach den größten zu nehmen, aber das scheint mir, besonders bei mehreren Punkten, etwas zu extrem. Mein Ziel ist es, herauszufinden, wie groß der theoretisch größtmögliche Bereich auf dem Bildschirm ist, auf dem sich projezierte Punkte befinden können. Dadurch wäre sichergestellt, dass das Fenster nicht unnötig groß ist und Punkte nie aus dem Rand verschwinden.
    Ist aber nicht so tragisch.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @~blaze~: Gemeint war's so: Wenn man einen Würfel mit 100 Pixel Kantenlänge hat und ein Eckpunkt am Nullpunkt fixiert wäre, wie groß müsste das Clip-Rectangle sein, damit bei allen möglichen Rotationen des Würfels nie ein Punkt außerhalb des Clip-Rectangles liegt?
    Und das ist dann einfach die größt mögliche (tatsächliche) Entfernung zum Nullpunkt. Das heißt, im schlechtesten Fall, kann ein Punkt genau so projeziert werden, dass er <hier Ergebnis einfügen> Pixel rechts vom Nullpunkt liegt (oder in eine beliebige andere Richtung.
    Also bei einem Würfel mit 100 Pixel Kantenlänge ist der am weitesten entfernte Punkt ca. 175 Pixel vom Nullpunkt entfernt. Das heißt, wenn das Fenster, in dem gezeichnet wird, 175*2 Pixel hoch und breit ist und der Nullpunkt in der Mitte liegt, kann dieser Punkt maximal genau am Rand des Fensters liegen.
    Es ist ein bisschen schwer das zu erklären.

    Und ich habe bemerkt, dass ich ja nicht die Unterschiede zwischen den Punkten, sondern die Unterschiede zwischen den Punkten und jeweils dem Nullpunkt benötige. Dann ist's weniger ein Problem, weil es nicht mehr so viele Berechnungen sind.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Wurzeln sind im Vgl. zu Additionen und Multiplikationen extrem teuer. Somit einfach die Wurzeln möglichst nur dann berechnen, wenn sie benötigt werden. Die größte Distanz (abgesehen von der Perspektive, da musst du wohl noch durch d teilen) ergibt sich eben durch den Pythagoras. Vergleiche statt den Wurzeln einfach die Quadrate selber und ziehe dann vom größten die Wurzel. Dann hast du es dahingehend ausreichend optimiert. Gleitkommazahlen sind bei der vermuteten Anzahl an Würfeln sowieso problemlos berechenbar. Bei meinem aktuellen Programm werden mehr als 50000 Punkte flüssig dargestellt, obwohl sie vorher mit einer Matrix multipliziert werden (50000 / 3 * (4+3) Gleitkommaoperationen pro Kern auf meinem PC).

    Gruß
    ~blaze~
    Hmmm... Jetzt hat sich ein weiteres Problem ergeben. Wie kann ich einen Punkt verschieben? Also angenommen ich habe einen Punkt bei {100, 100, 50} und der wird zu {100, 100} projezier (man sieht also direkt auf die Z-Achse drauf).
    Wenn ich diesen Punkt auf dem Bildschirm nach {120, 120} verschiebe, dann müsste der Punkt bei {120, 120, 50} liegen.
    Nur wie geht das bei beliebigen Rotationen (bzw. bei beliebigen Matrizen)?
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Du müsstest demnach die Matrix quasi invertieren. Invertieren kannst du sie über ein lineares Gleichungssystem (das hatte Artentus in einem Thema schon mal gefragt: Probleme beim Lösen von linearem Gleichungssystem). Der Punkt muss nämlich nicht zwangsweise bei (120, 120, 50) liegen, sondern kann wegen der Projektionsmatrix auch woanders liegen - wäre zumindest bei der perspektivischen Projektion so, denk ich mal?
    Ansonsten wird eine Verschiebung (Translation) eben in den Matrix-Komponenten m41, m42, m43 angegeben, da bei einer Multiplikation mit dem Vektor (x, y, z, 1) die Komponenten durch die Multiplikation mit der 1 einfach als m4j * 1 aufaddiert werden.
    Sobald du übrigens eine allgemeingültige Regel für Matrizen hast, ist es egal, wie die Matrix aussieht. Meist würde eine korrekte Kombination von Transformationen durch eine Matrixmultiplikation durchgeführt.

    Gruß
    ~blaze~
    Hallo Niko,

    ich möchte dich nicht mit Mathe "belasten", zumal blaze hier einen tollen Job macht und viele Details erklärt, aber im Grunde ist deine gesamte Fragestellung ein rein mathematisches Problem.

    Klar, Mathematik ist trocken und die Einstiegshürde ist aufgrund des fremden Vokabulars teilweise recht hoch. Vielleicht helfen dir die Videos von Jörn Loviscach auf Youtube. Der Mann bietet einen fantastischen Online-Grundkurs für Studienanfänger an und deckt auch das Themengebiet der Matrizen und zugehöriger Transformationen ab. Ich habe dir mal willkürlich ein Video (ab ca 1:30) des Themenkomplexes herausgepickt - evtl hast du ja Lust das mal nebenbei laufen zu lassen. Bei Interesse solltest du natürlich am Anfang des Themenkomplexes beginnen - es gibt eine Übersicht aller Videos in der Videobeschreibung.


    Anmerkung:
    Versteh meinen Beitrag bitte nicht falsch, aber im Endeffekt stehst du vor genau dem Problem vor dem sonst die Programmieranfänger hier im Forum stehen - du möchtest zeitnah Erfolgserlebnisse bei der Lösung eines bestimmten Problems. Allerdings ist die Frage nach einem simplen Weg zur Projektion von 3D nach 2D eigentlich falsch. Die Mathematik bietet dir den richtigen, allgemeingültigen Weg und du fängst dir unnötige Probleme ein, wenn du einen anderen Weg beschreitest.


    Viel Spass! ;)
    So. Ich hab mich da jetzt nochmal dran gesetzt.

    @~blaze~: und @cl10k:
    Ich danke euch für eure Unterstürzung in Sachen Matrixrechnungen. Aber ich möchte wirklich nur um diese beiden Achsen drehen Das hab ich mit der Matrixmultiplikation hinbekommen. Und weil's so praktisch war, hab ich gleich noch den Zoom als Matrix mit reingenommen. Wäre wohl performanter, einfach die resultierenden Komponenten zu multiplizieren.

    Ich habe das Zurückrechnen jetzt so gelöst: Ich multipliziere die Matrizen in umgekehrter Reihenfolge. Und die Matrizen haben die umgekehrten Werte.
    Also anstelle von

    Quellcode

    1. z, 0, 0, 0
    2. 0, z, 0, 0
    3. 0, 0, z, 0
    4. 0, 0, 0, 1
    ist's dann halt

    Quellcode

    1. 1/z, 0, 0, 0
    2. 0, 1/z, 0, 0
    3. 0, 0, 1/z, 0
    4. 0, 0, 0, 1

    Und winkel werden umgedreht.

    Dann ersetzt man einfach die X- und Y-Koordinaten des projezierten Punktes durch eine neue Position und projeziert die neuen Koordinaten zurück.

    Wegen der Anmerkung, cl10k:
    Ich gebe Dir absolut recht. Ich versuche wirklich nicht, mich komplett in Matrix-Rechnungen einzuarbeiten, weil ich das garnicht brauche. Mir geht's nur darum, Punkte aus einem 3D-Raum in einen 2D-Raum zu projezieren, an zwei Drehachsen. Und halt wieder zurückprojezieren.
    Ich finde es wirklich nett von euch, dass ihr mir Matrixrechnungen erklären wollt und mir viele Links dazu gebt. Ich unterstütze das ja auch eigentlich, wenn man nicht einfach die "Lösung für den Moment" gibt, sondern das Ganze allgemeiner und weiterverwendbarer behandelt.
    Aber ich muss es ganz ehrlich sagen: Ich steig bei Matrizen einfach nicht durch.
    Z.B. hier: de.wikipedia.org/wiki/Homogene…e_Transformationsmatrizen
    Warum ist da die Perspektive so:

    Quellcode

    1. 1, 0, 0, 0
    2. 0, 1, 0, 0
    3. 0, 0, 1, 0
    4. 0, 0, 1/d, 0

    Warum sieht das so aus? Warum ist das nicht das gleiche wie das hier?:

    Quellcode

    1. 1, 0, 0, 0
    2. 0, 1, 0, 0
    3. 0, 0, 1, 1/d
    4. 0, 0, 0, 0
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @jvbsl: Tatsächlich. So funktioniert's auch.
    Nur warum? Das ist die Sache bei Matrizen, die ich nicht verstehen. Warum ergibt das Vertauschen der Zeilen und Spalten genau diesen Effekt?
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Ich denke mal, das kommt auf die Art an, mit der man Vektoren betrachtet. Vektoren kannst du entweder als Zeilen- oder Spaltenvektoren betrachten. Wenn du dir die Regeln der Matrixmultiplikation dann anschaust, sind das zwei verschiedene Operationen. Ich denk' zumindest, dass das daher rührt.

    Alles Gute btw.

    Gruß
    ~blaze~
    @tobinator: Willkommen im Forum. Das Thema war eigentlich erledigt (hab anscheinend vergessen, auf den Erledigt-Button zu klicken).

    Ich hab's mir trotzdem mal heruntergeladen und angesehen.
    Du musst noch sehr viel lernen.


    1.
    Option Strict Off geht garnicht. Da schießt Du Dir nur selbst ins Knie.
    home.arcor.de/eckardahlers/Pro…/Blogs/WarumStrictOn.html

    2.
    Methoden sollen sinnvolle Namen bekommen. PictureBox1_Click ist kein sinnvoller Name.
    Ebenso sollen Controls sinnvolle Namen bekommen. PictureBox1, Form2 oder Button1290429387501394245 sind ebenfalls keine sinnvollen Namen.

    3.
    Pokemon-ExceptionHandling ist unsinnig.

    [VB.NET] TryCatch ist ein heißes Eisen
    [Allgemein] [Sammlung] Sinnvolle Verwendungen für Try-Catch

    4.
    Form2 ist eine Klasse und keine Instanz. Du rufst aber Form2.Show auf. Ich habe das hier erklärt (Auch den Link von RodFromGermany beachten):
    [VB 2010] invalid operation exception beim Aufrufen von Form2


    Lass Dich davon aber nicht abschrecken. Jeder hat mal angefangen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils