Projektion eines 3D Punktes

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Daedalus
User
Beiträge: 6
Registriert: Mittwoch 20. Juni 2018, 12:10

Hallo,
ich bin neu hier und heiße Christoph.
Was diesen Post betrifft, bin ich mir nicht ganz sicher, ob der im richtigen Forumsbereich erstellt wurde (habe aber nichts passenderes gefunden).

Nun mein Problem ist folgendes. Ich habe in Python ein Programm geschrieben, dass eine Liste mit 3D Punkten in 2D Punkte umrechnet durch die Bekannten Formeln ( einfachste Methode). Nun ist es aber so, dass mein Programm zwar funktioniert (ein paar verbesserungen müssen noch gemacht werden), aber das Quadrat sich nicht linear auf einen zubewegt sondern schräg an einem vorbei.
Der Code ist folgender:

Code: Alles auswählen

import pygame, math
from pygame.locals import *

points = [[1,1,1],[5,1,1],[5,5,1],[1,5,1]] #normal rectangle    

def main():
    pygame.init()
    width , height = 800 , 600
    screen = pygame.display.set_mode((width , height))

    points = [[1,1,1],[5,1,1],[5,5,1],[1,5,1]] #normal rectangle
    fov = 128
    view_dist = 10

    angle = 10

    BLACK = (0,0,0)
    WHITE = (255,255,255)
        
    done = False
    dist_change = 0

    w_change = 0
    h_change = 0
    
    while not done:
        #fill screen
        screen.fill(BLACK)

        #add changed distance if needed
        view_dist += dist_change
        width += w_change
        height += h_change
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_w:
                    dist_change = -0.25
                if event.key == pygame.K_s:
                    dist_change = 0.25
                if event.key == pygame.K_a:
                    w_change = -3
                if event.key == pygame.K_d:
                    w_change = 3
                if event.key == pygame.K_UP:
                    h_change = -1
                if event.key == pygame.K_DOWN:
                    h_change = 1
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_w or event.key == pygame.K_s:
                    dist_change = 0
                if event.key == pygame.K_a or event.key == pygame.K_d:
                    w_change = 0
                if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    h_change = 0
        
        #projection of the points
        point2d = []
        for i in range(0,len(points)):
            #calculate the x'
            xp = int(int(points[i][0]) * fov / (int(points[i][2])+view_dist) + (width/2))
            yp = int(-int(points[i][1]) * fov / (int(points[i][2])+view_dist) + (height/2))
            point2d.append([xp,yp])
            print(xp,yp)

        #drawing the points
        #list of the polygon is already complete so:
        pygame.draw.polygon(screen, WHITE, point2d, 0)

        pygame.display.flip()
                            
main()
pygame.quit()
ist erst einmal nichts spektakuläres, aber ich würde schon gerne, dass das Rechteck nicht an mir vorbei zieht. Die Rotate routinen hab ich komplett weggelassen, weil sie noch nicht von Nöten sind.
Könnte mir jemand helfen, wie ich das erreiche?

MfG,
Christoph
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich würde mal sagen das liegt an den Koordinaten des Rechtecks, das die eben nicht so sind das die Mitte des Rechtecks auch in der Mitte des Koordinatensystems liegen. `view_dist` ist ja die Distanz zum Ursprung des Koordinatensystems, nicht zum Rechteck.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Daedalus
User
Beiträge: 6
Registriert: Mittwoch 20. Juni 2018, 12:10

Aaah ok, vielen dank.
Wenn ich also mein Rechtecksmittelpunkt aber nicht am Koordinatenursprung liegen habe, muss ich dann doch die camera-Position meiner selbst ändern. Wie würde sich das in der Gleichung äußern?
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das läuft letztlich immer darauf hinaus, dass das auf das Du direkt schauen möchtest, im Ursprung des Koordinatensystems liegen muss.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Daedalus
User
Beiträge: 6
Registriert: Mittwoch 20. Juni 2018, 12:10

Und es gibt in dem Sinne keine andere Möglichkeit simple 3D Projektion zu betreiben?
Ich würde gerne diese Prinzipien von Grund auf intuitiv verstehen, jedoch sind alle Tutorials die typischen drehenden 3D Würfel und das bringt
mir in der Praxis nicht wirklich etwas, weil ich prinzipiell eine 3D Welt nicht aus Würfeln haben will.
MfG,
Christoph
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Daedalus: Ich verstehe jetzt das Problem nicht‽ Du kannst die 3D-Würfel ja durch etwas anderes ersetzten wenn Du da etwas anderes haben möchtest.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Daedalus
User
Beiträge: 6
Registriert: Mittwoch 20. Juni 2018, 12:10

Naja, ich will eine simple 3D WElt erstellen, durch die mein Beobachter laufen soll. Das gelingt mir aber eben nicht wegen dieser Koordinatenursprungsgeschichte. Und die Würfel Demos sind alle nicht modular, sie sind nur für diese ANwendung konzipiert :roll:
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Daedalus hat geschrieben: Donnerstag 21. Juni 2018, 11:35 Naja, ich will eine simple 3D WElt erstellen, durch die mein Beobachter laufen soll.
Wenn du das freizeitmäßig programmierst und du nur ein paar Abende oder Wochenenden an Arbeit investieren kannst, dann würde ich dir die Blender Game Engine empfehlen.
Daedalus hat geschrieben: Donnerstag 21. Juni 2018, 10:48 Ich würde gerne diese Prinzipien von Grund auf intuitiv verstehen
Das Prinzip ist einfach: du transferierst die Koordinaten in das lokale Koordinatensystem der Projektionsfläche und dann lässt du einfach die Z-Komponente weg, weil X- und Y-Achse in der Projektionsfläche liegen.

https://www.google.com/search?client=ub ... rmation+3d
a fool with a tool is still a fool, www.magben.de, YouTube
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Daedalus: Du muss die Koordinaten halt entsprechend anpassen. Vielleicht siehst Du das Problem von der falschen Seite: Durch die Welt zu laufen bedeutet das man die Weltkoordinaten so verschiebt und rotiert, das sie zur Sicht des Betrachters passen. Wenn Du also direkt auf das Rechteck zulaufen möchtest, dann musst Du vorher die Welt um den Betrachter rotieren, so dass das Rechteck auch direkt vor dem Betrachter liegt. Und dann kannst Du darauf zu laufen, in dem Du die Welt entsprechend auf den Betrachter zu verschiebst.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Daedalus
User
Beiträge: 6
Registriert: Mittwoch 20. Juni 2018, 12:10

@__blackjack__
vielen lieben Dank!!! Das hab ich natürlich mal wieder völlig vergessen. Wurde mal in einem Video über die DOOM Engine beschrieben, dass die Welt sich um den Betrachter dreht und nicht der Betrachter um die Welt (wahrer Egoismus :lol: )
Nein, jetzt weiß ich auch, wie ich das Programm verbessern kann. Beispielsweise reicht es sogar aus, wenn du z=0 setzt, dann wird die Figur trotzdem gedreht, dann ist die welt aber eben nur mit einer festen Höhe beschrieben.
Für den Anfang bin ich einen großen Schritt weiter !
Daedalus
User
Beiträge: 6
Registriert: Mittwoch 20. Juni 2018, 12:10

Also das ist jetzt der funktionierende Code:

Code: Alles auswählen

import pygame, math
from pygame.locals import *

points = [[1,1,0],[1,-1,0],[-1,-1,0],[-1,1,0]] #normal rectangle    

def main():
    pygame.init()
    width , height = 800 , 600
    screen = pygame.display.set_mode((width , height))

    fov = 128
    view_dist = 10

    angle = 0.05

    playerX , playerY = 10 , 10

    BLACK = (0,0,0)
    WHITE = (255,255,255)
        
    done = False
    dist_change = 0

    w_change = 0
    h_change = 0
    
    while not done:
        #fill screen
        screen.fill(BLACK)

        #add changed distance if needed
        view_dist += dist_change
        width += w_change
        height += h_change
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_w:
                    dist_change = -0.25
                if event.key == pygame.K_s:
                    dist_change = 0.25
                if event.key == pygame.K_a:
                    w_change = -3
                if event.key == pygame.K_d:
                    w_change = 3
                if event.key == pygame.K_UP:
                    h_change = -1
                if event.key == pygame.K_DOWN:
                    h_change = 1                    
                        
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_w or event.key == pygame.K_s:
                    dist_change = 0
                if event.key == pygame.K_a or event.key == pygame.K_d:
                    w_change = 0
                if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    h_change = 0
        
        #projection of the points
        point2d = []
        for i in range(0,len(points)):
            #calculate the x'
            xp = int(int(points[i][0]) * fov / (int(points[i][2])+view_dist) + (width/2))
            yp = int(-int(points[i][1]) * fov / (int(points[i][2])+view_dist) + (height/2))
            point2d.append([xp,yp])
            print(xp,yp)

        #drawing the points
        #list of the polygon is already complete so:
        pygame.draw.polygon(screen, WHITE, point2d, 0)

        pygame.display.flip()
                            
main()
pygame.quit()
    
Jetzt muss ich nur noch für die wand eine Rotation einrichten. Hätte da jemand einen Tip? Die Punkte selber müssen sich ändern.
Wenn ich die view_dist Variable als relative Distanz von meinem Punkt P(0;0) nehme und dann mit Vektorrechnung den Betrag des resultierenden Vektors zwischen dem Mittelpunkt der Wand und mir durchrechne, hätte ich ja die Variable sicher und könnte dann die Bewegung an sich ändern. Nur wenn ich die Z Variable ändere, dreht sich die Wand nicht mein sichtfeld :oops:
Ich bräuchte etwas Rat
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na dafür kommst du mE um die richtige Art damit umzugehen nicht mehr rum. Du musst homogene Koordinaten verwenden, und eine 4x4 Matrix für die „world transformation“ und danach eine zweite für die „view Transformation“ welch die Koordinaten perspektivisch projizieren.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Daedalus: Eine Randbemerkung: ``for i in range(len(sequence)):`` ist in Python ein „anti-pattern“. Du kannst mit ``for`` direkt über die Elemente von `sequence` iterieren, ohne den Umweg eines Indexzugriffs. Und mit „tupel unpacking“ (eigentlich geht da jedes iterierbare Objekt, nicht nur Tupel) kannst Du auch gleich in der Schleife schon die Koordinatenanteile auf Namen verteilen. Also anstatt:

Code: Alles auswählen

for i in range(0, len(points)):
    # points[i][0] und points[i][1] verwenden.

# pythonic:

for x, y in points:
    # x und y verwenden.
Falls man mal *zusätzlich* einen Index oder eine laufende Nummer braucht, gibt es die `enumerate()`-Funktion.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten