Class 2: Surface shaders
[Shader] programming

Shader programming involves computing quantities. In general, programming has to do with creating useful logical structures to solve problems. We do this using the language's syntax. Programming invariably involves the following:

In RSL we deal with quantities such as color, light intensity, points, normals, etc. whose values are stored (as all values are) in variables. Variables can be grouped as the following three types:

Using data from global (RMan's pre-defined) variables, RIB-derived (via shader call) and internal (local) variables, the shader writer computes and sets certain pre-defined global variables. This is the essence of shader programming.

Surface shaders

Here the idea is to calculate (set) values for Ci and Oi, which are pre-defined globals that mean "micropolygon color" and "micropolygon opacity" respectively.

Some RIB files you can use to test the shaders below:
SI teapot
Tribar illusion
Boy's surface



surface noop()
{

}
The above is the simplest possible surface shader. It has an empty "body". What are Ci and Oi set to?

surface constant()
{
    Oi = Os;
    Ci = Os * Cs;
}
Cs and Os are inherent surface color and opacity.. What look does the above shader produce?

surface red()
{
    Ci = color(1,0,0);
}
Pure red surface.

surface matte( float Ka=1, Kd=1 )
{
    normal Nf; // 'local' variable, normal 'type'

    Nf = faceforward(normalize(N),I);

    Oi = Os;
    Ci = Os * Cs * ( Ka*ambient() + Kd*diffuse(Nf) ) ;
}
Variables are of specific types in RSL. Available types are float, color, point, vector, normal, matrix, string. ambient() and diffuse() are 'built-in functions'. RSL has a rich, expressive set of such built-ins.

surface metal (float Ka=1, Ks=1, roughness=.1)
{
    normal Nf;
    vector V;

    Nf = faceforward(normalize(N), I) ;
    V = normalize(-I) ;

    Oi = Os;
    Ci = Os * Cs * ( Ka*ambient() + Ks*specular(Nf,V,roughness) );
}
Note that incoming parameters ("arguments") have (need) default values. Also, note that there is no formal return value! All output occurs through pre-determined globals (eg. Ci, Oi..).

surface plastic( float Ks=.5, Kd=.5, Ka=1, roughness=.1; color specularcolor=1 )
{
    normal Nf;
    vector V;

    Nf = faceforward( normalize(N), I );
    V = -normalize(I);

    Oi = Os;
    Ci = Os * ( Cs * (Ka*ambient() + Kd*diffuse(Nf)) +
	 	specularcolor * Ks * specular(Nf,V,roughness) );
}


// visualizes surface normals
surface showN()
{
  point PP;
  normal Nf;
  vector V;
  color Ct, Ot;

 
  normal NN = normalize(N);
  vector posNorm = 0.5*(vector(1,1,1)+NN);
  Oi = Os;
  Ci = color(comp(posNorm,0),comp(posNorm,1),comp(posNorm,2));
}


/* show_stb - Factors an object's color & opacity into (s,t,blue) */
surface show_stb(float blu=0.0)
{
      Oi = Os;
 Ci = Oi * Cs * (color "rgb" (s, t, blu));
}/* show_stb */


surface threePtDist(point P1=point(0,0,0), P2=point(0,0,1), P3=point(0,0,-1);float f1=1.0, f2=1.0, f3=1.0)
{
   point PP = transform("shader",P);

   float d1 = 0.5*(1.0 + sin(f1*distance(PP,P1)));
   float d2 = 0.5*(1.0 + sin(f2*distance(PP,P2)));
   float d3 = 0.5*(1.0 + sin(f3*distance(PP,P3)));

   Oi = Os;
   Ci = Cs*Oi*color(d1,d2,d3);

   
}


// randomly shades each micropoly grid!
surface rsg(color Cm=color(.5,.5,.5);float mixf=0.5)
{
    Ci = Cs*randomgrid() ;
    Ci = mix(Ci,Cm,mixf);
    Oi = Os;
}

Our next shader uses noise() calls to create a turbulence pattern.


// not used, is here just for study
float turb(varying point PP)
{
  varying point PPP = PP/2;	/* frequency adjustment (S-shaped curve) */
  float pixelsize = sqrt(area(PPP));
  float twice = 2 * pixelsize;
  float scale, weight, turbulence;
  
  /* compute turbulence */
  turbulence = 0;
  for (scale = 1; scale > twice; scale /= 2) {
    turbulence += scale * abs(noise(PPP/scale)-0.5);
  }
  /* gradually fade out of highest freq component near visibility limit */
  if (scale > pixelsize) {
    weight = (scale / pixelsize) - 1;
    weight = clamp(weight, 0, 1);
    turbulence += weight * scale * abs(noise(PPP/scale)-0.5);
  }

  float ct = turbulence;
  // float ct = clamp(turbulence,0.75,1);
  // ct -= 0.75;
  // ct *= 4.0;

  // printf("Turb: %f\n",ct);

  return ct;

}

// this simpler version is used instead of turb() above
float turbul(varying point PP)
{
  float t = noise(PP) + 0.5*(noise(2*PP)) + 0.25*(noise(4*PP)) + 0.125*(noise(8*PP));
  return (0.5*t);
}


surface smarble(float Ka=1, Kd=.6, Ks=0.4, roughness=.2,freq=0.1;)
{
  varying point PP;
  varying float cmi;
  varying normal Nf;
  varying vector V;
  color diffusecolor ;
  


  Nf = faceforward( normalize(N), I);
  V = -normalize(I);
  PP = transform("shader",P) * freq;

  float t = 0.5*(1+sin(15*3.1415*turbul(PP)));
  color base = color spline(t,
		color (0.8, 0.2, 0.05),
		color (0.8, 0.2, 0.05),
		color (0.8,0.5,0.3),
		color (0.6,0.594,0.58),
		color (0.3,0.3,0.4),
		color (0.05, 0.05, 0.1),
		color (0.8,0.79,0.77),
		color (0.8,0.8,0.79)
		);

  Ci = Os*base*(Ka*ambient() + Kd*diffuse(Nf) + Ks * specular(Nf,V,roughness));


}// smarble()

// Surface "smarble" "freq" 1.5 "Kd" .46 "Ks" 2.5 "Ka" .4 "roughness" .2

Here is a RIB file with an 'smarble' call.

The following is a simple texture mapping shader:
surface simpletex(
    string txnm="";
)
{
  Oi = Os;

  if(txnm!="")
      Ci = Oi*color texture(txnm,s,t);
  else
    Ci = Oi*Cs;
}// simpletex()
Use it with this RIB file and texture map.