For my final experimentation, I had originally decided to play with Pixar's gummi light shader documented in Anyone Can Cook -- Inside Ratatouille's Kitchen (PDF) published in July 2007. This light shader has the ability to fake subsurface scattering on objects that have no scatter shader qualities. It can also be used to compliment existing scatter qualities already present on surfaces. Another huge advantage of gummi is that it can be used to approximate scatter on background objects to make rendering more efficient. However, in light of the fact that the required display mode for the deep shadow map ("deepprevdisttotal"), refered to as the "thickness" map, seems to be no longer supported by prman as warned in the fine print of the paper (either that, or I'm doing something horribly wrong). So instead, I've decided to tackle something I've wanted to do for awhile, and that is create a shader the mimics the beautiful effects seen in jellyfish:


To tackle this problem, there are several components that I will need to address. The first component that I have integrated into my shader is the accentuation of edges brought on by the translucent nature of the animal. Facing ratio controls were added to let the artist define the amount of color that can be added to the edges:

The next step is going to be getting some internal shaping to the creature through various noise functions. I've also added some subsurface scattering controls at this stage, however, more development is needed within this shader before those controls will become useful.


I've started adding both color variation as well as surface-relief variation. The noise() function is quite useful for creating a wide variety of effects, including the dark stripes and striations that protrude from the surface.

I've also started using noise to break up the opacity of the jellyfish so that it is not the same value all the way across the surface. I clamped the values of the noise so that the opacity varies in a spot-like pattern.

Many controls and features have been added to the jellyfish surface shader. Now, instead of having a randomized color variant generated by the noise() function, two colors can be specified and the noise function will ramp between them.

Also, there are several implementations of the facing ratio technique. Three attributes are given to the user; rim_width, glow_width, innerGlowBrightness, and glow_hardness. rim_width controls the overall spread of the brightening that happens on the edges. glow_width gives the opposite effect, brightening the interior of the shapes. This attribute is used to control the amount of spread across the surface. innerGlowBrightness is simply a multiplier that effects the brightness of this control. glow_hardness controls the falloff of the glow.

Below is the full surface shader for the jellyfish along with the simple displacement shader that creates the striations on the surface:

jellyfish(  float   Ka = 1,            /* ambient brightness */
                    Kd = 0.5,        /* basic brightness */
                    Ks = 0.7,        /* hilite brightness */
                    roughness = 0.1,
                    rim_width = 0.2,
                    glow_width = 0.2,
                    glow_hardness = 0.01,
                    spot_freq = 10,
                    innerGlowBrightness = 0.1;/* hilite spread */
            color     color1 = (0,0,1),
                    color2 = (1,0,0),
                    hilitecolor = 1,    /* hilite color */
                    edgecolor_thin = 1,
                    edgecolor_fat = 1,    
                    spotColor = 1,                                        
                    transparency = 1)
color    ambientcolor, diffusecolor, speccolor, fratio_thin, fratio_fat, 
        spotMult, spotColor2, transparency2, innerGlow,
        surfcolor, stripes = 1;
normal    n = normalize(N);
normal    nf = faceforward(n, I);
// Colored stripes
float stripesMult = noise(t * 40);
float stripesMult2 = noise(t)*2;
stripes = stripes * stripesMult;
color colorStripes = mix(color1,color2,(noise(t * 40) * stripesMult2));
colorStripes = colorStripes * (1 - s) * 5;
// Dark stripes
color darkStripes = clamp((noise(t *300) * .2),0,0.7);
// Light stripes - not connected
color lightStripes = (s * (s * 1.3)) * noise(t * 300 * .3) * 2;
// Spots
float spotsNoise = noise(s * spot_freq, t * spot_freq * 10);
if (spotsNoise < 0.5)
    spotMult = 0.8;
    spotMult = 1;
// Facing ratio stuff
vector i = normalize(-I);
float dot = 1 -;
// Thin
fratio_thin = smoothstep(1 - rim_width, 1.0, dot);
fratio_thin = edgecolor_thin * fratio_thin * (1 - s * 1.05) * 3;
// Fat
fratio_fat = smoothstep(1 - (rim_width*3), 1.0, dot);
fratio_fat = edgecolor_fat * fratio_fat * (1 - s * 1.05);
// Inner Glow
innerGlow = (1 - smoothstep(glow_hardness, glow_width, dot)) * innerGlowBrightness;
// Calculate lighting
vector V = -normalize(I);
color amb, diff, spec, sss = 0;
amb = Ka * ambient();
diff = Kd * diffuse(n) * colorStripes + fratio_thin + fratio_fat - darkStripes * spotMult + innerGlow;
spec = Ks * specular(n, V, roughness);
// Transparency
transparency2 = transparency * spotMult;
Ci = (amb + diff + sss) * Cs + spec;
Ci *= Os;
Oi = Os * transparency2;

jellyfishDisp(float Km = 0.1, /* displacement magnitude */
                    stripeFreq = 50)
float    hump = 0;
normal n = normalize(N);
hump = noise(t*stripeFreq);
P = P - n * hump * Km;
N = calculatenormal(P);
Here are some images with the various settings being adjusted (specifically those dealing with the inner glow):
  0.1 0.3 0.5 0.7 0.9
  0.01 0.1 0.2 0.3  


As you can see, this shader's settings can be tweaked to produce non-photorealistic, yet interesting, imagery. The following is a render that illustrates this idea by making a "toon" like look:



1) More controls for the user, specifically those regarding color variation, would be useful in a future release.

2) Testing of this shader on different models with varying complexity would be beneficial when debugging potential problems.

3) It would be fun to see this shader on a moving object to see how the facing ratio is affected when the rays are cast at different angles.


These caveats among others may be worth fixing if the time presents itself later in life.