Home > Shaders

Plasma Shader

The inspiration for this shader came from some artwork created using a strange attractor particle simulation. These images feature bands of plasma-like energy which are extremely bright in areas and then fade off into grainy, sublime trails. The images, created by Richard Baily, are available here. Baily uses advanced proprietary software to render out over 400,000,000 particles to create the effect. A friend of mine (Tom) suggested creating a surface shader to approximate the same effect. So, I gave it a shot and below are my first attempts after about four hours of coding work.

The above image features the shame shader, applied three times on a sphere. Each shader was given slightly different parameters to exhibit the wide range of possiblities. The shader is simply a line drawing algorithm with a fractal function which draws smaller and more detailed lines. These lines leave a trailing edge, created by ramping the transparency on one side of the line as the shading point approaches the line. Then, different amplitudes and frequencies of noise may be added to create the boiling color effect. Around 20 input variables are used to control every aspect of the shader including recusion depth, hue, noise, falloffs, banding tightness, and banding direction. While the shader seems terribly complex, the basic idea was using many layers of detail with noise. All the layers are similar and thus can be created with a simple loop. In reality, the shader is fairly effecient while providing a wide range of control and very interesting output.

Of course, this look is still rather old-school. Fractal art has been around for a while and early computer art is saturated with the stuff. Why make it then? Well, I think a big part of being a good artist is making do with what is available. For modern artists, this often meant appropriation. For me, I'm a nerd, and what is available to me is math functions and such, so I create art with what I have at hand : ) Really though, assembling a knowledge of mathematics provides computer artists with a toolbox that they can reach into, pull out a tool, and connect it together with other things. This is the way this shader was basically created, by combining mathematic functions until I got something I liked. In that way, it's similar to a collage, where pieces of commercial art are combined together to form a composition. The mathematic functions already exist, I simply assemble them.

This image shows the shader applied to a torace.

Here is an attempt to use the opacity of the image to create a displacement map. The overall form of the shader was too clean and looked a lot like a marble. So, I thought adding displacement would help. However, it seems that a more complicated scheme than just using displacement is necessary to achieve a more refined look.

Bird of Paradise - an application of plasma shader to NPR rendering, simulating brush work. Modeled in Maya. Rendered in MTOR.

 

Here's the source code (which isn't really fit for human consumption).

#define snoise(x) (2*noise(x) -1)
/* Shader description goes here */
   surface
   plasma(float Kd = 1, /* basic brightness */
   jPower = 1.1, /*affects how different layers are from each other */
   centerS = 0.5,
   centerT = 0.5,
   rotationK = 0.1,
   fallOffK = 30,
   tightnessK = 10,
   minDepth = 2,
   maxDepth = 40,
   beginningHue = 0.17,
   endingHue = -0.2,
   lineNoiseFreq = 100,
   lineNoiseAmp = 0.005,
   flareNoiseFreq = 200,
   flareNoiseAmp = 0.1,
   displacementK = 0.1;
   vector flareDirection = (1,0,0);)
   {
   normal nf, n;
   color diffusecolor,
   surfcolor = 1, surfopac = Os;
/* STEP 1 - make a copy of the surface normal one unit in length */
   n = normalize(N);
   nf = faceforward(n, I);
/* STEP 2 - set the apparent surface opacity */
   Oi = surfopac;
/* STEP 3 - calculate the diffuse lighting component */
   float ss, tt;
   float sr, tr;
   float j = 1;
   float layerOpacity = 0;
   color layerColor = 0;
   color totalColor = 0;
   float totalOpacity = 0;
   vector direction = flareDirection;
   float overallScale = 1;
   direction = normalize(direction) * overallScale;
for (j = minDepth; j < maxDepth; j = j + 1)
   {
   /*rotate the current pattern */
   float x = s * - centerS; /*find the distance from the center */
   float y = t * - centerT;
   float theta = atan(x/y); /* find the angle of the current point */
   float jj = pow(j,jPower);
   theta += jj * rotationK; /* increment theta according to the current depth */
   float radius = sqrt((x*x) + (y*y));
   sr = radius * sin(theta); /*find the new s after rotation */
   tr = radius * cos(theta); /*find the new t after rotation */
   
   ss = (s * comp(direction,0)) + (t * comp(direction,1));
   ss = (jj * sr * sqrt(ss)) - floor(jj * sr* sqrt(ss));
   
   
   float r = ss - 0.5 + (snoise(P * lineNoiseFreq)*lineNoiseAmp);
   if (r > 0) layerOpacity = (max(pow(cos(r * 2),fallOffK),pow(1.07 / (abs(r * tightnessK) + 1),30)) 
   * (1/(jj * 0.4))) + (snoise(P*flareNoiseFreq) * flareNoiseAmp);
   else layerOpacity = pow(1.07 / (abs(r * tightnessK) + 1),30) * (1/(jj * 0.4));
   float jratio = (j - minDepth) / ( maxDepth - minDepth);
   float layerHue = mix(beginningHue, endingHue, jratio);
   layerColor = color "hsv" (layerHue, (noise(P) * 0.1) + 0.8, 1.0);
   /*layerOpacity = min ( 1 - totalOpacity , layerOpacity);*/
   if (totalOpacity > 0) layerOpacity = max((1 - totalOpacity) * layerOpacity,0);
   totalColor = mix(totalColor, layerColor, layerOpacity); 
   totalOpacity = min(totalOpacity + layerOpacity, 1);
   }
/* displace the most opaque sections */
float spaceScale = length(transform("shader", nf));
   vector Ndisp = nf * pow(totalOpacity,2) / spaceScale;
   P += (Ndisp * displacementK);
   N = normalize(calculatenormal(P + Ndisp)); 
Oi = totalOpacity;
   Ci = totalColor * totalOpacity;
   }