Login
7 branches 0 tags
Ben (Win10) Fixed invalid memory accesses as well as adding a [return] SF aae3659 3 years ago 594 Commits
nujel / tests / raymarch.nuj
#!/usr/bin/env nujel
; Taken from here: https://www.shadertoy.com/view/sd2Gzt

;;; Term / Raymarch Framework

[def iTime 1.3]

[defn term/cls []
  [print "\e[2J\e[H"]]

[defn term/locate [x y]
  [print [cat "\e[" [+ 1 y] ";" [+ 1 x] "H"]]]

[defn term/cursor-position []
  [print "\e[6n"]]

[defn term/out/6bit [color]
  [let [[ir [int [* 6.0 [vec/x color]]]]
        [ig [int [* 6.0 [vec/y color]]]]
        [ib [int [* 6.0 [vec/z color]]]]]
       [def code [+ 16 ib [* 6 ig] [* 36 ir]]]
       [fmt "\e[48;5;{code}m "]]]

[defn term/out/truecolor [color]
  [let [[ir [int [* 256.0 [vec/x color]]]]
        [ig [int [* 256.0 [vec/y color]]]]
        [ib [int [* 256.0 [vec/z color]]]]]
       [fmt "\e[48;2;{ir};{ig};{ib}m  "]]]

[defn term/blit [img colorfun outfun]
  [for [y 0 [ref img :height]]
    [for [x 0 [ref img :width]]
      [outfun [colorfun [array/2d/ref img x y]]]]
    [outfun "\n"]]
  [outfun ansi-reset]]

[defn term/raymarch [renderFun w h]
       [def resolution [vec w h 1]]
       [def out [array/2d/allocate w h]]
       [for [x 0 w]
         [for [y 0 h]
           [array/2d/set! out x y [renderFun [vec x y 1] resolution]]]]
       out]

[defn slideshow [slides colorfun width height]
  [def first #t]
  [for-in [shader slides]
          [if first
              [set! first #f]
              [input "Press enter for the next slide"]]
          [term/cls]
          [term/locate 0 0]
          [def img [term/raymarch shader width height]]
          [term/blit img colorfun print]]]

[defn animate [shader colorfun width height times δ]
  [for [i 0 times]
    [set! iTime [* δ i]]
    [def img [term/raymarch shader width height]]
    [term/cls]
    [term/locate 0 0]
    [term/blit img colorfun print]]]

;;; Gradient shader
[defn shader/gradient [fragCoord resolution]
     [def uv [/ fragCoord resolution]]
     [vec [vec/x uv]
          [vec/y uv]
          [- 1.0 [max [vec/x uv] [vec/y uv]]]]]

;;; Sphere shader
;;; Pretty basic port of https://www.shadertoy.com/view/llt3R4
[def shader/sphere/simple
     [let* [def MAX_MARCHING_STEPS 255]
           [def MIN_DIST 0.0]
           [def MAX_DIST 100.0]
           [def EPSILON 0.001]

           [defn sphereSDF [samplePoint]
             [- [vec/magnitude samplePoint] 1.0]]

           [defn sceneSDF [samplePoint]
             [sphereSDF samplePoint]]

           [defn shortestDistanceToSurface [eye marchingDirection start end]
             [def depth start]
             [for [i 0 MAX_MARCHING_STEPS]
                  [def dist [sceneSDF [+ eye [* [vec depth] marchingDirection]]]]
                  [when [< dist EPSILON] [return depth]]
                  [set! depth [+ depth dist]]
                  [when [>= depth end] [return end]]]
             [return end]]

           [defn rayDirection [fieldOfView size fragCoord]
             [def xy [- fragCoord [/ size [vec 2]]]]
             [def z [/ [vec/y size] [tan [/ [radians fieldOfView] 2]]]]
             [vec/normalize [vec [vec/x xy] [vec/y xy] z]]]

           [fn [fragCoord resolution]
             [def dir [rayDirection 50.0 resolution fragCoord]]
             [def eye [vec 0 0 -5]]
             [def dist [shortestDistanceToSurface eye dir MIN_DIST MAX_DIST]]
             [if [> dist [- MAX_DIST EPSILON]]
                 [vec 0 0.05 0.15]
                 [vec 0.95 0.2 0]]]
]]

;;; Sphere phone shader
;;; Pretty basic port of https://www.shadertoy.com/view/llt3R7
[def shader/sphere/phong
     [let* [def MAX_MARCHING_STEPS 255]
           [def MIN_DIST 0.0]
           [def MAX_DIST 100.0]
           [def EPSILON 0.001]

           [defn translate [p offset]
             [- p offset]]

           #|
            | Signed distance function for a sphere centered at the origin with radius 1.0;
            |#
           [defn sphereSDF [samplePoint]
             [- [vec/magnitude [translate samplePoint [vec [* [sin iTime] 2] [* [cos iTime] 1.3] 0]]] 1.0]]

           [defn secondSphereSDF [samplePoint]
             [- [vec/magnitude [translate samplePoint [vec [* [sin [* iTime -0.7]] 2] [* [cos [* iTime -0.7]] 1.3] 0]]] 1.0]]

           [defn thirdSphereSDF [samplePoint]
             [- [vec/magnitude [translate samplePoint [vec [* [sin [* iTime -2.3]] 2] 0 [* [cos [* iTime -2.7]] 1.3]]]] 1.0]]

           #|
            | Signed distance function describing the scene.
            |
            | Absolute value of the return value indicates the distance to the surface.
            | Sign indicates whether the point is inside or outside the surface,
            | negative indicating inside.
            |#
           [defn sceneSDF [samplePoint]
             [min [sphereSDF samplePoint]
                  [secondSphereSDF samplePoint]
                  [thirdSphereSDF samplePoint]]]

           #|
            | Using the gradient of the SDF, estimate the normal on the surface at point p.
            |#
           [defn estimateNormal [p]
             [vec/normalize [vec [- [sceneSDF [+ p [vec EPSILON 0 0]]] [sceneSDF [- p [vec EPSILON 0 0]]]]
                                 [- [sceneSDF [+ p [vec 0 EPSILON 0]]] [sceneSDF [- p [vec 0 EPSILON 0]]]]
                                 [- [sceneSDF [+ p [vec 0 0 EPSILON]]] [sceneSDF [- p [vec 0 0 EPSILON]]]]]]]

           #|
            | Lighting contribution of a single point light source via Phong illumination.
            |
            | The vec3 returned is the RGB color of the light's contribution.
            |
            | k_d: Diffuse color
            | k_s: Specular color
            | alpha: Shininess coefficient
            | p: position of point being lit
            | eye: the position of the camera
            | lightPos: the position of the light
            | lightIntensity: color/intensity of the light
            |
            | See https://en.wikipedia.org/wiki/Phong_reflection_model#Description
            |#
           [defn phongContribForLight [k_d k_s α p eye lightPos lightIntensity]
              [def N [estimateNormal p]]
              [def L [vec/normalize [- lightPos p]]]
              [def V [vec/normalize [- eye p]]]
              [def R [vec/normalize [vec/reflect [- L] N]]]

              [def dotLN [vec/dot L N]]
              [def dotRV [vec/dot R V]]

              [cond [[< dotLN 0.0] [vec 0]]
                    [[< dotRV 0.0] [* lightIntensity k_d [vec dotLN]]]
                    [#t [* lightIntensity k_d [vec dotLN] k_s [vec [pow dotRV α]]]]]]
           #|
            | Lighting via Phong illumination.
            |
            | The vec3 returned is the RGB color of that point after lighting is applied.
            | k_a: Ambient color
            | k_d: Diffuse color
            | k_s: Specular color
            | alpha: Shininess coefficient
            | p: position of point being lit
            | eye: the position of the camera
            |
            | See https://en.wikipedia.org/wiki/Phong_reflection_model#Description
            |#
           [defn phongIllumination [k_a k_d k_s α p eye]
             [def ambientLight [* [vec 0.5] [vec 1.0 1.0 1.0]]]
             [def light1Pos [vec [* 4.0 [sin iTime]]
                                 2.0
                                 [* -4.0 [cos iTime]]]]
             [def light1Intensity [vec 0.4 0.4 0.4]]

             [def light2Pos [vec [* 2 [sin [* 0.27 iTime]]]
                                 [* 2 [cos [* 0.27 iTime]]]
                                 -6]]
             [def light2Intensity [vec 0.4 0.4 0.4]]

             [+ [* ambientLight k_a]
                [phongContribForLight k_d k_s α p eye light1Pos light1Intensity]
                [phongContribForLight k_d k_s α p eye light2Pos light2Intensity]]]

           #|
            | Return the shortest distance from the eyepoint to the scene surface along
            | the marching direction. If no part of the surface is found between start and end,
            | return end.
            |
            | eye: the eye point, acting as the origin of the ray
            | marchingDirection: the normalized direction to march in
            | start: the starting distance away from the eye
            | end: the max distance away from the ey to march before giving up
            |#
           [defn shortestDistanceToSurface [eye marchingDirection start end]
             [def depth start]
             [for [i 0 MAX_MARCHING_STEPS]
                  [def dist [sceneSDF [+ eye [* [vec depth] marchingDirection]]]]
                  [when [< dist EPSILON] [return depth]]
                  [set! depth [+ depth dist]]
                  [when [>= depth end] [return end]]]
             [return end]]

           #|
            | Return the normalized direction to march in from the eye point for a single pixel.
            |
            | fieldOfView: vertical field of view in degrees
            | size: resolution of the output image
            | fragCoord: the x,y coordinate of the pixel in the output image
            |#
           [defn rayDirection [fieldOfView size fragCoord]
             [def xy [- fragCoord [/ size [vec 2]]]]
             [def z [/ [vec/y size] [tan [/ [radians fieldOfView] 2]]]]
             [vec/normalize [vec [vec/x xy] [vec/y xy] z]]]

           [defn getColor [p]
             [if [< [sphereSDF p] [secondSphereSDF p]]
                 [if [< [thirdSphereSDF p] [sphereSDF p]]
                     [vec 0.80 0.85 0.1]
                     [vec 0.9 0.2 0.2]]
                 [if [< [thirdSphereSDF p] [secondSphereSDF p]]
                     [vec 0.80 0.85 0.1]
                     [vec 0.1 0.7 0.4]]]]

           [fn [fragCoord resolution]
             [def uv [/ fragCoord resolution]]
             [def dir [rayDirection 50.0 resolution fragCoord]]
             [def eye [vec 0 0 -12]]
             [def dist [shortestDistanceToSurface eye dir MIN_DIST MAX_DIST]]
             [def p [+ eye [* dist dir]]]

             [def K_a [vec 0.2]]
             [def K_s [vec 1.0]]
             [def shinyness 1.0]

             [if [> dist [- MAX_DIST EPSILON]]
                 [+ [vec 0 0.05 0.1] [vec 0 [* [vec/y uv] 0.05] [* [vec/y uv] 0.15]]]
                 [phongIllumination K_a [getColor p] K_s shinyness p eye]]]
]]


[def slides [list shader/gradient
                  shader/sphere/simple
                  shader/sphere/phong]]
[slideshow slides term/out/truecolor 116 54]
#;[animate shader/sphere/phong term/out/truecolor 116 54 1000 0.1]