Login
7 branches 0 tags
Ben (Win10) Revert "Removed [$roots/save] [$roots/restore] bcOps" e3102ac 3 years ago 740 Commits
nujel / tests / raymarch.nuj
#!/usr/bin/env nujel
;; Taken from here: https://www.shadertoy.com/view/sd2Gzt
;; Term / Raymarch Framework

[require :array/2d]
[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]
      [dotimes [y [ref img :height]]
               [dotimes [x [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]]
      [dotimes [x w]
               [dotimes [y h]
                        [array/2d/set! out x y [renderFun [vec x y 1] resolution]]]]
      out]

[defn slideshow [slides colorfun width height]
      [def first #t]
      [doseq [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 δ]
      [dotimes [i 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]
              [dotimes [i 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]
              [dotimes [i 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]