Gabor noise for Cycles OSL

Hidden in the the treasure trove that is OSL lies a spanking new noise type: Gabor noise. Surprisingly this not yet exposed as a node for artists to explore. This article remedies that situation.

And now for something completely different

Gabor noise is fairly new and has some interesting qualities. Apart from the mathematical properties the most important aspects for the artist are that it is easy to tinker with and offers a change from the ubiquitous Perlin noise.

The shader is almost to simple to show here:


shader gabor(
point Pos = P,
int anisotropic = 0,
vector direction = vector(1,0,0),
int do_filter = 1,
float bandwidth = 1.0,
float impulses = 16,
output float Noise = 0
){
Noise = noise("gabor",Pos,
"anisotropic", anisotropic,
"direction", direction,
"do_filter", do_filter,
"bandwidth", bandwidth,
"impulses", impulses);
}
It offers all the options documented in the OSL language specification and produces a single float value. This can of course be adapted easily to take for example 4 dimensions (position and time) as input and produce a 3D noise vector as OSLs noise() is very versatile but this is left as an excercise for the reader.

Example node setup

Suzanne was patterned with the following node setup:

Gabor noise is of course capable of much more: there is plenty of room to experiment. You might like to show some results on Blender Artists. Some examples are shown below:

Recently I reread a long running thread on Blender Artists and saw that phoneybone implemented a pure OSL version of Gabor noise that gives a bit more control than the built-in function. Do read on his solution because it is interesting to compare.

OSL leaf veins shader for Cycles

When we look at images the addition of veins greatly adds to the preceived realism of rendered leaves and in this article I present a simple veins shader that complements the leaf shader discussed previously.

Leaf veins

I forgot to update the page that describes equations.h (thanks samblerdevel for pointing that out). I just corrected that so if you had any errors complaining abot a function splinedist() that was missing, download & install equations.h and try again

The node setup used to create the leaf shape and veination in the close-up image above is relatievely complicated and presented at the end of this article. Luckily, the basic stuff of generating vein patterns is not that complicated so lets have a look at that first.

As illustrated in the images above the veins in the leaf are all represented by cubic splines, starting at the red dots on the central vein and curving to the green end points on the edge of the leaf. Their curvature is controled by the blue control points. A node setup for the image above looks like this:
The Angle and L parameters mimic the ones in the leaf shape shader and are kept the same in this case to let the endpoints of the veins coincide with the actual leaf edge. The number of veins, their distribution, width and the way they curve are controlled by the Veins, Width, Squish and Up parameters as we will see later on. Their is some randomness in the placement as well which can bee influenced by the Seed and Var parameters. The outputs consist chiefly of a Vein socket which will be one for a vein, and a Fac socket which is the square root of the distance to the center of the vein and can be used to drive displacement.

The code for this node is shown below makes use of the equations.h include discussed in the article on leaf shapes.


#include "equations.h"

shader arcuateveins(
point Pos = P,
int Veins = 7,
int Seed = 42,
float Var = 0,
float Width = 0.05,
float NWidth = 0.25, // size of the reticulated area

float Squish = 0.5, // distribution of endpoints on edge
float Squish2 = 0.5, // distribution of controlpoints
float Squish3 = 0.5, // distribution of starting points
float Up = 0.5,

float Angle1 = 70,
float L1 = 1,
float Angle2 = 70,
float L2 = 1,

output float Vein = 0,
output float Net = 0,
output float Fac = 0
){

float delta = 1.0/((float)Veins+1);
float delta2= delta/2;
float delta4= delta/4;

// calculate the four control points of the cubic spline that defines the leaf edge
float x1,y1,x2,y2;
sincos(radians(Angle1),y1,x1);
sincos(radians(Angle2),y2,x2);
point P0 = point(0 , 0 ,0);
point P1 = point(x1 , y1,0)*L1;
point P2 = point(1-x2*L2, y2*L2,0);
point P3 = point(1 , 0 ,0);

point P0q = point(P0[0],P0[1]*Up,P0[2]);
point P1q = point(P1[0],P1[1]*Up,P1[2]);
point P2q = point(P2[0],P2[1]*Up,P2[2]);
point P3q = point(P3[0],P3[1]*Up,P3[2]);

int i;
for(i=0;i < Veins;i++){

// determine the starting points of the veins
float x = (i*delta+delta2*Var*cellnoise(i+10+Seed))*Squish3;
float dx = (delta4*Var*cellnoise(i+17+Seed))*Squish3;
point P0up = point(delta2+x+dx,0,0);
point P0down = point(delta2+x,0,0);
// determine the endpoints on the leaf edge
float t=(i*delta+delta2)*Squish+1-Squish;
point P2up = cubicspline(t,P0,P1,P2,P3);
point P2down = point(P2up[0],-P2up[1],P2up[2]);
// the veins are quadratic splines, so need one additional control point
t=(i*delta+delta2)*Squish2+1-Squish2;
point P1up = cubicspline(t,P0q,P1q,P2q,P3q);
point P1down = point(P1up[0],-P1up[1],P1up[2]);

float r;
int f = splinedist(P0up, P1up, P2up, Pos, r, t);
if ( f && (r < NWidth ) ) Net = 1 ;
if ( f && (r < Width * ( 1- t) * (1-Pos[0]) ) ) { Vein = 1; Fac = sqrt(1-r/Width); break; }
f = splinedist(P0down, P1down, P2down, Pos, r , t);
if ( f && (r < NWidth ) ) Net = 1 ;
if ( f && (r < Width * ( 1- t) * (1-Pos[0]) ) ) { Vein = 1; Fac = sqrt(1-r/Width); break; }
}

// the central vein
float d = distance(point(0,0,0),point(1,0,0),Pos);
if ( d < NWidth ) Net = 1 ;
if (d < (Width * (1-Pos[0])) ) { Vein = 1; Fac = sqrt(1-d/Width);}
}

Relation to real venation patterns in leaves

The shader in its current form is able to model pinnate and arcuate venation patters and intermediate forms of these. (For an explanation of terminolgy refer to Wikipedia, especially this overview sheet). Its spline-based modelling of the veins is not based on any underlying theory of the formation of veins as it happens in nature, as these reaction-diffusion equations cannot so easily be implemented in an OSL shader (at least not at present: we wouldn't want to redo such a costly simulation again and again for each point being shaded so we would need peform the simulation before we start shading each pixel. Currently there is no facility for adding something to a shader that will be executed once beforehand, although there might be in the future. An alternative approach might be to perform the simulation, maybe in a Python add-on, and store the result in a texture. Here we opted for art before science: if it looks all right we don't care what it is based on).

Controlling the curve shape of the veins

In the following images I have illustrated how you can control the shape of the veins. How much the starting points on the central vein and the control points in the middle and the end points on the leaf edge are bunched up, is controlled by the Squish parameters. The blue control points all lie on a spline that is a copy of the spline that defines the leaf edge by scaled by the Up parameter. Some experimenting shows that is is possible to create both pinnate venation patterns as well as arcuate patterns:

Example node setup

The leaves in the image at the top of this article were created with the following node setup:

The values in the blue box simultaneously control the shape of the leaf edge both in the leaf shader and in the vein shader. The leaf coloring is controled by the nodes in the green box (leaves are both glossy and translucent) while the vein coloring is is defined by the nodes in the red box, the choice being determined by the Vein output socket of the vein shader. The yellow box provides some noisy patterns to drive both the colloration of the leaf as well as mix with the bump patern from the vein shader to drive the displacement. The exact contribution of these displacements is controlled by the purple nodes.

Room for improvement

Although the shader is already quite versatile there is ample room for imrpovement. For example, I would like it to be able to produce palmate vein patters and to control the narrowing towards the tips of the veins. On the other hand the shader is not limited to producing vein pattersn: I imagine it can be used to produce fish bones and bird feather patterns (barbs) as well. I might expand on that in the future.

A OSL leaf shape shader for Cycles

In this first part of a series I present a simple shader that can be used to generate different leaf shapes. In later articles we will add shaders that produce the veins of the leaf.

Simple leaf shape with cubic splines

In the example image we have used the IvyGen addon to generate a single limb of some climbing plant species and used the shader from this article to produce the leaf shapes The node setup for this specific material is dicussed later on. (the stone texture is from cgtextures.com and was converted to a normal map and a displacement map with Shadermap CL. The backplate and environment lighting are from the Topanga Forest B collection by Blochi as found on Sibl archive)

The leaf shape shader is essentially creates one side of a leaf from a cubic spline and mirrors that to the other side to create a symmetrical shape. The spline that is used has four controls.

It starts on the left in the direction of P1 and ends on the right coming from the direction of P2. The lenght and angles of the vectors going to P1 and P2 are inputs to the shader. Some of the shapes you can create are shown below.
Note that it is also possible to define a shape that crosses itself in which case the behavior of the shader is undefined.

The annotated code for the shader is shown below. It includes a small library of functions that will be reused by shaders that will appear on this blog in the near future and this include has its separate page where you can download it and find installation instructions.


#include "equations.h"

shader leaf(
point Pos = P,
float Angle1 = 70,
float L1 = 1,
float Angle2 = 70,
float L2 = 1,
output float Leaf = 0
){

// calculate the four control point of the cubic spline
float x1,y1,x2,y2;
sincos(radians(Angle1),y1,x1);
sincos(radians(Angle2),y2,x2);
point P0 = point(0 , 0 ,0);
point P1 = point(x1 , y1,0)*L1;
point P2 = point(1-x2*L2, y2*L2,0);
point P3 = point(1 , 0 ,0);

// to determin the y value(s) of the spline at the x position we
// are located, we want to solve spline(t) - x = 0
// we therefore gather all factors and solve the cubic equation
float tfactor[4] = { P0[0]-Pos[0],
3*P0[0]+3*P1[0],
3*P0[0]-6*P1[0]+3*P2[0],
P0[0]+3*P1[0]-3*P2[0]+P3[0] };
float t[3];
int nrealroots;
cubic(tfactor, t, nrealroots);

// at this point, the array t holds up to 3 real roots
// remove any real root that is not in range [0,1]
int i=0;
while(i < nrealroots){
if ((t[i] < 0) || (t[i] > 1)) {
int j=i;
while(j < (nrealroots-1)){
t[j]=t[j+1];
j++;
}
nrealroots--;
}
i++;
}

// note that a cubic funtion can have 3 real roots,
// but in this case we ignore such very warped curves
// TODO: w. 3 real roots w could set leaf = 1, if y < y0 OR y between y1,y2
// TODO: seration, possible by determining the closest
// distance (if inside leaf) to the spline and
// determining if w are within some periodic funtion f(t)

// we generate the shape mirrored about the x-axis
float y = Pos[1];
if(y<0) y = -y;

if(nrealroots > 0){
point Sy0 = cubicspline(t[0],P0,P1,P2,P3);
if(nrealroots > 1){
// if we have 2 roots we calculate and order the y values
// and check whether the current y values is between them
point Sy1 = cubicspline(t[1],P0,P1,P2,P3);
if ( Sy1[1] < Sy0[1] ){
if( (y > Sy1[1]) && (y < Sy0[1]) ) Leaf = 1;
}else{
if( (y > Sy0[1]) && (y < Sy1[1]) ) Leaf = 1;
}
}else{
// with a single value we check if we are below the y value
if( y < Sy0[1] ) Leaf = 1;
}
}
}

Example node setup

The node setup used to create the sample shapes look like this:

As you can see there is nothing fancy going on here. The leaves are simple squares with a reset uv-map and a mapping node is used to position the leave onto this square (in this case rotated 90 degrees). The output of the shader is then used to drive a mix shader which shows some green material where there is a leaf and a fully transparent material where there isn't.

Combining things with the Ivy Generator

The sample image at the beginning was create with the IvyGen addon. The leaves it produces are simple square faces that are not connected but nevertheless consist of a single mesh object. Because we want to orient each individual leaf a bit different we need a random number for each leaf. We therefore have to separate each face into its own object.

The workflow to achieve that then becomes:

  1. Create your ivy
  2. Assign the material shown in the noodle below to the leaf object
  3. Goto edit mode
  4. Select all
  5. Select Mesh->Vertices->Separate->By loose parts
  6. Go back to object mode.
Each leaf now is its own object we our leaf material attacthed.

Example node setup II

The noodle consists of three distinct parts:
  • The green part adds a small random rotation around the z axis to each uv map. This will be different for each individual leaf. The actual rotation is done by another OSL shader (given below) because the vector mapping node cannot be driven by inputs.
  • The red part maps the rotated uv to the correct position before handing it to our leaf shader. This mapping is necessary because the squares created by IvyGen are sort of centered on the vines and we want our leaves to protrude from the vined instead of being pierced by them.
  • The blue part is just a simple mottled green shader with some gloss.

The rotation of the uv-map arond the z-axis is performed by an OSL shader because the vector mapping node has no inputs to control the rotation and it would take a lot of vector math nodes to achieve what we want whereas thanks to OSL this rotation is a no-brainer:


shader rotate_z(
point Pos = P,
float Angle = 0,
output point Pout = P
){
Pout = rotate(Pos,radians(Angle),point(0,0,0),point(0,0,1));
}

Next steps

In a next article I will show how to add veins to the leaf shapes.

An OSL Wood Shader for Blender Cycles

Good looking procedural wood is not that simple to implement but fortunately for us their is a whole host of renderman shading experience available online. Based on a shader by Larry Gritz we implement a fairly realistic wood shader in OSL.

Both the dark wood of Suzanne and the torus and the lighter wood of the planks they are resting on was done with the shader we present in this blog article. The algorithms used to produce the ring and the grain are essentialy the same as those implemented in Larry Gritz' oak shader. However, Open Shading Language (OSL) is not quite the same as Renderman so there was in the end quite some adaptation needed.

Beside syntactic differences between RSL and OSL, the main differences are that OSL provides us natively with snoise() functions but on the other hand, a function like area() doesn't seem to work well and neither are the derivative functions. However, because of the inherent antialiasing in Cycles we don't have to bother much about filter width (as we would have in other types of render engines) so we can do without I think. Functionally the biggest adaptation was that the original implementation provided a complete lighting model (a closure) for the wood shader while we adopt a more modular approach provinding color and displacement output that can be combined with existing closures for maximum flexibility. See the node setup at the end of the article for an example.

The code is for the OSL shader is shown below. Implementation mistakes are mine but the credits go to Larry Gritz:


// for the original renderman shader, check http://www.larrygritz.com/arman/materials.html

// adapted from larry gritz advanced renderman patterns.h
float smoothpulse (float e0, float e1, float e2, float e3, float x)
{
return smoothstep(e0,e1,x) - smoothstep(e2,e3,x);
}

/* A pulse train of smoothsteps: a signal that repeats with a given
* period, and is 0 when 0 <= mod(x/period,1) < edge, and 1 when
* mod(x/period,1) > edge.
*/
float smoothpulsetrain (float e0, float e1, float e2, float e3, float period, float x)
{
return smoothpulse (e0, e1, e2, e3, mod(x,period));
}

// adapted from larry gritz advanced renderman noises.h
/* fractional Brownian motion
* Inputs:
* p position
* octaves max # of octaves to calculate
* lacunarity frequency spacing between successive octaves
* gain scaling factor between successive octaves
*/

/* A vector-valued antialiased fBm. */
vector vfBm (point p, float octaves, float lacunarity, float gain)
{
float amp = 1;
point pp = p;
vector sum = 0;
float i;

for (i = 0; i < octaves; i += 1) {
vector d = snoise(pp);
sum += amp * d;
amp *= gain;
pp *= lacunarity;
}
return sum;
}

// adapted from larry gritz oak.sl and oak.h
// original comments between /* ... */
// my comments start with //
// note that I dropped the whole filterwidth stuff, partly
// because I don't think it necessary in Blender Cycles, partly
// because the derivatives and area() function doesn't seem to work (yet)
// all specialized snoise defines are replaced by snoise() function calls
float oaktexture (point Pshad,
float dPshad,
float ringfreq,
float ringunevenness,
float grainfreq,
float ringnoise,
float ringnoisefreq,
float trunkwobble,
float trunkwobblefreq,
float angularwobble,
float angularwobblefreq,
float ringy,
float grainy)
{
/* We shade based on Pshad, but we add several layers of warping: */
/* Some general warping of the domain */
vector offset = vfBm(Pshad*ringnoisefreq, 2, 4, 0.5);

point Pring = Pshad + ringnoise*offset;
/* The trunk isn't totally steady xy as you go up in z */
vector d = snoise(Pshad[2]*trunkwobblefreq) ;
Pring += trunkwobble * d * vector(1,1,0);

/* Calculate the radius from the center. */
float r = hypot(Pring[0], Pring[1]) * ringfreq;
/* Add some noise around the trunk */
r += angularwobble * smoothstep(0,5,r)
* snoise (angularwobblefreq*(Pring)*vector(1,1,0.1));

/* Now add some noise so all rings are not equal width */
r += ringunevenness*snoise(r);

float inring = smoothpulsetrain (.1, .55, .7, .95, 1, r);

point Pgrain = Pshad*grainfreq*vector(1,1,.05);
float dPgrain = dPshad; //dropped filterwidthp(Pgrain);
float grain = 0;
float i, amp=1;
for (i = 0; i < 2; i += 1) {
float grain1valid = 1-smoothstep(.2,.6,dPgrain);
if (grain1valid > 0) {
float g = grain1valid * snoise (Pgrain);
g *= (0.3 + 0.7*inring);
g = pow(clamp(0.8 - (g),0,1),2);
g = grainy * smoothstep (0.5, 1, g);
if (i == 0)
inring *= (1-0.4*grain1valid);
grain = max (grain, g);
}
Pgrain *= 2;
dPgrain *= 2;
amp *= 0.5;
}

return mix (inring*ringy, 1, grain);
}

// larry gritz' original shader was a closure but this shader
// provides different outputs that you can plug into your own
// closures/shaders
surface oak(
point Pos = P,
float Sharpness = 0.01, // sharpness of the grain. hand tweaked because we lack derivatives.
float ringfreq = 8,
float ringunevenness = 0.5,
float ringnoise = 0.02,
float ringnoisefreq = 1,
float grainfreq = 25,
float trunkwobble = 0.15,
float trunkwobblefreq = 0.025,
float angularwobble = 1,
float angularwobblefreq = 1.5,
color Clightwood = color(.5, .2, .067),
color Cdarkwood = color(0.15, 0.077, 0.028),
float ringy = 1,
float grainy = 1,
output color Color = 0,
output float Spec = 0.1,
output float Roughness = 0.1,
output float Disp = 0
)
{
float wood = oaktexture (Pos, Sharpness, ringfreq, ringunevenness, grainfreq,
ringnoise, ringnoisefreq, trunkwobble, trunkwobblefreq,
angularwobble, angularwobblefreq, ringy, grainy);

Color = mix (Clightwood, Cdarkwood, wood);
Disp = -wood; // lightwood = 0, darkwood is deeper/lower = -1
Spec = 0.1*(1-0.5*wood); // darkwood is less specular
Roughness = 0.1+0.1*wood; // and rougher
}

Example node setup

The node setup for the Suzanne and the torus looks like this: