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
.
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).
rand()
, basically).
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).
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.
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θ2Ninstead of
(n1/n2)(u - cosθ1N) - cosθ2NSee 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:
where
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
just the average of the material surface properties.
Kd = (diffuse[0] + diffuse[1] + diffuse[2])/3.0;
Ks = (specular[0] + specular[1] + specular[2])/3.0;
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.
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) |
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.
README
.
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
}
tar.gz
archive of your asg##/ directory, including:
README
file containing
Makefile
.h
headers and .cpp
source)
make clean
before tar
)
handin
notes