application/octet-stream
•
10.03 KB
•
250 lines
#!/usr/bin/env nujel
; Taken from here: https://www.shadertoy.com/view/sd2Gzt
;;; Term / Raymarch Framework
[def iTime 1.2]
[defun 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 "]]]
[defun term/blit [img colorfun outfun]
[def out ""]
[for [y 0 [ref img :height]]
[for [x 0 [ref img :width]]
[set! out [cat out [term/out/truecolor [array/2d/ref img x y]]]]]
[set! out [cat out "\n"]]]
[cat out ansi-reset]]
[defun 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]
;;; Gradient shader
[defun 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]
[defun sphereSDF [samplePoint]
[- [vec/magnitude samplePoint] 1.0]]
[defun sceneSDF [samplePoint]
[sphereSDF samplePoint]]
[defun 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]]]
[defun 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]
[defun translate [p offset]
[- p offset]]
#|
| Signed distance function for a sphere centered at the origin with radius 1.0;
|#
[defun sphereSDF [samplePoint]
[- [vec/magnitude [translate samplePoint [vec [* [sin iTime] 2] [* [cos iTime] 1.3] 0]]] 1.0]]
[defun secondSphereSDF [samplePoint]
[- [vec/magnitude [translate samplePoint [vec [* [sin [* iTime -0.7]] 2] [* [cos [* iTime -0.7]] 1.3] 0]]] 1.0]]
[defun 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.
|#
[defun sceneSDF [samplePoint]
[min [sphereSDF samplePoint]
[secondSphereSDF samplePoint]
[thirdSphereSDF samplePoint]]]
#|
| Using the gradient of the SDF, estimate the normal on the surface at point p.
|#
[defun 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
|#
[defun 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
|#
[defun 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
|#
[defun 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
|#
[defun 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]]]
[defun 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 img/gradient [term/raymarch shader/gradient 40 30]]
[def img/sphere/simple [term/raymarch shader/sphere/simple 40 30]]
[def img/sphere/phong [term/raymarch shader/sphere/phong 40 30]]
[def digest/gradient [hash/adler32 [term/blit img/gradient]]]
[def digest/sphere/simple [hash/adler32 [term/blit img/sphere/simple]]]
[def digest/sphere/phong [hash/adler32 [term/blit img/sphere/phong]]]
[when [!= digest/gradient 3691418998]
[println [term/blit img/gradient]]
[throw [list :wrong-result "Raymarched gradient wrong" digest/gradient]]]
[when [!= digest/sphere/simple 2493881632]
[println [term/blit img/sphere/simple]]
[throw [list :wrong-result "Raymarched simple sphere wrong" digest/sphere/simple]]]
[when [!= digest/sphere/phong 4145117077]
[println [term/blit img/sphere/phong]]
[throw [list :wrong-result "Raymarched phong spheres wrong" digest/sphere/phong]]]