Basic Globs

Here's some amusement I whomped up over a couple of weekends, using a free implementation of the Renderman standard plus some ad hoc C++ code.

The idea was to play with the idea of hierarchical b-splines as a modelling mechanism. (If you have a copy of Computer Graphics: Principles and Practice by Foley, van Dam, Feiner & Hughes, Plate IVa in the back shows a nice dragon head modelled by David Forsey of UWaterloo using hierarchical b-splines.)

Since it is always easiest and most amusing to start with something working, I grabbed a teapots example:
.

(If you're on an X-based system and the image looks dark, you may need to do 'xgamma -gamma 2.2'.)

As usual, click on the thumbnails to see the images at full resolution.

To start off with, I just wanted to make naturalistic, biological sorts of random blob shapes by layering bicubic spline patch meshes one upon the next, each one at twice the linear spatial resolution and introducing new "random" offsets at each control point, perpendicular to the underlying surface, producing something like this:
.
(The commandline to produce that using my little C++ hack is ./glob --greenmarble.)

Here's how that works.

We start by approximating a sphere using a 4x4 bicubic spline mesh. I'm using catmull-rom splines here because they interpolate -- they run through the control points -- which makes me feel good, but it doesn't much matter:
.
(./glob --lumpy1=0.0 --plastic --checker --layers=1.) Not a great approximation, as you can tell from the squarish shadow, but I'm not much interested in spheres here anyhow.

Now we perturb the control points randomly a bit perpendicular to the underlying surface. That's the lumpy1 parameter:
.
(./glob --lumpy1=0.3 --plastic --checker --layers=1.)

Now we overlay the above surface with one of twice the resolution, and offset it in turn normal to the underlying surface. (You can think of this as being a bit like simple wavelets, or any other divide-and-conquer powers-of-two style decomposition.) The perturbation distance is given by the --lump2 parameter:
.
(./glob --lumpy1=0.3 --lumpy2=0.3 --plastic --checker --layers=2.)
Now it really doesn't look much like a sphere. :)

And so it goes. The great thing about computers is that if you can do it once, you can overdo it indefinitely:
.
(./glob --lumpy1=0.3 --lumpy2=0.3 --lumpy3=0.3 --plastic --checker --layers=3.)

This, obviously, also quite similar in spirit to the classical midpoint-displacement triangle-subdivision algorithm for making fractal mountains:
.
(./glob --lumpy1=0.3 --lumpy2=0.3 --lumpy3=0.3 --lumpy4=0.3 --plastic --checker --layers=4.)

This approach will obviously never appeal to control freaks who want direct control of every detail and nuance, but I think it has great promise as a substrate for procedurally generated worlds:
.
(./glob --lumpy1=0.3 --lumpy2=0.3 --lumpy3=0.3 --lumpy4=0.3 --lumpy5=0.3 --plastic --checker --layers=5.)

.
(./glob --lumpy1=0.3 --lumpy2=0.3 --lumpy3=0.3 --lumpy4=0.3 --lumpy5=0.3 --lumpy6=0.3 --plastic --checker --layers=6.)

And so forth -- you get the idea, and bicubic patches of pixel size or below aren't too interesting. In general, I find three or four layer models most interesting to play with.

Now, the plastic look and checkerboard pattern are of course only for debugging and didactic purposes: The fun thing about Renderman is the exquisite control it gives you of (in particular) the surfaces being rendered. Here's the four-layer model above done with a glass surface:
.
(./glob --glass --lumpy1=0.3 --lumpy2=0.3 --lumpy3=0.3 --lumpy4=0.3 --layers=4.)

Or a screen-door pattern:
.
(./glob --screen --lumpy1=0.3 --lumpy2=0.3 --lumpy3=0.3 --lumpy4=0.3 --layers=4.)

Or clay:
.
(./glob --clay --lumpy1=0.3 --lumpy2=0.3 --lumpy3=0.3 --lumpy4=0.3 --layers=4.)

We can't omit classic veined marble:
.
(./glob --veinedmarble --lumpy1=0.3 --lumpy2=0.3 --lumpy3=0.3 --lumpy4=0.3 --layers=4.)

I'm particularly fond of David Griz' "superplank" solid texture:
.
(./glob --superplank --lumpy1=0.3 --lumpy2=0.3 --lumpy3=0.3 --lumpy4=0.3 --layers=4.)

A classic fractal is by definition self-similar on all scales, but having separate -lumpyN parameters for each scale lets us control the statistics of different spatial scales independently. I find I actually like having the most detail on the second scale. The shapes that emerge remind me of the wooden "handy" sculptures which I used to make in Jr High, which were intended to be held in the palm of the hand and experienced at least as much by touch as by vision:
.
(./glob --superplank --lumpy1=0.2 --lumpy2=0.7 --lumpy3=0.2 --lumpy4=0.2 --layers=4.)

The cool thing about procedural models generally, of course, is that once you've built one, you can make an unlimited number of similar things just by turning the crank. Here's the same settings, but a different seed value:
.
(./glob --seed=sculpture --superplank --lumpy1=0.2 --lumpy2=0.7 --lumpy3=0.2 --lumpy4=0.2 --layers=4.)

And yet a third object from the same family -- same spatial statistics, different seed value:
.
(./glob --seed=sculpture2 --superplank --lumpy1=0.2 --lumpy2=0.7 --lumpy3=0.2 --lumpy4=0.2 --layers=4.)

The human eye loves symmetry, so I've hacked in some options for introducing various sorts of symmetries. Here are the same spatial statistics with a mirror symmetry on the X axis:
.
(./glob --symmetry=x --seed=sculpture2 --greenmarble --lumpy1=0.2 --lumpy2=0.7 --lumpy3=0.2 --lumpy4=0.2 --layers=4.)

Same spatial statitics, but with mirror rotational symmetry. Note how the 'organic' feel of the earlier shapes has turned mechanical with the increasing degree of symmetry:
.
(./glob --symmetry=t --seed=sculpture2 --greenmarble --lumpy1=0.2 --lumpy2=0.7 --lumpy3=0.2 --lumpy4=0.2 --layers=4.)

... and now with axial symmetry. Oddly, this one looks "organic" again despite having just as low a shape information content as the preceding shape:
.
(./glob --symmetry=s --seed=sculpture2 --greenmarble --lumpy1=0.2 --lumpy2=0.7 --lumpy3=0.2 --lumpy4=0.2 --layers=4.)

That's probably enough for now!

Overall, the results were just about what I'd hoped, which is always a nice, if improbable, result.

One of the things I'd like to try next is using simulated annealing to automatically fit models of this class to, say, a picture of a face, somewhat in the spirit of the radial constraint models invented by my old boss Jim Brinkley at the UW. (His Digital Anatomist project there has earlier examples of my biological 3D graphics software efforts...)

If you'd like to play with this without re-doing the grunge hacking involved, here's the source.
Binaries for the Linux renderer I used are here.
Other Renderman implementations are listed here.
The Renderman FAQ is here.

Back to Cynbe's "Silly RenderMan Tricks" Page.


Cynbe ru Taren
Last modified: Fri Aug 9 22:27:46 CDT 2002