Asg 4: Photon Emission

Objectives

To implement photon emission, shooting caustic and global photons

Notes

Keep in mind that photons are just like rays, except that no new photons are spawned upoon reflection or transmission; rather the selfsame photon is itself reflected or transmitted throughout the scene until it sticks to a surface.

Assignment

  1. Before the parallel ray tracer render pass (before the nested for loops where rays are spawned and shot through the image lattice), declare an STL std::vector of pointers to photons, where each photon is of type photon_t.
  2. Shoot a (large) number of photons into the scene—a good object that could be made to do this would be the model_t, e.g., write a routine
    void model_t::shoot(std::vector<photon_t* >& photons)
    	
    whose job it is to fill up the std::vector array with photons that "stick" to surfaces. This routine emits two types of photons (say 250,000 each): caustic and global photons. Each of the photons are emitted from the the position of each light in the scene (e.g., the photon constructor accepts the position from where the photon is emitted).
  3. Photons are like rays. Caustic photons are similar to transmission rays in that they pass through transmissive objects. Global photons are similar to reflection rays in that they reflect off specular and diffuse objects. Both types of photons can also stick to surfaces besides reflecting or transmitting. Whether photons stick or not is determined stochastically via Monte Carlo simulation (for our purposes a call to rand(), basically).
  4. Because photons behave like rays, to re-use some of the ray functionalty, simply subclass (derive) the photon_t class from the ray_t class:
    class photon_t : public ray_t
    {
    ...
    };
    	
    The only additional private data member that photon requires is a vec_t to store the photon's power, initialized to (100.0,100.0,100.0).

    Take care to re-write the ray_t's private: data members as protected:.

    Don't forget to provide the C++ "big three" as well as overriding the friend std::ostream& operator<<() and friend std::istream& operator>>() operators (you'll need both to output and input photons for visualization purposes).

  5. Note that as photons are created (instantiated), their inherited data members (distance, position, and direction) contained in the parent ray_t class must be initialized—call the parent constructor with the position received by the photon_t constructor, but set the direction to the vec_t returned by the following routine:
    vec_t photon_t::genrand_hemisphere()
    {
            double  azimuth = genrand(0.0,2.0*M_PI);
            double  elevation = genrand(0.0,2.0*M_PI);
    
            double  sinA = sin(azimuth), sinE = sin(elevation);
            double  cosA = cos(azimuth), cosE = cos(elevation);
    
            vec_t   dir, vup;
    
      dir[0] = -sinA*cosE;  vup[0] =  sinA*sinE;
      dir[1] =  sinE;       vup[1] =  cosE;
      dir[2] =  cosA*cosE;  vup[2] = -cosA*sinE;
    
      return(dir + vup);
    }
    	
    where genrand() is declared as:
    double   genrand(double lo,double hi)
    { return( (double)(((double)rand()/(double)RAND_MAX)*hi + lo) ); }
    	
    Both can be made public or private to the photon_t class.
  6. You'll need two public photon_t member functions:
    bool photon_t::caustic(model_t& model, int bounce);
    bool photon_t::global(model_t& model, int bounce);
    	
    Both functions are similar to the ray_t::trace() routine, except that they return a bool variable. The caustic function sends the photon through a transmissive object if one is encountered, otherwise the photon sticks to the hit surface. A photon shoots itself recursively through a transparent object by resetting its position and direction to the hit point and transmission ray direction. If an object is hit that is not transmissive, the caustic routine returns false.

    Note that the caustic routine refracts photons in almost the same way as rays, but with one small but significant change: rays are refracted via

    (n1/n2)(u + cosθ1N) - cosθ2N
    	
    instead of
    (n1/n2)(u - cosθ1N) - cosθ2N
    	
    See the sign change difference?

    The global function does one of three things, based on the value of a randomly generated number between 0 and 1, e.g., roulette = genrand(0.0,1.0), follwing this algorithm:

        if( (0.0 < roulette) && (roulette < Kd) ) {
          // diffuse reflection:
          // set pos and dir to hit and genrand_hemisphere().norm();
          // reflect this photon
        } else if( (Kd < roulette) && (roulette < Kd+Ks) ) {
          // specular reflection:
          // set pos and dir to hit and dir.reflect(N);
          // reflect this photon
        } else if( (Kd+Ks < roulette) && (roulette < 1.0) ) {
          // absorbtion ("stick" photon):
          // set pos to hit
          // return true
    	
    where
        Kd = (diffuse[0] + diffuse[1] + diffuse[2])/3.0;
        Ks = (specular[0] + specular[1] + specular[2])/3.0;
    	
    just the average of the material surface properties.
  7. Back in main() once you have everything else put together, and have shot all photons, write them all out to a file so that each photon's x, y, z values are written out per line.
  8. Use a model.txt that exercises photon transmission, it is a good idea to eliminate all but one light, and then move it around in relation to the transparent sphere. The photons focused beneath the sphere should move accordingly, as seen below, rendered by the Qt visualizer developed in lab.

    caustic photons (250,000)

    global photons (250,000)

    both types of photons (500,000)

Optional

  1. Besides the model.txt file that is used for grading purposes, come up with your own scene(s)—feel free to try different materials, colors, arrangements of objects in the scene.
  2. Set up a web page with sample images, be sure to include the URL in your README.

Input

  1. The airball.txt file used to generate the above image is:
    camera cam1
    {
       pixeldim 640 480
       worlddim 8   6
       viewpoint 4 3 6
    }
    
    light topright
    {
       location 4 4.3 1
       emissivity 1 1 1
    }
    
    material green
    {
       ambient 0 5 0
    }
    
    material red
    {
       ambient 5 0 0
       diffuse  .7 .7 .7
      specular  .3 .3 .3
    }
    
    material blue
    {
       ambient 0 0 5
       diffuse  .2 .2 .2
      specular  .8 .8 .8
    }
    
    material cyan
    {
       ambient 0 4 5
       diffuse  .2 .2 .2
      specular  .8 .8 .8
        alpha  .4
          ior  1.000293
    }
    
    material yellow
    {
       ambient  5 4 0
       diffuse  .7 .7 .7
      specular  .3 .3 .3
    }
    
    plane leftwall
    {
       material green
       normal 3 0 1
       point  0 0 0
    }
    
    plane rightwall
    {
       material yellow
       normal -3 0 1
       point   8 0 0
    }
    
    material gray
    {
       ambient 2 2 2
    }
    
    plane floor
    {
       material gray
       normal 0 1 0
       point  0 -0.2 0
    }
    
    sphere upperright
    {
       material red
       center 6 4 -2
       radius 2
    }
    
    sphere central
    {
       material blue
       center 2 2 -2
       radius 1.3
    }
    
    sphere vitreous
    {
       material cyan
       center 4 2 -1
       radius 2
    }
    	

Turn in

Turn in all of your code, in one tar.gz archive of your asg##/ directory, including:
  1. A README file containing
    1. Course id--section no
    2. Name
    3. Lab description
    4. Brief solution description (e.g., program design, description of algorithm, etc., however appropriate).
    5. Lessons learned, identified interesting features of your program
    6. Any special usage instructions
  2. Makefile
  3. source code (.h headers and .cpp source)
  4. object code (do a make clean before tar)

How to hand in

See handin notes