Ray Tracing in NetLogo

Ray Tracing in NetLogo preview image

1 collaborator

Default-person Sam Belliveau (Author)

Tags

(This model has yet to be categorized with any tags)
Visible to everyone | Changeable by everyone
Model was written in NetLogo 6.1.1 • Viewed 109 times • Downloaded 10 times • Run 0 times
Download the 'Ray Tracing in NetLogo' modelDownload this modelEmbed this model

Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)


Comments and Questions

Please start the discussion about this model! (You'll first need to log in.)

Click to Run Model

globals [
  cam-x
  cam-y
  cam-z

  cam-yaw
  cam-pitch

  cam-fov
]

patches-own [
  patch-xcor
  patch-ycor
  patch-zcor

  patch-xvel
  patch-yvel
  patch-zvel

  patch-hits

  patch-is-done
]

turtles-own [
  world-xcor
  world-ycor
  world-zcor
  world-size
]

;;; CAMERA ;;;

to reset-camera
  set cam-x 0
  set cam-y 0
  set cam-z 0
  set cam-yaw 0
  set cam-pitch 0
  reset-patches
end 

to move-camera [ dist ]
  set cam-x (cam-x + dist * (sin cam-yaw) * (cos cam-pitch))
  set cam-y (cam-y + dist * (sin cam-pitch))
  set cam-z (cam-z + dist * (cos cam-yaw) * (cos cam-pitch))
  reset-patches
end 

to move-camera-up [ dist ]
  set cam-y (cam-y + dist)
  reset-patches
end 

to move-camera-sideways [ dist ]
  set cam-x (cam-x + dist * (sin (cam-yaw + 90)))
  set cam-z (cam-z + dist * (cos (cam-yaw + 90)))
  reset-patches
end 

to-report loop-angle [ ang ]
  let looped-ang (((ang mod 360) + 3600) mod 360)
  if (looped-ang > 180)
  [ set looped-ang (looped-ang - 360) ]
  report looped-ang
end 

to turn-camera-yaw [ ang ]
  set cam-yaw (loop-angle (cam-yaw + ang))
  reset-patches
end 

to turn-camera-pitch [ ang ]
  set cam-pitch (loop-angle (cam-pitch + ang))
  reset-patches
end 

;;; PATCHES ;;;

to reset-patches
  ; camera must be above ground
  set cam-y (abs cam-y)

  ; direction camera is facing
  let start-xvel ((sin cam-yaw) * (cos cam-pitch))
  let start-yvel (sin cam-pitch)
  let start-zvel ((cos cam-yaw) * (cos cam-pitch))

  ; amount that the camera must turn due to fov
  let delt (tan (cam-fov / 2))

  ; amount that each of the axis
  ; will turn due to change in yaw
  let yaw-xdelt (delt * (sin (90 + cam-yaw)))
  let yaw-ydelt (0)
  let yaw-zdelt (delt * (cos (90 + cam-yaw)))

  ; amount that each of the axis
  ; will turn due to change in pitch
  let pitch-xdelt (delt * (0 - sin cam-yaw) * (sin cam-pitch))
  let pitch-ydelt (delt * (cos cam-pitch))
  let pitch-zdelt (delt * (0 - cos cam-yaw) * (sin cam-pitch))

  ; amount that the patches scale themself too
  let div-cor ((max-pxcor + max-pycor) / 2)

  ask patches [
    ; move ray to camera
    set patch-xcor (cam-x)
    set patch-ycor (cam-y)
    set patch-zcor (cam-z)

    ; add yaw and pitch to ray
    set patch-xvel (start-xvel + (yaw-xdelt * pxcor + pitch-xdelt * pycor) / div-cor)
    set patch-yvel (start-yvel + (yaw-ydelt * pxcor + pitch-ydelt * pycor) / div-cor)
    set patch-zvel (start-zvel + (yaw-zdelt * pxcor + pitch-zdelt * pycor) / div-cor)

    ; reset hits
    set patch-hits (0)

    ; activate-patch
    set patch-is-done false

    ; render really quickly
    move-patch
    update-patch-color
  ]
end 

to-report distance-xyz [ x y z ]
  ; report pythagorean theorem
  report sqrt (x * x + y * y + z * z)
end 

to-report patch-distancexyz [ x y z ]
  ; take distance of each axis
  let xdist (x - patch-xcor)
  let ydist (y - patch-ycor)
  let zdist (z - patch-zcor)

  ; report relative distance
  report (distance-xyz xdist ydist zdist)
end 

to-report turtle-distancexyz [ x y z ]
  ; take distance of each axis
  let xdist (x - world-xcor)
  let ydist (y - world-ycor)
  let zdist (z - world-zcor)

  ; report relative distance
  report (distance-xyz xdist ydist zdist)
end 

to-report is-touching-turtle [ x y z ]
  ; take distance of each axis
  let xdist (x - world-xcor)
  let ydist (y - world-ycor)
  let zdist (z - world-zcor)

  ; test if two are touching without sqrt
  report ((xdist * xdist + ydist * ydist + zdist * zdist) <= (world-size * world-size))
end 

to-report max-step-size [ x y z xv yv zv ]
  ; take distance of each axis
  let xoc (x - world-xcor)
  let yoc (y - world-ycor)
  let zoc (z - world-zcor)

  ; calculate roots
  let a (xv * xv + yv * yv + zv * zv)
  let b (2 * (xoc * xv + yoc * yv + zoc * zv))
  let c ((xoc * xoc + yoc * yoc + zoc * zoc) - (world-size * world-size))

  ; get square root part of equation
  let step-size (b * b - 4 * a * c)

  ; if root is negative return big number
  if (step-size < 0) [ report 65536 ]

  ; get entire equation
  set step-size ((0 - (b + (sqrt step-size))) / (2 * a))

  ; if answer is negative return really big number
  if (step-size < 0) [ report 65536 ]

  ; return answer
  report step-size
end 

to normalize-patch
  ; get distance of velocity
  let mag (distance-xyz patch-xvel patch-yvel patch-zvel)

  ; scale velocity down
  set patch-xvel (patch-xvel / mag)
  set patch-yvel (patch-yvel / mag)
  set patch-zvel (patch-zvel / mag)
end 

to move-patch
  if (not patch-is-done) [
    ; normalize the velocitys
    normalize-patch

    ; push data into local memory
    let local-xcor (patch-xcor)
    let local-ycor (patch-ycor)
    let local-zcor (patch-zcor)
    let local-xvel (patch-xvel)
    let local-yvel (patch-yvel)
    let local-zvel (patch-zvel)
    let local-hits (patch-hits)
    let local-is-done (false)

    ; get largest steps size possible
    let floor-distance (65535)

    ; if patch is moving down, limit step size
    if (local-yvel < 0) [
      set patch-is-done false
      let scaled-ycor (abs (local-ycor / local-yvel))
      set floor-distance scaled-ycor
    ]

    ; calculate the closest turtle to the ray
    let closest-turtle (min-one-of turtles [
      max-step-size local-xcor local-ycor local-zcor
                    local-xvel local-yvel local-zvel
    ])

    ; do reflection calculation on closest turtle
    ask closest-turtle [
      ; get the distance from the ray to the turtle
      let dist (max-step-size local-xcor local-ycor local-zcor local-xvel local-yvel local-zvel)

      ; check which is closer, the turtle or floor
      ifelse(dist < floor-distance) [
        ; move the ray to the surface of the circle
        set local-xcor (local-xcor + local-xvel * dist)
        set local-ycor (local-ycor + local-yvel * dist)
        set local-zcor (local-zcor + local-zvel * dist)

        ; calculate dot product of the
        let normal-dist (turtle-distancexyz local-xcor local-ycor local-zcor)
        let normal-x ((local-xcor - world-xcor) / normal-dist)
        let normal-y ((local-ycor - world-ycor) / normal-dist)
        let normal-z ((local-zcor - world-zcor) / normal-dist)
        let dot (normal-x * local-xvel + normal-y * local-yvel + normal-z * local-zvel)

        ; reflect the ray off the turtle
        set local-xvel (local-xvel - 2 * dot * normal-x)
        set local-yvel (local-yvel - 2 * dot * normal-y)
        set local-zvel (local-zvel - 2 * dot * normal-z)

        ; increase the amount of hits
        set local-hits (local-hits + 1)
      ] [
        ; only bounce off floor when moving down
        ifelse (local-yvel < 0) [
          ; move ray to the floor
          set local-xcor (local-xcor + local-xvel * floor-distance)
          set local-ycor (local-ycor + local-yvel * floor-distance)
          set local-zcor (local-zcor + local-zvel * floor-distance)

          ; bounce the ray off the floor
          set local-ycor (abs local-ycor)
          set local-yvel (abs local-yvel)

          ; floor patch settings
          let square-size 20
          let floor-hits 2.5
          let square-diff 0.5

          ; calculate if it is a bright or dark square
          if-else (
            ((local-xcor mod square-size) <= (square-size / 2)) !=
            ((local-zcor mod square-size) <= (square-size / 2)))
          [ set local-hits (local-hits + floor-hits - square-diff) ]
          [ set local-hits (local-hits + floor-hits + square-diff) ]
        ] [
          ; if everything is done, tell ray that it is done
          set local-is-done (true)
        ]
      ]
    ]

    ; push data into patch memory
    set patch-xcor (local-xcor)
    set patch-ycor (local-ycor)
    set patch-zcor (local-zcor)
    set patch-xvel (local-xvel)
    set patch-yvel (local-yvel)
    set patch-zvel (local-zvel)
    set patch-hits (local-hits)
    set patch-is-done (local-is-done)
  ]
end 

to-report cap-rgb [ val ]
  ; cap value from 0 - 255
  if (val < 000) [ report 000 ]
  if (val > 255) [ report 255 ]
  report val
end 

to update-patch-color
  ; copy amount of hits into local memory
  let local-hits (patch-hits)

  ; if patch is traveling down, simulate hit
  if (patch-yvel < 0) [ set local-hits (local-hits + 3) ]

  ; get angle of the ray for rainbow color
  let angle 270
  if (rainbow and ((patch-xvel != 0) or (patch-zvel != 0)))
  [ set angle ((atan patch-xvel patch-zvel) * (2)) ]

  ; get pitch of the ray
  let xz-mag (sqrt (patch-xvel * patch-xvel + patch-zvel * patch-zvel))
  let hit-scale (0.618 ^ local-hits)
  let sky-scale (0.25 + 1.6 * (cos (atan xz-mag (abs patch-yvel))))

  ; turn angles into rainbow and shadows
  let r (128 + 128 * (sin (angle + 000)))
  let g (128 + 128 * (sin (angle + 120)))
  let b (128 + 128 * (sin (angle + 240)))

  ; darken colors due to sky
  set r (r * sky-scale)
  set g (g * sky-scale)
  set b (b * sky-scale)

  ; average color with hits
  set r (r * hit-scale + circle-r * (1 - hit-scale))
  set g (g * hit-scale + circle-g * (1 - hit-scale))
  set b (b * hit-scale + circle-b * (1 - hit-scale))

  ; set color to the calculated color
  set pcolor (list (cap-rgb r) (cap-rgb g) (cap-rgb b))
end 

to update-progress
  set render-progress (round (1000 * (count patches with [ patch-is-done ]) / (count patches)) / 10)
end 

to turtle-init [ x y z in-size]
  set hidden? true
  set world-xcor x
  set world-ycor y
  set world-zcor z
  set world-size in-size
end 

;;; TEST ;;;

to setup
  ca

  ; create the spheres
  cro 1 [ turtle-init 0   12  10  3 ]
  cro 1 [ turtle-init -48 12  10  3 ]
  cro 1 [ turtle-init -24 40  10  3 ]
  cro 1 [ turtle-init -24 16  16  6 ]
  cro 1 [ turtle-init 0   24  50  25 ]
  cro 1 [ turtle-init -48 24  50  24 ]
  cro 1 [ turtle-init 0   50  150 36 ]
  cro 1 [ turtle-init -72 50  150 36 ]
  reset-camera

  ; setup camera
  set cam-x -30
  set cam-y 42
  set cam-z -40
  set cam-pitch -15
  set cam-yaw 7
  set cam-fov 90
  set fov cam-fov

  reset-patches
end 

to go
  ; if fov changes, draw new image
  if (fov != cam-fov) [
    set cam-fov (fov)
    reset-patches
  ]

  ; cycle through update groups
  ask patches [
    move-patch
    update-patch-color
  ]

  ; update progress bar
  update-progress

  display
end 

There is only one version of this model, created about 4 years ago by Sam Belliveau.

Attached files

File Type Description Last updated
Ray Tracing in NetLogo.png preview Preview for 'Ray Tracing in NetLogo' about 4 years ago, by Sam Belliveau Download

This model does not have any ancestors.

This model does not have any descendants.