Page 1 of 1

How does 3d math work?

Posted: Fri Sep 18, 2009 11:23 am
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

Re: How does 3d math work?

Posted: Fri Sep 18, 2009 12:49 pm
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.

Re: How does 3d math work?

Posted: Fri Sep 18, 2009 1:06 pm
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.

Re: How does 3d math work?

Posted: Fri Sep 18, 2009 1:14 pm
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...

Re: How does 3d math work?

Posted: Fri Sep 18, 2009 7:22 pm
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)

Re: How does 3d math work?

Posted: Fri Sep 18, 2009 10:13 pm
by Trond
Thank you very much, Demivec, I will read it thoroughly tomorrow, it seems like I can understand it with your help.

Re: How does 3d math work?

Posted: Fri Sep 18, 2009 10:31 pm
by utopiomania
Check out 'Donald E Knuth - The Art of Computer Programming' Algo heaven. :)

Re: How does 3d math work?

Posted: Sat Sep 19, 2009 6:17 am
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.

Re: How does 3d math work?

Posted: Sat Sep 19, 2009 9:35 pm
by jack
nice example Demivec, I just had to add a InitKeyboard() before the Repeat..Forever loop.

Re: How does 3d math work?

Posted: Sun Sep 20, 2009 2:35 am
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.