Bark
Organic patters really inspire me to create. Their multiple levels of noise, fractal patterns, and subtle colors create a sublime beauty I find particularly appealing. Also, there is the element of freshness. Often, computer graphics feel stale to me. Creating forms that break this feeling of staleness liberates digital art from the norms the film and game industry have placed it in. So, here is bark Bark is an extremely complex form. There are many ridges of hard, crusty material which gradually flake off. Colors shift and change depending upon the amount of sunlight and rain the tree recieves. Then, there are cracks and pores like skin. Finally, secondary organisms like moss grow across the surface.
How to tackle such a pattern? Well, first, because bark is organic, using a fractal is a fine place to start. The correct pattern must be selected however, for the bark to appear natural. I chose a Voronoi diagram. Voronoi diagrams are stochastic, cellular patterns which resemble lizard skin or stained glass. They are created by scattering dots across a surface and then connecting the closest dots with lines to form cells.

Note that one point exists within each polygon. Thus, Voronoi diagrams are useful for subdividing any space where multiple objects exist and are very useful for optimization algorithms such as those used in ray-tracing or collission detection.
To create the diagrams in SL, I used the code from Advanced Renderman by Larry Gritz.
Here is the basic pattern, with each cell colored differently.

So, the next step was the add some more lifelike color and put the pattern through a fractal generator, which basically repeats the pattern at higher and higher frequencies and lower and lower amplitudes. This process is explained in the animated fractal article on the shaders page. Using the fractal, I created a displacement shader. The color and the displacement are shown below:

Not bad so far! However, the bark is not "sharp" enough and looks a little more like skin. Actually, if you experiment with this shader some, you will come up with somethin that looks just like rough leather. But, bark is what I'm after so I adjusted some smoothstep parameters and got the image below.

To this basic layer I added two layers of dirt: one which represented moss in the cracks and another that had all-over blemishes. This early render shows the patterns clearly. Once adjusted correctly they become very subtle.

The following sequence shows the progression of layers:


And, the final shader applied to a tree trunk:

The source code is below:
point absVector(point p)
{
point pr;
setxcomp(pr,abs(xcomp(p)));
setycomp(pr,abs(ycomp(p)));
setzcomp(pr,abs(zcomp(p)));
return pr;
}
point floorVector(point p)
{
point pr;
setxcomp(pr,xcomp(p) - floor(xcomp(p)));
setycomp(pr,ycomp(p) - floor(ycomp(p)));
setzcomp(pr,zcomp(p) - floor(zcomp(p)));
return pr;
}
void
voronoi(point p; float jitter;
output float f1;
output float f2;
output point pos1;
output point pos2;)
{
point thiscell = point (floor(xcomp(p)) + 0.5,
floor(ycomp(p)) + 0.5,
floor(zcomp(p)) + 0.5);
f1 = 1000;
uniform float i,j,k;
for (i = -1; i <= 1; i += 1)
{
for (j = -1; j <= 1; j += 1)
{
for (k = -1; k <=1; k += 1)
{
point testcell = thiscell + vector(i,j,k);
point pos = testcell +
jitter * (vector cellnoise(testcell) - 0.5);
vector offset = pos - p;
float dist = offset . offset;
if (dist < f1)
{
f2 = f1; pos2 = pos1;
f1 = dist; pos1 = pos;
} else if (dist < f2)
{
f2 = dist;
pos2 = pos;
}
}
}
}
f1 = sqrt(f1);
f2 = sqrt(f2);
}
surface bark( vector freq = (1,1,1);
float kM = 1.0;
string space = "";
)
{
vector f = freq;
float f1 , f2;
point pos1, pos2;
float hump = 0;
float a = 1.0;
point PP = transform(space, P);
PP = vector (comp(f,0) * comp(PP,0),
comp(f,1) * comp(PP,1),
comp(f,2) * comp(PP,2));
normal n = normalize(N);
normal nf = faceforward(n,I);
voronoi(PP, 1.0, f1, f2, pos1, pos2);
float scalefactor = distance(pos1, pos2) / distance(pos1, PP) + distance(PP, pos2);
float diff = 1 - (f2 - f1);
point pos1prime = pos1 * 10;
pos1prime = absVector(pos1prime);
pos1prime = floorVector(pos1prime);
color layerColor, totalColor;
float layerOpacity, totalOpacity;
totalColor = 0;
totalOpacity = 0;
float diffColor = 0;
/* calculate the displacement map */
float i = 0;
for ( i = 0; i < 10; i+=1)
{
PP = transform(space,P);
PP = vector (comp(f,0) * comp(PP,0),
comp(f,1) * comp(PP,1),
comp(f,2) * comp(PP,2));
PP += noise(PP * 4) * 0.35;
voronoi(PP, 1.0, f1, f2, pos1, pos2);
scalefactor = distance(pos1, pos2) / distance(pos1, PP) + distance(PP, pos2);
diff = 1 - (f2 - f1);
if (i == 0) diffColor = diff;
hump += (a * (1 - smoothstep(0.77,0.87,diff)));
f = f * 1.56;
a *= 0.6;
}
/* recalculate the surface information */
P = P + n * hump * kM;
N = calculatenormal(P);
n = normalize(N);
nf = faceforward(n,I);
PP = transform(space,P);
/* layer one, base color */
layerColor = color "hsv" (comp(normalize(pos1prime),0) * 0.05 + 0.05, 0.8, 0.3) * diffuse(normalize(N)); layerOpacity = 1; totalColor = mix(totalColor,layerColor, layerOpacity); totalOpacity = min(totalOpacity + layerOpacity, 1); /* layer two, crevice dirt */ layerColor = color "hsv" (0.1 + 0.7 * noise(PP), 0.5,0.2); layerOpacity = pow(diffColor,2) * noise(PP*10) * 0.5; totalColor = mix(totalColor,layerColor, layerOpacity); totalOpacity = min(totalOpacity + layerOpacity, 1); /*layer three, all over dirt */ layerColor = color (0.3,0.3,0.2); layerOpacity = pow(noise(PP * 5),2); totalColor = mix(totalColor,layerColor, layerOpacity); totalOpacity = min(totalOpacity + layerOpacity, 1); Ci = totalColor * diffuse(n); Oi = 1; }