Clojure Voxel

The geometry of these images are defined in Lisp, colors and lights are made with Blender.

Lisp is a good way to build domain specific languages. In this case a small language to define how geometrical forms should be placed in 3D space. The basic shapes are cubes, spheres or spline curves. The more elaborate forms are noise functions.

Images shown on the Voxel page use only a few OpenVDB operations. But here lots of objects are needed. Therefor Lisp is used to produce all these OpenVDB function calls.

Two Clojure programs define the geometry of the above image. One for the body sphere cobbled with a band of small spheres. The second program to define the curvature of the tentacles.

(ns grow.scratch.vdb-poisson-sphere)
(use 'grow.core)
(vdb-template)

(grow
  (def overlap 0.66)
  (def body-scale 4)
  (def band-scale (* 0.7 body-scale))
  
  (defn inside [n] (> (rnd-uniform band-scale) (max n (- n))))
  
  (rule S0
    (let [zband (inside (coord-z))]
      (when zband
        (object ["sphere"] :scale [(scale (* 1 overlap))])
        (object ["union"])))
  )
  (rule S1
    (let [zband (inside (coord-z))]
      (when zband
        (object ["sphere"] :scale [(scale (* 0.66 overlap))])
        (object ["union"])))
  )
  (rule S2
    (let [zband (inside (coord-z))]
      (when zband
        (object ["sphere"] :scale [(scale (* 0.66 0.66 overlap))])
        (object ["union"])))
  )

  (rule G 
    (object ["sphere"] :scale [(scale body-scale)])
    (generator [S0] (import-neighbour-rt body-scale "../grow/resources/Icosphere-2724.L3N6.level0.neighbour"))
    (generator [S1] (import-neighbour-rt body-scale "../grow/resources/Icosphere-2724.L3N6.level1.neighbour"))
    (generator [S2] (import-neighbour-rt body-scale "../grow/resources/Icosphere-2724.L3N6.level2.neighbour"))
  )

  (start G 99)
)

To build the body one big sphere is joined with several smaller spheres forming a band around the center. Positions of the small spheres are read form file. But only position near the equator are accepted. Because we have small spheres in three different sizes, reading is down from three different files. The output of this script are OpenVDB commands to build the union of all spheres and calculate a single mesh covering the body.

(ns grow.scratch.trace-poisson-sphere)
(use 'grow.core)
(obj-template)

(grow
  (def ay (radian 5))
  (def tz 0.5)
  (def curve-len 150)
  (def body-scale 4)
  (def top-scale (* body-scale 0.95))
 
  (defn topside [n] (> n top-scale))
  
  (rule T [a]
    (object ["curve_point"])
    (if (pos? a)
      (invoke T (dec a)
              :coord [(rotate-y ay) (rotate-z (rnd-gaussian 0.5))
                      (scale 0.95) (translate 0 0 tz)]))
  )

  (rule A
    (let [ztop (topside (coord-z))]
      (when ztop
        (object ["curve_base"])
        (invoke T curve-len
                 :coord [(rotate-y mPi2)]
        )
        (object ["curve_end"])))
  )
        
  (rule G 
    (rnd-seed 955)
    (generator [A] (import-neighbour-rt body-scale "../grow/resources/Icosphere-2724.L3N6.level2.neighbour"))
  )

  (start G 99)
)

The tentacle are made of Bezier curves disturbed by Gaussian noise. The output of the second Lisp script are curve points in a format suitable for Blender.

Combining a noise function and a sphere with OpenVDB.

Software used

  • Clojure to describe the geometrical structure.
  • OpenVDB to fill narrow-band level sets with spheres and export a single mesh covering all spheres.
  • Blender to assign material properties to the mesh and illuminate the scene with light sources.
  • Cycles or LuxCoreRender to render these images.