How does 3d math work?

Everything else that doesn't fall into one of the other PB categories.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

How does 3d math work?

Post by Trond »

I don't know anything about 3d math except I know about the basic coordinate system. I made this starfield from a tutorial, and it's pretty cool. Unfortunately, I don't get how it works (specifically, the world-to-screen coordinate transformation procedure). So I was wondering if anyone would care to explain it to me? Please, in a simple fashion!

Code: Select all

Structure PT3
  x.f
  y.f
  z.f
EndStructure

Structure iPT2
  x.i
  y.i
EndStructure

Global Eye.PT3
Eye\x = 512
Eye\y = 384
Eye\z = 30

Procedure Cam2Scr(*Cam.PT3, *Scr.iPT2)
  If *Cam\z > 0
    ; These two lines works wonders, and how/why is a complete mystery to me.
    *Scr\x = eye\x + *Cam\x * eye\z / (*Cam\z + eye\z)
    *Scr\y = eye\y - *Cam\y * eye\z / (*Cam\z + eye\z)
    ProcedureReturn 1
  EndIf
  *Scr\x = -1
  *Scr\y = -1
EndProcedure

If InitSprite() = 0
  MessageRequester("Error", "Can't open screen & sprite enviroment!", 0)
  End
EndIf

OpenWindow(0, 0, 0, 1024, 768, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_Invisible)
OpenWindowedScreen(WindowID(0), 0, 0, 1024, 768, 0, 0, 0)
HideWindow(0, 0)

NewList Stars.PT3()

Macro sxy
  Stars()\x = -1000+Random(2000)
  Stars()\y = -1000+Random(2000)
EndMacro

For I = 0 To 400
  AddElement(Stars())
  Stars()\z = Random(255)
  sxy
Next

Repeat
  Repeat
    Event = WindowEvent()   
    Select Event
      Case #PB_Event_CloseWindow
        End
    EndSelect
  Until Event = 0

  FlipBuffers()
  ClearScreen(RGB(0, 0, 0))
  
  StartDrawing(ScreenOutput())
  ForEach Stars()
    Cam2Scr(@Stars()\x, @p.iPT2)
    If p\x >= 0 And p\x < 1024 And p\y >= 0 And p\y < 768
      Col = 255-(Stars()\z)
      Col = RGB(col, col, col)
      Plot(p\x, p\y, Col)
      Stars()\z - 1
    Else
      DeleteElement(Stars())
      AddElement(Stars())
      Stars()\z = 255
      sxy
    EndIf
  Next
  StopDrawing()
  
  Delay(10)
ForEver
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Re: How does 3d math work?

Post by Kaeru Gaman »

a lot of this, including the projection, is really well explained in the POV-Ray documentation.
www.povray.org
helped me to understand much of the basics.
oh... and have a nice day.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: How does 3d math work?

Post by Trond »

Are you sure? I can't find anything about the actual formula, only the concept is explained, and I already understand that, I just can't relate it to the formula.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Re: How does 3d math work?

Post by Kaeru Gaman »

oh.. could be, it's a while ago I worked thru it.
sorry.

what you need to transform a 3D-vector into a 2D-vector is a "Matrix".
http://en.wikipedia.org/wiki/Matrix_(mathematics)
the Values the Matrix needs to contain are directly dependant of scale and projection...

sorry I can't help you much further...
oh... and have a nice day.
User avatar
Demivec
Addict
Addict
Posts: 4283
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: How does 3d math work?

Post by Demivec »

@Trond: Here's a simple explanation.:wink: I'll rewrite the formulas to use different variable names.

Code: Select all

x = x1 + x2 * (z1 / (z2 + z1))
y = y1 + y2 * (z1 / (z2 + z1))
(x,y) is the screen position. (x1,y1,z1) are the eye (or typically camera position). (x2,y2,z2) are the object position (which you call Cam).

Both the 'x' and 'y' position of the object are scaled by the combined 'z' position of the eye and the object and then added to the eye position for the 'x' and 'y'.

This means that the farther away something is (greater 'z') the smaller the change it will cause in the original 'x' and 'y' of the eye position.

If the 'z' value was ignored for both the object and the eye position for instance the equations would reduce to:

Code: Select all

x = x1 + x2
y = y1 + y2
This would show the object's position without being scaled before it is added to the eye position. You can try this out in your code sample by simply commenting out the remainder of the formula. It will show all the stars with their varying brightnesses but without the benefit of depth distortion.

If you want to increase the distortion (or foreshortening effect for depth) just put a multiplier in the divisor portion of the formulas (i.e. 'x = x1 + x2 * (z1 / (M * z2 + z1))' where M = the multiplier) to create a tunnel like effect. If the multiplier is greater than one it increased the effect, if less than one it decreases it. Fifty is one that I like. It will also expose the fact that your start world isn't very wide or tall when its depth seems to be increased.

Just for fun, if you wanted to make the eye position move place this line before the delay. It will cause a shaking or jittering effect.

Code: Select all

Eye\x + 1 - Random(2): Eye\y + 1 - Random(2)
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: How does 3d math work?

Post by Trond »

Thank you very much, Demivec, I will read it thoroughly tomorrow, it seems like I can understand it with your help.
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Re: How does 3d math work?

Post by utopiomania »

Check out 'Donald E Knuth - The Art of Computer Programming' Algo heaven. :)
User avatar
Demivec
Addict
Addict
Posts: 4283
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: How does 3d math work?

Post by Demivec »

@Trond: I thought I would just post a small modification to your original code sample.

I renamed a few of the variables for clarity and modified one of the structures. I implemented a dynamic depthChangeRate for a simple special effect. Press SpaceBar to activate it and it runs until it completes before it can be activated again. It was fun to do and I thought it might give you some variations of the formulas for comparison. I also got to test out the new timer functions.:wink:

Code: Select all

Structure PT3
  x.f
  y.f
  Z.f
  px.i
  py.i
EndStructure

Structure iPT2
  x.i
  y.i
EndStructure

Global Cam.PT3
Cam\x = 512
Cam\y = 384
Cam\Z = 30

Procedure obj2Scr(*obj.PT3, *Scr.iPT2)
  If *obj\Z > 0
    *Scr\x = Cam\x + *obj\x * Cam\Z / (*obj\Z + Cam\Z)
    *Scr\y = Cam\y - *obj\y * Cam\Z / (*obj\Z + Cam\Z)
    ProcedureReturn 1
  EndIf
  *Scr\x = -1
  *Scr\y = -1
EndProcedure

If InitSprite() = 0
  MessageRequester("Error", "Can't open screen & sprite enviroment!", 0)
  End
EndIf

If InitKeyboard() = 0
  MessageRequester("Error", "Can't initialize Keyboard input!", 0)
  End
EndIf

OpenWindow(0, 0, 0, 1024, 768, "Press [Space] for special effect", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_Invisible)
OpenWindowedScreen(WindowID(0), 0, 0, 1024, 768, 0, 0, 0)
HideWindow(0, 0)

NewList Stars.PT3()

Macro sxy
  Stars()\x = -2000+Random(4000)
  Stars()\y = -2000+Random(4000)
EndMacro

For i = 0 To 400
  AddElement(Stars())
  Stars()\Z = Random(255)
  sxy
Next

Define depthChangerate = 1 ;this will be subtracted from the Z depth each frame
Define Msg$ ;this will be text that is displayed over the star background

Repeat
  Repeat
    event = WindowEvent()   
    Select event
      Case #pb_event_timer
        Select EventTimer()
          Case 10 ;Hyperspace ramp-up
            If depthChangerate < 20
              depthChangerate + 1
              AddWindowTimer(0,10,time * 0.90)
            Else
              RemoveWindowTimer(0,10)
              AddWindowTimer(0,45,time * 10)
              AddWindowTimer(0,40,500)
              time = 500
            EndIf
          Case 15
            Msg$ = "Hyperspace Travel Activated"
            RemoveWindowTimer(0,15)
          Case 40 ;Hyperspace ramp-down
            If depthChangerate > 1
              depthChangerate - 1
              AddWindowTimer(0,40,time * 1.1)
            Else
              RemoveWindowTimer(0,40)
              Msg$ = ""
              inHyperspace = #False
            EndIf
          Case 45
            Msg$ = "Disengaging Hyperspace Travel"
            RemoveWindowTimer(0,45)
        EndSelect 
      Case #PB_Event_CloseWindow
        End
    EndSelect
  Until event = 0

  FlipBuffers()
  ClearScreen(RGB(0, 0, 0))
 
  StartDrawing(ScreenOutput())
  ForEach Stars()
    obj2Scr(@Stars()\x, @p.iPT2)
    If p\x >= 0 And p\x < 1024 And p\y >= 0 And p\y < 768
      Col = 255-(Stars()\Z)
      Col = RGB(Col, Col, Col)
      Plot(p\x, p\y, Col)
      If Stars()\px + Stars()\py <> 0 And inHyperspace
        LineXY(Stars()\px,Stars()\py,p\x,p\y,Col)
      EndIf
      
      Stars()\px = p\x
      Stars()\py = p\y
      Stars()\Z - depthChangerate
    Else
      DeleteElement(Stars())
      AddElement(Stars())
      Stars()\Z = 255
      sxy
    EndIf
  Next
  If Msg$ <> ""
    DrawingMode(#PB_2DDrawing_Transparent)
    DrawText(512 - TextWidth(Msg$) / 2,700,Msg$,#Green)
  EndIf 
  StopDrawing()
  
  ExamineKeyboard()
  If KeyboardReleased(#PB_Key_Space) And Not inHyperspace
    inHyperspace = #True
    time = 500
    Msg$ = "Engaging Hyperspace Travel"
    AddWindowTimer(0,15,time * 10)
    AddWindowTimer(0,10,time)
  EndIf
  
  Delay(10)
ForEver
@Edit: added InitKeyboard() and Title to window that contains directions.
Last edited by Demivec on Sun Sep 20, 2009 2:31 am, edited 1 time in total.
jack
Addict
Addict
Posts: 1359
Joined: Fri Apr 25, 2003 11:10 pm

Re: How does 3d math work?

Post by jack »

nice example Demivec, I just had to add a InitKeyboard() before the Repeat..Forever loop.
User avatar
Demivec
Addict
Addict
Posts: 4283
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: How does 3d math work?

Post by Demivec »

jack wrote:nice example Demivec, I just had to add a InitKeyboard() before the Repeat..Forever loop.
@Jack: Thanks, I always try to stop before I go too far. Even so, I still managed to end up in outer space... :wink:

The code functioned fine for me without the InitKeyboard(). I only received a warning when I used the debugger. I didn't do that while coding it and so I hadn't noticed it. Thanks for bringing it to my attention.

Code in previous post edited to correct the omission.
Post Reply