Showing posts with label renderman. Show all posts
Showing posts with label renderman. Show all posts

Renderman for Blender vs. OSL

Everybody is thrilled because of Pixar's decision to offer a version of Renderman that is free for non-commercial use, and rightly so because it will enable a lot of people to get acquainted with this industry standard.
Even better is the support by Pixar for a tight integration of Renderman with Blender, in the form of an open source add-on. This add-on may not be production quality yet, but once you have installed Renderman it works out of the box:

Open Shading Language

From my point of view Renderman is extra interesting because it supports shaders written in Open Shading Language (OSL) which means that we should be able to port OSL shaders written for Cycles to Renderman. In the image of Susanne the patterning on her head was actually done by plugging in this simple OSL shader:
shader marble (color Cin = .5,
               float freq = 1.0,
               output color result = 0)
{
    float sum = 0;
    float freqVal = freq;

    point Pshad = transform ("object", P);
    for (int i = 0; i < 6; i++) 
    {
        sum = sum + 1/freqVal * abs(.5 - noise( 4 * freqVal * Pshad)) ;
        freqVal = 2 * freqVal;
    }
    result = Cin * sum;
}

Workflow

There are a couple of restrictions and quirks so let me walk you through the steps I took to get this shader running.

Compile the shader

Renderman will not compile OSL shaders automatically for you like Cycles does, so you will have to do that yourself. If you do not have the oslc compiler installed separately the easiest way to do this is to create a Cycles material first that contains a Script node. When you refer this Script node to an external .osl file it gets compiled immediately to an .oso file and this .oso file can be used as is by Renderman.

Link it to the diffuse color

First select a PxrOSL pattern by clicking the dot next to the baseColor, and then type in the full path to the shader, but without the .oso externsion! (circled in red)

You will notice in the node editor that a PxrOSL node is wired up to the PxrDisney shader.

At this point you can render your scene.

Quirks

If you look closely at the PxrOSL node and at the code for the shader you see that the input and output sockets correspond just partially to the input and output parameters of the shader. Indeed, all input parameters are completely ignored and there is an extra float output socket called result1. The types and names of the input and output sockets are it seems defined in the file /opt/pixar/RenderManProServer-20.0/lib/RIS/pattern/Args/PxrOSL.args and are the same for all OSL shaders. So as far as I understand it, you can use different shaders but they all should use the same names for their input and output sockets (I think this is in line with other Renderman patterns). For this example it meant that I had to rename the output color from Cout to result to get it working (otherwise you get a rendertime error complaining it cannot link to the result socket). Maybe I am looking at it from the wrong perspective as I know next to nothing about Renderman, It is workable of course, just define enough suitable input and output parameters and use those predefined names in your shader but it feels a bit restrictive. Anyway, it is a very promising step. I am studying the add-on to see if I can tweak the OSL node part and maybe help out the original author.
If you would like to know more about programming OSL you might be interested in my book "Open Shading Language for Blender". More on the availability of this book and a sample can be found on this page.

OSL support in mainstream renderers (V-Ray and Renderman)

Things are getting interesting now that more major players start supporting OSL. This week Pixar announced the support for OSL in Renderman and a few months ago Chaosgroup released a version of V-Ray with OSL integration.

Not all integrations are equal, for example the one in Renderman doesn't appear to support closures (bsdf's), but nevertheless, now there is a huge potential to share programmatical patterns and textures between V-Ray, Renderman and Blender. I am quite exited and can't wait to see how this will foster the reuse of shaders across platforms. To illustrate the portability: some of the shaders provided as examples on the V-Ray page work on Blender without a single change, while others need some tweaks, mainly because each renderer provides its own set of closures (for eaxmple, Blender does not have the phong() closure) and more importantly, the output model is not identical: the V-Ray shaders don't provide closure outputs, but simply assign to the Ci variable, something that on Blender has no effect.

If you would like to know more about programming OSL you might be interested in my book "Open Shading Language for Blender". More on the availability of this book and a sample can be found on this page.

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: