Zentralprojektion

Übersicht BlitzBasic Allgemein

Neue Antwort erstellen

 

funkmaster5000

Betreff: Zentralprojektion

BeitragSa, März 16, 2013 7:33
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo Community!

Ich versuche mich mal wieder an der Zentralprojektion. Die Formel? Einfach! Das Handling? Kompliziert! Da ich mich bislang auch nicht an die 3D Programmierung mit Blitz3D gemacht habe, verstehe ich die Zusammenhänge zwischen x,y,z und dem Blickpunkt des "Betrachters" nicht. Vielleicht gehe ich es auch falsch an. So wie ich es derzeit habe, gebe ich die Punkte meiner Straße in 3D an und daraus werden dann 2D x- und y-Koordinaten.

Als Übung wollte ich so eine Pseudo-3D Straße programmieren a la:
user posted image

So weit so gut. Hier mal mein Code:
Code: [AUSKLAPPEN]

Graphics 320,200,32,2
SetBuffer BackBuffer()

Type point3d ;hält die Informationen meines 3D-Punktes
    Field x
    Field y
    Field z
End Type

Type camera ;ist der Betrachter
    Field x
    Field y
    Field z
End Type

Const screen_width = 320 ;die Auflösung x
Const screen_height = 200 ;die Auflösung y


cam.camera = New camera ;neuen Betrachter erzeugen
cam\x = 0
cam\y = 0
cam\z = -(screen_width / 2)

For i = 100 To 1 Step -1 ;funktioniert, aber warum? Wenn ich z <> 1 eingebe, sind die Ergebnisse komisch
    p.point3d = New point3d
   p\x = -i
    p\y = i
    p\z = 1
Next


While Not KeyHit(1)
Cls
For p.point3d = Each point3d
    x = (cam\x * p\z - p\x * cam\z) / (p\z - cam\z) + screen_width / 2
    y = (cam\y * p\z - p\y * cam\z) / (p\z - cam\z) + screen_height / 2
    Plot (x, y) ;Zeichne die Straße
Next


Flip
Wend


Welche Werte haben welchen Einfluss? Und warum? Warum muss ich mein z nicht verringern/erhöhen, wenn ich in die Tiefe gehe? Es funktioniert mit 1. Wenn ich die Straße jetzt auf den Betrachter zuscrollen lassen will, muss ich y und x verringern? Ich habe verschiedene Tutorials angeschaut, aber die waren alle nicht für BlitzBasic und das einzige (naja, war immerhin qbasic) ist nicht mehr verfügbar (siehe http://www.extentofthejam.com/pseudo/). Brauche ich den Betrachter überhaupt? Muss es überhaupt 3D sein, geht es nicht einfach mit einem Offset-Wert? Fragen über Fragen Wink

Danke im Voraus!

Trust

BeitragSa, März 16, 2013 18:06
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe mich zwar noch nie mit Zentralprojektion beschäftigt, aber ich glaube deine Formel geht davon aus, dass sich die Kamera hinter der Projektionsfläche befindet, was dazu führt, dass Bewegungen der Kamera im Z nur eine Art Skalierung bewirken würde.

Wenn du, wie du beschrieben hast, bewegungen in die Tiefe durch veränderung der Z-Koordinate der Kamera haben möchtest, musst du die Kamera vor die Projektionsfläche setzen und die Formel dementsprechend anpassen.

Hier mal ein Beispiel wie ich das in BMax lösen würde (in BB umschreiben sollte kein Problem darstellen):

Ich würde 3 Punkte als Fläche (Polygon) zusammenfügen und dann die ganze Fläche als 2D Projezieren.

Bewegung der Kamera nach der Reihenfolge:
W, S, A, D = Hoch, Runter, Links, Rechts
Q und E = Zurück, Vor


BlitzMax: [AUSKLAPPEN]
SuperStrict

Const GW:Int = 800
Const GH:Int = 600


Graphics GW, GH

' Der Nullpunkt der 2D-Projektionsebene
Const ZEROX:Int = GW/2
Const ZEROY:Int = GH/2


' Punkt im 3-Dimensionalen Raum
Type TPoint3D
Field _x:Float
Field _y:Float
Field _z:Float
End Type

' Eine Fläche bestehend aus 3 Punkten (Polygon) im 3-Dimensionalen Raum
Type TFace3D
Field _point:TPoint3D[3]

Function Create:TFace3D( x1:Float, y1:Float, z1:Float, ..
x2:Float, y2:Float, z2:Float, ..
x3:Float, y3:Float, z3:Float )

Local x:Float, y:Float, z:Float
Local f:TFace3D = New TFace3D
For Local i:Int = 0 To 2
If i = 0 Then x = x1 y = y1 z = z1
If i = 1 Then x = x2 y = y2 z = z2
If i = 2 Then x = x3 y = y3 z = z3
f._point[i] = New TPoint3D
f._point[i]._x = x
f._point[i]._y = y
f._point[i]._z = z
Next

Return f
End Function

' Projeziert die 3D Fläche auf eine 2D Ebene
Method Project2D(cam:TCamera, zeroX:Float, zeroY:Float)
Local j:Int
For Local i:Int = 0 To 2
If i < 2 Then j = 1 Else j = -i
Local x:Float = x2D(Self._point[i]._x, Self._point[i]._z, cam._x, cam._z, zeroX)
Local y:Float = y2D(Self._point[i]._y, Self._point[i]._z, cam._y, cam._z, zeroY)

' Punkte mit Lienen verbinden zur besseren Darstellung
DrawLine( x2D(Self._point[i]._x, Self._point[i]._z, cam._x, cam._z, zeroX), ..
y2D(Self._point[i]._y, Self._point[i]._z, cam._y, cam._z, zeroY), ..
x2D(Self._point[i+j]._x, Self._point[i+j]._z, cam._x, cam._z, zeroX), ..
y2D(Self._point[i+j]._y, Self._point[i+j]._z, cam._y, cam._z, zeroY) )

' Die Punkte rot einzeichnen
SetColor 255, 0, 0
DrawOval(x-1, y-1, 2, 2)
SetColor 255, 255, 255

Next

End Method
End Type


' Der Betrachter
Type TCamera
Field _x:Float
Field _y:Float
Field _z:Float

Method Move(x:Float, y:Float, z:Float)
Self._x = Self._x + x
Self._y = Self._y + y
Self._z = Self._z + z
' Die Projektionsebene befindet sich auf z 0
' Demnach würde sich die komplette Projektion spiegeln wenn z der Kamera < 0
' Das muss verhindert werden...
If Self._z < 0 Then Self._z = 0
End Method
End Type

' Neuen Betrachter erstellen
Local cam:TCamera = New TCamera
cam._x = 0
cam._y = 0
cam._z = 1


' Eine Fläche erstellen
Local face:TFace3D = TFace3D.Create(-3, 0, 1, 3, 0, 1, 0, 0, 2)



Repeat
Cls
' Fläche Projezieren
face.Project2D(cam, ZEROX, ZEROY)

' Kamera Bewegen
If KeyDown(KEY_W)
cam.Move(0, 0.1, 0)
End If
If KeyDown(KEY_S)
cam.Move(0, -0.1, 0)
End If
If KeyDown(KEY_D)
cam.Move(0.1, 0, 0)
End If
If KeyDown(KEY_A)
cam.Move(-0.1, 0, 0)
End If
If KeyDown(KEY_E)
cam.Move(0, 0, 0.1)
End If
If KeyDown(KEY_Q)
cam.Move(0, 0, -0.1)
End If

' Die x, y und z Koordinaten der Punkte im 3-Dimensionelen Raum ausgeben
For Local i:Int = 0 To 2
DrawText "Point"+i+": "+ face._point[i]._x + " | " + face._point[i]._y + " | " + face._point[i]._z, 0, i*20
Next

Flip()
Until AppTerminate() Or KeyDown(KEY_ESCAPE)




' Rechnet 3D koordinaten in 2D X-Koordinaten
Function x2D:Float(x3D:Float, z3D:Float, camX:Float, camZ:Float, zeroX:Float)
Return (x3D * camZ + (camX/z3D) * (z3D-camZ))*camZ + zeroX
End Function

' Rechnet 3D koordinaten in 2D Y-Koordinaten
Function y2D:Float(y3D:Float, z3D:Float, camY:Float, camZ:Float, zeroY:Float)
Return (y3D * camZ - (camY/z3D) * (z3D-camZ))*camZ + zeroY
End Function




Gruß Trust
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.
 

funkmaster5000

BeitragSa, März 16, 2013 19:56
Antworten mit Zitat
Benutzer-Profile anzeigen
Danke schonmal! Ich schreibe mir den Code heute abend um und dann sehen wir weiter Smile

Trust

BeitragSo, März 17, 2013 1:32
Antworten mit Zitat
Benutzer-Profile anzeigen
Man könnte die Umrechnung auch ganz simpel mit Dreiecksberechnung und Winkelfunktionen machen.

Und hier mal ein kleines Testprogramm um zu veranschaulichen, wie das mit dem Blickpunkt funktioniert.
Vielleicht wird dir dann einiges klarer Wink
BlickpunktTest.rar 52kb

Verschiebe die Kamera mit W,A,S,D



Gruß Trust
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.
  • Zuletzt bearbeitet von Trust am So, März 17, 2013 18:07, insgesamt einmal bearbeitet
 

funkmaster5000

BeitragSo, März 17, 2013 13:58
Antworten mit Zitat
Benutzer-Profile anzeigen
Aha, deswegen kann die Kamera logischerweise auch nicht bei 0,0,0 liegen. Und je größer y, desto höher befinde ich mich über dem 3D Objekt. Dementsprechend muss y für jeden 3D Punkt gleich bleiben (es sei denn, ich wollte einen Hügel darstellen), x bedeutet eine Verschiebung nach links/rechts und z nach vorne/hinten.

Folgende Formel hab ich auch gefunden:
Code: [AUSKLAPPEN]

x = FOV * X / (Z + ZCENTER) + XCENTER
y = FOV * Y / (Z + ZCENTER) + YCENTER


Für mein Beispiel sind folgende Werte perfekt:
Code: [AUSKLAPPEN]

CONST XCENTER = 160 'Mitte des BildschirmsX (Auflösung = 320*200 px)
CONST YCENTER = 100 'Mitte des BildschirmsY
CONST ZCENTER = 256 'Warum?
CONST FOV = ZCENTER 'Warum?
CONST YCoord = 100 'So schließt die "Rennstrecke" mit dem unteren Bildschirmrand ab


Ich verstehe die Wahl von FOV und ZCENTER nicht (habe ich aus einem anderen Tutorial). 256 wurde auch in anderen Tutorien als perfekt angesehen und das trifft auch zu, ich weiß nur nicht, warum? Außerdem muss FOV ZCENTER entsprechen, sonst klappts nicht. Fungiert das wie ein "Zoomfaktor"?

Trust

BeitragSo, März 17, 2013 14:52
Antworten mit Zitat
Benutzer-Profile anzeigen
FOV wird denke ich mal die "Brennweite" sein (Field Of View ??).

Schau dir dazu einfach mal mein BlickpunktTest-Programm an.
Die Verschiebung der Kamera zur 2D-Projektionsfläche ist im Prinzip nix anderes als die Brennweite oder den Zoomfaktor zu verstellen.

Und ZCENTER ist der Fluchtpunkt in der Tiefe.

Auf der YCENTER-Höhe befindet sich dein "Horizont".


Code: [AUSKLAPPEN]
x = FOV * X / (Z + ZCENTER) + XCENTER




Code: [AUSKLAPPEN]
FOV * X

Der Punkt wird im X, X mal zur Brennweite verschoben.


Code: [AUSKLAPPEN]
/ (Z + ZCENTER)

Wieviel dieser Punkt tatsächlich im X verschoben wird, ist abhängig davon, wie weit der Abstand dieses Punktes in der Tiefe, zum Fluchtpunkt ist.
Der Teiler Z+ZCENTER wird immer kleiner umso "näher" das 3D objekt der Kamera kommt, folglich wird die Verschiebung im X immer größer.


Code: [AUSKLAPPEN]
+ XCENTER

Stellt sicher das von der Sceenmitte aus gerechnet wird


Das selbe für Y.


Dadurch entsteht diese Verschiebung, bzw. dieser Tiefeneindruck.
Code: [AUSKLAPPEN]
        .
       / \
      /   \
     /     \
    /       \



Code: [AUSKLAPPEN]
FOV = ZCENTER = 256

Warum das Optimal ist kann ich auch nicht sagen, dafür müsste ich vielleicht den ganzen Code sehen.


Gruß Trust
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.
  • Zuletzt bearbeitet von Trust am So, März 17, 2013 17:14, insgesamt 3-mal bearbeitet
 

funkmaster5000

BeitragSo, März 17, 2013 15:17
Antworten mit Zitat
Benutzer-Profile anzeigen
Das ist im Prinzip der ganze Code! Erzeugt werden die Linien derzeit noch über Pixel, aber künftig möchte ich ein Type erstellen, der 2 Punkte enthält, die dann einfach über eine Linie verbunden werden.

Für eine Strecke liegt dann eine vorberechnete Datei mit den entsprechenden Koordinaten vor. Zumindest ist das die Idee. Ich errechne einmal die ganzen X-Werte für eine Gerade,Links-/Rechtskurve. Passieren dann 2 Punkte das Sichtfeld (zbewegung<= 0) wird ein Ereignis ausgelöst, das den kommenden Streckenabschnitt bestimmt. Die 2 Punkte übernehmen dann wieder ihr ursprüngliches z, erhalten aber je nach Ereignis das passende x. So verwerte ich die selben Types ständig wieder. So stelle ichs mir derzeit zumindest vor.

Angewendet sieht der Code so aus:

Code: [AUSKLAPPEN]
CONST XCENTER = 160
CONST YCENTER = 100
CONST ZCENTER = 256
CONST FOV = 256
CONST YCoord = 100

FOR XCoord = -160 TO 160
    FOR ZCoord = 1 TO 800
        x = FOV * XCoord / (ZCoord + ZCENTER) + XCENTER
        y = FOV * YCoord / (ZCoord + ZCENTER) + YCENTER
       Plot x,y
    NEXT
NEXT


Daraus entsteht genau das Bild, das du mir als Ascii aufgezeichnet hast. Hast du vielleicht eine Idee, wie ich den Skalierungsfaktror eines Sprites aus dem z herausbekomme? Ich würde es prozentual machen. z = 800 = 0%, z = 1 = 100%

Trust

BeitragSo, März 17, 2013 17:10
Antworten mit Zitat
Benutzer-Profile anzeigen
ZCENTER ist der Startpunkt nicht der Endpunkt in der Tiefe.
Und FOV ist der Brennwert/Zoom.

Warum FOV = ZCENTER?

Ist FOV kleiner als ZCENTER also vor dem Fokuspunkt, reicht die Strasse nicht bis zum Bildschirmrand, weil du einen Bereich siehst, der noch vor dem Anfang der Strasse liegt.

Ist FOV größer als ZCENTER, siehst du nicht die Strasse von Anfang an, sondern nur Teile vom Ende. Und dazu zerrt es das Bild.


Nimm einfach mal das hier und spiele mit FOV und ZCENTER rum.
BlitzBasic: [AUSKLAPPEN]
Const XCENTER = 320
Const YCENTER = 240
Const ZCENTER = 256
Const FOV = 256
Const YCoord = 100


Graphics 640, 480




Repeat
Cls

For XCoord = -160 To 160
For ZCoord = 1 To 800
x = FOV * XCoord / (ZCoord + ZCENTER) + XCENTER
y = FOV * YCoord / (ZCoord + ZCENTER) + YCENTER
Plot x,y
Next
Next

Color 200,0,0
Line(160, 140, 480, 140)
Line(160, 340, 480, 340)
Line(160, 340, 160, 140)
Line(480, 340, 480, 140)
Text 160, 350, "Viewport 320x200 px"
Color 255, 255, 255

Flip()
Until KeyHit(1)



Zitat:
Hast du vielleicht eine Idee, wie ich den Skalierungsfaktror eines Sprites aus dem z herausbekomme? Ich würde es prozentual machen. z = 800 = 0%, z = 1 = 100%

Ja genau, ist einfacher 3-Satz.
Nur das 800 nah an der Kamera ist und 1 weit weg, da ZCENTER der Startpunkt im Z ist.
Also: z = 1 = 0% und z = 800 = 100%
Code: [AUSKLAPPEN]
Also wenn 800 = 100% dann:
Spriteskalierung in % = (ZCoord/800) * 100



Gruß Trust
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.
 

funkmaster5000

BeitragSo, März 17, 2013 18:25
Antworten mit Zitat
Benutzer-Profile anzeigen
Erstmal Danke, dass du mir so nett hilfst! Interessiert dich das Thema? Ich finds ziemlich spannend Wink
So wurde es ja früher wirklich gemacht, als es noch kein "echtes" 3D gab. Schon ne super Leistung!

Aber ich habe (natürlich) noch eine Frage:

Trust hat Folgendes geschrieben:

Nur das 800 nah an der Kamera ist und 1 weit weg, da ZCENTER der Startpunkt im Z ist.
Also: z = 1 = 0% und z = 800 = 100%


Bist du dir sicher?
Bild 1: For ZCoord = 1 to 40
Bild 2: For ZCoord = 1 to 800

user posted image

Je größer mein z, desto weiter reicht die Strecke!

Trust

BeitragSo, März 17, 2013 19:17
Antworten mit Zitat
Benutzer-Profile anzeigen
Ja, finde es ziemlich interessant! Smile
Wie man früher aus wenig Leistung viel rausgeholt hat.
Deshalb hat es mir auch die Iso-Perspektive so angetan Wink


ZCENTER ist der um 256 verschobene Koordinatenursprung.
Genauso wie XCENTER der um 160 verschobene Koordinatenursprung
und YCENTER der um 100 verschobene Koordinatenursprung ist.

Da es Z in 2D nicht gibt wird versucht es in X und Y auszudrücken.
Umso weiter weg ein Objekt vom Z-Ursprung ist desto größer wird Y.
Umso näher ein Objekt am Z Ursprung ist desto kleiner wird Y.

Das "imaginäre" Z in 2D fängt wie du sagst bei der "Kamera" bzw. der 2D-Projektionfläche an.

Also wird auch vom unteren Bildschirmrand angefangen zu zeichnen ( Y ist groß, da Punkt weit weg vom Koordinatenursprung)

Zitat:

Nur das 800 nah an der Kamera ist und 1 weit weg, da ZCENTER der Startpunkt im Z ist.
Also: z = 1 = 0% und z = 800 = 100%

Da habe ich mich aber wirklich vertan. Habe das oben geschriebene mit Z und Y verwechselt.
Sorry Sad

Die Formel wäre dann nätürlich wenn z = 1 = 100% und z = 800 = 0%
Code: [AUSKLAPPEN]
Skalierung in % = 100 - (ZCoord/800) * 100



Gruß Trust
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.

Neue Antwort erstellen


Übersicht BlitzBasic Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group