application/octet-stream
•
10.23 KB
•
270 lines
#!/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]
[returnable
[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]
[returnable
[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]