/*
 *----- fjs_fisheyelens.sl
 *
 * Description:
 *   A near clip-plane shader that uses raytracing to provide
 *   a simple fisheye lens for the camera.
 *
 * Usage:
 *   User must define a square polygon just beyond the
 *   near clip-plane distance. Generally, this polygon
 *   fills the entire viewing frustrum.
 *
 *   This shader is applied to the polygon and the user
 *   should specify the polygon placement and the maximum
 *   angle of the lens.
 *
 *   The result is a circular lensed image on the square
 *   polygon, with black corners. The angular distribution
 *   is like polar graph paper, with angle increasing
 *   linearly with distance from the center.
 *
 *   Corners of polygon are assumed to be (in camera space):
 *     [ scale,  scale, zdistance]
 *     [-scale,  scale, zdistance]
 *     [-scale, -scale, zdistance]
 *     [ scale, -scale, zdistance]
 *
 *   RIB example:
 *     Clipping 0.099 1000.0
 *     Declare "lens_angle" "uniform float"
 *     Declare "zdistance" "uniform float"
 *     Declare "scale" "uniform float"
 *     Surface "fjs_fisheyelens" "lens_angle" [180] "zdistance" [0.1] "scale" [0.1]
 *     Polygon "P" [0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]
 *
 * History:
 *   12/17/01 - 1.0 - Cleaned up and documented - Frank Summers,
 *                    summers@SpamSucks_stsci.edu
 *
 */
surface fjs_fisheyelens (float lens_angle = 180.0;
                         float zdistance = 0.1;
                         float scale = 0.1)
{

/* Do not shade near clip-plane polygon more than once
 *
 *   The code below will begin the fisheye raytrace from the
 *   camera. The near clip-plane polygon will then be hit a
 *   second time by those rays.  Checking raylevel ensures
 *   that only rays originally from the camera will be shaded.
 */

  if (raylevel() > 0) {

    Oi = 0.0;
    Ci = color(0,0,0);

/* Otherwise, shade with fisheyelens */

  } else {

/* Transform the point being shaded into camera coordinates */

    varying point Pcam = transform("camera", P);

/* Generate coordinates relative to the center of the polygon */

    varying float ss = 0.5*xcomp(Pcam)/scale;
    varying float tt = 0.5*ycomp(Pcam)/scale;

/* Calculate distance from center of the polygon */

    varying float r = sqrt(ss*ss + tt*tt);

/* If point is outside of polygon filling circle, paint it opaque and black */

    if (r > 0.5) {
      Oi = 1.0;
      Ci = color(0,0,0);

/* Otherwise, calculate ray to trace */

    } else {

/* Angle increases linearly with distance from center */

      varying float polar_angle = radians(lens_angle)*r;

/* Direction is calculated from angle and shade point coordinates */

      varying float z = cos(polar_angle);

      varying float x = sin(polar_angle)*ss/r;
      varying float y = sin(polar_angle)*tt/r;

/* Set trace direction and start point (at camera) */

      varying vector tracedir = vector "camera" (x, y, z);
      varying point startpoint = point "camera" (0, 0, 0);
 
/* Call trace function to perform the raytrace */

      Oi = 1.0;
      Ci = trace(startpoint, tracedir);

    }
  }
}

